diff options
15 files changed, 585 insertions, 15 deletions
diff --git a/PermissionController/Android.bp b/PermissionController/Android.bp index d825ec62f..9710a06d8 100644 --- a/PermissionController/Android.bp +++ b/PermissionController/Android.bp @@ -154,6 +154,7 @@ android_library { "androidx.compose.runtime_runtime-livedata", "androidx.compose.ui_ui", "androidx.wear.compose_compose-material", + "android.content.pm.flags-aconfig-java-export", "android.os.flags-aconfig-java-export", ], diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/v34/SafetyLabelInfoLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/v34/SafetyLabelInfoLiveData.kt index 5c63de6ce..b033068ab 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/data/v34/SafetyLabelInfoLiveData.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/data/v34/SafetyLabelInfoLiveData.kt @@ -17,6 +17,7 @@ package com.android.permissioncontroller.permission.data.v34 import android.app.Application +import android.content.Context import android.content.pm.PackageManager import android.os.Process import android.os.UserHandle @@ -28,6 +29,7 @@ import com.android.permissioncontroller.permission.data.PackageBroadcastReceiver import com.android.permissioncontroller.permission.data.SmartAsyncMediatorLiveData import com.android.permissioncontroller.permission.data.get import com.android.permissioncontroller.permission.model.livedatatypes.v34.SafetyLabelInfo +import com.android.permissioncontroller.permission.utils.v34.SafetyLabelUtils import kotlinx.coroutines.Job /** @@ -88,9 +90,22 @@ private constructor( return } + val userContext = + if (user == Process.myUserHandle()) { + app + } else { + app.createContextAsUser(user, /* flags= */ 0) + } + + // Asl in Apk (V+) is not supported by permissions + if (!SafetyLabelUtils.isAppMetadataSourceSupported(userContext, packageName)) { + postValue(SafetyLabelInfo.UNAVAILABLE) + return + } + val safetyLabelInfo: SafetyLabelInfo = try { - val safetyLabel: SafetyLabel? = getSafetyLabel(packageName, user) + val safetyLabel: SafetyLabel? = getSafetyLabel(userContext, packageName) if (safetyLabel != null) { SafetyLabelInfo(safetyLabel, lightInstallSourceInfo) } else { @@ -106,14 +121,7 @@ private constructor( /** Returns the [SafetyLabel] for the given package and user. */ @Throws(PackageManager.NameNotFoundException::class) - private fun getSafetyLabel(packageName: String, user: UserHandle): SafetyLabel? { - val userContext = - if (user == Process.myUserHandle()) { - app - } else { - app.createContextAsUser(user, /* flags= */ 0) - } - + private fun getSafetyLabel(userContext: Context, packageName: String): SafetyLabel? { return SafetyLabel.getSafetyLabelFromMetadata( userContext.packageManager.getAppMetadata(packageName) ) diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/v34/SafetyLabelChangesJobService.kt b/PermissionController/src/com/android/permissioncontroller/permission/service/v34/SafetyLabelChangesJobService.kt index 4036e260a..4115a14eb 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/service/v34/SafetyLabelChangesJobService.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/service/v34/SafetyLabelChangesJobService.kt @@ -67,10 +67,12 @@ import com.android.permissioncontroller.permission.model.livedatatypes.AppPermGr import com.android.permissioncontroller.permission.model.v34.AppDataSharingUpdate import com.android.permissioncontroller.permission.utils.KotlinUtils import com.android.permissioncontroller.permission.utils.Utils.getSystemServiceSafe +import com.android.permissioncontroller.permission.utils.v34.SafetyLabelUtils import com.android.permissioncontroller.safetylabel.AppsSafetyLabelHistory import com.android.permissioncontroller.safetylabel.AppsSafetyLabelHistory.AppInfo import com.android.permissioncontroller.safetylabel.AppsSafetyLabelHistory.SafetyLabel as SafetyLabelForPersistence import com.android.permissioncontroller.safetylabel.AppsSafetyLabelHistoryPersistence +import com.android.permissioncontroller.safetylabel.SafetyLabelChangedBroadcastReceiver import java.time.Duration import java.time.Instant import java.time.ZoneId @@ -348,6 +350,12 @@ class SafetyLabelChangesJobService : JobService() { } else { context.createContextAsUser(user, 0) } + + // Asl in Apk (V+) is not supported by permissions + if (!SafetyLabelUtils.isAppMetadataSourceSupported(userContext, packageName)) { + return null + } + val appMetadataBundle: PersistableBundle = try { userContext.packageManager.getAppMetadata(packageName) @@ -513,8 +521,7 @@ class SafetyLabelChangesJobService : JobService() { } ?.keys ?.filter { packageUser: Pair<String, UserHandle> -> packageUser.first == packageName } - ?.map { packageUser: Pair<String, UserHandle> -> packageUser.second } - ?: listOf() + ?.map { packageUser: Pair<String, UserHandle> -> packageUser.second } ?: listOf() } private fun AppDataSharingUpdate.containsLocationCategoryUpdate() = diff --git a/PermissionController/src/com/android/permissioncontroller/permission/utils/v34/SafetyLabelUtils.kt b/PermissionController/src/com/android/permissioncontroller/permission/utils/v34/SafetyLabelUtils.kt index 9fd8ab916..b654b7e1c 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/utils/v34/SafetyLabelUtils.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/utils/v34/SafetyLabelUtils.kt @@ -16,6 +16,11 @@ package com.android.permissioncontroller.permission.utils.v34 +import android.content.Context +import android.content.pm.PackageManager +import android.content.pm.PackageManager.APP_METADATA_SOURCE_APK +import android.util.Log +import com.android.modules.utils.build.SdkLevel import com.android.permission.safetylabel.DataCategory import com.android.permission.safetylabel.DataType import com.android.permission.safetylabel.DataTypeConstants @@ -23,6 +28,8 @@ import com.android.permission.safetylabel.SafetyLabel import com.android.permissioncontroller.permission.utils.PermissionMapping object SafetyLabelUtils { + private val LOG_TAG = SafetyLabelUtils::class.java.simpleName + /* * Get the sharing purposes for a SafetyLabel related to a specific permission group. */ @@ -55,4 +62,25 @@ object SafetyLabelUtils { return purposeSet } + + /** + * Returns the {@code TRUE} if [AppMetadataSource] for the given package is + * supported for permission rationale, as well as for U- where getAppMetadataSource isn't + * available. + */ + fun isAppMetadataSourceSupported(userContext: Context, packageName: String): Boolean { + if (!SdkLevel.isAtLeastV() || !android.content.pm.Flags.aslInApkAppMetadataSource()) { + // PackageManager.getAppMetadataSource() is not available and ASL in APK is ignored in + // U and below. We can assume it came from oem/pre-install or installer source (app + // store). Treat this as AppMetadataSource allowed. + return true + } + + return try { + userContext.packageManager.getAppMetadataSource(packageName) != APP_METADATA_SOURCE_APK + } catch (e: PackageManager.NameNotFoundException) { + Log.w(LOG_TAG, "AppMetadataSource for $packageName not found") + false + } + } } diff --git a/PermissionController/src/com/android/permissioncontroller/safetylabel/SafetyLabelChangedBroadcastReceiver.kt b/PermissionController/src/com/android/permissioncontroller/safetylabel/SafetyLabelChangedBroadcastReceiver.kt index 7378b77fe..298c59710 100644 --- a/PermissionController/src/com/android/permissioncontroller/safetylabel/SafetyLabelChangedBroadcastReceiver.kt +++ b/PermissionController/src/com/android/permissioncontroller/safetylabel/SafetyLabelChangedBroadcastReceiver.kt @@ -37,6 +37,7 @@ import com.android.permissioncontroller.permission.model.livedatatypes.LightPack import com.android.permissioncontroller.permission.utils.KotlinUtils import com.android.permissioncontroller.permission.utils.PermissionMapping import com.android.permissioncontroller.permission.utils.Utils +import com.android.permissioncontroller.permission.utils.v34.SafetyLabelUtils import com.android.permissioncontroller.safetylabel.AppsSafetyLabelHistory.SafetyLabel as SafetyLabelForPersistence import java.time.Instant import kotlinx.coroutines.Dispatchers @@ -142,6 +143,12 @@ class SafetyLabelChangedBroadcastReceiver : BroadcastReceiver() { } else { context.createContextAsUser(user, 0) } + + // Asl in Apk (V+) is not supported by permissions + if (!SafetyLabelUtils.isAppMetadataSourceSupported(userContext, packageName)) { + return + } + val appMetadataBundle = try { userContext.packageManager.getAppMetadata(packageName) diff --git a/tests/cts/permissionui/Android.bp b/tests/cts/permissionui/Android.bp index 481aec778..04783f487 100644 --- a/tests/cts/permissionui/Android.bp +++ b/tests/cts/permissionui/Android.bp @@ -42,6 +42,7 @@ android_test { "CtsAccessibilityCommon", "platform-test-rules", "platform-test-annotations", + "android.content.pm.flags-aconfig-java-export", "android.permission.flags-aconfig-java", ], data: [ @@ -58,6 +59,7 @@ android_test { ":CtsUsePermissionApp30WithBackground", ":CtsUsePermissionApp30WithBluetooth", ":CtsUsePermissionApp31", + ":CtsUsePermissionApp31WithAsl", ":CtsUsePermissionApp32", ":CtsUsePermissionAppLatest", ":CtsUsePermissionAppLatestNone", diff --git a/tests/cts/permissionui/AndroidTest.xml b/tests/cts/permissionui/AndroidTest.xml index 9b80799c2..cac0b5737 100644 --- a/tests/cts/permissionui/AndroidTest.xml +++ b/tests/cts/permissionui/AndroidTest.xml @@ -59,6 +59,7 @@ <option name="push" value="CtsUsePermissionApp30WithBackground.apk->/data/local/tmp/cts-permissionui/CtsUsePermissionApp30WithBackground.apk" /> <option name="push" value="CtsUsePermissionApp30WithBluetooth.apk->/data/local/tmp/cts-permissionui/CtsUsePermissionApp30WithBluetooth.apk" /> <option name="push" value="CtsUsePermissionApp31.apk->/data/local/tmp/cts-permissionui/CtsUsePermissionApp31.apk" /> + <option name="push" value="CtsUsePermissionApp31WithAsl.apk->/data/local/tmp/cts-permissionui/CtsUsePermissionApp31WithAsl.apk" /> <option name="push" value="CtsUsePermissionApp32.apk->/data/local/tmp/cts-permissionui/CtsUsePermissionApp32.apk" /> <option name="push" value="CtsUsePermissionAppLatest.apk->/data/local/tmp/cts-permissionui/CtsUsePermissionAppLatest.apk" /> <option name="push" value="CtsUsePermissionAppLatestNone.apk->/data/local/tmp/cts-permissionui/CtsUsePermissionAppLatestNone.apk" /> diff --git a/tests/cts/permissionui/UsePermissionApp31WithAsl/Android.bp b/tests/cts/permissionui/UsePermissionApp31WithAsl/Android.bp new file mode 100644 index 000000000..1d18a1a1f --- /dev/null +++ b/tests/cts/permissionui/UsePermissionApp31WithAsl/Android.bp @@ -0,0 +1,35 @@ +// +// Copyright (C) 2024 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +android_test_helper_app { + name: "CtsUsePermissionApp31WithAsl", + srcs: [ + ":CtsUsePermissionAppSrc", + ], + manifest: "AndroidManifest.xml", + assets: ["app.metadata"], + static_libs: [ + "kotlin-stdlib", + "compatibility-device-util-axt", + ], + certificate: ":cts-testkey2", + target_sdk_version: "31", + min_sdk_version: "31", +} diff --git a/tests/cts/permissionui/UsePermissionApp31WithAsl/AndroidManifest.xml b/tests/cts/permissionui/UsePermissionApp31WithAsl/AndroidManifest.xml new file mode 100644 index 000000000..02126f1b4 --- /dev/null +++ b/tests/cts/permissionui/UsePermissionApp31WithAsl/AndroidManifest.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<manifest + xmlns:android="http://schemas.android.com/apk/res/android" + package="android.permissionui.cts.usepermission"> + <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" /> + <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> + <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> + <uses-permission android:name="android.permission.RECORD_AUDIO" /> + <uses-permission android:name="android.permission.CAMERA" /> + <uses-permission android:name="android.permission.BODY_SENSORS" /> + <application> + <activity android:name=".CheckCalendarAccessActivity" android:exported="true" /> + <activity android:name=".FinishOnCreateActivity" android:exported="true" /> + <activity android:name=".RequestPermissionsActivity" android:exported="true" /> + <property android:name="android.content.SAFETY_LABEL_PATH" + android:value="assets/app.metadata"/> + </application> +</manifest> diff --git a/tests/cts/permissionui/UsePermissionApp31WithAsl/app.metadata b/tests/cts/permissionui/UsePermissionApp31WithAsl/app.metadata new file mode 100644 index 000000000..d83d081d8 --- /dev/null +++ b/tests/cts/permissionui/UsePermissionApp31WithAsl/app.metadata @@ -0,0 +1,17 @@ +<bundle> +<long name="version" value="1" /> +<pbundle_as_map name="safety_labels"> +<pbundle_as_map name="data_labels"> +<pbundle_as_map name="data_shared"> +<pbundle_as_map name="location"> +<pbundle_as_map name="approx_location"> +<int-array name="purposes" num="1"> +<item value="5" /> +</int-array> +</pbundle_as_map> +</pbundle_as_map> +</pbundle_as_map> +</pbundle_as_map> +<long name="version" value="1" /> +</pbundle_as_map> +</bundle> diff --git a/tests/cts/permissionui/src/android/permissionui/cts/AppDataSharingUpdatesTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/AppDataSharingUpdatesTest.kt index cd002ebfc..1f27b9df0 100644 --- a/tests/cts/permissionui/src/android/permissionui/cts/AppDataSharingUpdatesTest.kt +++ b/tests/cts/permissionui/src/android/permissionui/cts/AppDataSharingUpdatesTest.kt @@ -30,6 +30,8 @@ import android.permission.cts.PermissionUtils import android.permissionui.cts.AppMetadata.createAppMetadataWithLocationSharingAds import android.permissionui.cts.AppMetadata.createAppMetadataWithLocationSharingNoAds import android.permissionui.cts.AppMetadata.createAppMetadataWithNoSharing +import android.platform.test.annotations.EnableFlags +import android.platform.test.flag.junit.SetFlagsRule import android.provider.DeviceConfig import android.safetylabel.SafetyLabelConstants.SAFETY_LABEL_CHANGE_NOTIFICATIONS_ENABLED import android.util.Log @@ -50,6 +52,7 @@ import org.junit.Ignore import org.junit.Rule import org.junit.Test + /** Tests the UI that displays information about apps' updates to their data sharing policies. */ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") @FlakyTest @@ -59,6 +62,9 @@ class AppDataSharingUpdatesTest : BaseUsePermissionTest() { private var activityManager: ActivityManager? = null @get:Rule + val setFlagsRule = SetFlagsRule() + + @get:Rule val deviceConfigSafetyLabelChangeNotificationsEnabled = DeviceConfigStateChangerRule( context, @@ -376,7 +382,7 @@ class AppDataSharingUpdatesTest : BaseUsePermissionTest() { } @Test - fun startActivityWithIntent_whenAppGrantedFineLocation_packageSourceUnspecified_showsUpdate() { + fun startActivityWithIntent_whenAppGrantedLocation_packageSourceUnspecified_showsUpdate() { installAndWaitTillPackageAdded( APP_APK_NAME_31, createAppMetadataWithNoSharing(), @@ -499,10 +505,140 @@ class AppDataSharingUpdatesTest : BaseUsePermissionTest() { } } + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName = + "VanillaIceCream") + @EnableFlags(android.content.pm.Flags.FLAG_ASL_IN_APK_APP_METADATA_SOURCE) + @Test + fun startActivityWithIntent_whenAppGrantedLocation_packageSourceUnspecified_asAslInApk_doesntShowUpdate() { + installAndWaitTillPackageAdded( + APP_APK_NAME_31, + createAppMetadataWithNoSharing(), + PACKAGE_SOURCE_UNSPECIFIED, + waitTillBroadcastProcessed = true + ) + installAndWaitTillPackageAdded( + APP_APK_NAME_31_WITH_ASL, + packageSource = PACKAGE_SOURCE_UNSPECIFIED + ) + grantFineLocationPermission(APP_PACKAGE_NAME) + + startAppDataSharingUpdatesActivity() + + try { + assertNoUpdatesPresent() + } finally { + pressBack() + } + } + + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName = + "VanillaIceCream") + @EnableFlags(android.content.pm.Flags.FLAG_ASL_IN_APK_APP_METADATA_SOURCE) + @Test + fun startActivityWithIntent_whenAppGrantedLocation_packageSourceOther_asAslInApk_doesntShowUpdate() { + installAndWaitTillPackageAdded( + APP_APK_NAME_31, + createAppMetadataWithNoSharing(), + PACKAGE_SOURCE_OTHER, + waitTillBroadcastProcessed = true + ) + installAndWaitTillPackageAdded( + APP_APK_NAME_31_WITH_ASL, + packageSource = PACKAGE_SOURCE_OTHER + ) + grantFineLocationPermission(APP_PACKAGE_NAME) + + startAppDataSharingUpdatesActivity() + + try { + assertNoUpdatesPresent() + } finally { + pressBack() + } + } + + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName = + "VanillaIceCream") + @EnableFlags(android.content.pm.Flags.FLAG_ASL_IN_APK_APP_METADATA_SOURCE) + @Test + fun startActivityWithIntent_whenAppGrantedLocation_packageSourceStore_asAslInApk_doesntShowUpdate() { + installAndWaitTillPackageAdded( + APP_APK_NAME_31, + createAppMetadataWithNoSharing(), + PACKAGE_SOURCE_STORE, + waitTillBroadcastProcessed = true + ) + installAndWaitTillPackageAdded( + APP_APK_NAME_31_WITH_ASL, + packageSource = PACKAGE_SOURCE_STORE + ) + grantFineLocationPermission(APP_PACKAGE_NAME) + + startAppDataSharingUpdatesActivity() + + try { + assertNoUpdatesPresent() + } finally { + pressBack() + } + } + + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName = + "VanillaIceCream") + @EnableFlags(android.content.pm.Flags.FLAG_ASL_IN_APK_APP_METADATA_SOURCE) + @Test + fun startActivityWithIntent_whenAppGrantedLocation_packageSourceLocalFile_asAslInApk_doesntShowUpdate() { + installAndWaitTillPackageAdded( + APP_APK_NAME_31, + createAppMetadataWithNoSharing(), + PACKAGE_SOURCE_LOCAL_FILE, + waitTillBroadcastProcessed = true + ) + installAndWaitTillPackageAdded( + APP_APK_NAME_31_WITH_ASL, + packageSource = PACKAGE_SOURCE_LOCAL_FILE + ) + grantFineLocationPermission(APP_PACKAGE_NAME) + + startAppDataSharingUpdatesActivity() + + try { + assertNoUpdatesPresent() + } finally { + pressBack() + } + } + + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName = + "VanillaIceCream") + @EnableFlags(android.content.pm.Flags.FLAG_ASL_IN_APK_APP_METADATA_SOURCE) + @Test + fun startActivityWithIntent_whenAppGrantedLocation_packageSourceDownloaded_asAslInApk_doesntShowUpdate() { + installAndWaitTillPackageAdded( + APP_APK_NAME_31, + createAppMetadataWithNoSharing(), + PACKAGE_SOURCE_DOWNLOADED_FILE, + waitTillBroadcastProcessed = true + ) + installAndWaitTillPackageAdded( + APP_APK_NAME_31_WITH_ASL, + packageSource = PACKAGE_SOURCE_DOWNLOADED_FILE + ) + grantFineLocationPermission(APP_PACKAGE_NAME) + + startAppDataSharingUpdatesActivity() + + try { + assertNoUpdatesPresent() + } finally { + pressBack() + } + } + /** Installs an app and waits for the package added broadcast to be dispatched. */ private fun installAndWaitTillPackageAdded( apkPath: String, - appMetadata: PersistableBundle, + appMetadata: PersistableBundle? = null, packageSource: Int? = null, waitTillBroadcastProcessed: Boolean = false ) { diff --git a/tests/cts/permissionui/src/android/permissionui/cts/AppPermissionTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/AppPermissionTest.kt index cb356f6a3..f0c3c17c2 100644 --- a/tests/cts/permissionui/src/android/permissionui/cts/AppPermissionTest.kt +++ b/tests/cts/permissionui/src/android/permissionui/cts/AppPermissionTest.kt @@ -21,9 +21,11 @@ import android.Manifest.permission_group.PHONE import android.Manifest.permission_group.SMS import android.os.Build import android.permission.flags.Flags +import android.platform.test.annotations.EnableFlags import android.platform.test.annotations.RequiresFlagsEnabled import android.platform.test.flag.junit.CheckFlagsRule import android.platform.test.flag.junit.DeviceFlagsValueProvider +import android.platform.test.flag.junit.SetFlagsRule import android.provider.DeviceConfig import android.provider.Settings import android.provider.Settings.Secure.USER_SETUP_COMPLETE @@ -56,6 +58,9 @@ class AppPermissionTest : BaseUsePermissionTest() { @get:Rule val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule() + @get:Rule + val setFlagsRule = SetFlagsRule() + @Before fun setup() { Assume.assumeTrue("Permission rationale is only available on U+", SdkLevel.isAtLeastU()) @@ -123,6 +128,67 @@ class AppPermissionTest : BaseUsePermissionTest() { assertAppPermissionRationaleContainerIsVisible(false) } + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName = + "VanillaIceCream") + @EnableFlags(android.content.pm.Flags.FLAG_ASL_IN_APK_APP_METADATA_SOURCE) + @Test + fun showPermissionRationaleContainer_withInstallSourceAndNoMetadata_packageSourceUnspecified() { + // Unspecified is the default, so no need to explicitly set it + installPackageWithInstallSourceAndNoMetadata(APP_APK_NAME_31_WITH_ASL) + + navigateToIndividualPermissionSetting(ACCESS_COARSE_LOCATION) + + assertAppPermissionRationaleContainerIsVisible(false) + } + + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName = + "VanillaIceCream") + @EnableFlags(android.content.pm.Flags.FLAG_ASL_IN_APK_APP_METADATA_SOURCE) + @Test + fun showPermissionRationaleContainer_withInstallSourceAndNoMetadata_packageSourceStore() { + installPackageWithInstallSourceAndNoMetadataFromStore(APP_APK_NAME_31_WITH_ASL) + + navigateToIndividualPermissionSetting(ACCESS_COARSE_LOCATION) + + assertAppPermissionRationaleContainerIsVisible(false) + } + + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName = + "VanillaIceCream") + @EnableFlags(android.content.pm.Flags.FLAG_ASL_IN_APK_APP_METADATA_SOURCE) + @Test + fun showPermissionRationaleContainer_withInstallSourceAndNoMetadata_packageSourceLocalFile() { + installPackageWithInstallSourceAndNoMetadataFromLocalFile(APP_APK_NAME_31_WITH_ASL) + + navigateToIndividualPermissionSetting(ACCESS_COARSE_LOCATION) + + assertAppPermissionRationaleContainerIsVisible(false) + } + + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName = + "VanillaIceCream") + @EnableFlags(android.content.pm.Flags.FLAG_ASL_IN_APK_APP_METADATA_SOURCE) + @Test + fun showPermissionRationaleContainer_withInstallSourceAndNoMetadata_packageSourceDownloadedFile() { + installPackageWithInstallSourceAndNoMetadataFromDownloadedFile(APP_APK_NAME_31_WITH_ASL) + + navigateToIndividualPermissionSetting(ACCESS_COARSE_LOCATION) + + assertAppPermissionRationaleContainerIsVisible(false) + } + + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName = + "VanillaIceCream") + @EnableFlags(android.content.pm.Flags.FLAG_ASL_IN_APK_APP_METADATA_SOURCE) + @Test + fun showPermissionRationaleContainer_withInstallSourceAndNoMetadata_packageSourceOther() { + installPackageWithInstallSourceAndNoMetadataFromOther(APP_APK_NAME_31_WITH_ASL) + + navigateToIndividualPermissionSetting(ACCESS_COARSE_LOCATION) + + assertAppPermissionRationaleContainerIsVisible(false) + } + @Test fun noShowPermissionRationaleContainer_withInstallSourceAndNoMetadata() { installPackageWithInstallSourceAndNoMetadata(APP_APK_NAME_31) @@ -331,6 +397,6 @@ class AppPermissionTest : BaseUsePermissionTest() { companion object { private const val PERMISSION_RATIONALE_ENABLED = "permission_rationale_enabled" private val ENHANCED_CONFIRMATION_DIALOG_SELECTOR = By.res( - "com.android.permissioncontroller:id/enhanced_confirmation_dialog_title"); + "com.android.permissioncontroller:id/enhanced_confirmation_dialog_title") } } diff --git a/tests/cts/permissionui/src/android/permissionui/cts/BaseUsePermissionTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/BaseUsePermissionTest.kt index cc6c87999..03e57788a 100644 --- a/tests/cts/permissionui/src/android/permissionui/cts/BaseUsePermissionTest.kt +++ b/tests/cts/permissionui/src/android/permissionui/cts/BaseUsePermissionTest.kt @@ -66,6 +66,7 @@ import org.junit.Before abstract class BaseUsePermissionTest : BasePermissionTest() { companion object { const val APP_APK_NAME_31 = "CtsUsePermissionApp31.apk" + const val APP_APK_NAME_31_WITH_ASL = "CtsUsePermissionApp31WithAsl.apk" const val APP_APK_NAME_LATEST = "CtsUsePermissionAppLatest.apk" const val APP_APK_PATH_22 = "$APK_DIRECTORY/CtsUsePermissionApp22.apk" @@ -477,6 +478,34 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { installPackageViaSession(apkName) } + protected fun installPackageWithInstallSourceAndNoMetadataFromStore(apkName: String) { + installPackageViaSession( + apkName, + packageSource = PACKAGE_SOURCE_STORE + ) + } + + protected fun installPackageWithInstallSourceAndNoMetadataFromLocalFile(apkName: String) { + installPackageViaSession( + apkName, + packageSource = PACKAGE_SOURCE_LOCAL_FILE + ) + } + + protected fun installPackageWithInstallSourceAndNoMetadataFromDownloadedFile(apkName: String) { + installPackageViaSession( + apkName, + packageSource = PACKAGE_SOURCE_DOWNLOADED_FILE + ) + } + + protected fun installPackageWithInstallSourceAndNoMetadataFromOther(apkName: String) { + installPackageViaSession( + apkName, + packageSource = PACKAGE_SOURCE_OTHER + ) + } + protected fun installPackageWithInstallSourceAndInvalidMetadata(apkName: String) { installPackageViaSession(apkName, AppMetadata.createInvalidAppMetadata()) } diff --git a/tests/cts/permissionui/src/android/permissionui/cts/PermissionRationalePermissionGrantDialogTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/PermissionRationalePermissionGrantDialogTest.kt index 73faaa7f6..b0247b6bb 100644 --- a/tests/cts/permissionui/src/android/permissionui/cts/PermissionRationalePermissionGrantDialogTest.kt +++ b/tests/cts/permissionui/src/android/permissionui/cts/PermissionRationalePermissionGrantDialogTest.kt @@ -20,6 +20,8 @@ import android.Manifest.permission.ACCESS_COARSE_LOCATION import android.Manifest.permission.ACCESS_FINE_LOCATION import android.Manifest.permission.CAMERA import android.os.Build +import android.platform.test.annotations.EnableFlags +import android.platform.test.flag.junit.SetFlagsRule import android.provider.DeviceConfig import android.safetylabel.SafetyLabelConstants.PERMISSION_RATIONALE_ENABLED import androidx.test.filters.FlakyTest @@ -40,6 +42,9 @@ import org.junit.Test class PermissionRationalePermissionGrantDialogTest : BaseUsePermissionTest() { @get:Rule + val setFlagsRule = SetFlagsRule() + + @get:Rule val deviceConfigPermissionRationaleEnabled = DeviceConfigStateChangerRule( context, @@ -243,6 +248,76 @@ class PermissionRationalePermissionGrantDialogTest : BaseUsePermissionTest() { } } + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName = + "VanillaIceCream") + @EnableFlags(android.content.pm.Flags.FLAG_ASL_IN_APK_APP_METADATA_SOURCE) + @Test + fun requestCoarseLocationPerm_hasAslInApk_packageSourceUnspecified() { + installPackageWithInstallSourceAndNoMetadata(APP_APK_NAME_31_WITH_ASL) + + assertAppHasPermission(ACCESS_COARSE_LOCATION, false) + + requestAppPermissionsForNoResult(ACCESS_COARSE_LOCATION) { + assertPermissionRationaleContainerOnGrantDialogIsVisible(false) + } + } + + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName = + "VanillaIceCream") + @EnableFlags(android.content.pm.Flags.FLAG_ASL_IN_APK_APP_METADATA_SOURCE) + @Test + fun requestCoarseLocationPerm_hasAslInApk_packageSourceStore() { + installPackageWithInstallSourceAndNoMetadataFromStore(APP_APK_NAME_31_WITH_ASL) + + assertAppHasPermission(ACCESS_COARSE_LOCATION, false) + + requestAppPermissionsForNoResult(ACCESS_COARSE_LOCATION) { + assertPermissionRationaleContainerOnGrantDialogIsVisible(false) + } + } + + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName = + "VanillaIceCream") + @EnableFlags(android.content.pm.Flags.FLAG_ASL_IN_APK_APP_METADATA_SOURCE) + @Test + fun requestCoarseLocationPerm_hasAslInApk_packageSourceLocalFile() { + installPackageWithInstallSourceAndNoMetadataFromLocalFile(APP_APK_NAME_31_WITH_ASL) + + assertAppHasPermission(ACCESS_COARSE_LOCATION, false) + + requestAppPermissionsForNoResult(ACCESS_COARSE_LOCATION) { + assertPermissionRationaleContainerOnGrantDialogIsVisible(false) + } + } + + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName = + "VanillaIceCream") + @EnableFlags(android.content.pm.Flags.FLAG_ASL_IN_APK_APP_METADATA_SOURCE) + @Test + fun requestCoarseLocationPerm_hasAslInApk_packageSourceDownloadedFile() { + installPackageWithInstallSourceAndNoMetadataFromDownloadedFile(APP_APK_NAME_31_WITH_ASL) + + assertAppHasPermission(ACCESS_COARSE_LOCATION, false) + + requestAppPermissionsForNoResult(ACCESS_COARSE_LOCATION) { + assertPermissionRationaleContainerOnGrantDialogIsVisible(false) + } + } + + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName = + "VanillaIceCream") + @EnableFlags(android.content.pm.Flags.FLAG_ASL_IN_APK_APP_METADATA_SOURCE) + @Test + fun requestCoarseLocationPerm_hasAslInApk_packageSourceOther() { + installPackageWithInstallSourceAndNoMetadataFromOther(APP_APK_NAME_31_WITH_ASL) + + assertAppHasPermission(ACCESS_COARSE_LOCATION, false) + + requestAppPermissionsForNoResult(ACCESS_COARSE_LOCATION) { + assertPermissionRationaleContainerOnGrantDialogIsVisible(false) + } + } + @Test fun requestFineLocationPerm_hasPermissionRationale() { installPackageWithInstallSourceAndMetadata(APP_APK_NAME_31) diff --git a/tests/cts/permissionui/src/android/permissionui/cts/SafetyLabelChangesJobServiceTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/SafetyLabelChangesJobServiceTest.kt index bb49bc3c2..12089b728 100644 --- a/tests/cts/permissionui/src/android/permissionui/cts/SafetyLabelChangesJobServiceTest.kt +++ b/tests/cts/permissionui/src/android/permissionui/cts/SafetyLabelChangesJobServiceTest.kt @@ -36,6 +36,8 @@ import android.permission.cts.PermissionUtils import android.permission.cts.TestUtils import android.permissionui.cts.AppMetadata.createAppMetadataWithLocationSharingNoAds import android.permissionui.cts.AppMetadata.createAppMetadataWithNoSharing +import android.platform.test.annotations.EnableFlags +import android.platform.test.flag.junit.SetFlagsRule import android.provider.DeviceConfig import android.safetylabel.SafetyLabelConstants import android.safetylabel.SafetyLabelConstants.SAFETY_LABEL_CHANGE_NOTIFICATIONS_ENABLED @@ -61,6 +63,9 @@ import org.junit.Test class SafetyLabelChangesJobServiceTest : BaseUsePermissionTest() { @get:Rule + val setFlagsRule = SetFlagsRule() + + @get:Rule val safetyLabelChangeNotificationsEnabledConfig = DeviceConfigStateChangerRule( context, @@ -304,7 +309,7 @@ class SafetyLabelChangesJobServiceTest : BaseUsePermissionTest() { } @Test - fun runNotificationJob_packageSourceDownloadedFile_udoesNotShowNotification() { + fun runNotificationJob_packageSourceDownloadedFile_doesNotShowNotification() { installPackageViaSession( APP_APK_NAME_31, createAppMetadataWithNoSharing(), @@ -325,6 +330,126 @@ class SafetyLabelChangesJobServiceTest : BaseUsePermissionTest() { assertNotificationNotShown() } + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName = + "VanillaIceCream") + @EnableFlags(android.content.pm.Flags.FLAG_ASL_IN_APK_APP_METADATA_SOURCE) + @Test + fun runNotificationJob_packageSourceUnspecified_aslInApk_doesNotShowNotification() { + installPackageViaSession( + APP_APK_NAME_31, + createAppMetadataWithNoSharing(), + PACKAGE_SOURCE_UNSPECIFIED + ) + waitForBroadcastReceiverFinished() + installPackageNoBroadcast( + APP_APK_NAME_31_WITH_ASL, + packageSource = PACKAGE_SOURCE_UNSPECIFIED + ) + grantLocationPermission(APP_PACKAGE_NAME) + + // Run the job to check whether the missing safety label for the above app update is + // identified and recorded. + runNotificationJob() + + assertNotificationNotShown() + } + + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName = + "VanillaIceCream") + @EnableFlags(android.content.pm.Flags.FLAG_ASL_IN_APK_APP_METADATA_SOURCE) + @Test + fun runNotificationJob_packageSourceOther_aslInApk_doesNotShowNotification() { + installPackageViaSession( + APP_APK_NAME_31, + createAppMetadataWithNoSharing(), + PACKAGE_SOURCE_OTHER + ) + waitForBroadcastReceiverFinished() + installPackageNoBroadcast( + APP_APK_NAME_31_WITH_ASL, + packageSource = PACKAGE_SOURCE_OTHER + ) + grantLocationPermission(APP_PACKAGE_NAME) + + // Run the job to check whether the missing safety label for the above app update is + // identified and recorded. + runNotificationJob() + + assertNotificationNotShown() + } + + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName = + "VanillaIceCream") + @EnableFlags(android.content.pm.Flags.FLAG_ASL_IN_APK_APP_METADATA_SOURCE) + @Test + fun runNotificationJob_packageSourceStore_aslInApk_doesNotShowNotification() { + installPackageViaSession( + APP_APK_NAME_31, + createAppMetadataWithNoSharing(), + PACKAGE_SOURCE_STORE + ) + waitForBroadcastReceiverFinished() + installPackageNoBroadcast( + APP_APK_NAME_31_WITH_ASL, + packageSource = PACKAGE_SOURCE_STORE + ) + grantLocationPermission(APP_PACKAGE_NAME) + + // Run the job to check whether the missing safety label for the above app update is + // identified and recorded. + runNotificationJob() + + assertNotificationNotShown() + } + + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName = + "VanillaIceCream") + @EnableFlags(android.content.pm.Flags.FLAG_ASL_IN_APK_APP_METADATA_SOURCE) + @Test + fun runNotificationJob_packageSourceLocalFile_aslInApk_doesNotShowNotification() { + installPackageViaSession( + APP_APK_NAME_31, + createAppMetadataWithNoSharing(), + PACKAGE_SOURCE_LOCAL_FILE + ) + waitForBroadcastReceiverFinished() + installPackageNoBroadcast( + APP_APK_NAME_31_WITH_ASL, + packageSource = PACKAGE_SOURCE_LOCAL_FILE + ) + grantLocationPermission(APP_PACKAGE_NAME) + + // Run the job to check whether the missing safety label for the above app update is + // identified and recorded. + runNotificationJob() + + assertNotificationNotShown() + } + + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName = + "VanillaIceCream") + @EnableFlags(android.content.pm.Flags.FLAG_ASL_IN_APK_APP_METADATA_SOURCE) + @Test + fun runNotificationJob_packageSourceDownloadedFile_aslInApk_doesNotShowNotification() { + installPackageViaSession( + APP_APK_NAME_31, + createAppMetadataWithNoSharing(), + PACKAGE_SOURCE_DOWNLOADED_FILE + ) + waitForBroadcastReceiverFinished() + installPackageNoBroadcast( + APP_APK_NAME_31_WITH_ASL, + packageSource = PACKAGE_SOURCE_DOWNLOADED_FILE + ) + grantLocationPermission(APP_PACKAGE_NAME) + + // Run the job to check whether the missing safety label for the above app update is + // identified and recorded. + runNotificationJob() + + assertNotificationNotShown() + } + private fun grantLocationPermission(packageName: String) { uiAutomation.grantRuntimePermission( packageName, |