diff options
21 files changed, 380 insertions, 63 deletions
diff --git a/PermissionController/res/values-bn/strings.xml b/PermissionController/res/values-bn/strings.xml index ae5f1c3cf..c17a53046 100644 --- a/PermissionController/res/values-bn/strings.xml +++ b/PermissionController/res/values-bn/strings.xml @@ -45,13 +45,13 @@ <string name="permission_add_background_warning_template" msgid="1812914855915092273">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> অ্যাপটিকে সব সময় এটি করার অনুমতি দেবেন?: <xliff:g id="ACTION">%2$s</xliff:g>"</string> <string name="allow_permission_foreground_only" msgid="116465816039675404">"শুধুমাত্র অ্যাপ ব্যবহার করার সময়"</string> <string name="allow_permission_always" msgid="5194342531206054051">"সব সময়"</string> - <string name="deny_permission_deny_and_dont_ask_again" msgid="6106035221490102341">"অনুমতি দেবেন না এবং আর জিজ্ঞাসা করবেন না"</string> + <string name="deny_permission_deny_and_dont_ask_again" msgid="6106035221490102341">"অনুমতি দেবেন না ও আবার জিজ্ঞাসা করা হোক তা চান না"</string> <string name="permission_revoked_count" msgid="4785082705441547086">"<xliff:g id="COUNT">%1$d</xliff:g>টি বন্ধ করা হয়েছে"</string> <string name="permission_revoked_all" msgid="3397649017727222283">"সবগুলি বন্ধ করা হয়েছে"</string> <string name="permission_revoked_none" msgid="9213345075484381180">"কোনওটিই বন্ধ করা হয়নি"</string> <string name="grant_dialog_button_allow" msgid="5314677880021102550">"অনুমতি দিন"</string> <string name="grant_dialog_button_allow_always" msgid="4485552579273565981">"সর্বদা অনুমতি দিন"</string> - <string name="grant_dialog_button_allow_foreground" msgid="501896824973636533">"অ্যাপ ব্যবহার করার সময়"</string> + <string name="grant_dialog_button_allow_foreground" msgid="501896824973636533">"শুধুমাত্র অ্যাপ ব্যবহার করার সময়"</string> <string name="grant_dialog_button_change_to_precise_location" msgid="3273115879467236033">"সুনির্দিষ্ট লোকেশনে পরিবর্তন করুন"</string> <string name="grant_dialog_button_keey_approximate_location" msgid="438025182769080011">"আনুমানিক লোকেশন রাখুন"</string> <string name="grant_dialog_button_allow_one_time" msgid="2618088516449706391">"শুধুমাত্র এই সময়ে"</string> diff --git a/PermissionController/res/values-en-rCA/strings.xml b/PermissionController/res/values-en-rCA/strings.xml index 6f7182552..a01a0cef2 100644 --- a/PermissionController/res/values-en-rCA/strings.xml +++ b/PermissionController/res/values-en-rCA/strings.xml @@ -421,7 +421,7 @@ <string name="ongoing_usage_dialog_separator" msgid="1715181526581520068">", "</string> <string name="ongoing_usage_dialog_last_separator" msgid="4170995004748832163">" and "</string> <string name="default_app_search_keyword" msgid="8330125736889689743">"default apps"</string> - <string name="permgroup_list_microphone_and_camera" msgid="962768198001487969">"Microphone & Camera"</string> + <string name="permgroup_list_microphone_and_camera" msgid="962768198001487969">"Microphone and camera"</string> <string name="settings_button" msgid="4414988414732479636">"Settings"</string> <string name="default_apps" msgid="5119201969348748639">"Default apps"</string> <string name="no_default_apps" msgid="2593466527182950231">"No default apps"</string> diff --git a/PermissionController/res/values-ur/strings.xml b/PermissionController/res/values-ur/strings.xml index e6f6ac167..f1d9a96dc 100644 --- a/PermissionController/res/values-ur/strings.xml +++ b/PermissionController/res/values-ur/strings.xml @@ -492,10 +492,10 @@ <string name="permgrouprequest_device_aware_read_media_visual" msgid="5492319750632751551">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> کو آپ کے <b><xliff:g id="DEVICE">%2$s</xliff:g></b> پر تصاویر اور ویڈیوز تک رسائی کی اجازت دیں؟"</string> <string name="permgrouprequest_more_photos" msgid="128933814654231321">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> کو اس آلے پر مزید تصاویر اور ویڈیوز تک رسائی کی اجازت دیں؟"</string> <string name="permgrouprequest_device_aware_more_photos" msgid="8946782319103584021">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> کو آپ کے <b><xliff:g id="DEVICE">%2$s</xliff:g></b> پر مزید تصاویر اور ویڈیوز تک رسائی کی اجازت دیں؟"</string> - <string name="permgrouprequest_microphone" msgid="2825208549114811299">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> کو آڈیو ریکارڈ کرنے کی اجازت دیں؟"</string> + <string name="permgrouprequest_microphone" msgid="2825208549114811299">"آڈیو ریکارڈ کرنے کے لیے <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> کو اجازت دیں؟"</string> <string name="permgrouprequest_device_aware_microphone" msgid="1266843551173029370">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> کو آپ کے <b><xliff:g id="DEVICE">%2$s</xliff:g></b> پر آڈیو ریکارڈ کرنے کی اجازت دیں؟"</string> <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"جب آپ ایپ استعمال کر رہے ہوں تب ایپ صرف آڈیو ریکارڈ کر پائے گی"</string> - <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> کو آڈیو ریکارڈ کرنے کی اجازت دیں؟"</string> + <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"آڈیو ریکارڈ کرنے کے لیے <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> کو اجازت دیں؟"</string> <string name="permgroupbackgroundrequest_device_aware_microphone" msgid="4990337225146130185">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> کو آپ کے <b><xliff:g id="DEVICE">%2$s</xliff:g></b> پر آڈیو ریکارڈ کرنے کی اجازت دیں؟"</string> <string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"ممکن ہے یہ ایپ ہر وقت آڈیو ریکارڈ کرنا چاہے، اگرچہ آپ ایپ استعمال نہ کر رہے ہوں۔ "<annotation id="link">"ترتیبات میں اجازت دیں۔"</annotation></string> <string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>? کے ليے مائیکروفون تک رسائی تبدیل کریں؟"</string> diff --git a/PermissionController/role-controller/java/com/android/role/controller/model/RoleParser.java b/PermissionController/role-controller/java/com/android/role/controller/model/RoleParser.java index 3d89e12c0..cc2d102c8 100644 --- a/PermissionController/role-controller/java/com/android/role/controller/model/RoleParser.java +++ b/PermissionController/role-controller/java/com/android/role/controller/model/RoleParser.java @@ -172,7 +172,7 @@ public class RoleParser { * Retrieves the roles.xml resource from a context */ private XmlResourceParser getRolesXml() { - if (SdkLevel.isAtLeastV() && Flags.roleControllerInSystemServer()) { + if (SdkLevel.isAtLeastV() && Flags.systemServerRoleControllerEnabled()) { Resources resources = ResourceUtils.getPermissionControllerResources(mContext); int resourceId = resources.getIdentifier("roles", "xml", ResourceUtils.RESOURCE_PACKAGE_NAME_PERMISSION_CONTROLLER); diff --git a/PermissionController/role-controller/java/com/android/role/controller/util/ResourceUtils.java b/PermissionController/role-controller/java/com/android/role/controller/util/ResourceUtils.java index 2617b953a..f8f12108a 100644 --- a/PermissionController/role-controller/java/com/android/role/controller/util/ResourceUtils.java +++ b/PermissionController/role-controller/java/com/android/role/controller/util/ResourceUtils.java @@ -42,7 +42,7 @@ public class ResourceUtils { @NonNull private static Context getPermissionControllerContext(@NonNull Context context) { - if (!SdkLevel.isAtLeastV() || !Flags.roleControllerInSystemServer()) { + if (!SdkLevel.isAtLeastV() || !Flags.systemServerRoleControllerEnabled()) { // We don't have the getPermissionControllerPackageName() API below V, // but role controller always runs in PermissionController below V. return context; diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionViewModel.kt index 65a715738..971542e2b 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionViewModel.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionViewModel.kt @@ -254,7 +254,6 @@ class AppPermissionViewModel( mutableMapOf<String, LightAppPermGroupLiveData>() init { - addSource(appPermGroupLiveData) { appPermGroup -> lightAppPermGroup = appPermGroup if (permGroupName in PermissionMapping.STORAGE_SUPERGROUP_PERMISSIONS) { @@ -397,6 +396,17 @@ class AppPermissionViewModel( deniedState.isChecked = !group.isGranted selectState.isChecked = isPartialStorageGrant(group) allowedState.isChecked = group.isGranted && !isPartialStorageGrant(group) + if (group.foreground.isPolicyFixed || group.foreground.isSystemFixed) { + allowedState.isEnabled = false + selectState.isEnabled = false + deniedState.isEnabled = false + showAdminSupportLiveData.value = admin + val detailId = + getDetailResIdForFixedByPolicyPermissionGroup(group, admin != null) + if (detailId != 0) { + detailResIdLiveData.value = detailId to null + } + } } else { // Allow / Deny case allowedState.isShown = true @@ -657,8 +667,12 @@ class AppPermissionViewModel( fun openPhotoPicker(fragment: Fragment) { val appPermGroup = lightAppPermGroup ?: return - openPhotoPickerForApp(fragment.requireActivity(), appPermGroup.packageInfo.uid, - appPermGroup.foregroundPermNames, 0) + openPhotoPickerForApp( + fragment.requireActivity(), + appPermGroup.packageInfo.uid, + appPermGroup.foregroundPermNames, + 0 + ) } /** diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsFragment.kt index 84acd09fd..a0703b10c 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsFragment.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsFragment.kt @@ -20,6 +20,7 @@ import android.app.Activity import android.content.Intent import android.content.pm.PackageInfo import android.content.pm.PackageManager +import android.os.Build import android.os.Bundle import android.os.UserHandle import android.util.Log @@ -27,20 +28,36 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.Toast +import androidx.annotation.RequiresApi import androidx.compose.ui.platform.ComposeView import androidx.core.os.BundleCompat import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider +import com.android.modules.utils.build.SdkLevel import com.android.permissioncontroller.Constants.EXTRA_SESSION_ID import com.android.permissioncontroller.R import com.android.permissioncontroller.permission.model.AppPermissions +import com.android.permissioncontroller.permission.model.v31.AppPermissionUsage +import com.android.permissioncontroller.permission.model.v31.PermissionUsages +import com.android.permissioncontroller.permission.model.v31.PermissionUsages.PermissionsUsagesChangeCallback import com.android.permissioncontroller.permission.ui.model.AppPermissionGroupsViewModel import com.android.permissioncontroller.permission.ui.model.AppPermissionGroupsViewModelFactory import com.android.permissioncontroller.permission.ui.wear.model.AppPermissionGroupsRevokeDialogViewModel import com.android.permissioncontroller.permission.ui.wear.model.AppPermissionGroupsRevokeDialogViewModelFactory +import com.android.permissioncontroller.permission.ui.wear.model.WearAppPermissionUsagesViewModel +import com.android.permissioncontroller.permission.ui.wear.model.WearAppPermissionUsagesViewModelFactory +import com.android.permissioncontroller.permission.utils.KotlinUtils.is7DayToggleEnabled +import java.time.Instant +import java.util.concurrent.TimeUnit -class WearAppPermissionGroupsFragment : Fragment() { +class WearAppPermissionGroupsFragment : Fragment(), PermissionsUsagesChangeCallback { + private lateinit var permissionUsages: PermissionUsages + private lateinit var wearViewModel: WearAppPermissionUsagesViewModel private lateinit var helper: WearAppPermissionGroupsHelper + + // Suppress warning of the deprecated class [android.app.LoaderManager] since other form factors + // are using the class to load PermissionUsages. + @Suppress("DEPRECATION") override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -74,17 +91,54 @@ class WearAppPermissionGroupsFragment : Fragment() { val factory = AppPermissionGroupsViewModelFactory(packageName, user, sessionId) val viewModel = ViewModelProvider(this, factory).get(AppPermissionGroupsViewModel::class.java) + wearViewModel = + ViewModelProvider(this, WearAppPermissionUsagesViewModelFactory()) + .get(WearAppPermissionUsagesViewModel::class.java) val revokeDialogViewModel = ViewModelProvider(this, AppPermissionGroupsRevokeDialogViewModelFactory()) .get(AppPermissionGroupsRevokeDialogViewModel::class.java) + + val context = requireContext() + + // If the build type is below S, the app ops for permission usage can't be found. Thus, we + // shouldn't load permission usages, for them. + if (SdkLevel.isAtLeastS()) { + permissionUsages = PermissionUsages(context) + val aggregateDataFilterBeginDays = + (if (is7DayToggleEnabled()) + AppPermissionGroupsViewModel.AGGREGATE_DATA_FILTER_BEGIN_DAYS_7 + else AppPermissionGroupsViewModel.AGGREGATE_DATA_FILTER_BEGIN_DAYS_1) + .toLong() + + val filterTimeBeginMillis = + Math.max( + System.currentTimeMillis() - + TimeUnit.DAYS.toMillis(aggregateDataFilterBeginDays), + Instant.EPOCH.toEpochMilli() + ) + permissionUsages.load( + null, + null, + filterTimeBeginMillis, + Long.MAX_VALUE, + PermissionUsages.USAGE_FLAG_LAST, + requireActivity().getLoaderManager(), + false, + false, + this, + false + ) + } helper = WearAppPermissionGroupsHelper( - context = requireContext(), + context = context, fragment = this, user = user, + packageName = packageName, sessionId = sessionId, appPermissions = appPermissions, viewModel = viewModel, + wearViewModel = wearViewModel, revokeDialogViewModel = revokeDialogViewModel ) @@ -96,6 +150,19 @@ class WearAppPermissionGroupsFragment : Fragment() { helper.logAndClearToggledGroups() } + @RequiresApi(Build.VERSION_CODES.S) + override fun onPermissionUsagesChanged() { + if (permissionUsages.usages.isEmpty()) { + return + } + if (getContext() == null) { + // Async result has come in after our context is gone. + return + } + wearViewModel.appPermissionUsages.value = + ArrayList<AppPermissionUsage>(permissionUsages.usages) + } + companion object { const val LOG_TAG = "WearAppPermissionGroups" } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsHelper.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsHelper.kt index d1e198b3d..0ccde86be 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsHelper.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsHelper.kt @@ -26,12 +26,14 @@ import android.util.ArraySet import android.util.Log import androidx.fragment.app.Fragment import androidx.navigation.fragment.findNavController +import com.android.permission.flags.Flags import com.android.permissioncontroller.R import com.android.permissioncontroller.hibernation.isHibernationEnabled import com.android.permissioncontroller.permission.model.AppPermissionGroup import com.android.permissioncontroller.permission.model.AppPermissions import com.android.permissioncontroller.permission.model.Permission import com.android.permissioncontroller.permission.model.livedatatypes.HibernationSettingState +import com.android.permissioncontroller.permission.model.v31.AppPermissionUsage import com.android.permissioncontroller.permission.ui.Category import com.android.permissioncontroller.permission.ui.LocationProviderInterceptDialog import com.android.permissioncontroller.permission.ui.handheld.AppPermissionFragment @@ -40,6 +42,7 @@ import com.android.permissioncontroller.permission.ui.model.AppPermissionGroupsV import com.android.permissioncontroller.permission.ui.model.AppPermissionGroupsViewModel.PermSubtitle import com.android.permissioncontroller.permission.ui.wear.model.AppPermissionGroupsRevokeDialogViewModel import com.android.permissioncontroller.permission.ui.wear.model.RevokeDialogArgs +import com.android.permissioncontroller.permission.ui.wear.model.WearAppPermissionUsagesViewModel import com.android.permissioncontroller.permission.utils.ArrayUtils import com.android.permissioncontroller.permission.utils.LocationUtils import com.android.permissioncontroller.permission.utils.Utils @@ -50,16 +53,26 @@ class WearAppPermissionGroupsHelper( val context: Context, val fragment: Fragment, val user: UserHandle, + val packageName: String, val sessionId: Long, private val appPermissions: AppPermissions, val viewModel: AppPermissionGroupsViewModel, + val wearViewModel: WearAppPermissionUsagesViewModel, val revokeDialogViewModel: AppPermissionGroupsRevokeDialogViewModel, private val toggledGroups: ArraySet<AppPermissionGroup> = ArraySet() ) { - fun getPermissionGroupChipParams(): List<PermissionGroupChipParam> { + fun getPermissionGroupChipParams( + appPermissionUsages: List<AppPermissionUsage> + ): List<PermissionGroupChipParam> { if (DEBUG) { Log.d(TAG, "getPermissionGroupChipParams() called") } + val groupUsageLastAccessTime: MutableMap<String, Long> = HashMap() + viewModel.extractGroupUsageLastAccessTime( + groupUsageLastAccessTime, + appPermissionUsages, + packageName + ) val groupUiInfos = viewModel.packagePermGroupsLiveData.value val groups: List<AppPermissionGroup> = appPermissions.permissionGroups @@ -107,7 +120,11 @@ class WearAppPermissionGroupsHelper( label = group.label.toString(), summary = bookKeeping[group.name]?.let { - getSummary(category, it.subtitle) + getSummary( + category, + it, + groupUsageLastAccessTime[it.groupName] + ) }, onClick = { onPermissionGroupClicked(group, category.categoryName) } ) @@ -118,7 +135,29 @@ class WearAppPermissionGroupsHelper( return list } - private fun getSummary(category: Category?, subtitle: PermSubtitle): Int? { + private fun getSummary( + category: Category?, + groupUiInfo: GroupUiInfo, + lastAccessTime: Long? + ): String { + val grantSummary = + getGrantSummary(category, groupUiInfo)?.let { context.getString(it) } ?: "" + if (!Flags.wearPrivacyDashboardEnabled()) { + return grantSummary + } + val accessSummary = + viewModel.getPreferenceSummary(groupUiInfo, context, lastAccessTime).let { + if (it.isNotEmpty()) { + System.lineSeparator() + it + } else { + it + } + } + return grantSummary + accessSummary + } + + private fun getGrantSummary(category: Category?, groupUiInfo: GroupUiInfo): Int? { + val subtitle = groupUiInfo.subtitle if (category != null) { when (category) { Category.ALLOWED -> return R.string.allowed_header @@ -335,7 +374,7 @@ data class PermissionGroupChipParam( val group: AppPermissionGroup, val perm: PermissionInfo? = null, val label: String, - val summary: Int? = null, + val summary: String? = null, val enabled: Boolean = true, val checked: Boolean? = null, val onClick: () -> Unit = {}, diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsScreen.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsScreen.kt index b84e23566..bc840bac9 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsScreen.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsScreen.kt @@ -37,13 +37,14 @@ import com.android.permissioncontroller.permission.ui.wear.model.RevokeDialogArg fun WearAppPermissionGroupsScreen(helper: WearAppPermissionGroupsHelper) { val packagePermGroups = helper.viewModel.packagePermGroupsLiveData.observeAsState(emptyMap()) val autoRevoke = helper.viewModel.autoRevokeLiveData.observeAsState(null) + val appPermissionUsages = helper.wearViewModel.appPermissionUsages.observeAsState(emptyList()) val showRevokeDialog = helper.revokeDialogViewModel.showDialogLiveData.observeAsState(false) var isLoading by remember { mutableStateOf(true) } Box { WearAppPermissionGroupsContent( isLoading, - helper.getPermissionGroupChipParams(), + helper.getPermissionGroupChipParams(appPermissionUsages.value), helper.getAutoRevokeChipParam(autoRevoke.value) ) RevokeDialog( @@ -80,7 +81,9 @@ internal fun WearAppPermissionGroupsContent( } else { Chip( label = info.label, - secondaryLabel = info.summary?.let { stringResource(info.summary) }, + labelMaxLines = Integer.MAX_VALUE, + secondaryLabel = info.summary?.let { info.summary }, + secondaryLabelMaxLines = Integer.MAX_VALUE, enabled = info.enabled, onClick = info.onClick ) diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionAppsFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionAppsFragment.kt index 4fb5b264b..d8eb71e0e 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionAppsFragment.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionAppsFragment.kt @@ -18,19 +18,26 @@ package com.android.permissioncontroller.permission.ui.wear import android.Manifest import android.content.Intent +import android.os.Build import android.os.Bundle import android.os.UserHandle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.annotation.RequiresApi import androidx.compose.ui.platform.ComposeView import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider import com.android.modules.utils.build.SdkLevel import com.android.permissioncontroller.Constants +import com.android.permissioncontroller.permission.model.v31.AppPermissionUsage +import com.android.permissioncontroller.permission.model.v31.PermissionUsages +import com.android.permissioncontroller.permission.model.v31.PermissionUsages.PermissionsUsagesChangeCallback import com.android.permissioncontroller.permission.ui.handheld.AppPermissionFragment import com.android.permissioncontroller.permission.ui.model.PermissionAppsViewModel import com.android.permissioncontroller.permission.ui.model.PermissionAppsViewModelFactory +import com.android.permissioncontroller.permission.ui.wear.model.WearAppPermissionUsagesViewModel +import com.android.permissioncontroller.permission.ui.wear.model.WearAppPermissionUsagesViewModelFactory /** * This is a condensed version of @@ -41,9 +48,15 @@ import com.android.permissioncontroller.permission.ui.model.PermissionAppsViewMo * * <p>Shows a list of apps which request at least on permission of this group. */ -class WearPermissionAppsFragment : Fragment() { +class WearPermissionAppsFragment : Fragment(), PermissionsUsagesChangeCallback { private val LOG_TAG = "PermissionAppsFragment" + private lateinit var permissionUsages: PermissionUsages + private lateinit var wearViewModel: WearAppPermissionUsagesViewModel + + // Suppress warning of the deprecated class [android.app.LoaderManager] since other form factors + // are using the class to load PermissionUsages. + @Suppress("DEPRECATION") override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -62,6 +75,9 @@ class WearPermissionAppsFragment : Fragment() { val factory = PermissionAppsViewModelFactory(activity.getApplication(), permGroupName, this, Bundle()) val viewModel = ViewModelProvider(this, factory).get(PermissionAppsViewModel::class.java) + wearViewModel = + ViewModelProvider(this, WearAppPermissionUsagesViewModelFactory()) + .get(WearAppPermissionUsagesViewModel::class.java) val onAppClick: (String, UserHandle, String) -> Unit = { packageName, user, category -> run { @@ -105,6 +121,26 @@ class WearPermissionAppsFragment : Fragment() { } } + // If the build type is below S, the app ops for permission usage can't be found. Thus, we + // shouldn't load permission usages, for them. + if (SdkLevel.isAtLeastS()) { + permissionUsages = PermissionUsages(requireContext()) + + val filterTimeBeginMillis: Long = viewModel.getFilterTimeBeginMillis() + permissionUsages.load( + null, + null, + filterTimeBeginMillis, + Long.MAX_VALUE, + PermissionUsages.USAGE_FLAG_LAST, + requireActivity().getLoaderManager(), + false, + false, + this, + false + ) + } + return ComposeView(requireContext()).apply { setContent { WearPermissionAppsScreen( @@ -112,6 +148,7 @@ class WearPermissionAppsFragment : Fragment() { activity.getApplication(), permGroupName, viewModel, + wearViewModel, isStorageAndLessThanT, onAppClick, onShowSystemClick, @@ -121,4 +158,17 @@ class WearPermissionAppsFragment : Fragment() { } } } + + @RequiresApi(Build.VERSION_CODES.S) + override fun onPermissionUsagesChanged() { + if (permissionUsages.usages.isEmpty()) { + return + } + if (context == null) { + // Async result has come in after our context is gone. + return + } + wearViewModel.appPermissionUsages.value = + ArrayList<AppPermissionUsage>(permissionUsages.usages) + } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionAppsHelper.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionAppsHelper.kt index 8d1247e6a..559160b38 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionAppsHelper.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionAppsHelper.kt @@ -19,12 +19,16 @@ package com.android.permissioncontroller.permission.ui.wear import android.app.Application import android.graphics.drawable.Drawable import android.os.UserHandle +import com.android.permission.flags.Flags import com.android.permissioncontroller.R +import com.android.permissioncontroller.permission.model.v31.AppPermissionUsage import com.android.permissioncontroller.permission.ui.Category import com.android.permissioncontroller.permission.ui.model.PermissionAppsViewModel +import com.android.permissioncontroller.permission.ui.wear.model.WearAppPermissionUsagesViewModel import com.android.permissioncontroller.permission.utils.KotlinUtils import com.android.permissioncontroller.permission.utils.KotlinUtils.getPermGroupDescription import com.android.permissioncontroller.permission.utils.KotlinUtils.getPermGroupLabel +import com.android.permissioncontroller.permission.utils.Utils import com.android.settingslib.utils.applications.AppUtils import java.text.Collator import java.util.Random @@ -34,6 +38,7 @@ class WearPermissionAppsHelper( val application: Application, val permGroupName: String, val viewModel: PermissionAppsViewModel, + val wearViewModel: WearAppPermissionUsagesViewModel, private val isStorageAndLessThanT: Boolean, private val onAppClick: (String, UserHandle, String) -> Unit, val onShowSystemClick: (Boolean) -> Unit, @@ -46,10 +51,15 @@ class WearPermissionAppsHelper( fun getTitle() = getPermGroupLabel(application, permGroupName).toString() fun getSubTitle() = getPermGroupDescription(application, permGroupName).toString() fun getChipsByCategory( - categorizedApps: Map<Category, List<Pair<String, UserHandle>>> + categorizedApps: Map<Category, List<Pair<String, UserHandle>>>, + appPermissionUsages: List<AppPermissionUsage> ): Map<String, List<ChipInfo>> { val chipsByCategory: MutableMap<String, MutableList<ChipInfo>> = HashMap() + // A mapping of user + packageName to their last access timestamps for the permission group. + val groupUsageLastAccessTime: Map<String, Long> = + viewModel.extractGroupUsageLastAccessTime(appPermissionUsages) + val context = application val collator = Collator.getInstance(context.resources.configuration.locales.get(0)) val comparator = ChipComparator(collator) @@ -64,7 +74,13 @@ class WearPermissionAppsHelper( .let { if (it.first.isNotEmpty()) { chipsByCategory[STORAGE_ALLOWED_FULL] = - convertToChips(category, it.first, viewIdForLogging, comparator) + convertToChips( + category, + it.first, + viewIdForLogging, + comparator, + groupUsageLastAccessTime + ) } if (it.second.isNotEmpty()) { chipsByCategory[STORAGE_ALLOWED_SCOPED] = @@ -72,7 +88,8 @@ class WearPermissionAppsHelper( category, it.second, viewIdForLogging, - comparator + comparator, + groupUsageLastAccessTime ) } } @@ -82,7 +99,13 @@ class WearPermissionAppsHelper( val list = categorizedApps[category] if (!list.isNullOrEmpty()) { chipsByCategory[category.categoryName] = - convertToChips(category, list, viewIdForLogging, comparator) + convertToChips( + category, + list, + viewIdForLogging, + comparator, + groupUsageLastAccessTime + ) } } @@ -95,17 +118,20 @@ class WearPermissionAppsHelper( category: Category, list: List<Pair<String, UserHandle>>, viewIdForLogging: Long, - comparator: Comparator<ChipInfo> + comparator: Comparator<ChipInfo>, + groupUsageLastAccessTime: Map<String, Long> ) = list .map { p -> + val lastAccessTime = groupUsageLastAccessTime[(p.second.toString() + p.first)] createAppChipInfo( application, p.first, p.second, category, onAppClick, - viewIdForLogging + viewIdForLogging, + lastAccessTime ) } .sortedWith(comparator) @@ -121,7 +147,8 @@ class WearPermissionAppsHelper( user: UserHandle, category: Category, onClick: (packageName: String, user: UserHandle, category: String) -> Unit, - viewIdForLogging: Long + viewIdForLogging: Long, + lastAccessTime: Long? ): ChipInfo { if (!viewModel.creationLogged) { logFragmentCreated( @@ -133,8 +160,24 @@ class WearPermissionAppsHelper( category == Category.DENIED ) } + val summary = + if (Flags.wearPrivacyDashboardEnabled()) { + lastAccessTime?.let { + viewModel.getPreferenceSummary( + application.resources, + Utils.getPermissionLastAccessSummaryTimestamp( + lastAccessTime, + application, + permGroupName + ) + ) + } + } else { + null + } return ChipInfo( title = KotlinUtils.getPackageLabel(application, packageName, user), + summary = summary, contentDescription = AppUtils.getAppContentDescription(application, packageName, user.getIdentifier()), icon = KotlinUtils.getBadgedPackageIcon(application, packageName, user), @@ -184,6 +227,7 @@ class WearPermissionAppsHelper( class ChipInfo( val title: String, + val summary: String? = null, val contentDescription: String? = null, val onClick: () -> Unit = {}, val icon: Drawable? = null, diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionAppsScreen.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionAppsScreen.kt index 08733d3b2..154fd0bff 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionAppsScreen.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionAppsScreen.kt @@ -38,12 +38,14 @@ fun WearPermissionAppsScreen(helper: WearPermissionAppsHelper) { val categorizedApps = helper.categorizedAppsLiveData().observeAsState(emptyMap()) val hasSystemApps = helper.hasSystemAppsLiveData().observeAsState(false) val showSystem = helper.shouldShowSystemLiveData().observeAsState(false) + val appPermissionUsages = helper.wearViewModel.appPermissionUsages.observeAsState(emptyList()) var isLoading by remember { mutableStateOf(true) } val title = helper.getTitle() val subTitle = helper.getSubTitle() val showAlways = helper.showAlways() - val chipsByCategory = helper.getChipsByCategory(categorizedApps.value) + val chipsByCategory = + helper.getChipsByCategory(categorizedApps.value, appPermissionUsages.value) WearPermissionAppsContent( chipsByCategory, @@ -89,6 +91,8 @@ internal fun WearPermissionAppsContent( Chip( label = it.title, labelMaxLines = Int.MAX_VALUE, + secondaryLabel = it.summary, + secondaryLabelMaxLines = Int.MAX_VALUE, icon = it.icon, enabled = it.enabled, onClick = { it.onClick() }, diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/model/WearAppPermissionUsagesViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/model/WearAppPermissionUsagesViewModel.kt new file mode 100644 index 000000000..85e9eaef2 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/model/WearAppPermissionUsagesViewModel.kt @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.permissioncontroller.permission.ui.wear.model + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.android.permissioncontroller.permission.model.v31.AppPermissionUsage + +class WearAppPermissionUsagesViewModel : ViewModel() { + val appPermissionUsages = MutableLiveData<List<AppPermissionUsage>>() +} + +/** Factory for a WearAppPermissionGroupsViewModel */ +class WearAppPermissionUsagesViewModelFactory : ViewModelProvider.Factory { + override fun <T : ViewModel> create(modelClass: Class<T>): T { + @Suppress("UNCHECKED_CAST") return WearAppPermissionUsagesViewModel() as T + } +} diff --git a/framework-s/api/system-current.txt b/framework-s/api/system-current.txt index f502a3231..a29a68acd 100644 --- a/framework-s/api/system-current.txt +++ b/framework-s/api/system-current.txt @@ -29,14 +29,14 @@ package android.app.role { method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public java.util.List<java.lang.String> getRoleHoldersAsUser(@NonNull String, @NonNull android.os.UserHandle); method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void isApplicationVisibleForRole(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public boolean isBypassingRoleQualification(); - method @FlaggedApi(android.permission.flags.Flags.FLAG_ROLE_CONTROLLER_IN_SYSTEM_SERVER) @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public boolean isRoleFallbackEnabled(@NonNull String); + method @FlaggedApi(android.permission.flags.Flags.FLAG_SYSTEM_SERVER_ROLE_CONTROLLER_ENABLED) @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public boolean isRoleFallbackEnabled(@NonNull String); method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void isRoleVisible(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); method @RequiresPermission(android.Manifest.permission.OBSERVE_ROLE_HOLDERS) public void removeOnRoleHoldersChangedListenerAsUser(@NonNull android.app.role.OnRoleHoldersChangedListener, @NonNull android.os.UserHandle); method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void removeRoleHolderAsUser(@NonNull String, @NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); method @Deprecated @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public boolean removeRoleHolderFromController(@NonNull String, @NonNull String); method @RequiresPermission(android.Manifest.permission.BYPASS_ROLE_QUALIFICATION) public void setBypassingRoleQualification(boolean); method @RequiresPermission(android.Manifest.permission.MANAGE_DEFAULT_APPLICATIONS) public void setDefaultApplication(@NonNull String, @Nullable String, int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); - method @FlaggedApi(android.permission.flags.Flags.FLAG_ROLE_CONTROLLER_IN_SYSTEM_SERVER) @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void setRoleFallbackEnabled(@NonNull String, boolean); + method @FlaggedApi(android.permission.flags.Flags.FLAG_SYSTEM_SERVER_ROLE_CONTROLLER_ENABLED) @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void setRoleFallbackEnabled(@NonNull String, boolean); method @Deprecated @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public void setRoleNamesFromController(@NonNull java.util.List<java.lang.String>); field public static final int MANAGE_HOLDERS_FLAG_DONT_KILL_APP = 1; // 0x1 field public static final String ROLE_DEVICE_POLICY_MANAGEMENT = "android.app.role.DEVICE_POLICY_MANAGEMENT"; diff --git a/framework-s/java/android/app/role/RoleManager.java b/framework-s/java/android/app/role/RoleManager.java index aeb229cae..3cf1e94ba 100644 --- a/framework-s/java/android/app/role/RoleManager.java +++ b/framework-s/java/android/app/role/RoleManager.java @@ -728,7 +728,7 @@ public final class RoleManager { */ @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS) - @FlaggedApi(Flags.FLAG_ROLE_CONTROLLER_IN_SYSTEM_SERVER) + @FlaggedApi(Flags.FLAG_SYSTEM_SERVER_ROLE_CONTROLLER_ENABLED) @UserHandleAware @SystemApi public boolean isRoleFallbackEnabled(@NonNull String roleName) { @@ -750,7 +750,7 @@ public final class RoleManager { */ @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS) - @FlaggedApi(Flags.FLAG_ROLE_CONTROLLER_IN_SYSTEM_SERVER) + @FlaggedApi(Flags.FLAG_SYSTEM_SERVER_ROLE_CONTROLLER_ENABLED) @UserHandleAware @SystemApi public void setRoleFallbackEnabled(@NonNull String roleName, boolean fallbackEnabled) { @@ -980,7 +980,7 @@ public final class RoleManager { @SystemApi public void isRoleVisible(@NonNull String roleName, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) { - if (SdkLevel.isAtLeastV() && Flags.roleControllerInSystemServer()) { + if (SdkLevel.isAtLeastV() && Flags.systemServerRoleControllerEnabled()) { int userId = getContextUserIfAppropriate().getIdentifier(); boolean visible; try { @@ -1021,7 +1021,7 @@ public final class RoleManager { @SystemApi public void isApplicationVisibleForRole(@NonNull String roleName, @NonNull String packageName, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) { - if (SdkLevel.isAtLeastV() && Flags.roleControllerInSystemServer()) { + if (SdkLevel.isAtLeastV() && Flags.systemServerRoleControllerEnabled()) { int userId = getContextUserIfAppropriate().getIdentifier(); boolean visible; try { diff --git a/service/api/system-server-current.txt b/service/api/system-server-current.txt index ea9c9750c..6a544126b 100644 --- a/service/api/system-server-current.txt +++ b/service/api/system-server-current.txt @@ -45,8 +45,8 @@ package com.android.role.persistence { public final class RolesState { ctor public RolesState(int, @Nullable String, @NonNull java.util.Map<java.lang.String,java.util.Set<java.lang.String>>); - ctor @FlaggedApi(android.permission.flags.Flags.FLAG_ROLE_CONTROLLER_IN_SYSTEM_SERVER) public RolesState(int, @Nullable String, @NonNull java.util.Map<java.lang.String,java.util.Set<java.lang.String>>, @NonNull java.util.Set<java.lang.String>); - method @FlaggedApi(android.permission.flags.Flags.FLAG_ROLE_CONTROLLER_IN_SYSTEM_SERVER) @NonNull public java.util.Set<java.lang.String> getFallbackEnabledRoles(); + ctor @FlaggedApi(android.permission.flags.Flags.FLAG_SYSTEM_SERVER_ROLE_CONTROLLER_ENABLED) public RolesState(int, @Nullable String, @NonNull java.util.Map<java.lang.String,java.util.Set<java.lang.String>>, @NonNull java.util.Set<java.lang.String>); + method @FlaggedApi(android.permission.flags.Flags.FLAG_SYSTEM_SERVER_ROLE_CONTROLLER_ENABLED) @NonNull public java.util.Set<java.lang.String> getFallbackEnabledRoles(); method @Nullable public String getPackagesHash(); method @NonNull public java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getRoles(); method public int getVersion(); diff --git a/service/java/com/android/role/RoleService.java b/service/java/com/android/role/RoleService.java index a282e67cf..c1a3ea4d9 100644 --- a/service/java/com/android/role/RoleService.java +++ b/service/java/com/android/role/RoleService.java @@ -328,7 +328,7 @@ public class RoleService extends SystemService implements RoleUserState.Callback if (controller == null) { UserHandle user = UserHandle.of(userId); Context context = getContext(); - if (SdkLevel.isAtLeastV() && Flags.roleControllerInSystemServer()) { + if (SdkLevel.isAtLeastV() && Flags.systemServerRoleControllerEnabled()) { controller = new LocalRoleController(user, context); } else { controller = new RemoteRoleController(user, context); diff --git a/service/java/com/android/role/persistence/RolesState.java b/service/java/com/android/role/persistence/RolesState.java index fc6b88f26..26644c358 100644 --- a/service/java/com/android/role/persistence/RolesState.java +++ b/service/java/com/android/role/persistence/RolesState.java @@ -79,7 +79,7 @@ public final class RolesState { * @param roles the roles * @param fallbackEnabledRoles the roles with fallback enabled */ - @FlaggedApi(Flags.FLAG_ROLE_CONTROLLER_IN_SYSTEM_SERVER) + @FlaggedApi(Flags.FLAG_SYSTEM_SERVER_ROLE_CONTROLLER_ENABLED) public RolesState(int version, @Nullable String packagesHash, @NonNull Map<String, Set<String>> roles, @NonNull Set<String> fallbackEnabledRoles) { mVersion = version; @@ -123,7 +123,7 @@ public final class RolesState { * @return fallback enabled roles */ @NonNull - @FlaggedApi(Flags.FLAG_ROLE_CONTROLLER_IN_SYSTEM_SERVER) + @FlaggedApi(Flags.FLAG_SYSTEM_SERVER_ROLE_CONTROLLER_ENABLED) public Set<String> getFallbackEnabledRoles() { return mFallbackEnabledRoles; } diff --git a/tests/cts/permission/src/android/permission/cts/NearbyDevicesRenouncePermissionTest.java b/tests/cts/permission/src/android/permission/cts/NearbyDevicesRenouncePermissionTest.java index a196cedfd..aeb4d1d28 100644 --- a/tests/cts/permission/src/android/permission/cts/NearbyDevicesRenouncePermissionTest.java +++ b/tests/cts/permission/src/android/permission/cts/NearbyDevicesRenouncePermissionTest.java @@ -40,15 +40,19 @@ import android.content.pm.PackageManager; import android.os.Process; import android.os.SystemClock; import android.platform.test.annotations.AppModeFull; +import android.provider.DeviceConfig; import android.util.ArraySet; import android.util.Base64; import android.util.Log; import androidx.test.InstrumentationRegistry; +import com.android.compatibility.common.util.DeviceConfigStateChangerRule; import com.android.compatibility.common.util.EnableLocationRule; import com.android.compatibility.common.util.SystemUtil; +import com.google.common.util.concurrent.Uninterruptibles; + import org.junit.After; import org.junit.Before; import org.junit.ClassRule; @@ -58,6 +62,8 @@ import org.junit.Test; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; /** * Tests behaviour when performing bluetooth scans with renounced location permission. @@ -73,11 +79,22 @@ public class NearbyDevicesRenouncePermissionTest { public static final EnableBluetoothRule sEnableBluetoothRule = new EnableBluetoothRule(true); @Rule + public DeviceConfigStateChangerRule safetyLabelChangeNotificationsEnabledConfig = + new DeviceConfigStateChangerRule( + mContext, + DeviceConfig.NAMESPACE_BLUETOOTH, + "scan_quota_count", + Integer.toString(1000) + ); + + @Rule public final EnableLocationRule enableLocationRule = new EnableLocationRule(); private AppOpsManager mAppOpsManager; - private int mLocationNoteCount; - private int mScanNoteCount; + + private volatile long mTestStartTimestamp; + private final AtomicInteger mLocationNoteCount = new AtomicInteger(0); + private final AtomicInteger mScanNoteCount = new AtomicInteger(0); private enum Result { UNKNOWN, EXCEPTION, EMPTY, FILTERED, FULL @@ -89,6 +106,13 @@ public class NearbyDevicesRenouncePermissionTest { @Before public void setUp() throws Exception { + // Sleep to guarantee that past noteOp timestamps are less than mTestStartTimestamp + Uninterruptibles.sleepUninterruptibly(2, TimeUnit.MILLISECONDS); + mTestStartTimestamp = System.currentTimeMillis(); + + mLocationNoteCount.set(0); + mScanNoteCount.set(0); + mAppOpsManager = getApplicationContext().getSystemService(AppOpsManager.class); mAppOpsManager.setOnOpNotedCallback(getApplicationContext().getMainExecutor(), new AppOpsManager.OnOpNotedCallback() { @@ -96,10 +120,12 @@ public class NearbyDevicesRenouncePermissionTest { public void onNoted(SyncNotedAppOp op) { switch (op.getOp()) { case OPSTR_FINE_LOCATION: - mLocationNoteCount++; + logNoteOp(op); + mLocationNoteCount.incrementAndGet(); break; case OPSTR_BLUETOOTH_SCAN: - mScanNoteCount++; + logNoteOp(op); + mScanNoteCount.incrementAndGet(); break; default: } @@ -113,10 +139,22 @@ public class NearbyDevicesRenouncePermissionTest { public void onAsyncNoted(AsyncNotedAppOp asyncOp) { switch (asyncOp.getOp()) { case OPSTR_FINE_LOCATION: - mLocationNoteCount++; + logNoteOp(asyncOp); + if (asyncOp.getTime() < mTestStartTimestamp) { + Log.i(TAG, "ignoring asyncOp that originated before test " + + "start"); + return; + } + mLocationNoteCount.incrementAndGet(); break; case OPSTR_BLUETOOTH_SCAN: - mScanNoteCount++; + logNoteOp(asyncOp); + if (asyncOp.getTime() < mTestStartTimestamp) { + Log.i(TAG, "ignoring asyncOp that originated before test " + + "start"); + return; + } + mScanNoteCount.incrementAndGet(); break; default: } @@ -124,26 +162,31 @@ public class NearbyDevicesRenouncePermissionTest { }); } + private void logNoteOp(SyncNotedAppOp op) { + Log.i(TAG, "OnOpNotedCallback::onNoted(op=" + op.getOp() + ")"); + } + + private void logNoteOp(AsyncNotedAppOp asyncOp) { + Log.i(TAG, "OnOpNotedCallback::" + + "onAsyncNoted(op=" + asyncOp.getOp() + + ", testStartTimestamp=" + mTestStartTimestamp + + ", noteOpTimestamp=" + asyncOp.getTime() + ")"); + } + @After public void tearDown() throws Exception { mAppOpsManager.setOnOpNotedCallback(null, null); } - private void clearNoteCounts() { - mLocationNoteCount = 0; - mScanNoteCount = 0; - } - @AppModeFull @Test public void scanWithoutRenouncingNotesBluetoothAndLocation() throws Exception { assumeTrue(supportsBluetoothLe()); - clearNoteCounts(); assertThat(performScan(Scenario.DEFAULT)).isEqualTo(Result.FULL); SystemUtil.eventually(() -> { - assertThat(mLocationNoteCount).isGreaterThan(0); - assertThat(mScanNoteCount).isGreaterThan(0); + assertThat(mLocationNoteCount.get()).isGreaterThan(0); + assertThat(mScanNoteCount.get()).isGreaterThan(0); }); } @@ -152,11 +195,10 @@ public class NearbyDevicesRenouncePermissionTest { public void scanRenouncingLocationNotesBluetoothButNotLocation() throws Exception { assumeTrue(supportsBluetoothLe()); - clearNoteCounts(); assertThat(performScan(Scenario.RENOUNCE)).isEqualTo(Result.FILTERED); SystemUtil.eventually(() -> { - assertThat(mLocationNoteCount).isEqualTo(0); - assertThat(mScanNoteCount).isGreaterThan(0); + assertThat(mLocationNoteCount.get()).isEqualTo(0); + assertThat(mScanNoteCount.get()).isGreaterThan(0); }); } @@ -165,22 +207,20 @@ public class NearbyDevicesRenouncePermissionTest { public void scanRenouncingInMiddleOfChainNotesBluetoothButNotLocation() throws Exception { assumeTrue(supportsBluetoothLe()); - clearNoteCounts(); assertThat(performScan(Scenario.RENOUNCE_MIDDLE)).isEqualTo(Result.FILTERED); SystemUtil.eventually(() -> { - assertThat(mLocationNoteCount).isEqualTo(0); - assertThat(mScanNoteCount).isGreaterThan(0); + assertThat(mLocationNoteCount.get()).isEqualTo(0); + assertThat(mScanNoteCount.get()).isGreaterThan(0); }); } @AppModeFull @Test public void scanRenouncingAtEndOfChainNotesBluetoothButNotLocation() throws Exception { - clearNoteCounts(); assertThat(performScan(Scenario.RENOUNCE_END)).isEqualTo(Result.FILTERED); SystemUtil.eventually(() -> { - assertThat(mLocationNoteCount).isEqualTo(0); - assertThat(mScanNoteCount).isGreaterThan(0); + assertThat(mLocationNoteCount.get()).isEqualTo(0); + assertThat(mScanNoteCount.get()).isGreaterThan(0); }); } diff --git a/tests/cts/permissionpolicy/res/raw/android_manifest.xml b/tests/cts/permissionpolicy/res/raw/android_manifest.xml index 80978bf2f..cc68a8a37 100644 --- a/tests/cts/permissionpolicy/res/raw/android_manifest.xml +++ b/tests/cts/permissionpolicy/res/raw/android_manifest.xml @@ -2285,7 +2285,7 @@ @hide --> <permission android:name="android.permission.SUSPEND_APPS" - android:protectionLevel="signature|role" /> + android:protectionLevel="signature|role|verifier" /> <!-- @SystemApi @hide @@ -7302,6 +7302,13 @@ <permission android:name="android.permission.MODIFY_TOUCH_MODE_STATE" android:protectionLevel="signature" /> + <!-- @SystemApi Allows the holder to launch an Intent Resolver flow with custom presentation + and/or targets. + @FlaggedApi("android.service.chooser.support_nfc_resolver") + @hide --> + <permission android:name="android.permission.SHOW_CUSTOMIZED_RESOLVER" + android:protectionLevel="signature|privileged" /> + <!-- @hide Allows an application to get a People Tile preview for a given shortcut. --> <permission android:name="android.permission.GET_PEOPLE_TILE_PREVIEW" android:protectionLevel="signature|recents" /> @@ -7652,6 +7659,22 @@ <permission android:name="android.permission.PREPARE_FACTORY_RESET" android:protectionLevel="signature|privileged" /> + <!-- @SystemApi Allows focused window to override the default behavior of supported system keys. + The following keycodes are supported: + <p> KEYCODE_STEM_PRIMARY + <p>If an app is granted this permission and has a focused window, it will be allowed to + receive supported key events that are otherwise handled by the system. The app can choose + to consume the key events and trigger its own behavior, in which case the default key + behavior will be skipped. + <p>For example, KEYCODE_STEM_PRIMARY by default opens recent app launcher. If the foreground + fitness app is granted this permission, it can repurpose the KEYCODE_STEM_PRIMARY button + to pause/resume the current fitness session. + <p>Protection level: signature|privileged + @FlaggedApi("com.android.input.flags.override_key_behavior_permission_apis") + @hide --> + <permission android:name="android.permission.OVERRIDE_SYSTEM_KEY_BEHAVIOR_IN_FOCUSED_WINDOW" + android:protectionLevel="signature|privileged" /> + <!-- Attribution for Geofencing service. --> <attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/> <!-- Attribution for Country Detector. --> diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestConfigs.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestConfigs.kt index fea9b45cc..60c3b4d6a 100644 --- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestConfigs.kt +++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestConfigs.kt @@ -49,7 +49,7 @@ class SafetyCenterTestConfigs(private val context: Context) { context.packageName, PackageInfoFlags.of(GET_SIGNING_CERTIFICATES.toLong()) ) - .signingInfo + .signingInfo!! .apkContentsSigners[0] .toByteArray() ) |