summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--PermissionController/res/values-bn/strings.xml4
-rw-r--r--PermissionController/res/values-ur/strings.xml4
-rw-r--r--PermissionController/res/xml/roles.xml1
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/model/RoleParser.java2
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/util/ResourceUtils.java2
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionViewModel.kt20
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsFragment.kt71
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsHelper.kt47
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsScreen.kt7
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionAppsFragment.kt52
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionAppsHelper.kt58
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionAppsScreen.kt6
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/model/WearAppPermissionUsagesViewModel.kt33
-rw-r--r--PermissionController/tests/permissionui/Android.bp1
-rw-r--r--PermissionController/tests/permissionui/AndroidTest.xml2
-rw-r--r--PermissionController/tests/permissionui/PermissionUiReadCalendarPermissionApp/Android.bp34
-rw-r--r--PermissionController/tests/permissionui/PermissionUiReadCalendarPermissionApp/AndroidManifest.xml26
-rw-r--r--PermissionController/tests/permissionui/PermissionUiReadCalendarPermissionApp/res/values/strings.xml21
-rw-r--r--PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/wear/WearPermissionUsageDetailsFragmentTest.kt100
-rw-r--r--PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/wear/WearPermissionUsageFragmentTest.kt109
-rw-r--r--PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/wear/WearUtils.kt14
-rw-r--r--framework-s/api/system-current.txt4
-rw-r--r--framework-s/java/android/app/role/RoleManager.java8
-rw-r--r--service/api/system-server-current.txt4
-rw-r--r--service/java/com/android/role/RoleService.java2
-rw-r--r--service/java/com/android/role/persistence/RolesState.java4
-rw-r--r--tests/cts/permission/Android.bp1
-rw-r--r--tests/cts/permission/AppThatAccessesCalendarContactsBodySensorCustomPermission/AndroidManifest.xml1
-rw-r--r--tests/cts/permission/src/android/permission/cts/NearbyDevicesRenouncePermissionTest.java86
-rw-r--r--tests/cts/permission/src/android/permission/cts/OneTimePermissionTest.java5
-rw-r--r--tests/cts/permission/src/android/permission/cts/PermissionUpdateListenerTest.java126
-rw-r--r--tests/cts/permissionmultidevice/src/android/permissionmultidevice/cts/DeviceAwarePermissionGrantTest.kt8
-rw-r--r--tests/cts/permissionpolicy/res/raw/android_manifest.xml25
-rw-r--r--tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestConfigs.kt2
34 files changed, 785 insertions, 105 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">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; অ্যাপটিকে সব সময় এটি করার অনুমতি দেবেন?: <xliff:g id="ACTION">%2$s</xliff:g>"</string>
<string name="allow_permission_foreground_only" msgid="116465816039675404">"শুধুমাত্র অ্যাপ ব্যবহার করার সময়"</string>
<string name="allow_permission_always" msgid="5194342531206054051">"সব সময়"</string>
- <string name="deny_permission_deny_and_dont_ask_again" msgid="6106035221490102341">"অনুমতি দেবেন না এবং আর জিজ্ঞাসা করবেন না"</string>
+ <string name="deny_permission_deny_and_dont_ask_again" msgid="6106035221490102341">"অনুমতি দেবেন না ও আবার জিজ্ঞাসা করা হোক তা চান না"</string>
<string name="permission_revoked_count" msgid="4785082705441547086">"<xliff:g id="COUNT">%1$d</xliff:g>টি বন্ধ করা হয়েছে"</string>
<string name="permission_revoked_all" msgid="3397649017727222283">"সবগুলি বন্ধ করা হয়েছে"</string>
<string name="permission_revoked_none" msgid="9213345075484381180">"কোনওটিই বন্ধ করা হয়নি"</string>
<string name="grant_dialog_button_allow" msgid="5314677880021102550">"অনুমতি দিন"</string>
<string name="grant_dialog_button_allow_always" msgid="4485552579273565981">"সর্বদা অনুমতি দিন"</string>
- <string name="grant_dialog_button_allow_foreground" msgid="501896824973636533">"অ্যাপ ব্যবহার করার সময়"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="501896824973636533">"শুধুমাত্র অ্যাপ ব্যবহার করার সময়"</string>
<string name="grant_dialog_button_change_to_precise_location" msgid="3273115879467236033">"সুনির্দিষ্ট লোকেশনে পরিবর্তন করুন"</string>
<string name="grant_dialog_button_keey_approximate_location" msgid="438025182769080011">"আনুমানিক লোকেশন রাখুন"</string>
<string name="grant_dialog_button_allow_one_time" msgid="2618088516449706391">"শুধুমাত্র এই সময়ে"</string>
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">"‏&lt;b&gt;‏<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کو آپ کے &lt;b&gt;‏<xliff:g id="DEVICE">%2$s</xliff:g>&lt;/b&gt; پر تصاویر اور ویڈیوز تک رسائی کی اجازت دیں؟"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"‏‎&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;‎ کو اس آلے پر مزید تصاویر اور ویڈیوز تک رسائی کی اجازت دیں؟"</string>
<string name="permgrouprequest_device_aware_more_photos" msgid="8946782319103584021">"‏&lt;b&gt;‏<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کو آپ کے &lt;b&gt;‏<xliff:g id="DEVICE">%2$s</xliff:g>&lt;/b&gt; پر مزید تصاویر اور ویڈیوز تک رسائی کی اجازت دیں؟"</string>
- <string name="permgrouprequest_microphone" msgid="2825208549114811299">"‏&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کو آڈیو ریکارڈ کرنے کی اجازت دیں؟"</string>
+ <string name="permgrouprequest_microphone" msgid="2825208549114811299">"‏آڈیو ریکارڈ کرنے کے لیے &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کو اجازت دیں؟"</string>
<string name="permgrouprequest_device_aware_microphone" msgid="1266843551173029370">"‏&lt;b&gt;‏<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کو آپ کے &lt;b&gt;‏<xliff:g id="DEVICE">%2$s</xliff:g>&lt;/b&gt; پر آڈیو ریکارڈ کرنے کی اجازت دیں؟"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"جب آپ ایپ استعمال کر رہے ہوں تب ایپ صرف آڈیو ریکارڈ کر پائے گی"</string>
- <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"‏&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کو آڈیو ریکارڈ کرنے کی اجازت دیں؟"</string>
+ <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"‏آڈیو ریکارڈ کرنے کے لیے &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کو اجازت دیں؟"</string>
<string name="permgroupbackgroundrequest_device_aware_microphone" msgid="4990337225146130185">"‏&lt;b&gt;‏<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کو آپ کے &lt;b&gt;‏<xliff:g id="DEVICE">%2$s</xliff:g>&lt;/b&gt; پر آڈیو ریکارڈ کرنے کی اجازت دیں؟"</string>
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"ممکن ہے یہ ایپ ہر وقت آڈیو ریکارڈ کرنا چاہے، اگرچہ آپ ایپ استعمال نہ کر رہے ہوں۔ "<annotation id="link">"ترتیبات میں اجازت دیں۔"</annotation></string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"‏&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;? کے ليے مائیکروفون تک رسائی تبدیل کریں؟"</string>
diff --git a/PermissionController/res/xml/roles.xml b/PermissionController/res/xml/roles.xml
index e6be02bde..497bfbeb5 100644
--- a/PermissionController/res/xml/roles.xml
+++ b/PermissionController/res/xml/roles.xml
@@ -545,6 +545,7 @@
<permission-set name="sms" />
<permission-set name="contacts" />
<permission-set name="nearby_devices" />
+ <permission-set name="notifications" minSdkVersion="35" />
</permissions>
<app-op-permissions>
<app-op-permission name="android.permission.MANAGE_ONGOING_CALLS" />
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/PermissionController/tests/permissionui/Android.bp b/PermissionController/tests/permissionui/Android.bp
index 9a898d88b..41b5f88ed 100644
--- a/PermissionController/tests/permissionui/Android.bp
+++ b/PermissionController/tests/permissionui/Android.bp
@@ -65,6 +65,7 @@ android_test {
":PermissionUiDefineAdditionalPermissionApp",
":PermissionUiUseAdditionalPermissionApp",
":PermissionUiUseTwoAdditionalPermissionsApp",
+ ":PermissionUiReadCalendarPermissionApp",
],
per_testcase_directory: true,
}
diff --git a/PermissionController/tests/permissionui/AndroidTest.xml b/PermissionController/tests/permissionui/AndroidTest.xml
index 85ccf61ce..566410777 100644
--- a/PermissionController/tests/permissionui/AndroidTest.xml
+++ b/PermissionController/tests/permissionui/AndroidTest.xml
@@ -53,6 +53,8 @@
value="/data/local/tmp/pc-permissionui/PermissionUiUseAdditionalPermissionApp.apk" />
<option name="push-file" key="PermissionUiUseTwoAdditionalPermissionsApp.apk"
value="/data/local/tmp/pc-permissionui/PermissionUiUseTwoAdditionalPermissionsApp.apk" />
+ <option name="push-file" key="PermissionUiReadCalendarPermissionApp.apk"
+ value="/data/local/tmp/pc-permissionui/PermissionUiReadCalendarPermissionApp.apk" />
</target_preparer>
<!-- Uninstall test-apps -->
diff --git a/PermissionController/tests/permissionui/PermissionUiReadCalendarPermissionApp/Android.bp b/PermissionController/tests/permissionui/PermissionUiReadCalendarPermissionApp/Android.bp
new file mode 100644
index 000000000..edb0da6fc
--- /dev/null
+++ b/PermissionController/tests/permissionui/PermissionUiReadCalendarPermissionApp/Android.bp
@@ -0,0 +1,34 @@
+//
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "packages_modules_Permission_PermissionController_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: [
+ "packages_modules_Permission_PermissionController_license",
+ ],
+}
+
+android_test_helper_app {
+ name: "PermissionUiReadCalendarPermissionApp",
+
+ srcs: ["src/**/*.kt"],
+
+ sdk_version: "30",
+}
diff --git a/PermissionController/tests/permissionui/PermissionUiReadCalendarPermissionApp/AndroidManifest.xml b/PermissionController/tests/permissionui/PermissionUiReadCalendarPermissionApp/AndroidManifest.xml
new file mode 100644
index 000000000..8ebb502e8
--- /dev/null
+++ b/PermissionController/tests/permissionui/PermissionUiReadCalendarPermissionApp/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.permissioncontroller.tests.appthatrequestpermission">
+ <uses-permission android:name="android.permission.READ_CALENDAR"/>
+
+ <attribution android:tag="testTag" android:label="@string/test_attribution_label" />
+
+ <application android:label="CalendarRequestApp" />
+</manifest>
+
diff --git a/PermissionController/tests/permissionui/PermissionUiReadCalendarPermissionApp/res/values/strings.xml b/PermissionController/tests/permissionui/PermissionUiReadCalendarPermissionApp/res/values/strings.xml
new file mode 100644
index 000000000..91cb8c1fd
--- /dev/null
+++ b/PermissionController/tests/permissionui/PermissionUiReadCalendarPermissionApp/res/values/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Placeholder attribution label for testing [CHAR LIMIT=32] -->
+ <string name="test_attribution_label">Test Attribution Label</string>
+</resources>
diff --git a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/wear/WearPermissionUsageDetailsFragmentTest.kt b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/wear/WearPermissionUsageDetailsFragmentTest.kt
new file mode 100644
index 000000000..999c89fc7
--- /dev/null
+++ b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/wear/WearPermissionUsageDetailsFragmentTest.kt
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permissionui.ui.wear
+
+import android.Manifest.permission.CAMERA
+import android.content.Intent
+import android.os.Build
+import android.permission.cts.PermissionUtils.grantPermission
+import android.permission.cts.PermissionUtils.install
+import android.permission.cts.PermissionUtils.uninstallApp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
+import androidx.test.uiautomator.By
+import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
+import com.android.compatibility.common.util.UiAutomatorUtils2.waitFindObject
+import com.android.permissioncontroller.permissionui.PermissionHub2Test
+import com.android.permissioncontroller.permissionui.pressHome
+import com.android.permissioncontroller.permissionui.wakeUpScreen
+import org.junit.After
+import org.junit.Assume.assumeTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** Tests for [WearPermissionUsageDetailsFragment] */
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
+class WearPermissionUsageDetailsFragmentTest : PermissionHub2Test() {
+ private val CAMERA_APK =
+ "/data/local/tmp/pc-permissionui/PermissionUiUseCameraPermissionApp.apk"
+ private val CAMERA_APP = "com.android.permissioncontroller.tests.appthatrequestpermission"
+ private val CAMERA_APP_LABEL = "CameraRequestApp"
+ private val CAMERA_PREF_LABEL = "Camera"
+ private val MANAGE_PERMISSION_LABEL = "Manage permission"
+ private val APP_PERMISSIONS_TITLE = "App permissions"
+ private val TIMEOUT = 30_000L
+
+ @Before
+ fun setup() {
+ assumeTrue(isWear())
+ wakeUpScreen()
+ install(CAMERA_APK)
+ grantPermission(CAMERA_APP, CAMERA)
+ accessCamera()
+ goToPermissionUsageDetails()
+ }
+
+ private fun goToPermissionUsageDetails() {
+ runWithShellPermissionIdentity {
+ context.startActivity(
+ Intent(Intent.ACTION_REVIEW_PERMISSION_USAGE).apply {
+ addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ }
+ )
+ }
+
+ clickObject(CAMERA_PREF_LABEL)
+ }
+
+ @Test
+ fun testClickAppThenTransitToAppPermissionGroups() {
+ clickObject(CAMERA_APP_LABEL)
+
+ // Find the title of AppPermissionGroups.
+ waitFindObject(By.textContains(APP_PERMISSIONS_TITLE), TIMEOUT)
+ // Find the permission in the list.
+ waitFindObject(By.textContains(CAMERA_PREF_LABEL), TIMEOUT)
+ }
+
+ @Test
+ fun testClickManagePermissionThenTransitToPermissionApps() {
+ clickObject(MANAGE_PERMISSION_LABEL)
+
+ // Find the title of PermissionApps.
+ waitFindObject(By.textContains(CAMERA_PREF_LABEL), TIMEOUT)
+ // Find the test app is in the app list.
+ waitFindObject(By.textContains(CAMERA_APP_LABEL), TIMEOUT)
+ }
+
+ @After
+ fun tearDown() {
+ uninstallApp(CAMERA_APP)
+ pressHome()
+ }
+}
diff --git a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/wear/WearPermissionUsageFragmentTest.kt b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/wear/WearPermissionUsageFragmentTest.kt
index 21c080862..e37b97874 100644
--- a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/wear/WearPermissionUsageFragmentTest.kt
+++ b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/wear/WearPermissionUsageFragmentTest.kt
@@ -16,8 +16,16 @@
package com.android.permissioncontroller.permissionui.ui.wear
+import android.Manifest.permission.CAMERA
+import android.Manifest.permission.READ_CALENDAR
+import android.app.AppOpsManager
import android.content.Intent
+import android.content.pm.PackageManager
import android.os.Build
+import android.os.Process
+import android.permission.cts.PermissionUtils.grantPermission
+import android.permission.cts.PermissionUtils.install
+import android.permission.cts.PermissionUtils.uninstallApp
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SdkSuppress
import androidx.test.uiautomator.By
@@ -27,6 +35,7 @@ import com.android.compatibility.common.util.UiAutomatorUtils2.waitFindObject
import com.android.permissioncontroller.permissionui.PermissionHub2Test
import com.android.permissioncontroller.permissionui.pressHome
import com.android.permissioncontroller.permissionui.wakeUpScreen
+import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Assume.assumeTrue
import org.junit.Before
@@ -37,6 +46,19 @@ import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
class WearPermissionUsageFragmentTest : PermissionHub2Test() {
+ private val CAMERA_APK =
+ "/data/local/tmp/pc-permissionui/PermissionUiUseCameraPermissionApp.apk"
+ private val CAMERA_APP_LABEL = "CameraRequestApp"
+ private val CAMERA_PREF_LABEL = "Camera"
+
+ private val CALENDAR_APK =
+ "/data/local/tmp/pc-permissionui/PermissionUiReadCalendarPermissionApp.apk"
+ private val CALENDAR_APP_LABEL = "CalendarRequestApp"
+ private val CALENDAR_PREF_LABEL = "Calendar"
+
+ private val TEST_PACKAGE_NAME =
+ "com.android.permissioncontroller.tests.appthatrequestpermission"
+
private val SHOW_SYSTEM_LABEL = "Show system"
private val HIDE_SYSTEM_LABEL = "Hide system"
private val TIMEOUT = 30_000L
@@ -58,27 +80,88 @@ class WearPermissionUsageFragmentTest : PermissionHub2Test() {
)
}
- eventually {
- try {
- waitFindObject(By.textContains(SHOW_SYSTEM_LABEL), TIMEOUT).click()
- } catch (e: Exception) {
- throw e
- }
+ clickObject(SHOW_SYSTEM_LABEL)
+ waitFindObject(By.textContains(HIDE_SYSTEM_LABEL), TIMEOUT)
+
+ clickObject(HIDE_SYSTEM_LABEL)
+ waitFindObject(By.textContains(SHOW_SYSTEM_LABEL), TIMEOUT)
+ }
+
+ @Test
+ fun testTransitToPermissionUsageDetails() {
+ install(CAMERA_APK)
+ grantPermission(TEST_PACKAGE_NAME, CAMERA)
+ accessCamera()
+
+ runWithShellPermissionIdentity {
+ context.startActivity(
+ Intent(Intent.ACTION_REVIEW_PERMISSION_USAGE).apply {
+ addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ }
+ )
}
- eventually {
- try {
- waitFindObject(By.textContains(HIDE_SYSTEM_LABEL), TIMEOUT).click()
- } catch (e: Exception) {
- throw e
- }
+ clickObject(CAMERA_PREF_LABEL)
+
+ waitFindObject(By.textContains(CAMERA_APP_LABEL), TIMEOUT)
+ }
+
+ @Test
+ fun testTransitToPermissionApps() {
+ install(CALENDAR_APK)
+ grantPermission(TEST_PACKAGE_NAME, READ_CALENDAR)
+
+ accessCalendar()
+
+ runWithShellPermissionIdentity {
+ context.startActivity(
+ Intent(Intent.ACTION_REVIEW_PERMISSION_USAGE).apply {
+ addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ }
+ )
}
- waitFindObject(By.textContains(SHOW_SYSTEM_LABEL), TIMEOUT)
+ clickObject(CALENDAR_PREF_LABEL)
+
+ waitFindObject(By.textContains(CALENDAR_PREF_LABEL), TIMEOUT)
+ waitFindObject(By.textContains(CALENDAR_APP_LABEL), TIMEOUT)
+ }
+
+ private fun accessCalendar() {
+ runWithShellPermissionIdentity {
+ eventually {
+ assertThat(
+ context.packageManager.getPermissionFlags(
+ READ_CALENDAR,
+ TEST_PACKAGE_NAME,
+ Process.myUserHandle()
+ ) and PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED
+ )
+ .isNotEqualTo(0)
+ }
+
+ eventually {
+ assertThat(
+ context
+ .getSystemService(AppOpsManager::class.java)
+ .startOp(
+ AppOpsManager.OPSTR_READ_CALENDAR,
+ context.packageManager.getPackageUid(TEST_PACKAGE_NAME, 0),
+ TEST_PACKAGE_NAME,
+ null,
+ null
+ )
+ )
+ .isEqualTo(AppOpsManager.MODE_ALLOWED)
+ }
+ }
}
@After
fun tearDown() {
+ uninstallApp(TEST_PACKAGE_NAME)
pressHome()
}
}
diff --git a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/wear/WearUtils.kt b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/wear/WearUtils.kt
index 00811a267..cf8098f01 100644
--- a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/wear/WearUtils.kt
+++ b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/wear/WearUtils.kt
@@ -18,9 +18,23 @@ package com.android.permissioncontroller.permissionui.ui.wear
import android.content.pm.PackageManager
import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.By
+import com.android.compatibility.common.util.SystemUtil.eventually
+import com.android.compatibility.common.util.UiAutomatorUtils2.waitFindObject
+import org.junit.Assert.assertNotNull
+
+private const val TIMEOUT = 30_000L
fun isWear() =
InstrumentationRegistry.getInstrumentation()
.targetContext
.packageManager
.hasSystemFeature(PackageManager.FEATURE_WATCH)
+
+fun clickObject(text: String) {
+ eventually {
+ val obj = waitFindObject(By.textContains(text), TIMEOUT)
+ assertNotNull(obj)
+ obj.click()
+ }
+}
diff --git a/framework-s/api/system-current.txt b/framework-s/api/system-current.txt
index f502a3231..1111470d2 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.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.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..30fbab484 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.system_server_role_controller_enabled") public RolesState(int, @Nullable String, @NonNull java.util.Map<java.lang.String,java.util.Set<java.lang.String>>, @NonNull java.util.Set<java.lang.String>);
+ method @FlaggedApi("android.permission.flags.system_server_role_controller_enabled") @NonNull public java.util.Set<java.lang.String> getFallbackEnabledRoles();
method @Nullable public String getPackagesHash();
method @NonNull public java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getRoles();
method public int getVersion();
diff --git a/service/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/Android.bp b/tests/cts/permission/Android.bp
index 8849f41a7..9c07544a3 100644
--- a/tests/cts/permission/Android.bp
+++ b/tests/cts/permission/Android.bp
@@ -52,6 +52,7 @@ android_test {
"sts-device-util",
"platform-test-rules",
"CtsVirtualDeviceCommonLib",
+ "android.permission.flags-aconfig-java",
],
jni_libs: [
"libctspermission_jni",
diff --git a/tests/cts/permission/AppThatAccessesCalendarContactsBodySensorCustomPermission/AndroidManifest.xml b/tests/cts/permission/AppThatAccessesCalendarContactsBodySensorCustomPermission/AndroidManifest.xml
index ef4d82dfc..4b0c60bdb 100644
--- a/tests/cts/permission/AppThatAccessesCalendarContactsBodySensorCustomPermission/AndroidManifest.xml
+++ b/tests/cts/permission/AppThatAccessesCalendarContactsBodySensorCustomPermission/AndroidManifest.xml
@@ -34,6 +34,7 @@
<uses-permission android:name="android.permission.WRITE_CALENDAR" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.BODY_SENSORS" />
+ <uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.cts.appthatrequestcustompermission.TEST_PERMISSION" />
<application />
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/permission/src/android/permission/cts/OneTimePermissionTest.java b/tests/cts/permission/src/android/permission/cts/OneTimePermissionTest.java
index 1410434a7..f5f222f80 100644
--- a/tests/cts/permission/src/android/permission/cts/OneTimePermissionTest.java
+++ b/tests/cts/permission/src/android/permission/cts/OneTimePermissionTest.java
@@ -252,12 +252,13 @@ public class OneTimePermissionTest {
// Just waiting for the revocation
eventually(() -> Assert.assertEquals(PackageManager.PERMISSION_DENIED,
mContext.getPackageManager()
- .checkPermission(CAMERA, CUSTOM_CAMERA_PERM_APP_PKG_NAME)));
+ .checkPermission(CAMERA, CUSTOM_CAMERA_PERM_APP_PKG_NAME)), 30000);
// This checks the vulnerability
eventually(() -> Assert.assertEquals(PackageManager.PERMISSION_DENIED,
mContext.getPackageManager()
- .checkPermission(CUSTOM_PERMISSION, CUSTOM_CAMERA_PERM_APP_PKG_NAME)));
+ .checkPermission(CUSTOM_PERMISSION, CUSTOM_CAMERA_PERM_APP_PKG_NAME)),
+ 30000);
}
diff --git a/tests/cts/permission/src/android/permission/cts/PermissionUpdateListenerTest.java b/tests/cts/permission/src/android/permission/cts/PermissionUpdateListenerTest.java
index 05aa41d69..2c39f27d4 100644
--- a/tests/cts/permission/src/android/permission/cts/PermissionUpdateListenerTest.java
+++ b/tests/cts/permission/src/android/permission/cts/PermissionUpdateListenerTest.java
@@ -23,18 +23,26 @@ import static com.android.compatibility.common.util.SystemUtil.runWithShellPermi
import static com.google.common.truth.Truth.assertThat;
import android.companion.virtual.VirtualDeviceManager;
+import android.companion.virtual.VirtualDeviceParams;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.OnPermissionsChangedListener;
+import android.permission.flags.Flags;
import android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.virtualdevice.cts.common.FakeAssociationRule;
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.compatibility.common.util.AdoptShellPermissionsRule;
import com.android.compatibility.common.util.SystemUtil;
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -52,15 +60,28 @@ public class PermissionUpdateListenerTest {
+ "CtsAppThatRequestsCalendarContactsBodySensorCustomPermission.apk";
private static final String PACKAGE_NAME =
"android.permission.cts.appthatrequestcustompermission";
- private static final String PERMISSION_NAME = "android.permission.READ_CONTACTS";
+ private static final String PERMISSION_NAME = "android.permission.CAMERA";
private static final int TIMEOUT = 10000;
- private final Context mContext =
+ private final Context mDefaultContext =
InstrumentationRegistry.getInstrumentation().getContext();
- private final PackageManager mPackageManager = mContext.getPackageManager();
+ private final PackageManager mPackageManager = mDefaultContext.getPackageManager();
private int mTestAppUid;
+ private VirtualDeviceManager mVirtualDeviceManager;
+
+ @Rule
+ public FakeAssociationRule mFakeAssociationRule = new FakeAssociationRule();
+
+ @Rule
+ public AdoptShellPermissionsRule mAdoptShellPermissionsRule = new AdoptShellPermissionsRule(
+ InstrumentationRegistry.getInstrumentation().getUiAutomation(),
+ android.Manifest.permission.CREATE_VIRTUAL_DEVICE);
+
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
@Before
public void setup() throws PackageManager.NameNotFoundException, InterruptedException {
runShellCommandOrThrow("pm install " + APK);
@@ -69,6 +90,7 @@ public class PermissionUpdateListenerTest {
SystemUtil.waitForBroadcasts();
Thread.sleep(1000);
mTestAppUid = mPackageManager.getPackageUid(PACKAGE_NAME, 0);
+ mVirtualDeviceManager = mDefaultContext.getSystemService(VirtualDeviceManager.class);
}
@After
@@ -89,7 +111,7 @@ public class PermissionUpdateListenerTest {
runWithShellPermissionIdentity(() -> {
mPackageManager.addOnPermissionsChangeListener(permissionsChangedListener);
mPackageManager.grantRuntimePermission(PACKAGE_NAME, PERMISSION_NAME,
- mContext.getUser());
+ mDefaultContext.getUser());
});
countDownLatch.await(TIMEOUT, TimeUnit.MILLISECONDS);
runWithShellPermissionIdentity(() -> {
@@ -100,61 +122,117 @@ public class PermissionUpdateListenerTest {
}
@Test
- public void testGrantPermissionNotifyListener() throws InterruptedException {
+ @RequiresFlagsEnabled(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
+ public void testVirtualDeviceGrantPermissionNotifyListener() throws InterruptedException {
+ VirtualDeviceManager.VirtualDevice virtualDevice =
+ mVirtualDeviceManager.createVirtualDevice(
+ mFakeAssociationRule.getAssociationInfo().getId(),
+ new VirtualDeviceParams.Builder().build());
+ Context deviceContext = mDefaultContext.createDeviceContext(virtualDevice.getDeviceId());
+ testGrantPermissionNotifyListener(deviceContext, virtualDevice.getPersistentDeviceId());
+ }
+
+ @Test
+ public void testDefaultDeviceGrantPermissionNotifyListener() throws InterruptedException {
+ testGrantPermissionNotifyListener(
+ mDefaultContext, VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT);
+ }
+
+ private void testGrantPermissionNotifyListener(
+ Context context, String expectedDeviceId) throws InterruptedException {
+ final PackageManager packageManager = context.getPackageManager();
TestOnPermissionsChangedListener permissionsChangedListener =
new TestOnPermissionsChangedListener(1);
runWithShellPermissionIdentity(() -> {
- mPackageManager.addOnPermissionsChangeListener(permissionsChangedListener);
- mPackageManager.grantRuntimePermission(PACKAGE_NAME, PERMISSION_NAME,
- mContext.getUser());
+ packageManager.addOnPermissionsChangeListener(permissionsChangedListener);
+ packageManager.grantRuntimePermission(PACKAGE_NAME, PERMISSION_NAME,
+ mDefaultContext.getUser());
});
permissionsChangedListener.waitForPermissionChangedCallbacks();
runWithShellPermissionIdentity(() -> {
- mPackageManager.removeOnPermissionsChangeListener(permissionsChangedListener);
+ packageManager.removeOnPermissionsChangeListener(permissionsChangedListener);
});
String deviceId = permissionsChangedListener.getNotifiedDeviceId(mTestAppUid);
- assertThat(deviceId).isEqualTo(VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT);
+ assertThat(deviceId).isEqualTo(expectedDeviceId);
}
@Test
- public void testRevokePermissionNotifyListener() throws InterruptedException {
+ public void testDefaultDeviceRevokePermissionNotifyListener() throws InterruptedException {
+ testRevokePermissionNotifyListener(
+ mDefaultContext, VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
+ public void testVirtualDeviceRevokePermissionNotifyListener() throws InterruptedException {
+ VirtualDeviceManager.VirtualDevice virtualDevice =
+ mVirtualDeviceManager.createVirtualDevice(
+ mFakeAssociationRule.getAssociationInfo().getId(),
+ new VirtualDeviceParams.Builder().build());
+ Context deviceContext = mDefaultContext.createDeviceContext(virtualDevice.getDeviceId());
+ testRevokePermissionNotifyListener(
+ deviceContext, virtualDevice.getPersistentDeviceId());
+ }
+
+ private void testRevokePermissionNotifyListener(
+ Context context, String expectedDeviceId) throws InterruptedException {
+ final PackageManager packageManager = context.getPackageManager();
TestOnPermissionsChangedListener permissionsChangedListener =
new TestOnPermissionsChangedListener(1);
runWithShellPermissionIdentity(() -> {
- mPackageManager.grantRuntimePermission(PACKAGE_NAME, PERMISSION_NAME,
- mContext.getUser());
- mPackageManager.addOnPermissionsChangeListener(permissionsChangedListener);
- mPackageManager.revokeRuntimePermission(PACKAGE_NAME, PERMISSION_NAME,
- mContext.getUser());
+ packageManager.grantRuntimePermission(PACKAGE_NAME, PERMISSION_NAME,
+ mDefaultContext.getUser());
+ packageManager.addOnPermissionsChangeListener(permissionsChangedListener);
+ packageManager.revokeRuntimePermission(PACKAGE_NAME, PERMISSION_NAME,
+ mDefaultContext.getUser());
});
permissionsChangedListener.waitForPermissionChangedCallbacks();
runWithShellPermissionIdentity(() -> {
- mPackageManager.removeOnPermissionsChangeListener(permissionsChangedListener);
+ packageManager.removeOnPermissionsChangeListener(permissionsChangedListener);
});
String deviceId = permissionsChangedListener.getNotifiedDeviceId(mTestAppUid);
- assertThat(deviceId).isEqualTo(VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT);
+ assertThat(deviceId).isEqualTo(expectedDeviceId);
}
@Test
- public void testUpdatePermissionFlagsNotifyListener() throws InterruptedException {
+ public void testDefaultDeviceUpdatePermissionFlagsNotifyListener() throws InterruptedException {
+ testUpdatePermissionFlagsNotifyListener(
+ mDefaultContext, VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
+ public void testVirtualDeviceUpdatePermissionFlagsNotifyListener() throws InterruptedException {
+ VirtualDeviceManager.VirtualDevice virtualDevice =
+ mVirtualDeviceManager.createVirtualDevice(
+ mFakeAssociationRule.getAssociationInfo().getId(),
+ new VirtualDeviceParams.Builder().build());
+ Context deviceContext = mDefaultContext.createDeviceContext(virtualDevice.getDeviceId());
+ testUpdatePermissionFlagsNotifyListener(
+ deviceContext, virtualDevice.getPersistentDeviceId());
+ }
+
+ private void testUpdatePermissionFlagsNotifyListener(
+ Context context, String expectedDeviceId) throws InterruptedException {
TestOnPermissionsChangedListener permissionsChangedListener =
new TestOnPermissionsChangedListener(1);
+ final PackageManager packageManager = context.getPackageManager();
runWithShellPermissionIdentity(() -> {
- mPackageManager.addOnPermissionsChangeListener(permissionsChangedListener);
+ packageManager.addOnPermissionsChangeListener(permissionsChangedListener);
int flag = PackageManager.FLAG_PERMISSION_USER_SET;
- mPackageManager.updatePermissionFlags(PERMISSION_NAME, PACKAGE_NAME, flag, flag,
- mContext.getUser());
+ packageManager.updatePermissionFlags(PERMISSION_NAME, PACKAGE_NAME, flag, flag,
+ mDefaultContext.getUser());
});
permissionsChangedListener.waitForPermissionChangedCallbacks();
runWithShellPermissionIdentity(() -> {
- mPackageManager.removeOnPermissionsChangeListener(permissionsChangedListener);
+ packageManager.removeOnPermissionsChangeListener(permissionsChangedListener);
});
String deviceId = permissionsChangedListener.getNotifiedDeviceId(mTestAppUid);
- assertThat(deviceId).isEqualTo(VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT);
+ assertThat(deviceId).isEqualTo(expectedDeviceId);
}
private class TestOnPermissionsChangedListener
diff --git a/tests/cts/permissionmultidevice/src/android/permissionmultidevice/cts/DeviceAwarePermissionGrantTest.kt b/tests/cts/permissionmultidevice/src/android/permissionmultidevice/cts/DeviceAwarePermissionGrantTest.kt
index c37b71cd4..09f4c7f08 100644
--- a/tests/cts/permissionmultidevice/src/android/permissionmultidevice/cts/DeviceAwarePermissionGrantTest.kt
+++ b/tests/cts/permissionmultidevice/src/android/permissionmultidevice/cts/DeviceAwarePermissionGrantTest.kt
@@ -74,7 +74,7 @@ class DeviceAwarePermissionGrantTest {
uninstallPackage(APP_PACKAGE_NAME, requireSuccess = false)
}
- @RequiresFlagsEnabled(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS)
+ @RequiresFlagsEnabled(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
@Test
fun onHostDevice_requestPermissionForHostDevice_shouldGrantPermission() {
testGrantPermissionForDevice(
@@ -87,7 +87,7 @@ class DeviceAwarePermissionGrantTest {
)
}
- @RequiresFlagsEnabled(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS)
+ @RequiresFlagsEnabled(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
@Test
fun onHostDevice_requestPermissionForRemoteDevice_shouldGrantPermission() {
testGrantPermissionForDevice(
@@ -100,7 +100,7 @@ class DeviceAwarePermissionGrantTest {
)
}
- @RequiresFlagsEnabled(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS)
+ @RequiresFlagsEnabled(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
@Test
fun onRemoteDevice_requestPermissionForHostDevice_shouldGrantPermission() {
testGrantPermissionForDevice(
@@ -113,7 +113,7 @@ class DeviceAwarePermissionGrantTest {
)
}
- @RequiresFlagsEnabled(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS)
+ @RequiresFlagsEnabled(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
@Test
fun onRemoteDevice_requestPermissionForRemoteDevice_shouldGrantPermission() {
testGrantPermissionForDevice(
diff --git a/tests/cts/permissionpolicy/res/raw/android_manifest.xml b/tests/cts/permissionpolicy/res/raw/android_manifest.xml
index d973a1b3f..5f9049841 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
@@ -7309,6 +7309,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" />
@@ -7659,6 +7666,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()
)