diff options
51 files changed, 607 insertions, 237 deletions
diff --git a/PermissionController/res/values-ar/strings.xml b/PermissionController/res/values-ar/strings.xml index 5381d11b4..e22ec0182 100644 --- a/PermissionController/res/values-ar/strings.xml +++ b/PermissionController/res/values-ar/strings.xml @@ -475,7 +475,7 @@ <string name="permgrouprequest_device_aware_storage_isolated" msgid="6463062962458809752">"هل تريد السماح لـ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> بالوصول إلى الصور والوسائط على <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>؟"</string> <string name="permgrouprequest_contacts" msgid="8391550064551053695">"هل تريد السماح لتطبيق <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> بالوصول إلى جهات الاتصال؟"</string> <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"هل تريد السماح لـ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> بالوصول إلى جهات اتصالك على <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>؟"</string> - <string name="permgrouprequest_location" msgid="6990232580121067883">"هل تريد السماح لتطبيق <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> بالوصول إلى الموقع الجغرافي لهذا الجهاز؟"</string> + <string name="permgrouprequest_location" msgid="6990232580121067883">"هل مطلوب السماح لتطبيق <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> بالوصول إلى الموقع الجغرافي لهذا الجهاز؟"</string> <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"هل تريد السماح لـ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> بالوصول إلى الموقع الجغرافي الخاص بـ <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>؟"</string> <string name="permgrouprequestdetail_location" msgid="2635935335778429894">"لن يكون بإمكان التطبيق الوصول إلى الموقع الجغرافي إلا عند استخدامك لهذا التطبيق."</string> <string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"هل تريد السماح لتطبيق <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> بالوصول إلى الموقع الجغرافي لهذا الجهاز؟"</string> @@ -495,7 +495,7 @@ <string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"تقريبي"</string> <string name="permgrouprequest_calendar" msgid="1493150855673603806">"هل تريد السماح لتطبيق <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> بالوصول إلى التقويم؟"</string> <string name="permgrouprequest_device_aware_calendar" msgid="7161929851377463612">"هل تريد السماح لـ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> بالوصول إلى تقويمك على <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>؟"</string> - <string name="permgrouprequest_sms" msgid="5672063688745420991">"هل تريد السماح لتطبيق <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> بإرسال رسائل SMS وعرضها؟"</string> + <string name="permgrouprequest_sms" msgid="5672063688745420991">"هل مطلوب السماح لتطبيق <xliff:g id="APP_NAME">%1$s</xliff:g> بإرسال رسائل SMS وعرضها؟"</string> <string name="permgrouprequest_device_aware_sms" msgid="6639977653040502291">"هل تريد السماح لـ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> بإرسال الرسائل القصيرة وعرضها على <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>؟"</string> <string name="permgrouprequest_storage" msgid="8717773092518621602">"هل تريد السماح لتطبيق <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> بالوصول إلى الصور والوسائط والملفات على جهازك؟"</string> <string name="permgrouprequest_device_aware_storage" msgid="6933251810928606636">"هل تريد السماح لـ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> بالوصول إلى الصور والوسائط والملفات على <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>؟"</string> diff --git a/PermissionController/res/values-cs/strings.xml b/PermissionController/res/values-cs/strings.xml index 8ca485fbe..a48d5afb9 100644 --- a/PermissionController/res/values-cs/strings.xml +++ b/PermissionController/res/values-cs/strings.xml @@ -383,7 +383,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í plochu na zařízení Android a dávají vám přístup\\nk 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> diff --git a/PermissionController/res/values-hu/strings.xml b/PermissionController/res/values-hu/strings.xml index 997dc71e2..038f0161f 100644 --- a/PermissionController/res/values-hu/strings.xml +++ b/PermissionController/res/values-hu/strings.xml @@ -495,7 +495,7 @@ <string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"Hozzávetőleges"</string> <string name="permgrouprequest_calendar" msgid="1493150855673603806">"Engedélyezi a(z) <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> számára, hogy hozzáférjen a naptárhoz?"</string> <string name="permgrouprequest_device_aware_calendar" msgid="7161929851377463612">"Engedélyezi a(z) <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> számára, hogy hozzáférjen a naptárához ezen az eszközön: <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g>’s</b>?"</string> - <string name="permgrouprequest_sms" msgid="5672063688745420991">"Engedélyezi a(z) <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> számára, hogy SMS-eket küldhessen és tekinthessen meg?"</string> + <string name="permgrouprequest_sms" msgid="5672063688745420991">"Engedélyezi a(z) <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> számára SMS-ek küldését és megtekintését?"</string> <string name="permgrouprequest_device_aware_sms" msgid="6639977653040502291">"Engedélyezi a(z) <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> számára az SMS-ek küldését és megtekintését ezen az eszközön: <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> <string name="permgrouprequest_storage" msgid="8717773092518621602">"Engedélyezi a(z) <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> 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) <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> számára, hogy fotókhoz, médiatartalmakhoz és fájlokhoz férjen hozzá ezen az eszközön: <b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>?"</string> diff --git a/PermissionController/res/values-ja/strings.xml b/PermissionController/res/values-ja/strings.xml index 43e204b69..0be93d464 100644 --- a/PermissionController/res/values-ja/strings.xml +++ b/PermissionController/res/values-ja/strings.xml @@ -491,8 +491,8 @@ <string name="permgrouprequest_device_aware_fineupgrade" msgid="4453775952305587571">"<b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>の位置情報に対する <b><xliff:g id="APP_NAME"><b>%1$s</b></xliff:g></b> のアクセス権を「おおよそ」から「正確」に変更しますか?"</string> <string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"このデバイスのおおよその位置情報へのアクセスを <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> に許可しますか?"</string> <string name="permgrouprequest_device_aware_coarselocation" msgid="8367540370912066757">"<b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>のおおよその位置情報へのアクセスを <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> に許可しますか?"</string> - <string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"正確"</string> - <string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"おおよそ"</string> + <string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"正確な位置"</string> + <string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"おおよその位置"</string> <string name="permgrouprequest_calendar" msgid="1493150855673603806">"カレンダーへのアクセスを「<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>」に許可しますか?"</string> <string name="permgrouprequest_device_aware_calendar" msgid="7161929851377463612">"<b><xliff:g id="DEVICE_NAME">%2$s</xliff:g></b>内のカレンダーへのアクセスを <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> に許可しますか?"</string> <string name="permgrouprequest_sms" msgid="5672063688745420991">"SMS メッセージの送信と表示を「<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>」に許可しますか?"</string> diff --git a/PermissionController/res/values-mk/strings.xml b/PermissionController/res/values-mk/strings.xml index 5f2735642..d8ba89c77 100644 --- a/PermissionController/res/values-mk/strings.xml +++ b/PermissionController/res/values-mk/strings.xml @@ -628,7 +628,7 @@ <string name="safety_center_background_location_access_reminder_summary" msgid="7431657777510537658">"Апликацијава може секогаш да пристапува до вашата локација, дури и кога е затворена.\n\nНа одредени апликации за безбедност и итни случаи им е потребен пристап до вашата локација во заднината за правилно да функционираат."</string> <string name="safety_center_background_location_access_revoked" msgid="6972274943343442213">"Пристапот е променет"</string> <string name="safety_center_view_recent_location_access" msgid="3524391299490678243">"Погледнете го неодамнешното користење на локацијата"</string> - <string name="privacy_controls_title" msgid="7605929972256835199">"Контроли на приватноста"</string> + <string name="privacy_controls_title" msgid="7605929972256835199">"Контроли за приватност"</string> <string name="camera_toggle_title" msgid="1251201397431837666">"Пристап до камерата"</string> <string name="mic_toggle_title" msgid="2649991093496110162">"Пристап до микрофонот"</string> <string name="perm_toggle_description" msgid="7801326363741451379">"За апликации и услуги"</string> diff --git a/PermissionController/res/values-nb/strings.xml b/PermissionController/res/values-nb/strings.xml index e2930ac8a..3835d7573 100644 --- a/PermissionController/res/values-nb/strings.xml +++ b/PermissionController/res/values-nb/strings.xml @@ -443,7 +443,7 @@ <string name="default_app_recommended" msgid="5669584821778942909">"Optimalisert for enheten"</string> <string name="default_app_others" msgid="7793029848126079876">"Andre"</string> <string name="default_app_none" msgid="9084592086808194457">"Ingen"</string> - <string name="default_app_system_default" msgid="6218386768175513760">"(System-&shy;standard)"</string> + <string name="default_app_system_default" msgid="6218386768175513760">"(System-&#173;standard)"</string> <string name="default_app_no_apps" msgid="115720991680586885">"Ingen apper"</string> <string name="default_payment_app_other_nfc_services" msgid="5957633798695758917">"Andre NFC-tjenester"</string> <string name="car_default_app_selected" msgid="5416420830430644174">"Valgt"</string> diff --git a/PermissionController/res/values-pl/strings.xml b/PermissionController/res/values-pl/strings.xml index 8316ac98c..0aefe4a69 100644 --- a/PermissionController/res/values-pl/strings.xml +++ b/PermissionController/res/values-pl/strings.xml @@ -348,7 +348,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 dostępu żadnym aplikacjom"</string> + <string name="no_apps_denied" msgid="7663435886986784743">"Nie odmówiono 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> diff --git a/PermissionController/res/values-ru-v33/strings.xml b/PermissionController/res/values-ru-v33/strings.xml index b45662712..5506ccd97 100644 --- a/PermissionController/res/values-ru-v33/strings.xml +++ b/PermissionController/res/values-ru-v33/strings.xml @@ -41,7 +41,7 @@ <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_open_action" msgid="2760200829912423728">"Открыть"</string> - <string name="safety_center_review_settings_button" msgid="938981137942443930">"Перейти в настройки"</string> + <string name="safety_center_review_settings_button" msgid="938981137942443930">"Открыть настройки"</string> <string name="safety_center_gear_label" msgid="5175877094379694098">"Настройки"</string> <string name="safety_center_info_label" msgid="8993181584061825412">"Информация"</string> </resources> diff --git a/PermissionController/res/xml/roles.xml b/PermissionController/res/xml/roles.xml index 0f9ff7112..4ba524462 100644 --- a/PermissionController/res/xml/roles.xml +++ b/PermissionController/res/xml/roles.xml @@ -902,6 +902,8 @@ <permission name="android.permission.READ_SMS" minSdkVersion="33" /> <permission name="android.permission.READ_PEOPLE_DATA" /> <permission name="android.permission.READ_GLOBAL_APP_SEARCH_DATA" /> + <permission name="android.permission.READ_BLOCKED_NUMBERS" + featureFlag="android.permission.flags.Flags.grantReadBlockedNumbersToSystemUiIntelligence" /> </permissions> </role> 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 1b7617b0d..6a7251563 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 @@ -26,7 +26,6 @@ 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.UserHandle; @@ -505,8 +504,14 @@ public class Role { && Build.VERSION.SDK_INT <= mMaxSdkVersion; } - public boolean isStatic() { - return mStatic; + /** + * Check whether this role is static, which may change due to bypassing qualification. + * + * @param context the {@code Context} to retrieve system services + * @return whether this role is static + */ + public boolean isStatic(@NonNull Context context) { + return mStatic && !isBypassingQualification(context); } /** @@ -621,6 +626,12 @@ public class Role { return mAllowBypassingQualification; } + private boolean isBypassingQualification(@NonNull Context context) { + RoleManager roleManager = context.getSystemService(RoleManager.class); + return shouldAllowBypassingQualification(context) + && RoleManagerCompat.isBypassingRoleQualification(roleManager); + } + /** * Check whether a package is qualified for this role, i.e. whether it contains all the required * components (plus meeting some other general restrictions). @@ -633,9 +644,7 @@ public class Role { */ public boolean isPackageQualifiedAsUser(@NonNull String packageName, @NonNull UserHandle user, @NonNull Context context) { - RoleManager roleManager = context.getSystemService(RoleManager.class); - if (shouldAllowBypassingQualification(context) - && RoleManagerCompat.isBypassingRoleQualification(roleManager)) { + if (isBypassingQualification(context)) { return true; } @@ -1107,7 +1116,8 @@ public class Role { // all users List<UserHandle> profiles = (crossUserRoleUxBugfixEnabled && getExclusivity() == EXCLUSIVITY_PROFILE_GROUP) - ? UserUtils.getUserProfiles(context, true) : List.of(user); + ? UserUtils.getUserProfiles(user, context, true) + : List.of(user); final int profilesSize = profiles.size(); for (int i = 0; i < profilesSize; i++) { UserHandle profile = profiles.get(i); diff --git a/PermissionController/role-controller/java/com/android/role/controller/service/RoleControllerServiceImpl.java b/PermissionController/role-controller/java/com/android/role/controller/service/RoleControllerServiceImpl.java index d00fd47af..1a0c83ab8 100644 --- a/PermissionController/role-controller/java/com/android/role/controller/service/RoleControllerServiceImpl.java +++ b/PermissionController/role-controller/java/com/android/role/controller/service/RoleControllerServiceImpl.java @@ -173,7 +173,7 @@ public class RoleControllerServiceImpl extends RoleControllerService { // or fallback holders, if any. currentPackageNames = mUserRoleManager.getRoleHolders(roleName); currentPackageNamesSize = currentPackageNames.size(); - boolean isStaticRole = role.isStatic(); + boolean isStaticRole = role.isStatic(mContext); if (currentPackageNamesSize == 0 || isStaticRole) { List<String> packageNamesToAdd = null; if (addedRoleNames.contains(roleName) || isStaticRole) { 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 00c05b17c..41233a23e 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 @@ -139,14 +139,16 @@ public final class UserUtils { /** * Returns all the enabled user profiles on the device * + * @param user the {@link UserHandle} to get profiles for * @param context the {@link Context} * @param excludePrivate {@code true} to exclude private profiles from returned list of users */ @NonNull - public static List<UserHandle> getUserProfiles(@NonNull Context context, - boolean excludePrivate) { - UserManager userManager = context.getSystemService(UserManager.class); - List<UserHandle> profiles = userManager.getUserProfiles(); + public static List<UserHandle> getUserProfiles(@NonNull UserHandle user, + @NonNull Context context, boolean excludePrivate) { + Context userContext = getUserContext(context, user); + UserManager userUserManager = userContext.getSystemService(UserManager.class); + List<UserHandle> profiles = userUserManager.getUserProfiles(); if (!excludePrivate) { return profiles; } @@ -154,7 +156,7 @@ public final class UserUtils { final int profilesSize = profiles.size(); for (int i = 0; i < profilesSize; i++) { UserHandle profile = profiles.get(i); - if (!isPrivateProfile(profile, context)) { + if (!isPrivateProfile(profile, userContext)) { filteredProfiles.add(profile); } } diff --git a/PermissionController/src/com/android/permissioncontroller/role/Role.md b/PermissionController/src/com/android/permissioncontroller/role/Role.md index 8d995d557..29a184190 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/Role.md +++ b/PermissionController/src/com/android/permissioncontroller/role/Role.md @@ -55,14 +55,13 @@ apps, separated by a colon (`:`) with the package name, for instance receive short text messages, photos, videos, and more". For default apps, this string will appear in the default app detail page as a footer. This attribute is required if the role is `visible`. - `exclusive`: Whether the role is exclusive. If a role is exclusive, at most one application is -allowed to be its holder. +allowed to be its holder. This attribute is being deprecated and `exclusivity` should be used. - `exclusivity`: Whether the role is exclusive and what type of exclusivity behavior it has. A role can have exclusivity of `none`, `user`, or `profileGroup`. - `none`: Role allows multiple holders - `user`: Role allows at most one holder within each user - - `profileGroup`: (SDK 36+ only, fallsback to `user` on lower SDK) Role allows at most one holder -within a profile group (e.g. full user and work -profile) + - `profileGroup`: (SDK 36+ only, falls back to `user` on lower SDK) Role allows at most one holder +within a profile group (e.g. full user and work profile) - `fallBackToDefaultHolder`: Whether the role should fall back to the default holder. This attribute is optional and defaults to `false`. - `featureFlag`: Optional feature flag for the role be available, as the fully qualified name of diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppChildFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppChildFragment.java index 268633c4f..814a312c6 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppChildFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppChildFragment.java @@ -16,6 +16,8 @@ package com.android.permissioncontroller.role.ui; +import static com.android.permissioncontroller.PermissionControllerStatsLog.ROLE_SETTINGS_FRAGMENT_ACTION_REPORTED; + import android.app.Activity; import android.app.role.RoleManager; import android.content.Context; @@ -41,6 +43,7 @@ import androidx.preference.TwoStatePreference; 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.utils.Utils; import com.android.permissioncontroller.role.utils.PackageUtils; @@ -48,6 +51,7 @@ import com.android.permissioncontroller.role.utils.RoleUiBehaviorUtils; import com.android.permissioncontroller.role.utils.SettingsCompat; import com.android.role.controller.model.Role; import com.android.role.controller.model.Roles; +import com.android.settingslib.utils.applications.AppUtils; import java.util.List; import java.util.Objects; @@ -77,8 +81,8 @@ public class DefaultAppChildFragment<PF extends PreferenceFragmentCompat DefaultAppChildFragment.class.getName() + ".preference.OTHER_NFC_SERVICES"; private static final String PREFERENCE_EXTRA_PACKAGE_NAME = DefaultAppChildFragment.class.getName() + ".extra.PACKAGE_NAME"; - private static final String PREFERENCE_EXTRA_USER = DefaultAppChildFragment.class.getName() - + ".extra.USER"; + private static final String PREFERENCE_EXTRA_UID = DefaultAppChildFragment.class.getName() + + ".extra.UID"; @NonNull private String mRoleName; @@ -302,10 +306,13 @@ public class DefaultAppChildFragment<PF extends PreferenceFragmentCompat // the `NONE` item passes a null applicationinfo object. NFC uses a different preference // method for adding, and a different onclick method if (applicationInfo != null) { + UserHandle user = UserHandle.getUserHandleForUid(applicationInfo.uid); + roleApplicationPreference.setContentDescription( + AppUtils.getAppContentDescription( + context, applicationInfo.packageName, user.getIdentifier())); Bundle extras = preference.getExtras(); extras.putString(PREFERENCE_EXTRA_PACKAGE_NAME, applicationInfo.packageName); - extras.putParcelable(PREFERENCE_EXTRA_USER, - UserHandle.getUserHandleForUid(applicationInfo.uid)); + extras.putInt(PREFERENCE_EXTRA_UID, applicationInfo.uid); } } else { preference = roleApplicationPreference.asTwoStatePreference(); @@ -348,24 +355,25 @@ public class DefaultAppChildFragment<PF extends PreferenceFragmentCompat } else { String packageName = preference.getExtras().getString(PREFERENCE_EXTRA_PACKAGE_NAME); - UserHandle user = - preference.getExtras().getParcelable(PREFERENCE_EXTRA_USER); + int uid = preference.getExtras().getInt(PREFERENCE_EXTRA_UID); CharSequence confirmationMessage = RoleUiBehaviorUtils.getConfirmationMessage(mRole, packageName, requireContext()); if (confirmationMessage != null) { - DefaultAppConfirmationDialogFragment.show(packageName, user, confirmationMessage, + DefaultAppConfirmationDialogFragment.show(packageName, uid, confirmationMessage, this); } else { - setDefaultApp(packageName, user); + setDefaultApp(packageName, uid); } } return true; } @Override - public void setDefaultApp(@NonNull String packageName, @NonNull UserHandle user) { - mViewModel.setDefaultApp(packageName, user); + public void setDefaultApp(@NonNull String packageName, int uid) { + PermissionControllerStatsLog.write( + ROLE_SETTINGS_FRAGMENT_ACTION_REPORTED, uid, packageName, mRoleName); + mViewModel.setDefaultApp(packageName, UserHandle.getUserHandleForUid(uid)); } private void addNonPaymentNfcServicesPreference(@NonNull PreferenceScreen preferenceScreen, diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppConfirmationDialogFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppConfirmationDialogFragment.java index 5f399a0b8..9a9606291 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppConfirmationDialogFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppConfirmationDialogFragment.java @@ -20,11 +20,9 @@ import android.app.AlertDialog; import android.app.Dialog; import android.content.Intent; import android.os.Bundle; -import android.os.UserHandle; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.core.os.BundleCompat; import androidx.fragment.app.DialogFragment; import androidx.fragment.app.Fragment; @@ -34,27 +32,27 @@ import androidx.fragment.app.Fragment; public class DefaultAppConfirmationDialogFragment extends DialogFragment { private String mPackageName; - private UserHandle mUser; + private int mUid; private CharSequence mMessage; /** * Create a new instance of this fragment. * * @param packageName the package name of the application - * @param user the user the specified package is running in + * @param uid the UID the specified package is running in * @param message the confirmation message * * @return a new instance of this fragment * - * @see #show(String, UserHandle, CharSequence, Fragment) + * @see #show(String, int, CharSequence, Fragment) */ @NonNull public static DefaultAppConfirmationDialogFragment newInstance(@NonNull String packageName, - @NonNull UserHandle user, @NonNull CharSequence message) { + int uid, @NonNull CharSequence message) { DefaultAppConfirmationDialogFragment fragment = new DefaultAppConfirmationDialogFragment(); Bundle arguments = new Bundle(); arguments.putString(Intent.EXTRA_PACKAGE_NAME, packageName); - arguments.putParcelable(Intent.EXTRA_USER, user); + arguments.putInt(Intent.EXTRA_UID, uid); arguments.putCharSequence(Intent.EXTRA_TEXT, message); fragment.setArguments(arguments); return fragment; @@ -64,15 +62,15 @@ public class DefaultAppConfirmationDialogFragment extends DialogFragment { * Show a new instance of this fragment. * * @param packageName the package name of the application - * @param user the user the specified package is running in + * @param uid the UID the specified package is running in * @param message the confirmation message * @param fragment the parent fragment * - * @see #newInstance(String, UserHandle, CharSequence) + * @see #newInstance(String, int, CharSequence) */ - public static void show(@NonNull String packageName, @NonNull UserHandle user, + public static void show(@NonNull String packageName, int uid, @NonNull CharSequence message, @NonNull Fragment fragment) { - newInstance(packageName, user, message).show(fragment.getChildFragmentManager(), null); + newInstance(packageName, uid, message).show(fragment.getChildFragmentManager(), null); } @Override @@ -81,7 +79,7 @@ public class DefaultAppConfirmationDialogFragment extends DialogFragment { Bundle arguments = getArguments(); mPackageName = arguments.getString(Intent.EXTRA_PACKAGE_NAME); - mUser = BundleCompat.getParcelable(arguments, Intent.EXTRA_USER, UserHandle.class); + mUid = arguments.getInt(Intent.EXTRA_UID); mMessage = arguments.getCharSequence(Intent.EXTRA_TEXT); } @@ -97,7 +95,7 @@ public class DefaultAppConfirmationDialogFragment extends DialogFragment { private void onOk() { Listener listener = (Listener) getParentFragment(); - listener.setDefaultApp(mPackageName, mUser); + listener.setDefaultApp(mPackageName, mUid); } /** @@ -110,6 +108,6 @@ public class DefaultAppConfirmationDialogFragment extends DialogFragment { * * @param packageName the package name of the application */ - void setDefaultApp(@NonNull String packageName, @NonNull UserHandle user); + void setDefaultApp(@NonNull String packageName, int uid); } } diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListChildFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListChildFragment.java index 2a987167e..fd91d0d3b 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListChildFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListChildFragment.java @@ -40,10 +40,12 @@ import androidx.preference.PreferenceScreen; import com.android.modules.utils.build.SdkLevel; import com.android.permissioncontroller.R; import com.android.permissioncontroller.permission.utils.Utils; +import com.android.permissioncontroller.role.UserPackage; 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.settingslib.utils.applications.AppUtils; import java.util.List; @@ -241,10 +243,16 @@ public class DefaultAppListChildFragment<PF extends PreferenceFragmentCompat if (holderApplicationInfos.isEmpty()) { preference.setIcon(null); preference.setSummary(R.string.default_app_none); + rolePreference.setSummaryContentDescription(null); } else { ApplicationInfo holderApplicationInfo = holderApplicationInfos.get(0); preference.setIcon(Utils.getBadgedIcon(context, holderApplicationInfo)); preference.setSummary(Utils.getAppLabel(holderApplicationInfo, context)); + UserPackage userPackage = UserPackage.from(holderApplicationInfo); + rolePreference.setSummaryContentDescription( + AppUtils.getAppContentDescription(context, + userPackage.packageName, + userPackage.user.getIdentifier())); } RoleUiBehaviorUtils.preparePreferenceAsUser(role, holderApplicationInfos, rolePreference, user, context); diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleFragment.java index 4731c8b0f..89a17004b 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleFragment.java +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleFragment.java @@ -59,6 +59,7 @@ import com.android.permissioncontroller.role.utils.UiUtils; import com.android.permissioncontroller.role.utils.UserUtils; import com.android.role.controller.model.Role; import com.android.role.controller.model.Roles; +import com.android.settingslib.utils.applications.AppUtils; import java.util.ArrayList; import java.util.List; @@ -698,6 +699,7 @@ public class RequestRoleFragment extends DialogFragment { Drawable icon; String title; String subtitle; + String contentDescription; if (applicationItem == null) { applicationInfo = null; restricted = false; @@ -706,6 +708,7 @@ public class RequestRoleFragment extends DialogFragment { title = context.getString(R.string.default_app_none); subtitle = mHolderUserPackage == null ? context.getString( R.string.request_role_current_default) : null; + contentDescription = null; } else { applicationInfo = applicationItem.getApplicationInfo(); UserPackage userPackage = UserPackage.from(applicationInfo); @@ -717,14 +720,18 @@ public class RequestRoleFragment extends DialogFragment { subtitle = applicationItem.isHolderApplication() ? context.getString(R.string.request_role_current_default) : checked ? context.getString(mRole.getRequestDescriptionResource()) : null; + contentDescription = AppUtils.getAppContentDescription(context, + userPackage.packageName, userPackage.user.getIdentifier()); } boolean enabled = isEnabled(position); UiUtils.setViewTreeEnabled(view, enabled && !restricted); view.setEnabled(enabled); view.setChecked(checked); + holder.iconImage.setImageDrawable(icon); holder.titleText.setText(title); + holder.titleText.setContentDescription(contentDescription); holder.subtitleText.setVisibility(!TextUtils.isEmpty(subtitle) ? View.VISIBLE : View.GONE); holder.subtitleText.setText(subtitle); diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/RoleApplicationPreference.java b/PermissionController/src/com/android/permissioncontroller/role/ui/RoleApplicationPreference.java index 1d3e32c9c..3336aad50 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/RoleApplicationPreference.java +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/RoleApplicationPreference.java @@ -17,6 +17,7 @@ package com.android.permissioncontroller.role.ui; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.preference.TwoStatePreference; /** @@ -29,4 +30,9 @@ public interface RoleApplicationPreference extends RestrictionAwarePreference { */ @NonNull TwoStatePreference asTwoStatePreference(); + + /** + * Sets the preference's content description. + */ + void setContentDescription(@Nullable String contentDescription); } diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/RolePreference.java b/PermissionController/src/com/android/permissioncontroller/role/ui/RolePreference.java index bbc123cfe..feafb0ae5 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/RolePreference.java +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/RolePreference.java @@ -17,6 +17,7 @@ package com.android.permissioncontroller.role.ui; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.preference.Preference; /** @@ -28,4 +29,9 @@ public interface RolePreference extends TwoTargetPreference, RestrictionAwarePre */ @NonNull Preference asPreference(); + + /** + * Sets the preference's summary content description. + */ + void setSummaryContentDescription(@Nullable String summaryContentDescription); } 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 764c07497..f0a27284d 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoRadioPreference.java +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoRadioPreference.java @@ -19,6 +19,7 @@ package com.android.permissioncontroller.role.ui.auto; import android.content.Context; import android.content.Intent; import android.widget.RadioButton; +import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -30,10 +31,15 @@ import com.android.permissioncontroller.R; import com.android.permissioncontroller.role.ui.RestrictionAwarePreferenceMixin; import com.android.permissioncontroller.role.ui.RoleApplicationPreference; +import java.util.Objects; + /** Preference used to represent apps that can be picked as a default app. */ public class AutoRadioPreference extends TwoStatePreference implements RoleApplicationPreference { + @Nullable + private String mContentDescription; + private final RestrictionAwarePreferenceMixin mRestrictionAwarePreferenceMixin = new RestrictionAwarePreferenceMixin(this); @@ -56,10 +62,21 @@ public class AutoRadioPreference extends TwoStatePreference implements RadioButton radioButton = (RadioButton) holder.findViewById(R.id.radio_button); radioButton.setChecked(isChecked()); + TextView titleText = (TextView) holder.findViewById(android.R.id.title); + titleText.setContentDescription(mContentDescription); + mRestrictionAwarePreferenceMixin.onAfterBindViewHolder(holder); } @Override + public void setContentDescription(@Nullable String contentDescription) { + if (!Objects.equals(mContentDescription, contentDescription)) { + mContentDescription = contentDescription; + notifyChanged(); + } + } + + @Override public void setRestrictionIntent(@Nullable Intent restrictionIntent) { mRestrictionAwarePreferenceMixin.setRestrictionIntent(restrictionIntent); } 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 15fd117d1..28dfc67f9 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoRolePreference.java +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoRolePreference.java @@ -19,6 +19,7 @@ package com.android.permissioncontroller.role.ui.auto; import android.content.Context; import android.content.Intent; import android.util.AttributeSet; +import android.widget.TextView; import androidx.annotation.AttrRes; import androidx.annotation.NonNull; @@ -31,12 +32,17 @@ import com.android.permissioncontroller.role.ui.RestrictionAwarePreferenceMixin; import com.android.permissioncontroller.role.ui.RolePreference; import com.android.permissioncontroller.role.ui.TwoTargetPreference; +import java.util.Objects; + /** * Preference for use in auto lists. Extends {@link TwoTargetPreference} in order to make sure of * shared logic between phone and auto settings UI. */ public class AutoRolePreference extends Preference implements RolePreference { + @Nullable + private String mSummaryContentDescription; + private RestrictionAwarePreferenceMixin mRestrictionAwarePreferenceMixin = new RestrictionAwarePreferenceMixin(this); @@ -62,6 +68,14 @@ public class AutoRolePreference extends Preference implements RolePreference { public void setOnSecondTargetClickListener(@Nullable OnSecondTargetClickListener listener) {} @Override + public void setSummaryContentDescription(@Nullable String summaryContentDescription) { + if (!Objects.equals(mSummaryContentDescription, summaryContentDescription)) { + mSummaryContentDescription = summaryContentDescription; + notifyChanged(); + } + } + + @Override public void setRestrictionIntent(@Nullable Intent restrictionIntent) { mRestrictionAwarePreferenceMixin.setRestrictionIntent(restrictionIntent); } @@ -70,6 +84,9 @@ public class AutoRolePreference extends Preference implements RolePreference { public void onBindViewHolder(@NonNull PreferenceViewHolder holder) { super.onBindViewHolder(holder); + TextView summaryText = (TextView) holder.findViewById(android.R.id.summary); + summaryText.setContentDescription(mSummaryContentDescription); + mRestrictionAwarePreferenceMixin.onAfterBindViewHolder(holder); } 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 bfb2b5d1d..e7279278c 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoSwitchPreference.java +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoSwitchPreference.java @@ -19,6 +19,7 @@ package com.android.permissioncontroller.role.ui.auto; import android.content.Context; import android.content.Intent; import android.util.AttributeSet; +import android.widget.TextView; import androidx.annotation.AttrRes; import androidx.annotation.NonNull; @@ -30,12 +31,16 @@ import androidx.preference.SwitchPreference; import com.android.permissioncontroller.role.ui.RestrictionAwarePreferenceMixin; import com.android.permissioncontroller.role.ui.RoleApplicationPreference; +import java.util.Objects; + /** * Role application preference represented as a switch. */ public class AutoSwitchPreference extends SwitchPreference implements RoleApplicationPreference { + @Nullable + private String mContentDescription; private RestrictionAwarePreferenceMixin mRestrictionAwarePreferenceMixin = new RestrictionAwarePreferenceMixin(this); @@ -58,6 +63,14 @@ public class AutoSwitchPreference extends SwitchPreference } @Override + public void setContentDescription(@Nullable String contentDescription) { + if (!Objects.equals(mContentDescription, contentDescription)) { + mContentDescription = contentDescription; + notifyChanged(); + } + } + + @Override public void setRestrictionIntent(@Nullable Intent restrictionIntent) { mRestrictionAwarePreferenceMixin.setRestrictionIntent(restrictionIntent); } @@ -66,6 +79,9 @@ public class AutoSwitchPreference extends SwitchPreference public void onBindViewHolder(@NonNull PreferenceViewHolder holder) { super.onBindViewHolder(holder); + TextView titleText = (TextView) holder.findViewById(android.R.id.title); + titleText.setContentDescription(mContentDescription); + mRestrictionAwarePreferenceMixin.onAfterBindViewHolder(holder); } 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 index 09aaa0532..eb1dbb5ef 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/v35/WalletRoleUiBehavior.java +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/v35/WalletRoleUiBehavior.java @@ -42,6 +42,7 @@ import androidx.core.util.Pair; import androidx.preference.Preference; import com.android.launcher3.icons.IconFactory; +import com.android.permissioncontroller.role.ui.RequestRoleItemView; import com.android.permissioncontroller.role.ui.TwoTargetPreference; import com.android.permissioncontroller.role.ui.behavior.RoleUiBehavior; import com.android.role.controller.model.Role; @@ -73,6 +74,21 @@ public class WalletRoleUiBehavior implements RoleUiBehavior { } @Override + public void prepareRequestRoleItemViewAsUser(@NonNull Role role, + @NonNull RequestRoleItemView itemView, @NonNull ApplicationInfo applicationInfo, + @NonNull UserHandle user, @NonNull Context context) { + if (isSystemApplication(applicationInfo)) { + Pair<Drawable, CharSequence> bannerAndLabel = getLabelAndIconIfItExists( + applicationInfo, user, context); + + if (bannerAndLabel != null) { + itemView.getIconImageView().setImageDrawable(bannerAndLabel.first); + itemView.getTitleTextView().setText(bannerAndLabel.second); + } + } + } + + @Override public void prepareApplicationPreferenceAsUser(@NonNull Role role, @NonNull Preference preference, @NonNull ApplicationInfo applicationInfo, @NonNull UserHandle user, @NonNull Context context) { @@ -84,21 +100,8 @@ public class WalletRoleUiBehavior implements RoleUiBehavior { @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 = null; - // If the flag is enabled , attempt to fetch it from property - if (Flags.walletRoleIconPropertyEnabled()) { - bannerAndLabel = - getBannerAndLabelFromPackageProperty(context, user, - applicationInfo.packageName); - } - // If it's null, indicating that the property is not set, perform a legacy icon lookup. - if (bannerAndLabel == null) { - bannerAndLabel = - getNonPaymentServiceBannerAndLabelIfExists(serviceInfos, user, context); - } + Pair<Drawable, CharSequence> bannerAndLabel = getLabelAndIconIfItExists( + applicationInfo, user, context); if (bannerAndLabel != null) { preference.setIcon(bannerAndLabel.first); if (setTitle) { @@ -110,6 +113,26 @@ public class WalletRoleUiBehavior implements RoleUiBehavior { } } + @Nullable + private Pair<Drawable, CharSequence> getLabelAndIconIfItExists( + @NonNull ApplicationInfo applicationInfo, + @NonNull UserHandle user, + @NonNull Context context) { + Pair<Drawable, CharSequence> result = null; + // If the flag is enabled , attempt to fetch it from property + if (Flags.walletRoleIconPropertyEnabled()) { + result = getBannerAndLabelFromPackageProperty(context, user, + applicationInfo.packageName); + } + if (result != null) { + return result; + } + List<ApduServiceInfo> serviceInfos = getNfcServicesForPackage( + applicationInfo.packageName, user, context); + // If it's null, indicating that the property is not set, perform a legacy icon lookup. + return getNonPaymentServiceBannerAndLabelIfExists(serviceInfos, user, context); + } + @Nullable private Pair<Drawable, CharSequence> getBannerAndLabelFromPackageProperty( 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 67f04051c..2a943d3d7 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/handheld/HandheldRadioPreference.java +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/handheld/HandheldRadioPreference.java @@ -19,6 +19,7 @@ package com.android.permissioncontroller.role.ui.handheld; import android.content.Context; import android.content.Intent; import android.util.AttributeSet; +import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -28,12 +29,17 @@ import com.android.permissioncontroller.role.ui.RestrictionAwarePreferenceMixin; import com.android.permissioncontroller.role.ui.RoleApplicationPreference; import com.android.settingslib.widget.SelectorWithWidgetPreference; +import java.util.Objects; + /** * Preference used to represent apps that can be picked as a default app. */ public class HandheldRadioPreference extends SelectorWithWidgetPreference implements RoleApplicationPreference { + @Nullable + private String mContentDescription; + private final RestrictionAwarePreferenceMixin mRestrictionAwarePreferenceMixin = new RestrictionAwarePreferenceMixin(this); @@ -56,6 +62,14 @@ public class HandheldRadioPreference extends SelectorWithWidgetPreference implem } @Override + public void setContentDescription(@Nullable String contentDescription) { + if (!Objects.equals(mContentDescription, contentDescription)) { + mContentDescription = contentDescription; + notifyChanged(); + } + } + + @Override public void setRestrictionIntent(@Nullable Intent restrictionIntent) { mRestrictionAwarePreferenceMixin.setRestrictionIntent(restrictionIntent); } @@ -64,6 +78,9 @@ public class HandheldRadioPreference extends SelectorWithWidgetPreference implem public void onBindViewHolder(@NonNull PreferenceViewHolder holder) { super.onBindViewHolder(holder); + TextView titleText = (TextView) holder.findViewById(android.R.id.title); + titleText.setContentDescription(mContentDescription); + mRestrictionAwarePreferenceMixin.onAfterBindViewHolder(holder); } 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 3d09f0b46..46bf3b173 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/handheld/HandheldRolePreference.java +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/handheld/HandheldRolePreference.java @@ -20,6 +20,7 @@ import android.content.Context; import android.content.Intent; import android.util.AttributeSet; import android.view.View; +import android.widget.TextView; import androidx.annotation.AttrRes; import androidx.annotation.NonNull; @@ -33,6 +34,8 @@ import com.android.permissioncontroller.role.ui.RestrictionAwarePreferenceMixin; import com.android.permissioncontroller.role.ui.RolePreference; import com.android.settingslib.widget.TwoTargetPreference; +import java.util.Objects; + /** * {@link Preference} with a settings button. * @@ -45,6 +48,9 @@ public class HandheldRolePreference extends TwoTargetPreference implements RoleP new RestrictionAwarePreferenceMixin(this); @Nullable + private String mSummaryContentDescription; + + @Nullable private OnSecondTargetClickListener mOnSecondTargetClickListener; public HandheldRolePreference(@NonNull Context context, @Nullable AttributeSet attrs, @@ -94,6 +100,14 @@ public class HandheldRolePreference extends TwoTargetPreference implements RoleP } @Override + public void setSummaryContentDescription(@Nullable String summaryContentDescription) { + if (!Objects.equals(mSummaryContentDescription, summaryContentDescription)) { + mSummaryContentDescription = summaryContentDescription; + notifyChanged(); + } + } + + @Override public void setRestrictionIntent(@Nullable Intent restrictionIntent) { mRestrictionAwarePreferenceMixin.setRestrictionIntent(restrictionIntent); } @@ -114,6 +128,9 @@ public class HandheldRolePreference extends TwoTargetPreference implements RoleP // Make the settings button enabled even if the preference itself is disabled. settingsButton.setEnabled(true); + TextView summaryText = (TextView) holder.findViewById(android.R.id.summary); + summaryText.setContentDescription(mSummaryContentDescription); + mRestrictionAwarePreferenceMixin.onAfterBindViewHolder(holder); } 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 ded6d5cb5..a801c2182 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 @@ -19,6 +19,7 @@ package com.android.permissioncontroller.role.ui.specialappaccess.handheld; import android.content.Context; import android.content.Intent; import android.util.AttributeSet; +import android.widget.TextView; import androidx.annotation.AttrRes; import androidx.annotation.NonNull; @@ -30,10 +31,14 @@ import com.android.permissioncontroller.role.ui.RestrictionAwarePreferenceMixin; import com.android.permissioncontroller.role.ui.RoleApplicationPreference; import com.android.settingslib.widget.AppSwitchPreference; +import java.util.Objects; + /** {@link AppSwitchPreference} that is a role application preference. */ public class HandheldSwitchPreference extends AppSwitchPreference implements RoleApplicationPreference { + @Nullable + private String mContentDescription; private RestrictionAwarePreferenceMixin mRestrictionAwarePreferenceMixin = new RestrictionAwarePreferenceMixin(this); @@ -56,6 +61,14 @@ public class HandheldSwitchPreference extends AppSwitchPreference } @Override + public void setContentDescription(@Nullable String contentDescription) { + if (!Objects.equals(mContentDescription, contentDescription)) { + mContentDescription = contentDescription; + notifyChanged(); + } + } + + @Override public void setRestrictionIntent(@Nullable Intent restrictionIntent) { mRestrictionAwarePreferenceMixin.setRestrictionIntent(restrictionIntent); } @@ -64,6 +77,9 @@ public class HandheldSwitchPreference extends AppSwitchPreference public void onBindViewHolder(@NonNull PreferenceViewHolder holder) { super.onBindViewHolder(holder); + TextView titleText = (TextView) holder.findViewById(android.R.id.title); + titleText.setContentDescription(mContentDescription); + mRestrictionAwarePreferenceMixin.onAfterBindViewHolder(holder); } diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRoleApplicationPreference.kt b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRoleApplicationPreference.kt index 6cd52f576..cb12bf70a 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRoleApplicationPreference.kt +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRoleApplicationPreference.kt @@ -18,6 +18,7 @@ package com.android.permissioncontroller.role.ui.wear import android.content.Context import android.content.Intent +import androidx.preference.PreferenceViewHolder import androidx.preference.TwoStatePreference import com.android.permissioncontroller.role.ui.RoleApplicationPreference @@ -30,7 +31,8 @@ class WearRoleApplicationPreference( defaultLabel: String, val checked: Boolean, val onDefaultCheckChanged: (Boolean) -> Unit = {}, - private var restrictionIntent: Intent? = null + private var restrictionIntent: Intent? = null, + private var contentDescription: String? = null, ) : TwoStatePreference(context), RoleApplicationPreference { init { title = defaultLabel @@ -39,10 +41,22 @@ class WearRoleApplicationPreference( fun getOnCheckChanged(): (Boolean) -> Unit = restrictionIntent?.let { { _ -> context.startActivity(it) } } ?: onDefaultCheckChanged + override fun setContentDescription(contentDescription: String?) { + if (this.contentDescription != contentDescription) { + this.contentDescription = contentDescription + notifyChanged() + } + } + override fun setRestrictionIntent(restrictionIntent: Intent?) { this.restrictionIntent = restrictionIntent isEnabled = restrictionIntent == null } override fun asTwoStatePreference(): TwoStatePreference = this + + override fun onBindViewHolder(holder: PreferenceViewHolder) { + super.onBindViewHolder(holder) + holder.findViewById(android.R.id.title)?.let { it.contentDescription = contentDescription } + } } diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRolePreference.kt b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRolePreference.kt index 43acf4293..670c136ea 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRolePreference.kt +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRolePreference.kt @@ -19,6 +19,7 @@ package com.android.permissioncontroller.role.ui.wear import android.content.Context import android.content.Intent import androidx.preference.Preference +import androidx.preference.PreferenceViewHolder import com.android.permissioncontroller.role.ui.RolePreference import com.android.permissioncontroller.role.ui.TwoTargetPreference.OnSecondTargetClickListener import com.android.settingslib.widget.TwoTargetPreference @@ -28,13 +29,21 @@ class WearRolePreference( context: Context, val label: String, val onDefaultClicked: () -> Unit = {}, - private var restrictionIntent: Intent? = null + private var restrictionIntent: Intent? = null, + private var summaryContentDescription: String? = null, ) : TwoTargetPreference(context), RolePreference { override fun setOnSecondTargetClickListener(listener: OnSecondTargetClickListener?) { // no-op } + override fun setSummaryContentDescription(summaryContentDescription: String?) { + if (this.summaryContentDescription != summaryContentDescription) { + this.summaryContentDescription = summaryContentDescription + notifyChanged() + } + } + override fun setRestrictionIntent(restrictionIntent: Intent?) { this.restrictionIntent = restrictionIntent setEnabled(restrictionIntent == null) @@ -42,6 +51,14 @@ class WearRolePreference( override fun asPreference(): Preference = this + override fun onBindViewHolder(holder: PreferenceViewHolder) { + super.onBindViewHolder(holder) + + holder.findViewById(android.R.id.summary)?.let { + it.contentDescription = summaryContentDescription + } + } + fun getOnClicked(): () -> Unit = restrictionIntent?.let { { context.startActivity(it) } } ?: onDefaultClicked } diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterFragment.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterFragment.kt index b5a66da06..d29b0aa3e 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterFragment.kt +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterFragment.kt @@ -61,7 +61,6 @@ abstract class SafetyCenterFragment : SettingsBasePreferenceFragment() { } else { super.onCreateAdapter(preferenceScreen) } - /* By default, the PreferenceGroupAdapter does setHasStableIds(true). Since each Preference * is internally allocated with an auto-incremented ID, it does not allow us to gracefully * update only changed preferences based on SafetyPreferenceComparisonCallback. In order to @@ -77,10 +76,15 @@ abstract class SafetyCenterFragment : SettingsBasePreferenceFragment() { .split(",") safetyCenterSessionId = requireArguments().getLong(EXTRA_SESSION_ID, INVALID_SESSION_ID) + val activity = requireActivity() safetyCenterViewModel = ViewModelProvider( - requireActivity(), - LiveSafetyCenterViewModelFactory(requireActivity().getApplication()), + activity, + LiveSafetyCenterViewModelFactory( + activity.application, + activity.taskId, + sameTaskSourceIds, + ), ) .get(SafetyCenterViewModel::class.java) safetyCenterViewModel.safetyCenterUiLiveData.observe(this) { uiData: SafetyCenterUiData? -> @@ -91,8 +95,7 @@ abstract class SafetyCenterFragment : SettingsBasePreferenceFragment() { displayErrorDetails(errorDetails) } - val safetyCenterIntent: ParsedSafetyCenterIntent = - requireActivity().intent.toSafetyCenterIntent() + val safetyCenterIntent: ParsedSafetyCenterIntent = activity.intent.toSafetyCenterIntent() val isQsFragment = getArguments()?.getBoolean(QUICK_SETTINGS_SAFETY_CENTER_FRAGMENT, false) ?: false collapsableIssuesCardHelper = diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/IssueUiData.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/IssueUiData.kt new file mode 100644 index 000000000..e260bb917 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/IssueUiData.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.safetycenter.ui.model + +import android.safetycenter.SafetyCenterIssue + +/** UI model representation of [SafetyCenterIssue] */ +data class IssueUiData( + val issue: SafetyCenterIssue, + val isDismissed: Boolean, + val resolvedIssueActionId: String? = null, + val launchTaskId: Int? = null, +) 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 4ddcf1c3d..0b976f49d 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt @@ -43,11 +43,16 @@ import com.android.safetycenter.internaldata.SafetyCenterIds /* A SafetyCenterViewModel that talks to the real backing service for Safety Center. */ @RequiresApi(Build.VERSION_CODES.TIRAMISU) -class LiveSafetyCenterViewModel(app: Application) : SafetyCenterViewModel(app) { +class LiveSafetyCenterViewModel( + app: Application, + private val taskId: Int, + private val sameTaskSourceIds: List<String>, +) : SafetyCenterViewModel(app) { private val TAG: String = LiveSafetyCenterViewModel::class.java.simpleName override val statusUiLiveData: LiveData<StatusUiData> get() = safetyCenterUiLiveData.map { StatusUiData(it.safetyCenterData) } + override val safetyCenterUiLiveData: LiveData<SafetyCenterUiData> by this::_safetyCenterLiveData override val errorLiveData: LiveData<SafetyCenterErrorDetails> by this::_errorLiveData @@ -65,7 +70,7 @@ class LiveSafetyCenterViewModel(app: Application) : SafetyCenterViewModel(app) { private val safetyCenterManager = app.getSystemService(SafetyCenterManager::class.java)!! override fun getCurrentSafetyCenterDataAsUiData(): SafetyCenterUiData = - SafetyCenterUiData(safetyCenterManager.safetyCenterData) + uiData(safetyCenterManager.safetyCenterData) override fun dismissIssue(issue: SafetyCenterIssue) { safetyCenterManager.dismissSafetyCenterIssue(issue.id) @@ -74,7 +79,7 @@ class LiveSafetyCenterViewModel(app: Application) : SafetyCenterViewModel(app) { override fun executeIssueAction( issue: SafetyCenterIssue, action: SafetyCenterIssue.Action, - launchTaskId: Int? + launchTaskId: Int?, ) { val issueId = if (launchTaskId != null) { @@ -107,9 +112,7 @@ class LiveSafetyCenterViewModel(app: Application) : SafetyCenterViewModel(app) { override fun navigateToSafetyCenter(context: Context, navigationSource: NavigationSource?) { val intent = Intent(ACTION_SAFETY_CENTER) - if (navigationSource != null) { - navigationSource.addToIntent(intent) - } + navigationSource?.addToIntent(intent) context.startActivity(intent) } @@ -132,7 +135,7 @@ class LiveSafetyCenterViewModel(app: Application) : SafetyCenterViewModel(app) { } else { safetyCenterManager.refreshSafetySources( SafetyCenterManager.REFRESH_REASON_PAGE_OPEN, - safetySourceIds + safetySourceIds, ) } } @@ -174,7 +177,7 @@ class LiveSafetyCenterViewModel(app: Application) : SafetyCenterViewModel(app) { override fun onActive() { safetyCenterManager.addOnSafetyCenterDataChangedListener( getMainExecutor(app.applicationContext), - this + this, ) super.onActive() } @@ -209,7 +212,7 @@ class LiveSafetyCenterViewModel(app: Application) : SafetyCenterViewModel(app) { Log.d( TAG, "Received SafetyCenterData while issue resolution animations" + - " occurring. Will update UI with new data soon." + " occurring. Will update UI with new data soon.", ) return } @@ -254,7 +257,7 @@ class LiveSafetyCenterViewModel(app: Application) : SafetyCenterViewModel(app) { private fun isCurrentlyScanning(): Boolean = value?.safetyCenterData?.isScanning() ?: false private fun sendNextData() { - value = SafetyCenterUiData(safetyCenterDataQueue.removeFirst()) + value = uiData(safetyCenterDataQueue.removeFirst()) } private fun skipNextData() = safetyCenterDataQueue.removeFirst() @@ -270,7 +273,7 @@ class LiveSafetyCenterViewModel(app: Application) : SafetyCenterViewModel(app) { // The current SafetyCenterData still contains the resolved SafetyCenterIssue objects. // Send it with the resolved IDs so the UI can generate the correct preferences and // trigger the right animations for issue resolution. - value = SafetyCenterUiData(currentData, currentResolvedIssues) + value = uiData(currentData, currentResolvedIssues) } @MainThread @@ -279,6 +282,11 @@ class LiveSafetyCenterViewModel(app: Application) : SafetyCenterViewModel(app) { maybeProcessDataToNextResolvedIssues() } } + + private fun uiData( + safetyCenterData: SafetyCenterData, + resolvedIssues: Map<IssueId, ActionId> = emptyMap(), + ) = SafetyCenterUiData(safetyCenterData, taskId, sameTaskSourceIds, resolvedIssues) } /** Returns inflight issues pending resolution */ @@ -309,8 +317,15 @@ private val SafetyCenterData.allResolvableIssues: Sequence<SafetyCenterIssue> } @RequiresApi(Build.VERSION_CODES.TIRAMISU) -class LiveSafetyCenterViewModelFactory(private val app: Application) : ViewModelProvider.Factory { +class LiveSafetyCenterViewModelFactory +@JvmOverloads +constructor( + private val app: Application, + private val taskId: Int = 0, + private val sameTaskSourceIds: List<String> = emptyList(), +) : ViewModelProvider.Factory { override fun <T : ViewModel> create(modelClass: Class<T>): T { - @Suppress("UNCHECKED_CAST") return LiveSafetyCenterViewModel(app) as T + @Suppress("UNCHECKED_CAST") + return LiveSafetyCenterViewModel(app, taskId, sameTaskSourceIds) as T } } 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 69a315f08..d8aadae2f 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/SafetyCenterUiData.kt +++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/SafetyCenterUiData.kt @@ -29,11 +29,21 @@ import com.android.safetycenter.internaldata.SafetyCenterIds import com.android.safetycenter.internaldata.SafetyCenterIssueKey /** UI model representation of Safety Center Data */ +@RequiresApi(TIRAMISU) data class SafetyCenterUiData( val safetyCenterData: SafetyCenterData, - val resolvedIssues: Map<IssueId, ActionId> = emptyMap() + private val taskId: Int, + private val sameTaskSourceIds: List<String>, + val resolvedIssues: Map<IssueId, ActionId> = emptyMap(), ) { - @RequiresApi(TIRAMISU) + + val issueUiDatas: List<IssueUiData> by + lazy(LazyThreadSafetyMode.NONE) { + safetyCenterData.issues.map { + IssueUiData(it, false, resolvedIssues[it.id], getLaunchTaskIdForIssue(it)) + } + } + fun getMatchingIssue(issueKey: SafetyCenterIssueKey): SafetyCenterIssue? { return safetyCenterData.issues.find { SafetyCenterIds.issueIdFromString(it.id).safetyCenterIssueKey == issueKey @@ -67,7 +77,7 @@ data class SafetyCenterUiData( @RequiresApi(UPSIDE_DOWN_CAKE) private fun selectMatchingIssuesForGroup( groupId: String, - issues: List<SafetyCenterIssue> + issues: List<SafetyCenterIssue>, ): List<SafetyCenterIssue> { val issuesToGroups = safetyCenterData.extras.getBundle(ISSUES_TO_GROUPS_BUNDLE_KEY) return issues.filter { @@ -84,4 +94,12 @@ data class SafetyCenterUiData( @RequiresApi(UPSIDE_DOWN_CAKE) fun SafetyCenterData.visibleDismissedIssues() = dismissedIssues.filter { it.severityLevel > ISSUE_SEVERITY_LEVEL_OK } + + private fun getLaunchTaskIdForIssue(issue: SafetyCenterIssue): Int? { + val sourceId: String = + SafetyCenterIds.issueIdFromString(issue.id) + .getSafetyCenterIssueKey() + .getSafetySourceId() + return if (sameTaskSourceIds.contains(sourceId)) taskId else null + } } 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 ca0392716..e53fabc90 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 @@ -18,6 +18,7 @@ package com.android.permissioncontroller.tests.mocking.safetycenter.ui.model import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE import android.os.Bundle +import android.os.UserHandle import android.safetycenter.SafetyCenterData import android.safetycenter.SafetyCenterEntryGroup import android.safetycenter.SafetyCenterEntryOrGroup @@ -28,8 +29,14 @@ import android.safetycenter.SafetyCenterIssue.ISSUE_SEVERITY_LEVEL_RECOMMENDATIO import android.safetycenter.SafetyCenterStatus import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SdkSuppress +import com.android.permissioncontroller.safetycenter.ui.model.ActionId +import com.android.permissioncontroller.safetycenter.ui.model.IssueId +import com.android.permissioncontroller.safetycenter.ui.model.IssueUiData import com.android.permissioncontroller.safetycenter.ui.model.SafetyCenterUiData import com.android.safetycenter.internaldata.SafetyCenterBundles.ISSUES_TO_GROUPS_BUNDLE_KEY +import com.android.safetycenter.internaldata.SafetyCenterIds +import com.android.safetycenter.internaldata.SafetyCenterIssueId +import com.android.safetycenter.internaldata.SafetyCenterIssueKey import com.google.common.truth.Truth.assertThat import org.junit.Test import org.junit.runner.RunWith @@ -40,42 +47,42 @@ class SafetyCenterUiDataTest { @Test fun getMatchingGroup_validMatchingGroup_returnsExpectedEntryGroup() { - val matchingGroup = createSafetyCenterEntryGroup(MATCHING_GROUP_ID) - val nonMatchingGroup = createSafetyCenterEntryGroup(NON_MATCHING_GROUP_ID) + val matchingGroup = entryGroup(MATCHING_GROUP_ID) + val nonMatchingGroup = entryGroup(NON_MATCHING_GROUP_ID) val safetyCenterData = createSafetyCenterData(entryGroups = listOf(matchingGroup, nonMatchingGroup)) - val result = SafetyCenterUiData(safetyCenterData).getMatchingGroup(MATCHING_GROUP_ID) + val result = uiData(safetyCenterData).getMatchingGroup(MATCHING_GROUP_ID) assertThat(result).isEqualTo(matchingGroup) } @Test fun getMatchingGroup_noMatchingGroup_returnsNull() { - val nonMatchingGroup = createSafetyCenterEntryGroup(NON_MATCHING_GROUP_ID) + val nonMatchingGroup = entryGroup(NON_MATCHING_GROUP_ID) val safetyCenterData = createSafetyCenterData(entryGroups = listOf(nonMatchingGroup)) - val result = SafetyCenterUiData(safetyCenterData).getMatchingGroup(MATCHING_GROUP_ID) + val result = uiData(safetyCenterData).getMatchingGroup(MATCHING_GROUP_ID) assertThat(result).isNull() } @Test fun getMatchingIssues_defaultMatchingIssue_noExtras_returnsListOfIssues() { - val defaultMatchingIssue = createSafetyCenterIssue("id1", MATCHING_GROUP_ID) - val nonMatchingIssue = createSafetyCenterIssue("id2", NON_MATCHING_GROUP_ID) + val defaultMatchingIssue = issue("id1", MATCHING_GROUP_ID) + val nonMatchingIssue = issue("id2", NON_MATCHING_GROUP_ID) val safetyCenterData = createSafetyCenterData(issues = listOf(defaultMatchingIssue, nonMatchingIssue)) - val result = SafetyCenterUiData(safetyCenterData).getMatchingIssues(MATCHING_GROUP_ID) + val result = uiData(safetyCenterData).getMatchingIssues(MATCHING_GROUP_ID) assertThat(result).containsExactly(defaultMatchingIssue) } @Test fun getMatchingIssues_defaultMatchingIssue_unrelatedExtras_returnsListOfIssues() { - val defaultMatchingIssue = createSafetyCenterIssue("id1", MATCHING_GROUP_ID) - val nonMatchingIssue = createSafetyCenterIssue("id2", NON_MATCHING_GROUP_ID) + val defaultMatchingIssue = issue("id1", MATCHING_GROUP_ID) + val nonMatchingIssue = issue("id2", NON_MATCHING_GROUP_ID) val safetyCenterData = createSafetyCenterData( issues = listOf(defaultMatchingIssue, nonMatchingIssue), @@ -84,21 +91,21 @@ class SafetyCenterUiDataTest { Bundle().apply { putStringArrayList( nonMatchingIssue.id, - arrayListOf(NON_MATCHING_GROUP_ID) + arrayListOf(NON_MATCHING_GROUP_ID), ) } - ) + ), ) - val result = SafetyCenterUiData(safetyCenterData).getMatchingIssues(MATCHING_GROUP_ID) + val result = uiData(safetyCenterData).getMatchingIssues(MATCHING_GROUP_ID) assertThat(result).containsExactly(defaultMatchingIssue) } @Test fun getMatchingIssues_mappingMatchingIssue_returnsListOfIssues() { - val mappingMatchingIssue = createSafetyCenterIssue("id1", NON_MATCHING_GROUP_ID) - val nonMatchingIssue = createSafetyCenterIssue("id2", NON_MATCHING_GROUP_ID) + val mappingMatchingIssue = issue("id1", NON_MATCHING_GROUP_ID) + val nonMatchingIssue = issue("id2", NON_MATCHING_GROUP_ID) val safetyCenterData = createSafetyCenterData( issues = listOf(mappingMatchingIssue, nonMatchingIssue), @@ -107,51 +114,50 @@ class SafetyCenterUiDataTest { Bundle().apply { putStringArrayList( mappingMatchingIssue.id, - arrayListOf(MATCHING_GROUP_ID) + arrayListOf(MATCHING_GROUP_ID), ) } - ) + ), ) - val result = SafetyCenterUiData(safetyCenterData).getMatchingIssues(MATCHING_GROUP_ID) + val result = uiData(safetyCenterData).getMatchingIssues(MATCHING_GROUP_ID) assertThat(result).containsExactly(mappingMatchingIssue) } @Test fun getMatchingIssues_noDefaultMatchingIssue_returnsEmptyList() { - val nonMatchingIssue = createSafetyCenterIssue("id1", NON_MATCHING_GROUP_ID) - val dismissedIssue = createSafetyCenterIssue("id2", MATCHING_GROUP_ID) + val nonMatchingIssue = issue("id1", NON_MATCHING_GROUP_ID) + val dismissedIssue = issue("id2", MATCHING_GROUP_ID) val safetyCenterData = createSafetyCenterData( issues = listOf(nonMatchingIssue), - dismissedIssues = listOf(dismissedIssue) + dismissedIssues = listOf(dismissedIssue), ) - val result = SafetyCenterUiData(safetyCenterData).getMatchingIssues(MATCHING_GROUP_ID) + val result = uiData(safetyCenterData).getMatchingIssues(MATCHING_GROUP_ID) assertThat(result).isEmpty() } @Test fun getMatchingDismissedIssues_defaultMatchingDismissedIssue_returnsListOfDismissedIssues() { - val defaultMatchingDismissedIssue = createSafetyCenterIssue("id1", MATCHING_GROUP_ID) - val nonMatchingDismissedIssue = createSafetyCenterIssue("id2", NON_MATCHING_GROUP_ID) + val defaultMatchingDismissedIssue = issue("id1", MATCHING_GROUP_ID) + val nonMatchingDismissedIssue = issue("id2", NON_MATCHING_GROUP_ID) val safetyCenterData = createSafetyCenterData( dismissedIssues = listOf(defaultMatchingDismissedIssue, nonMatchingDismissedIssue) ) - val result = - SafetyCenterUiData(safetyCenterData).getMatchingDismissedIssues(MATCHING_GROUP_ID) + val result = uiData(safetyCenterData).getMatchingDismissedIssues(MATCHING_GROUP_ID) assertThat(result).containsExactly(defaultMatchingDismissedIssue) } @Test fun getMatchingDismissedIssues_defaultMatchingDismissedIssue2_returnsListOfDismissedIssues() { - val defaultMatchingDismissedIssue = createSafetyCenterIssue("id1", MATCHING_GROUP_ID) - val nonMatchingDismissedIssue = createSafetyCenterIssue("id2", NON_MATCHING_GROUP_ID) + val defaultMatchingDismissedIssue = issue("id1", MATCHING_GROUP_ID) + val nonMatchingDismissedIssue = issue("id2", NON_MATCHING_GROUP_ID) val safetyCenterData = createSafetyCenterData( dismissedIssues = listOf(defaultMatchingDismissedIssue, nonMatchingDismissedIssue), @@ -160,22 +166,21 @@ class SafetyCenterUiDataTest { Bundle().apply { putStringArrayList( nonMatchingDismissedIssue.id, - arrayListOf(NON_MATCHING_GROUP_ID) + arrayListOf(NON_MATCHING_GROUP_ID), ) } - ) + ), ) - val result = - SafetyCenterUiData(safetyCenterData).getMatchingDismissedIssues(MATCHING_GROUP_ID) + val result = uiData(safetyCenterData).getMatchingDismissedIssues(MATCHING_GROUP_ID) assertThat(result).containsExactly(defaultMatchingDismissedIssue) } @Test fun getMatchingDismissedIssues_mappingMatchingDismissedIssue_returnsListOfDismissedIssues() { - val mappingMatchingDismissedIssue = createSafetyCenterIssue("id1", NON_MATCHING_GROUP_ID) - val nonMatchingDismissedIssue = createSafetyCenterIssue("id2", NON_MATCHING_GROUP_ID) + val mappingMatchingDismissedIssue = issue("id1", NON_MATCHING_GROUP_ID) + val nonMatchingDismissedIssue = issue("id2", NON_MATCHING_GROUP_ID) val safetyCenterData = createSafetyCenterData( dismissedIssues = listOf(mappingMatchingDismissedIssue, nonMatchingDismissedIssue), @@ -184,30 +189,28 @@ class SafetyCenterUiDataTest { Bundle().apply { putStringArrayList( mappingMatchingDismissedIssue.id, - arrayListOf(MATCHING_GROUP_ID) + arrayListOf(MATCHING_GROUP_ID), ) } - ) + ), ) - val result = - SafetyCenterUiData(safetyCenterData).getMatchingDismissedIssues(MATCHING_GROUP_ID) + val result = uiData(safetyCenterData).getMatchingDismissedIssues(MATCHING_GROUP_ID) assertThat(result).containsExactly(mappingMatchingDismissedIssue) } @Test fun getMatchingDismissedIssues_noDefaultMatchingDismissedIssue_returnsEmptyList() { - val nonMatchingDismissedIssue = createSafetyCenterIssue("id1", NON_MATCHING_GROUP_ID) - val nonDismissedIssue = createSafetyCenterIssue("id2", MATCHING_GROUP_ID) + val nonMatchingDismissedIssue = issue("id1", NON_MATCHING_GROUP_ID) + val nonDismissedIssue = issue("id2", MATCHING_GROUP_ID) val safetyCenterData = createSafetyCenterData( issues = listOf(nonDismissedIssue), - dismissedIssues = listOf(nonMatchingDismissedIssue) + dismissedIssues = listOf(nonMatchingDismissedIssue), ) - val result = - SafetyCenterUiData(safetyCenterData).getMatchingDismissedIssues(MATCHING_GROUP_ID) + val result = uiData(safetyCenterData).getMatchingDismissedIssues(MATCHING_GROUP_ID) assertThat(result).isEmpty() } @@ -215,24 +218,12 @@ class SafetyCenterUiDataTest { @Test fun getMatchingDismissedIssues_doesntReturnGreenIssues() { val greenDismissedIssue = - createSafetyCenterIssue( - "id1", - MATCHING_GROUP_ID, - severityLevel = ISSUE_SEVERITY_LEVEL_OK - ) + issue("id1", MATCHING_GROUP_ID, severityLevel = ISSUE_SEVERITY_LEVEL_OK) val yellowDismissedIssue = - createSafetyCenterIssue( - "id2", - MATCHING_GROUP_ID, - severityLevel = ISSUE_SEVERITY_LEVEL_RECOMMENDATION - ) + issue("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) + issue("id3", MATCHING_GROUP_ID, severityLevel = ISSUE_SEVERITY_LEVEL_CRITICAL_WARNING) + val nonMatchingDismissedIssue = issue("id4", NON_MATCHING_GROUP_ID) val safetyCenterData = createSafetyCenterData( dismissedIssues = @@ -240,25 +231,82 @@ class SafetyCenterUiDataTest { redDismissedIssue, yellowDismissedIssue, greenDismissedIssue, - nonMatchingDismissedIssue - ), + nonMatchingDismissedIssue, + ) ) - val result = - SafetyCenterUiData(safetyCenterData).getMatchingDismissedIssues(MATCHING_GROUP_ID) + val result = uiData(safetyCenterData).getMatchingDismissedIssues(MATCHING_GROUP_ID) assertThat(result).containsExactly(redDismissedIssue, yellowDismissedIssue).inOrder() } + @Test + fun issueUiDatas_returnsIssueUiData() { + val issue1 = issue("id1", "group1") + val issue2 = issue("id2", "group2") + val safetyCenterData = createSafetyCenterData(listOf(issue1, issue2)) + + val result = uiData(safetyCenterData).issueUiDatas + + assertThat(result) + .containsExactly( + IssueUiData(issue1, isDismissed = false), + IssueUiData(issue2, isDismissed = false), + ) + .inOrder() + } + + @Test + fun issueUiDatas_withResolvedIssues_returnsExpectedIssueUiData() { + val resolvedActionId = "actionId" + val resolvedIssue = issue("resolvedId", "group1") + val unresolvedIssue = issue("unresolvedId", "group2") + val safetyCenterData = createSafetyCenterData(listOf(resolvedIssue, unresolvedIssue)) + + val result = + uiData(safetyCenterData, resolvedIssues = mapOf(resolvedIssue.id to resolvedActionId)) + .issueUiDatas + + assertThat(result[0].resolvedIssueActionId).isEqualTo(resolvedActionId) + assertThat(result[1].resolvedIssueActionId).isNull() + } + + @Test + fun issueUiDatas_withSameTaskSourceId_returnsExpectedIssueUiData() { + val taskId = 42 + val sameTaskSourceId = "sameTaskSourceId" + val sameTaskIssue = + issueWithEncodedId( + encodeIssueId("sameTaskIssue", sourceId = sameTaskSourceId), + "group1", + ) + val differentTaskIssue = issue("differentTaskIssue", "group2") + val safetyCenterData = createSafetyCenterData(listOf(sameTaskIssue, differentTaskIssue)) + + val result = + uiData(safetyCenterData, taskId, sameTaskSourceIds = listOf(sameTaskSourceId)) + .issueUiDatas + + assertThat(result[0].launchTaskId).isEqualTo(taskId) + assertThat(result[1].launchTaskId).isNull() + } + private companion object { const val MATCHING_GROUP_ID = "matching_group_id" const val NON_MATCHING_GROUP_ID = "non_matching_group_id" + private fun uiData( + safetyCenterData: SafetyCenterData, + taskId: Int = 0, + sameTaskSourceIds: List<String> = emptyList(), + resolvedIssues: Map<IssueId, ActionId> = emptyMap(), + ) = SafetyCenterUiData(safetyCenterData, taskId, sameTaskSourceIds, resolvedIssues) + fun createSafetyCenterData( issues: List<SafetyCenterIssue> = listOf(), entryGroups: List<SafetyCenterEntryGroup> = listOf(), dismissedIssues: List<SafetyCenterIssue> = listOf(), - extras: Bundle = Bundle() + extras: Bundle = Bundle(), ): SafetyCenterData { val safetyCenterStatus = SafetyCenterStatus.Builder("status title", "status summary").build() @@ -276,20 +324,44 @@ class SafetyCenterUiDataTest { return builder.build() } - fun createSafetyCenterEntryGroup(groupId: String) = + fun createSafetyCenterExtras(issuesToGroupsMapping: Bundle) = + Bundle().apply { putBundle(ISSUES_TO_GROUPS_BUNDLE_KEY, issuesToGroupsMapping) } + + fun entryGroup(groupId: String) = SafetyCenterEntryGroup.Builder(groupId, "group title").build() - fun createSafetyCenterIssue( + fun issue( issueId: String, groupId: String, - severityLevel: Int = ISSUE_SEVERITY_LEVEL_RECOMMENDATION + severityLevel: Int = ISSUE_SEVERITY_LEVEL_RECOMMENDATION, + ): SafetyCenterIssue = issueWithEncodedId(encodeIssueId(issueId), groupId, severityLevel) + + private fun issueWithEncodedId( + encodedIssueId: String, + groupId: String, + severityLevel: Int = ISSUE_SEVERITY_LEVEL_RECOMMENDATION, ) = - SafetyCenterIssue.Builder(issueId, "issue title", "issue summary") + SafetyCenterIssue.Builder(encodedIssueId, "issue title", "issue summary") .setSeverityLevel(severityLevel) .setGroupId(groupId) .build() - fun createSafetyCenterExtras(issuesToGroupsMapping: Bundle) = - Bundle().apply { putBundle(ISSUES_TO_GROUPS_BUNDLE_KEY, issuesToGroupsMapping) } + fun encodeIssueId( + sourceIssueId: String, + sourceId: String = "defaultSource", + issueTypeId: String = "defaultIssueTypeId", + ): String = + SafetyCenterIds.encodeToString( + SafetyCenterIssueId.newBuilder() + .setSafetyCenterIssueKey( + SafetyCenterIssueKey.newBuilder() + .setSafetySourceId(sourceId) + .setSafetySourceIssueId(sourceIssueId) + .setUserId(UserHandle.myUserId()) + .build() + ) + .setIssueTypeId(issueTypeId) + .build() + ) } } diff --git a/PermissionController/wear-permission-components/src/wear.permission.components/material2/Chip.kt b/PermissionController/wear-permission-components/src/wear.permission.components/material2/Chip.kt index f0dab21ec..23074a06f 100644 --- a/PermissionController/wear-permission-components/src/wear.permission.components/material2/Chip.kt +++ b/PermissionController/wear-permission-components/src/wear.permission.components/material2/Chip.kt @@ -33,7 +33,6 @@ 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.Hyphens import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow @@ -194,11 +193,7 @@ fun Chip( 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, - hyphens = Hyphens.Auto, - ), + style = MaterialTheme.typography.button.copy(hyphens = Hyphens.Auto), ) } diff --git a/PermissionController/wear-permission-components/src/wear.permission.components/material2/ListHeader.kt b/PermissionController/wear-permission-components/src/wear.permission.components/material2/ListHeader.kt index 7dc26179d..6ed81353a 100644 --- a/PermissionController/wear-permission-components/src/wear.permission.components/material2/ListHeader.kt +++ b/PermissionController/wear-permission-components/src/wear.permission.components/material2/ListHeader.kt @@ -34,7 +34,6 @@ 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.text.style.Hyphens import androidx.compose.ui.unit.dp import androidx.wear.compose.material.LocalContentColor @@ -72,11 +71,7 @@ fun ListHeader( ) { CompositionLocalProvider( LocalContentColor provides contentColor, - LocalTextStyle provides - MaterialTheme.typography.title3.copy( - fontWeight = FontWeight.W600, - hyphens = Hyphens.Auto, - ), + LocalTextStyle provides MaterialTheme.typography.title3.copy(hyphens = Hyphens.Auto), ) { content() } diff --git a/PermissionController/wear-permission-components/src/wear.permission.components/material3/WearPermissionButton.kt b/PermissionController/wear-permission-components/src/wear.permission.components/material3/WearPermissionButton.kt index 1a7524e7c..5f1c8dd2c 100644 --- a/PermissionController/wear-permission-components/src/wear.permission.components/material3/WearPermissionButton.kt +++ b/PermissionController/wear-permission-components/src/wear.permission.components/material3/WearPermissionButton.kt @@ -23,7 +23,6 @@ import androidx.compose.foundation.layout.requiredSizeIn import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Shape -import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.Hyphens import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp @@ -112,11 +111,7 @@ internal fun WearPermissionButtonInternal( text = label, modifier = Modifier.fillMaxWidth(), maxLines = labelMaxLines ?: LocalTextConfiguration.current.maxLines, - style = - LocalTextStyle.current.copy( - fontWeight = FontWeight.W600, - hyphens = Hyphens.Auto, - ), + style = LocalTextStyle.current.copy(hyphens = Hyphens.Auto), ) } } diff --git a/SafetyCenter/OWNERS b/SafetyCenter/OWNERS index d9039e915..229331d3c 100644 --- a/SafetyCenter/OWNERS +++ b/SafetyCenter/OWNERS @@ -4,7 +4,6 @@ simonjw@google.com davidcoffin@google.com elliotsisteron@google.com fiscella@google.com -shrigupt@google.com jtomljanovic@google.com deweytyl@google.com maxspencer@google.com diff --git a/SafetyCenter/Resources/res/values-mk-v35/strings.xml b/SafetyCenter/Resources/res/values-mk-v35/strings.xml index c4fc4bd17..1201ae74f 100644 --- a/SafetyCenter/Resources/res/values-mk-v35/strings.xml +++ b/SafetyCenter/Resources/res/values-mk-v35/strings.xml @@ -21,7 +21,7 @@ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Тип мрежа, шифрирање, контроли за известувања"</string> <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string> <string name="privacy_title" msgid="7047524783080782769">"Приватност"</string> - <string name="privacy_sources_title" msgid="309304028326660956">"Контроли на приватноста"</string> + <string name="privacy_sources_title" msgid="309304028326660956">"Контроли за приватност"</string> <string name="privacy_sources_summary" msgid="2165270848857537278">"Дозволи, контроли"</string> <string name="privacy_additional_title" msgid="4239060639056083649"></string> <string name="private_space_title" msgid="6158245041481535879">"Приватен простор"</string> diff --git a/SafetyCenter/Resources/res/values-mk/strings.xml b/SafetyCenter/Resources/res/values-mk/strings.xml index 2b2414d0d..379a5234b 100644 --- a/SafetyCenter/Resources/res/values-mk/strings.xml +++ b/SafetyCenter/Resources/res/values-mk/strings.xml @@ -33,9 +33,9 @@ <string name="permission_manager_title" msgid="5277347862821255015">"Управувач со дозволи"</string> <string name="permission_manager_summary" msgid="8099852107340970790">"Го контролира пристапот на апликациите до вашите податоци"</string> <string name="permission_manager_search_terms" msgid="2895147613099694722">"Дозволи, Управувач со дозволи"</string> - <string name="privacy_controls_title" msgid="5322875777945432395">"Контроли за приватноста"</string> + <string name="privacy_controls_title" msgid="5322875777945432395">"Контроли за приватност"</string> <string name="privacy_controls_summary" msgid="2402066941190435424">"Го контролира пристапот на уредот до микрофонот, камерата и друго"</string> - <string name="privacy_controls_search_terms" msgid="3774472175934304165">"Приватност, Контроли на приватноста"</string> + <string name="privacy_controls_search_terms" msgid="3774472175934304165">"Приватност, Контроли за приватност"</string> <string name="advanced_title" msgid="8745436380690561172">"Повеќе поставки"</string> <string name="advanced_security_title" msgid="1126833338772188155">"Повеќе поставки за безбедност"</string> <string name="advanced_security_summary" msgid="6172253327022425123">"Шифрирање, акредитиви и друго"</string> diff --git a/service/java/com/android/permission/util/UserUtils.java b/service/java/com/android/permission/util/UserUtils.java index 82e9cbbae..ae8e794b6 100644 --- a/service/java/com/android/permission/util/UserUtils.java +++ b/service/java/com/android/permission/util/UserUtils.java @@ -70,8 +70,7 @@ public final class UserUtils { return; } - Context userContext = UserUtils.getUserContext(userId, context); - List<UserHandle> profiles = getUserProfiles(userContext, true); + List<UserHandle> profiles = getUserProfiles(userId, context, true); final int profilesSize = profiles.size(); for (int i = 0; i < profilesSize; i++) { int profileId = profiles.get(i).getIdentifier(); @@ -112,26 +111,29 @@ public final class UserUtils { } } - /** Returns all the enabled user profiles on the device. */ + /** Returns all the enabled user profiles on the device for a specified user. */ @NonNull - public static List<UserHandle> getUserProfiles(@NonNull Context context) { - return getUserProfiles(context, false); + public static List<UserHandle> getUserProfiles(@UserIdInt int userId, + @NonNull Context context) { + return getUserProfiles(userId, context, false); } /** - * Returns all the enabled user profiles on the device + * Returns all the enabled user profiles on the device for a specified user * + * @param userId the user id to check * @param context the {@link Context} * @param excludePrivate {@code true} to exclude private profiles from returned list of users */ @NonNull - public static List<UserHandle> getUserProfiles(@NonNull Context context, + public static List<UserHandle> getUserProfiles(@UserIdInt int userId, @NonNull Context context, boolean excludePrivate) { - UserManager userManager = context.getSystemService(UserManager.class); // This call requires the QUERY_USERS permission. final long identity = Binder.clearCallingIdentity(); try { - List<UserHandle> profiles = userManager.getUserProfiles(); + Context userContext = getUserContext(userId, context); + UserManager userUserManager = userContext.getSystemService(UserManager.class); + List<UserHandle> profiles = userUserManager.getUserProfiles(); if (!excludePrivate) { return profiles; } @@ -139,7 +141,7 @@ public final class UserUtils { final int profilesSize = profiles.size(); for (int i = 0; i < profilesSize; i++) { UserHandle user = profiles.get(i); - if (!isPrivateProfile(user.getIdentifier(), context)) { + if (!isPrivateProfile(user.getIdentifier(), userContext)) { filteredProfiles.add(user); } } @@ -164,11 +166,11 @@ public final class UserUtils { @Nullable private static UserHandle getProfileParent(@UserIdInt int userId, @NonNull Context context) { Context userContext = getUserContext(userId, context); - UserManager userManager = userContext.getSystemService(UserManager.class); + UserManager userUserManager = userContext.getSystemService(UserManager.class); // This call requires the INTERACT_ACROSS_USERS permission. final long identity = Binder.clearCallingIdentity(); try { - return userManager.getProfileParent(UserHandle.of(userId)); + return userUserManager.getProfileParent(UserHandle.of(userId)); } finally { Binder.restoreCallingIdentity(identity); } @@ -201,8 +203,8 @@ public final class UserUtils { final long identity = Binder.clearCallingIdentity(); try { Context userContext = getUserContext(userId, context); - UserManager userManager = userContext.getSystemService(UserManager.class); - return userManager != null && userManager.isPrivateProfile(); + UserManager userUserManager = userContext.getSystemService(UserManager.class); + return userUserManager != null && userUserManager.isPrivateProfile(); } finally { Binder.restoreCallingIdentity(identity); } diff --git a/service/java/com/android/role/RoleService.java b/service/java/com/android/role/RoleService.java index 5bc79efbb..e5d20ef9d 100644 --- a/service/java/com/android/role/RoleService.java +++ b/service/java/com/android/role/RoleService.java @@ -564,8 +564,7 @@ public class RoleService extends SystemService implements RoleUserState.Callback + " for role"); return; } - Context userContext = UserUtils.getUserContext(userId, getContext()); - List<UserHandle> profiles = UserUtils.getUserProfiles(userContext, true); + List<UserHandle> profiles = UserUtils.getUserProfiles(userId, getContext(), true); if (!profiles.contains(UserHandle.of(activeUserId))) { Log.e(LOG_TAG, "User " + activeUserId + " is not in the same profile-group as " + userId); diff --git a/service/java/com/android/safetycenter/UserProfileGroup.java b/service/java/com/android/safetycenter/UserProfileGroup.java index 1f5258437..44cb8d3d6 100644 --- a/service/java/com/android/safetycenter/UserProfileGroup.java +++ b/service/java/com/android/safetycenter/UserProfileGroup.java @@ -129,9 +129,8 @@ public final class UserProfileGroup { * is disabled. */ public static UserProfileGroup fromUser(Context context, @UserIdInt int userId) { - Context userContext = UserUtils.getUserContext(userId, context); - List<UserHandle> userProfiles = UserUtils.getUserProfiles(userContext); - int profileParentUserId = UserUtils.getProfileParentIdOrSelf(userId, userContext); + List<UserHandle> userProfiles = UserUtils.getUserProfiles(userId, context); + int profileParentUserId = UserUtils.getProfileParentIdOrSelf(userId, context); int[] managedProfilesUserIds = new int[userProfiles.size()]; int[] managedRunningProfilesUserIds = new int[userProfiles.size()]; diff --git a/tests/cts/permissionpolicy/res/raw/android_manifest.xml b/tests/cts/permissionpolicy/res/raw/android_manifest.xml index cd80fe78e..ceefe3dfd 100644 --- a/tests/cts/permissionpolicy/res/raw/android_manifest.xml +++ b/tests/cts/permissionpolicy/res/raw/android_manifest.xml @@ -7759,7 +7759,17 @@ @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") @hide --> <permission android:name="android.permission.READ_BLOCKED_NUMBERS" - android:protectionLevel="signature" /> + android:protectionLevel="signature" + android:featureFlag="!android.permission.flags.grant_read_blocked_numbers_to_system_ui_intelligence" /> + + <!-- 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|role" + android:featureFlag="android.permission.flags.grant_read_blocked_numbers_to_system_ui_intelligence" /> <!-- Allows the holder to write blocked numbers. See {@link android.provider.BlockedNumberContract}. diff --git a/tests/cts/permissionui/src/android/permissionui/cts/PermissionRationalePermissionGrantDialogTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/PermissionRationalePermissionGrantDialogTest.kt index 751c56b3c..9a12765c0 100644 --- a/tests/cts/permissionui/src/android/permissionui/cts/PermissionRationalePermissionGrantDialogTest.kt +++ b/tests/cts/permissionui/src/android/permissionui/cts/PermissionRationalePermissionGrantDialogTest.kt @@ -41,8 +41,7 @@ import org.junit.Test @FlakyTest class PermissionRationalePermissionGrantDialogTest : BaseUsePermissionTest() { - @get:Rule - val checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule() + @get:Rule val checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule() @get:Rule val deviceConfigPermissionRationaleEnabled = @@ -50,7 +49,7 @@ class PermissionRationalePermissionGrantDialogTest : BaseUsePermissionTest() { context, DeviceConfig.NAMESPACE_PRIVACY, PERMISSION_RATIONALE_ENABLED, - true.toString() + true.toString(), ) @Before @@ -248,8 +247,10 @@ class PermissionRationalePermissionGrantDialogTest : BaseUsePermissionTest() { } } - @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName = - "VanillaIceCream") + @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() { @@ -262,8 +263,10 @@ class PermissionRationalePermissionGrantDialogTest : BaseUsePermissionTest() { } } - @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName = - "VanillaIceCream") + @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() { @@ -276,8 +279,10 @@ class PermissionRationalePermissionGrantDialogTest : BaseUsePermissionTest() { } } - @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName = - "VanillaIceCream") + @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() { @@ -290,8 +295,10 @@ class PermissionRationalePermissionGrantDialogTest : BaseUsePermissionTest() { } } - @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName = - "VanillaIceCream") + @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() { @@ -304,8 +311,10 @@ class PermissionRationalePermissionGrantDialogTest : BaseUsePermissionTest() { } } - @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName = - "VanillaIceCream") + @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() { @@ -338,7 +347,6 @@ class PermissionRationalePermissionGrantDialogTest : BaseUsePermissionTest() { requestAppPermissionsForNoResult(ACCESS_FINE_LOCATION) { clickPermissionRationaleViewInGrantDialog() assertPermissionRationaleDialogIsVisible(true) - assertPermissionRationaleContainerOnGrantDialogIsVisible(false) } } diff --git a/tests/cts/rolemultiuser/src/android/app/rolemultiuser/cts/RoleManagerMultiUserTest.kt b/tests/cts/rolemultiuser/src/android/app/rolemultiuser/cts/RoleManagerMultiUserTest.kt index 98aa5fbf1..f070fa9aa 100644 --- a/tests/cts/rolemultiuser/src/android/app/rolemultiuser/cts/RoleManagerMultiUserTest.kt +++ b/tests/cts/rolemultiuser/src/android/app/rolemultiuser/cts/RoleManagerMultiUserTest.kt @@ -31,6 +31,7 @@ import android.util.Pair import androidx.test.filters.SdkSuppress import androidx.test.rule.ActivityTestRule import androidx.test.uiautomator.By +import com.android.bedstead.enterprise.annotations.EnsureDoesNotHaveUserRestriction import com.android.bedstead.enterprise.annotations.EnsureHasNoWorkProfile import com.android.bedstead.enterprise.annotations.EnsureHasUserRestriction import com.android.bedstead.enterprise.annotations.EnsureHasWorkProfile @@ -53,6 +54,7 @@ import com.android.bedstead.nene.TestApis.context import com.android.bedstead.nene.TestApis.permissions import com.android.bedstead.nene.TestApis.users import com.android.bedstead.nene.types.OptionalBoolean +import com.android.bedstead.nene.userrestrictions.CommonUserRestrictions.DISALLOW_ADD_MANAGED_PROFILE import com.android.bedstead.nene.users.UserReference import com.android.bedstead.nene.users.UserType import com.android.bedstead.permissions.CommonPermissions.INTERACT_ACROSS_USERS_FULL @@ -69,7 +71,6 @@ import com.android.compatibility.common.util.UiAutomatorUtils2.waitFindObject import com.android.compatibility.common.util.UiAutomatorUtils2.waitFindObjectOrNull import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertWithMessage -import java.util.Objects import java.util.concurrent.CompletableFuture import java.util.concurrent.TimeUnit import java.util.function.Consumer @@ -315,7 +316,7 @@ class RoleManagerMultiUserTest { // initialUser needs to be not the targetUser val targetActiveUser = users().current().userHandle() val initialUser = - if (Objects.equals(targetActiveUser, deviceState.initialUser().userHandle())) { + if (targetActiveUser == deviceState.initialUser().userHandle()) { deviceState.workProfile().userHandle() } else { deviceState.initialUser().userHandle() @@ -570,6 +571,7 @@ class RoleManagerMultiUserTest { @EnsureCanAddUser @EnsureHasNoWorkProfile @RequireRunOnPrimaryUser + @EnsureDoesNotHaveUserRestriction(DISALLOW_ADD_MANAGED_PROFILE) @Test @Throws(Exception::class) fun ensureActiveUserSetToParentOnUserRemoved() { @@ -1645,7 +1647,12 @@ class RoleManagerMultiUserTest { getUiDevice().waitForIdle() // CollapsingToolbar title can't be found by text, so using description instead. - assertNull(waitFindObjectOrNull(By.desc(PROFILE_GROUP_EXCLUSIVITY_ROLE_LABEL))) + assertNull( + waitFindObjectOrNull( + By.desc(PROFILE_GROUP_EXCLUSIVITY_ROLE_LABEL), + IDLE_TIMEOUT_MILLIS, + ) + ) pressBack() pressBack() @@ -1681,7 +1688,12 @@ class RoleManagerMultiUserTest { getUiDevice().waitForIdle() // CollapsingToolbar title can't be found by text, so using description instead. - assertNull(waitFindObjectOrNull(By.desc(PROFILE_GROUP_EXCLUSIVITY_ROLE_LABEL))) + assertNull( + waitFindObjectOrNull( + By.desc(PROFILE_GROUP_EXCLUSIVITY_ROLE_LABEL), + IDLE_TIMEOUT_MILLIS, + ) + ) pressBack() pressBack() @@ -1816,7 +1828,8 @@ class RoleManagerMultiUserTest { if (isWatch) { assertNull( waitFindObjectOrNull( - By.clickable(true).checked(true).hasDescendant(By.text(targetAppLabel)) + By.clickable(true).checked(true).hasDescendant(By.text(targetAppLabel)), + IDLE_TIMEOUT_MILLIS, ) ) } else { @@ -1824,7 +1837,8 @@ class RoleManagerMultiUserTest { waitFindObjectOrNull( By.clickable(true) .hasDescendant(By.checkable(true).checked(true)) - .hasDescendant(By.text(targetAppLabel)) + .hasDescendant(By.text(targetAppLabel)), + IDLE_TIMEOUT_MILLIS, ) ) } @@ -1895,7 +1909,8 @@ class RoleManagerMultiUserTest { if (isWatch) { assertNull( waitFindObjectOrNull( - By.clickable(true).checked(true).hasDescendant(By.text(targetAppLabel)) + By.clickable(true).checked(true).hasDescendant(By.text(targetAppLabel)), + IDLE_TIMEOUT_MILLIS, ) ) } else { @@ -1903,7 +1918,8 @@ class RoleManagerMultiUserTest { waitFindObjectOrNull( By.clickable(true) .hasDescendant(By.checkable(true).checked(true)) - .hasDescendant(By.text(targetAppLabel)) + .hasDescendant(By.text(targetAppLabel)), + IDLE_TIMEOUT_MILLIS, ) ) } @@ -2069,10 +2085,8 @@ class RoleManagerMultiUserTest { } } - @Throws(java.lang.Exception::class) private fun installAppForAllUsers() { SystemUtil.runShellCommandOrThrow("pm install -r --user all $APP_APK_PATH") - SystemUtil.waitForBroadcasts() } private fun uninstallAppForAllUsers() { @@ -2099,8 +2113,7 @@ class RoleManagerMultiUserTest { } val result: Pair<Int, Intent?> = clickButtonAndWaitForResult(allow) val expectedResult = - if (allow && Objects.equals(targetActiveUser, users().instrumented().userHandle())) - Activity.RESULT_OK + if (allow && targetActiveUser == users().instrumented().userHandle()) Activity.RESULT_OK else Activity.RESULT_CANCELED assertThat(result.first).isEqualTo(expectedResult) @@ -2118,7 +2131,8 @@ class RoleManagerMultiUserTest { } private fun roleRequestNotShown() { - val requestRoleItem = waitFindObjectOrNull(By.textStartsWith(APP_LABEL)) + val requestRoleItem = + waitFindObjectOrNull(By.textStartsWith(APP_LABEL), IDLE_TIMEOUT_MILLIS) assertNull(requestRoleItem) val result: Pair<Int, Intent?> = waitForResult() @@ -2148,9 +2162,9 @@ class RoleManagerMultiUserTest { ) { for (userReference in users().profileGroup(deviceState.initialUser())) { val user = userReference.userHandle() - if (Objects.equals(user, expectedActiveUser)) { - val roleHolders = - roleManager.getRoleHoldersAsUser(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, user) + val roleHolders = + roleManager.getRoleHoldersAsUser(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, user) + if (user == expectedActiveUser) { assertWithMessage( "Expected user ${user.identifier} to have a role holder for " + " $PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME" @@ -2169,9 +2183,7 @@ class RoleManagerMultiUserTest { "Expected user ${user.identifier} to not have a role holder for" + " $PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME" ) - .that( - roleManager.getRoleHoldersAsUser(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME, user) - ) + .that(roleHolders) .isEmpty() } } @@ -2183,7 +2195,7 @@ class RoleManagerMultiUserTest { for (userReference in users().profileGroup(deviceState.initialUser())) { val userRoleManager = getRoleManagerForUser(userReference) val user = userReference.userHandle() - if (Objects.equals(user, expectedActiveUser)) { + if (user == expectedActiveUser) { assertWithMessage("Expected default application for user ${user.identifier}") .that( userRoleManager.getDefaultApplication(PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME) @@ -2251,6 +2263,7 @@ class RoleManagerMultiUserTest { companion object { private const val TIMEOUT_MILLIS: Long = (15 * 1000).toLong() + private const val IDLE_TIMEOUT_MILLIS: Long = (2 * 1000).toLong() private const val PROFILE_GROUP_EXCLUSIVITY_ROLE_NAME = RoleManager.ROLE_RESERVED_FOR_TESTING_PROFILE_GROUP_EXCLUSIVITY private const val PROFILE_GROUP_EXCLUSIVITY_ROLE_LABEL = diff --git a/tests/cts/safetycenter/AndroidTest.xml b/tests/cts/safetycenter/AndroidTest.xml index 6d8c3069c..ed161f0b6 100644 --- a/tests/cts/safetycenter/AndroidTest.xml +++ b/tests/cts/safetycenter/AndroidTest.xml @@ -47,6 +47,10 @@ <!-- Disable syncing to prevent overwriting flags during testing. --> <option name="run-command" value="device_config set_sync_disabled_for_tests persistent" /> <option name="teardown-command" value="device_config set_sync_disabled_for_tests none" /> + <!-- Belt-and-braces attempt to dismiss keyguard. Tradefed should have already done this + for us, but this is a precaution in an attempt to mitigate b/379620557. --> + <option name="run-command" value="input keyevent KEYCODE_WAKEUP" /> + <option name="run-command" value="wm dismiss-keyguard" /> <!-- Dismiss any system dialogs (e.g. crashes, ANR). --> <option name="run-command" value="am broadcast -a android.intent.action.CLOSE_SYSTEM_DIALOGS --receiver-foreground" /> </target_preparer> diff --git a/tests/functional/safetycenter/multiusers/AndroidTest.xml b/tests/functional/safetycenter/multiusers/AndroidTest.xml index 20032357a..bfb7fdbf2 100644 --- a/tests/functional/safetycenter/multiusers/AndroidTest.xml +++ b/tests/functional/safetycenter/multiusers/AndroidTest.xml @@ -47,6 +47,10 @@ <!-- Disable syncing to prevent overwriting flags during testing. --> <option name="run-command" value="device_config set_sync_disabled_for_tests persistent" /> <option name="teardown-command" value="device_config set_sync_disabled_for_tests none" /> + <!-- Belt-and-braces attempt to dismiss keyguard. Tradefed should have already done this + for us, but this is a precaution in an attempt to mitigate b/379620557. --> + <option name="run-command" value="input keyevent KEYCODE_WAKEUP" /> + <option name="run-command" value="wm dismiss-keyguard" /> <!-- Dismiss any system dialogs (e.g. crashes, ANR). --> <option name="run-command" value="am broadcast -a android.intent.action.CLOSE_SYSTEM_DIALOGS --receiver-foreground" /> </target_preparer> diff --git a/tests/functional/safetycenter/safetycenteractivity/AndroidTest.xml b/tests/functional/safetycenter/safetycenteractivity/AndroidTest.xml index a1826653f..ee79dcd2a 100644 --- a/tests/functional/safetycenter/safetycenteractivity/AndroidTest.xml +++ b/tests/functional/safetycenter/safetycenteractivity/AndroidTest.xml @@ -47,6 +47,10 @@ <!-- Disable syncing to prevent overwriting flags during testing. --> <option name="run-command" value="device_config set_sync_disabled_for_tests persistent" /> <option name="teardown-command" value="device_config set_sync_disabled_for_tests none" /> + <!-- Belt-and-braces attempt to dismiss keyguard. Tradefed should have already done this + for us, but this is a precaution in an attempt to mitigate b/379620557. --> + <option name="run-command" value="input keyevent KEYCODE_WAKEUP" /> + <option name="run-command" value="wm dismiss-keyguard" /> <!-- Dismiss any system dialogs (e.g. crashes, ANR). --> <option name="run-command" value="am broadcast -a android.intent.action.CLOSE_SYSTEM_DIALOGS --receiver-foreground" /> </target_preparer> diff --git a/tests/functional/safetycenter/singleuser/AndroidTest.xml b/tests/functional/safetycenter/singleuser/AndroidTest.xml index af040eb6f..f778ca93e 100644 --- a/tests/functional/safetycenter/singleuser/AndroidTest.xml +++ b/tests/functional/safetycenter/singleuser/AndroidTest.xml @@ -47,8 +47,8 @@ <!-- Disable syncing to prevent overwriting flags during testing. --> <option name="run-command" value="device_config set_sync_disabled_for_tests persistent" /> <option name="teardown-command" value="device_config set_sync_disabled_for_tests none" /> - <!-- TODO(b/379928062): Ensure device not on lockscreen. Reassess when keyguard bug is - closed --> + <!-- Belt-and-braces attempt to dismiss keyguard. Tradefed should have already done this + for us, but this is a precaution in an attempt to mitigate b/379620557. --> <option name="run-command" value="input keyevent KEYCODE_WAKEUP" /> <option name="run-command" value="wm dismiss-keyguard" /> <!-- Dismiss any system dialogs (e.g. crashes, ANR). --> diff --git a/tests/functional/safetycenter/subpages/AndroidTest.xml b/tests/functional/safetycenter/subpages/AndroidTest.xml index c3245e9d7..ac493841f 100644 --- a/tests/functional/safetycenter/subpages/AndroidTest.xml +++ b/tests/functional/safetycenter/subpages/AndroidTest.xml @@ -47,6 +47,10 @@ <!-- Disable syncing to prevent overwriting flags during testing. --> <option name="run-command" value="device_config set_sync_disabled_for_tests persistent" /> <option name="teardown-command" value="device_config set_sync_disabled_for_tests none" /> + <!-- Belt-and-braces attempt to dismiss keyguard. Tradefed should have already done this + for us, but this is a precaution in an attempt to mitigate b/379620557. --> + <option name="run-command" value="input keyevent KEYCODE_WAKEUP" /> + <option name="run-command" value="wm dismiss-keyguard" /> <!-- Dismiss any system dialogs (e.g. crashes, ANR). --> <option name="run-command" value="am broadcast -a android.intent.action.CLOSE_SYSTEM_DIALOGS --receiver-foreground" /> </target_preparer> diff --git a/tests/hostside/safetycenter/AndroidTest.xml b/tests/hostside/safetycenter/AndroidTest.xml index a28b70c3c..41f0bcc40 100644 --- a/tests/hostside/safetycenter/AndroidTest.xml +++ b/tests/hostside/safetycenter/AndroidTest.xml @@ -32,6 +32,10 @@ <!-- Disable syncing to prevent overwriting flags during testing. --> <option name="run-command" value="device_config set_sync_disabled_for_tests persistent" /> <option name="teardown-command" value="device_config set_sync_disabled_for_tests none" /> + <!-- Belt-and-braces attempt to dismiss keyguard. Tradefed should have already done this + for us, but this is a precaution in an attempt to mitigate b/379620557. --> + <option name="run-command" value="input keyevent KEYCODE_WAKEUP" /> + <option name="run-command" value="wm dismiss-keyguard" /> <!-- Dismiss any system dialogs (e.g. crashes, ANR). --> <option name="run-command" value="am broadcast -a android.intent.action.CLOSE_SYSTEM_DIALOGS --receiver-foreground" /> </target_preparer> |