diff options
225 files changed, 5693 insertions, 2157 deletions
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml index f2f1d5bf364..10f40eac2b7 100644 --- a/apps/CtsVerifier/AndroidManifest.xml +++ b/apps/CtsVerifier/AndroidManifest.xml @@ -3573,6 +3573,8 @@ android:value="multi_display_mode" /> <meta-data android:name="ApiTest" android:value="android.widget.Toast#makeText" /> + <meta-data android:name="test_excluded_features" + android:value="android.hardware.type.watch" /> </activity> <activity android:name=".notifications.BubblesVerifierActivity" diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml index 0caea4ecee3..2f0bd55f599 100644 --- a/apps/CtsVerifier/res/values/strings.xml +++ b/apps/CtsVerifier/res/values/strings.xml @@ -1208,6 +1208,8 @@ <string name="multinetwork_connectivity_turn_wifi_off">Please turn Wi-Fi off.</string> <string name="multinetwork_connectivity_turn_wifi_positive">OK</string> <string name="multinetwork_connectivity_turn_wifi_negative">Cancel</string> + <string name="multinetwork_connectivity_connect_to_target_ap_positive">OK</string> + <string name="multinetwork_connectivity_connect_to_target_ap">Connect to configured SSID: %s</string> <!-- Strings for NfcTestActivity --> <string name="nfc_test">NFC Test</string> <string name="nfc_test_info">The Peer-to-Peer Data Exchange tests require two devices with diff --git a/apps/CtsVerifier/res/xml/offhost_aid_list.xml b/apps/CtsVerifier/res/xml/offhost_aid_list.xml index 524e54d7a01..8329955d63d 100644 --- a/apps/CtsVerifier/res/xml/offhost_aid_list.xml +++ b/apps/CtsVerifier/res/xml/offhost_aid_list.xml @@ -1,9 +1,7 @@ <offhost-apdu-service xmlns:android="http://schemas.android.com/apk/res/android" android:description="@string/offhostService"> <aid-group> - <!-- OBTH card manager AID --> <aid-filter android:name="A000000151000000"/> - <!-- NXP card manager AID --> - <aid-filter android:name="A000000003000000"/> + <aid-filter android:name="A000000444000000"/> </aid-group> </offhost-apdu-service> diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/ReportExporter.java b/apps/CtsVerifier/src/com/android/cts/verifier/ReportExporter.java index baa19519c26..f603baa858f 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/ReportExporter.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/ReportExporter.java @@ -138,15 +138,16 @@ public class ReportExporter extends AsyncTask<Void, Void, String> { verifierReportsDir.mkdirs(); String suiteName = Version.getMetadata(mContext, SUITE_NAME_METADATA_KEY); + String reportName = getReportName(suiteName); // create a temporary directory for this particular report - File tempDir = new File(verifierReportsDir, getReportName(suiteName)); + File tempDir = new File(verifierReportsDir, reportName); tempDir.mkdirs(); // Pull in any ReportLogs copyReportFiles(tempDir); // create a File object for a report ZIP file - File reportZipFile = new File(verifierReportsDir, getReportName(suiteName) + ZIP_EXTENSION); + File reportZipFile = new File(verifierReportsDir, reportName + ZIP_EXTENSION); try { // Serialize the report diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/net/MultiNetworkConnectivityTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/net/MultiNetworkConnectivityTestActivity.java index f1deb8d527b..44136cb59f4 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/net/MultiNetworkConnectivityTestActivity.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/net/MultiNetworkConnectivityTestActivity.java @@ -20,14 +20,10 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; -import static com.android.cts.verifier.net.MultiNetworkConnectivityTestActivity.ValidatorState - .COMPLETED; -import static com.android.cts.verifier.net.MultiNetworkConnectivityTestActivity.ValidatorState - .NOT_STARTED; -import static com.android.cts.verifier.net.MultiNetworkConnectivityTestActivity.ValidatorState - .STARTED; -import static com.android.cts.verifier.net.MultiNetworkConnectivityTestActivity.ValidatorState - .WAITING_FOR_USER_INPUT; +import static com.android.cts.verifier.net.MultiNetworkConnectivityTestActivity.ValidatorState.COMPLETED; +import static com.android.cts.verifier.net.MultiNetworkConnectivityTestActivity.ValidatorState.NOT_STARTED; +import static com.android.cts.verifier.net.MultiNetworkConnectivityTestActivity.ValidatorState.STARTED; +import static com.android.cts.verifier.net.MultiNetworkConnectivityTestActivity.ValidatorState.WAITING_FOR_USER_INPUT; import android.app.ActivityManager; import android.app.AlertDialog; @@ -44,7 +40,6 @@ import android.net.NetworkInfo; import android.net.NetworkRequest; import android.net.NetworkSpecifier; import android.net.wifi.SupplicantState; -import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.net.wifi.WifiNetworkSpecifier; @@ -70,7 +65,7 @@ import java.util.Collections; import java.util.List; /** - * A CTS verifier to ensure that when an app calls WifiManager#enableNetwork, + * A CTS verifier to ensure that when device connect to a new Wi-Fi network, * - When the wifi network does not have internet connectivity, the device should * not disable other forms or connectivity, for example cellular. * - When the wifi network that the phone connects to loses connectivity, then @@ -212,7 +207,6 @@ public class MultiNetworkConnectivityTestActivity extends PassFailButtons.Activi protected void onDestroy() { super.onDestroy(); destroyBroadcastReceivers(); - restoreOriginalWifiState(); } private void recordCurrentWifiState() { @@ -245,12 +239,6 @@ public class MultiNetworkConnectivityTestActivity extends PassFailButtons.Activi return result; } - private void restoreOriginalWifiState() { - if (mRecordedWifiConfiguration >= 0) { - mWifiManager.enableNetwork(mRecordedWifiConfiguration, true); - } - } - private boolean requestSystemAlertWindowPerimissionIfRequired() { if (isLowRamDevice()) { // For low ram devices, we won't run tests that depend on this permission. @@ -298,6 +286,23 @@ public class MultiNetworkConnectivityTestActivity extends PassFailButtons.Activi alertDialog.show(); } + private void requestUserConnectToApAsync(ConnectApCallback callback) { + if (isConnectedToExpectedWifiNetwork()) { + callback.onComplete(/* isSuccess = */ true); + return; + } + + AlertDialog alertDialog = new AlertDialog.Builder(this) + .setMessage(getString(R.string.multinetwork_connectivity_connect_to_target_ap, + mAccessPointSsid)) + .setPositiveButton(R.string.multinetwork_connectivity_turn_wifi_positive, + (a, b) -> requestUserConnectToApAsync(callback)) + .setNegativeButton(R.string.multinetwork_connectivity_turn_wifi_negative, + (a, b) -> callback.onComplete(/* isSuccess = */ false)) + .create(); + alertDialog.show(); + } + private void toggleWifiAsync(SetWifiCallback callback) { // Turn off WiFi. requestUserEnableWifiAsync(false, (isSuccess) -> { @@ -489,31 +494,6 @@ public class MultiNetworkConnectivityTestActivity extends PassFailButtons.Activi } } - private WifiConfiguration buildWifiConfiguration() { - WifiConfiguration wifiConfiguration = new WifiConfiguration(); - wifiConfiguration.SSID = "\"" + mAccessPointSsid + "\""; - wifiConfiguration.preSharedKey = "\"" + mPskValue + "\""; - wifiConfiguration.status = WifiConfiguration.Status.ENABLED; - wifiConfiguration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP); - wifiConfiguration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP); - wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK); - wifiConfiguration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP); - wifiConfiguration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP); - wifiConfiguration.allowedProtocols.set(WifiConfiguration.Protocol.RSN); - return wifiConfiguration; - } - - private int getOrAddLegacyNetwork() { - List<WifiConfiguration> availableConfigurations = mWifiManager.getConfiguredNetworks(); - for (WifiConfiguration configuration : availableConfigurations) { - if (mAccessPointSsid.equals(configuration.SSID)) { - return configuration.networkId; - } - } - int newNetwork = mWifiManager.addNetwork(buildWifiConfiguration()); - return newNetwork; - } - private boolean isConnectedToExpectedWifiNetwork() { WifiInfo wifiInfo = mWifiManager.getConnectionInfo(); DhcpInfo dhcpInfo = mWifiManager.getDhcpInfo(); @@ -601,15 +581,7 @@ public class MultiNetworkConnectivityTestActivity extends PassFailButtons.Activi } } - private void legacyConnectToWifiNetwork(boolean requireInternet) { - // If device is not connected to the expected WifiNetwork, connect to the wifi Network. - // Timeout with failure if it can't connect. - if (!isConnectedToExpectedWifiNetwork()) { - int network = getOrAddLegacyNetwork(); - WifiManager wifiManager = (WifiManager) getApplicationContext() - .getSystemService(Context.WIFI_SERVICE); - wifiManager.enableNetwork(network, true); - } + private void requestNetwork(boolean requireInternet) { startTimerDisplay(WIFI_NETWORK_CONNECT_TIMEOUT_MS / 1000); NetworkRequest.Builder networkRequestBuilder = new NetworkRequest.Builder() .addTransportType(TRANSPORT_WIFI); @@ -750,6 +722,9 @@ public class MultiNetworkConnectivityTestActivity extends PassFailButtons.Activi /** Called when cellular network is connected. */ void onCellularNetworkConnected(Network network) { + if (mValidatorState != NOT_STARTED) { + return; + } onContinuePreWifiConnect(); } @@ -855,7 +830,14 @@ public class MultiNetworkConnectivityTestActivity extends PassFailButtons.Activi void connectToWifi() { mTestCallback.testProgress(R.string.multinetwork_connectivity_test_connect_wifi); - mConnectivityState.legacyConnectToWifiNetwork(false); + requestUserConnectToApAsync((isSuccess) -> { + if (isSuccess) { + // Request network + mConnectivityState.requestNetwork(false); + } else { + endTest(false, R.string.multinetwork_status_wifi_connect_wrong_ap); + } + }); } } @@ -898,7 +880,14 @@ public class MultiNetworkConnectivityTestActivity extends PassFailButtons.Activi void connectToWifi() { mTestCallback.testProgress(R.string.multinetwork_connectivity_test_connect_wifi); - mConnectivityState.legacyConnectToWifiNetwork(true); + requestUserConnectToApAsync((isSuccess) -> { + if (isSuccess) { + // Request network + mConnectivityState.requestNetwork(true); + } else { + endTest(false, R.string.multinetwork_status_unable_to_toggle_wifi); + } + }); } @Override @@ -1042,4 +1031,8 @@ public class MultiNetworkConnectivityTestActivity extends PassFailButtons.Activi private interface SetWifiCallback { void onComplete(boolean isSuccess); } + + private interface ConnectApCallback { + void onComplete(boolean isSuccess); + } } diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/OffHostService.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/OffHostService.java index d8cdf1986db..d9b18abb90b 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/OffHostService.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hce/OffHostService.java @@ -10,7 +10,7 @@ public class OffHostService { public static final CommandApdu[] APDU_COMMAND_SEQUENCE = { HceUtils.buildSelectApdu("A000000151000000", true), HceUtils.buildCommandApdu("80CA9F7F00", true), - HceUtils.buildSelectApdu("A000000003000000", true), + HceUtils.buildSelectApdu("A000000444000000", true), HceUtils.buildCommandApdu("80CA9F7F00", true) }; diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/security/FingerprintBoundKeysTest.java b/apps/CtsVerifier/src/com/android/cts/verifier/security/FingerprintBoundKeysTest.java index b6abc3cd39d..3def4b07543 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/security/FingerprintBoundKeysTest.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/security/FingerprintBoundKeysTest.java @@ -346,7 +346,7 @@ public class FingerprintBoundKeysTest extends PassFailButtons.Activity { boolean tokenExpired = false; long startTime = System.currentTimeMillis(); while (((System.currentTimeMillis() - startTime) - < (AUTHENTICATION_DURATION_SECONDS * 1000)) + < ((AUTHENTICATION_DURATION_SECONDS + 1) * 1000)) && (!tokenExpired)) { try { Thread.sleep(1000); diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/activities/ActivityReference.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/activities/ActivityReference.java index a81588a4833..a9de0ace599 100644 --- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/activities/ActivityReference.java +++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/activities/ActivityReference.java @@ -54,10 +54,14 @@ public final class ActivityReference extends ComponentReference { // level and work for any component try (PermissionContext p = TestApis.permissions() .withPermission(INTERACT_ACROSS_USERS_FULL)) { - return TestApis.context().androidContextAsUser(user) - .getPackageManager() - .getActivityInfo(componentName(), MATCH_DISABLED_COMPONENTS) - .enabled; + PackageManager pm = TestApis.context().androidContextAsUser(user).getPackageManager(); + int runtimeEnabledSetting = pm.getComponentEnabledSetting(componentName()); + if (runtimeEnabledSetting == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) { + return true; + } else if ((runtimeEnabledSetting == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT)) { + return pm.getActivityInfo(componentName(), MATCH_DISABLED_COMPONENTS).enabled; + } + return false; } catch (PackageManager.NameNotFoundException e) { throw new NeneException("Activity does not exist or is not activity " + this, e); } diff --git a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/LocaleDeviceInfo.java b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/LocaleDeviceInfo.java index d76c7b9c64e..4862679e70d 100644 --- a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/LocaleDeviceInfo.java +++ b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/LocaleDeviceInfo.java @@ -15,6 +15,9 @@ */ package com.android.compatibility.common.deviceinfo; +import static java.util.stream.Collectors.toList; +import static java.util.stream.Collectors.toSet; + import android.icu.util.ULocale; import android.icu.util.VersionInfo; import android.util.Log; @@ -35,9 +38,9 @@ import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.stream.Collectors; import java.util.stream.Stream; /** @@ -60,8 +63,8 @@ public final class LocaleDeviceInfo extends DeviceInfo { store.addListResult("locale", locales); List<String> icuLocales = Arrays.stream(ULocale.getAvailableLocales()) - .map((uLocale -> uLocale.toLanguageTag())) - .collect(Collectors.toList()); + .map((uLocale -> uLocale.toLanguageTag())) + .collect(toList()); if (icuLocales.isEmpty()) { // default locale icuLocales.add(ULocale.US.toLanguageTag()); @@ -82,7 +85,7 @@ public final class LocaleDeviceInfo extends DeviceInfo { return matcher.find() ? Strings.nullToEmpty(matcher.group(1)) : ""; }) .sorted() - .collect(Collectors.toList()); + .collect(toList()); } catch (IOException e) { Log.w(TAG,"Hyphenation binary folder is not exist" , e); } @@ -92,16 +95,27 @@ public final class LocaleDeviceInfo extends DeviceInfo { } /** - * Collect the fingerprints of ICU data files. On AOSP build, there are only 2 data files. - * The example paths are /apex/com.android.tzdata/etc/icu/icu_tzdata.dat and + * Collect the fingerprints of ICU data files. On AOSP build, there are 5 data files. + * The example paths are /apex/com.android.tzdata/etc/icu/zoneinfo64.res and * /apex/com.android.i18n/etc/icu/icudt65l.dat */ private void collectLocaleDataFilesInfo(DeviceInfoStore store) throws IOException { int icuVersion = VersionInfo.ICU_VERSION.getMajor(); - File[] fixedDatPaths = new File[] { - new File("/apex/com.android.tzdata/etc/icu/icu_tzdata.dat"), - new File("/apex/com.android.i18n/etc/icu/icudt" + icuVersion + "l.dat"), - }; + + String icuDatFilePath = "/apex/com.android.i18n/etc/icu/icudt" + icuVersion + "l.dat"; + Stream<String> tzdataModuleResFiles = + Stream.of( + "metaZones.res", + "zoneinfo64.res", + "timezoneTypes.res", + "windowsZones.res") + .map(fileName -> "/apex/com.android.tzdata/etc/icu/" + fileName); + Set<File> fixedIcuFilesPaths = + Stream.concat( + Stream.of(icuDatFilePath), + tzdataModuleResFiles) + .map(File::new) + .collect(toSet()); // This property has been deprecated since Android 12. The property will not work if this // app targets SDK level 31 or higher. Thus, we add the above fixedDatPaths in case that @@ -110,33 +124,33 @@ public final class LocaleDeviceInfo extends DeviceInfo { String prop = System.getProperty("android.icu.impl.ICUBinary.dataPath"); store.startArray("icu_data_file_info"); - List<File> datFiles = new ArrayList<>(); + List<File> icuFiles = new ArrayList<>(); if (prop != null) { String[] dataDirs = prop.split(":"); // List all ".dat" files in the directories. - datFiles = Arrays.stream(dataDirs) + icuFiles = Arrays.stream(dataDirs) .filter((dir) -> dir != null && !dir.isEmpty()) .map((dir) -> new File(dir)) .filter((f) -> f.canRead() && f.isDirectory()) .map((f) -> f.listFiles()) .filter((files) -> files != null) .flatMap(files -> Stream.of(files)) - .collect(Collectors.toList()); + .collect(toList()); } - datFiles.addAll(Arrays.asList(fixedDatPaths)); + icuFiles.addAll(fixedIcuFilesPaths); // Keep the readable paths only. - datFiles = datFiles.stream() + icuFiles = icuFiles.stream() .distinct() - .filter((f) -> f != null && f.canRead() && f.getName().endsWith(".dat")) - .collect(Collectors.toList()); + .filter((f) -> f != null && f.canRead() && isIcuFile(f)) + .collect(toList()); - for (File datFile : datFiles) { - String sha256Hash = sha256(datFile); + for (File icuFile : icuFiles) { + String sha256Hash = sha256(icuFile); store.startGroup(); - store.addResult("file_path", datFile.getPath()); + store.addResult("file_path", icuFile.getPath()); // Still store the null hash to indicate an error occurring when obtaining the hash. store.addResult("file_sha256", sha256Hash); store.endGroup(); @@ -144,6 +158,10 @@ public final class LocaleDeviceInfo extends DeviceInfo { store.endArray(); } + private static boolean isIcuFile(File file) { + return file.getName().endsWith(".dat") || file.getName().endsWith(".res"); + } + public static @Nullable String sha256(File file) { try (FileInputStream in = new FileInputStream(file); FileChannel fileChannel = in.getChannel()) { diff --git a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/OWNERS b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/OWNERS index 0231a04fe9c..4de51e6ed90 100644 --- a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/OWNERS +++ b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/OWNERS @@ -3,3 +3,6 @@ # Bug component: 1084732 = per-file KeystoreAttestationDeviceInfo.java # Bug template url: http://b/new?component=1084732&template=1604634 = per-file KeystoreAttestationDeviceInfo.java per-file KeystoreAttestationDeviceInfo.java = file:platform/cts:/tests/tests/keystore/OWNERS + +# Bug component: 137825 = per-file PermissionDeviceInfo.java +per-file PermissionDeviceInfo.java = file:platform/frameworks/base:/core/java/android/permission/OWNERS diff --git a/common/device-side/interactive/src/main/AndroidManifest.xml b/common/device-side/interactive/src/main/AndroidManifest.xml index efb8643a5c0..386a0ce1864 100644 --- a/common/device-side/interactive/src/main/AndroidManifest.xml +++ b/common/device-side/interactive/src/main/AndroidManifest.xml @@ -23,7 +23,7 @@ <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /> - <application> + <application android:requestLegacyExternalStorage="true"> <provider android:name="com.android.interactive.AutomationFileProvider" android:authorities="${applicationId}.interactive.automation" @@ -33,4 +33,4 @@ </provider> </application> -</manifest>
\ No newline at end of file +</manifest> diff --git a/common/device-side/interactive/src/main/java/com/android/interactive/ScreenshotUtil.java b/common/device-side/interactive/src/main/java/com/android/interactive/ScreenshotUtil.java index f53f6a660c4..64afa197bbd 100644 --- a/common/device-side/interactive/src/main/java/com/android/interactive/ScreenshotUtil.java +++ b/common/device-side/interactive/src/main/java/com/android/interactive/ScreenshotUtil.java @@ -45,8 +45,9 @@ public final class ScreenshotUtil { Environment.getExternalStorageDirectory().getAbsolutePath() + "/Documents/xts/screenshots/"; File file = new File(screenshotDir); - if (!file.exists()) { - file.mkdirs(); + if (!file.exists() && !file.mkdirs()) { + // Let the steps that require screenshots fail immediately. + throw new RuntimeException("Failed to create " + screenshotDir + " directory on DUT."); } File screenshotFile = diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/FrameworkSpecificTest.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/FrameworkSpecificTest.java new file mode 100644 index 00000000000..e1d38acc07f --- /dev/null +++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/FrameworkSpecificTest.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.compatibility.common.util; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation for tests that specifically exercises Framework code (e.g. not in a mainline + * module). This is used to identify tests that should be skipped when running mainline + * module tests, such as MTS or MCTS. + * in summary: + * tag with '@FrameworkSpecificTest' -- run: CTS, skip: MCTS, MTS + * tag with '@ModuleSpecificTest' -- run: MCTS, MTS; skip: CTS + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD, ElementType.TYPE}) +public @interface FrameworkSpecificTest { +} diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/ModuleSpecificTest.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/ModuleSpecificTest.java new file mode 100644 index 00000000000..5b387018188 --- /dev/null +++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/ModuleSpecificTest.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.compatibility.common.util; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation for tests that specifically exercises Mainline Module code. + * This is used to identify tests that should be skipped when running CTS. + * in summary: + * tag with '@FrameworkSpecificTest' -- run: CTS, skip: MCTS, MTS + * tag with '@ModuleSpecificTest' -- run: MCTS, MTS; skip: CTS + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD, ElementType.TYPE}) +public @interface ModuleSpecificTest { +} diff --git a/hostsidetests/adb/OWNERS b/hostsidetests/adb/OWNERS index 5050f5c0b5a..f86fdc1d47d 100644 --- a/hostsidetests/adb/OWNERS +++ b/hostsidetests/adb/OWNERS @@ -1,3 +1,3 @@ # Bug component: 1352 -shaju@google.com +sanglardf@google.com include platform/system/core:/janitors/OWNERS diff --git a/hostsidetests/appcompat/compatchanges/src/com/android/cts/appcompat/CompatChangesValidConfigTest.java b/hostsidetests/appcompat/compatchanges/src/com/android/cts/appcompat/CompatChangesValidConfigTest.java index f57fe29ac70..af840c90fae 100644 --- a/hostsidetests/appcompat/compatchanges/src/com/android/cts/appcompat/CompatChangesValidConfigTest.java +++ b/hostsidetests/appcompat/compatchanges/src/com/android/cts/appcompat/CompatChangesValidConfigTest.java @@ -108,6 +108,7 @@ public final class CompatChangesValidConfigTest extends CompatChangeGatingTestCa "OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED", "OVERRIDE_RESPECT_REQUESTED_ORIENTATION", "OVERRIDE_SANDBOX_VIEW_BOUNDS_APIS", + "OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION", "DEFAULT_RESCIND_BAL_FG_PRIVILEGES_BOUND_SERVICE", "DEFAULT_RESCIND_BAL_PRIVILEGES_FROM_PENDING_INTENT_SENDER", "RETURN_DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY", diff --git a/hostsidetests/art/Android.bp b/hostsidetests/art/Android.bp index 155990d2e21..ef70a2790eb 100644 --- a/hostsidetests/art/Android.bp +++ b/hostsidetests/art/Android.bp @@ -16,6 +16,7 @@ package { default_applicable_licenses: ["Android-Apache-2.0"], + default_team: "trendy_team_art_performance", } java_test_host { diff --git a/hostsidetests/classloaders/Android.bp b/hostsidetests/classloaders/Android.bp new file mode 100644 index 00000000000..190d39a502c --- /dev/null +++ b/hostsidetests/classloaders/Android.bp @@ -0,0 +1,17 @@ +// Copyright (C) 2024 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + default_team: "trendy_team_art_mainline", +} diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/DeviceIdentifiersTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/DeviceIdentifiersTest.java index 236192e4957..ac90bba23bf 100644 --- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/DeviceIdentifiersTest.java +++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/DeviceIdentifiersTest.java @@ -49,12 +49,18 @@ public class DeviceIdentifiersTest extends BaseDeviceAdminTest { assertEquals(String.format(DEVICE_ID_WITH_PERMISSION_ERROR_MESSAGE, "getDeviceId"), ShellIdentityUtils.invokeMethodWithShellPermissions(telephonyManager, TelephonyManager::getDeviceId), telephonyManager.getDeviceId()); - assertEquals(String.format(DEVICE_ID_WITH_PERMISSION_ERROR_MESSAGE, "getImei"), - ShellIdentityUtils.invokeMethodWithShellPermissions(telephonyManager, - TelephonyManager::getImei), telephonyManager.getImei()); - assertEquals(String.format(DEVICE_ID_WITH_PERMISSION_ERROR_MESSAGE, "getMeid"), - ShellIdentityUtils.invokeMethodWithShellPermissions(telephonyManager, - TelephonyManager::getMeid), telephonyManager.getMeid()); + if (mContext.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_TELEPHONY_GSM)) { + assertEquals(String.format(DEVICE_ID_WITH_PERMISSION_ERROR_MESSAGE, "getImei"), + ShellIdentityUtils.invokeMethodWithShellPermissions(telephonyManager, + TelephonyManager::getImei), telephonyManager.getImei()); + } + if (mContext.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_TELEPHONY_CDMA)) { + assertEquals(String.format(DEVICE_ID_WITH_PERMISSION_ERROR_MESSAGE, "getMeid"), + ShellIdentityUtils.invokeMethodWithShellPermissions(telephonyManager, + TelephonyManager::getMeid), telephonyManager.getMeid()); + } assertEquals(String.format(DEVICE_ID_WITH_PERMISSION_ERROR_MESSAGE, "getSubscriberId"), ShellIdentityUtils.invokeMethodWithShellPermissions(telephonyManager, TelephonyManager::getSubscriberId), telephonyManager.getSubscriberId()); diff --git a/hostsidetests/dexmetadata/Android.bp b/hostsidetests/dexmetadata/Android.bp new file mode 100644 index 00000000000..190d39a502c --- /dev/null +++ b/hostsidetests/dexmetadata/Android.bp @@ -0,0 +1,17 @@ +// Copyright (C) 2024 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + default_team: "trendy_team_art_mainline", +} diff --git a/hostsidetests/edi/src/android/edi/cts/NativeDeviceInfo.java b/hostsidetests/edi/src/android/edi/cts/NativeDeviceInfo.java index cab0581d396..9bbf0b583b7 100644 --- a/hostsidetests/edi/src/android/edi/cts/NativeDeviceInfo.java +++ b/hostsidetests/edi/src/android/edi/cts/NativeDeviceInfo.java @@ -29,6 +29,38 @@ import java.util.regex.Pattern; */ public class NativeDeviceInfo extends DeviceInfo { + private void collectMemCG(ITestDevice device, HostInfoStore store) throws Exception { + CommandResult commandResult = device.executeShellV2Command("grep memory /proc/cgroups"); + + store.startGroup("memcg"); + if (commandResult.getExitCode() == 0) { + String[] tokens = commandResult.getStdout().split("\\s+"); + boolean memcg_enabled = tokens[3].equals("1"); + store.addResult("enabled", memcg_enabled); + if (memcg_enabled) store.addResult("version", tokens[1].equals("0") ? "2" : "1"); + } else if (commandResult.getExitCode() == 1) { // "memory" not found by grep + store.addResult("version", -3); + } else if (commandResult.getStderr().contains("No such file")) { + store.addResult("version", -1); + } else if (commandResult.getStderr().contains("Permission denied")) { + store.addResult("version", -2); + } + store.endGroup(); + } + + private void collectMGLRU(ITestDevice device, HostInfoStore store) throws Exception { + CommandResult commandResult = device.executeShellV2Command( + "cat /sys/kernel/mm/lru_gen/enabled"); + + if (commandResult.getExitCode() == 0) { + store.addResult("mglru_enabled", Integer.decode(commandResult.getStdout().trim())); + } else if (commandResult.getStderr().contains("No such file")) { + store.addResult("mglru_enabled", -1); + } else if (commandResult.getStderr().contains("Permission denied")) { + store.addResult("mglru_enabled", -2); + } + } + @Override protected void collectDeviceInfo(HostInfoStore store) throws Exception { ITestDevice device = getDevice(); @@ -77,5 +109,8 @@ public class NativeDeviceInfo extends DeviceInfo { allocatorName += "_lowmemory"; } store.addResult("allocator", allocatorName); + + collectMemCG(device, store); + collectMGLRU(device, store); } } diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecConstants.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecConstants.java index 1d5d2af1805..beddf500fe4 100644 --- a/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecConstants.java +++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecConstants.java @@ -30,6 +30,7 @@ public final class HdmiCecConstants { public static final int REBOOT_TIMEOUT = 60000; public static final int TIMEOUT_CEC_REINIT_SECONDS = 5; public static final int TIMEOUT_SAFETY_MS = 500; + public static final long TIMEOUT_UI_AND_STANDBY_AFTER_ACTIVE_SOURCE_LOST_SECONDS = 35; public static final int INVALID_VENDOR_ID = 0xFFFFFF; diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecRoutingControlTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecRoutingControlTest.java index 7ad69634cfd..13f0b74a31c 100644 --- a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecRoutingControlTest.java +++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecRoutingControlTest.java @@ -18,6 +18,8 @@ package android.hdmicec.cts.playback; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assume.assumeFalse; + import android.hdmicec.cts.BaseHdmiCecCtsTest; import android.hdmicec.cts.CecMessage; import android.hdmicec.cts.CecOperand; @@ -161,6 +163,9 @@ public final class HdmiCecRoutingControlTest extends BaseHdmiCecCtsTest { */ @Test public void testPowerStateChangeOnActiveSourceLost_standby() throws Exception { + assumeFalse("Skip for audio system devices (b/323469502)", + hasDeviceType(HdmiCecConstants.CEC_DEVICE_TYPE_AUDIO_SYSTEM)); + String previousActionOnActiveSourceLost = setPowerStateChangeOnActiveSourceLost( HdmiCecConstants.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW); try { @@ -172,6 +177,8 @@ public final class HdmiCecRoutingControlTest extends BaseHdmiCecCtsTest { // Now make the TV the active source hdmiCecClient.sendCecMessage(LogicalAddress.TV, LogicalAddress.BROADCAST, CecOperand.ACTIVE_SOURCE, CecMessage.formatParams("0000")); + TimeUnit.SECONDS.sleep( + HdmiCecConstants.TIMEOUT_UI_AND_STANDBY_AFTER_ACTIVE_SOURCE_LOST_SECONDS); assertDeviceWakefulness(HdmiCecConstants.WAKEFULNESS_ASLEEP); } finally { /* Wake up the device */ @@ -197,6 +204,8 @@ public final class HdmiCecRoutingControlTest extends BaseHdmiCecCtsTest { // Now make the TV the active source hdmiCecClient.sendCecMessage(LogicalAddress.TV, LogicalAddress.BROADCAST, CecOperand.ACTIVE_SOURCE, CecMessage.formatParams("0000")); + TimeUnit.SECONDS.sleep( + HdmiCecConstants.TIMEOUT_UI_AND_STANDBY_AFTER_ACTIVE_SOURCE_LOST_SECONDS); assertDeviceWakefulness(HdmiCecConstants.WAKEFULNESS_AWAKE); } finally { /* Wake up the device */ diff --git a/hostsidetests/incrementalinstall/Android.bp b/hostsidetests/incrementalinstall/Android.bp index fdc611aa00c..abdabe73add 100644 --- a/hostsidetests/incrementalinstall/Android.bp +++ b/hostsidetests/incrementalinstall/Android.bp @@ -18,6 +18,7 @@ package { java_test_host { name: "CtsIncrementalInstallHostTestCases", + team: "trendy_team_incremental", srcs: ["src/**/*.java"], libs: [ "compatibility-host-util", diff --git a/hostsidetests/jdwpsecurity/Android.bp b/hostsidetests/jdwpsecurity/Android.bp index 1575dbea170..54e5c64929e 100644 --- a/hostsidetests/jdwpsecurity/Android.bp +++ b/hostsidetests/jdwpsecurity/Android.bp @@ -14,6 +14,7 @@ package { default_applicable_licenses: ["Android-Apache-2.0"], + default_team: "trendy_team_art_performance", } java_test_host { diff --git a/hostsidetests/jdwpsecurity/OWNERS b/hostsidetests/jdwpsecurity/OWNERS index 6e0629999c7..131c53fbd08 100644 --- a/hostsidetests/jdwpsecurity/OWNERS +++ b/hostsidetests/jdwpsecurity/OWNERS @@ -1,2 +1,2 @@ -# Bug component: 86431 -include /hostsidetests/jvmti/run-tests/OWNERS +# Bug component: 1304319 +include platform/art:/OWNERS diff --git a/hostsidetests/jdwptunnel/Android.bp b/hostsidetests/jdwptunnel/Android.bp index b32d2ffda00..46d9707fe28 100644 --- a/hostsidetests/jdwptunnel/Android.bp +++ b/hostsidetests/jdwptunnel/Android.bp @@ -14,6 +14,7 @@ package { default_applicable_licenses: ["Android-Apache-2.0"], + default_team: "trendy_team_art_performance", } java_test_host { diff --git a/hostsidetests/jdwptunnel/OWNERS b/hostsidetests/jdwptunnel/OWNERS index 6e0629999c7..131c53fbd08 100644 --- a/hostsidetests/jdwptunnel/OWNERS +++ b/hostsidetests/jdwptunnel/OWNERS @@ -1,2 +1,2 @@ -# Bug component: 86431 -include /hostsidetests/jvmti/run-tests/OWNERS +# Bug component: 1304319 +include platform/art:/OWNERS diff --git a/hostsidetests/jdwptunnel/src/android/jdwptunnel/cts/JdwpTunnelTest.java b/hostsidetests/jdwptunnel/src/android/jdwptunnel/cts/JdwpTunnelTest.java index 9961273349c..c7ebd52c49c 100644 --- a/hostsidetests/jdwptunnel/src/android/jdwptunnel/cts/JdwpTunnelTest.java +++ b/hostsidetests/jdwptunnel/src/android/jdwptunnel/cts/JdwpTunnelTest.java @@ -345,13 +345,39 @@ public class JdwpTunnelTest extends BaseHostJUnit4Test { assertTrue((is.read() & 0x80) == 0x80); } - private void assertThreadSuspensionState(VirtualMachine vm, boolean expected) { + private boolean testThreadSuspensionState(VirtualMachine vm, boolean expected) { for (ThreadReference tr : vm.allThreads()) { boolean isSuspended = tr.isSuspended(); if (isSuspended != expected) { - fail("Thread in unexpected state '" + tr.name() + "' isSuspended=" + isSuspended); + return false; } } + return true; + } + + private String dumpThreads(VirtualMachine vm) { + StringBuilder result = new StringBuilder(); + for (ThreadReference tr : vm.allThreads()) { + result.append("Thread: '"); + result.append(tr.name()); + result.append("' isSuspended="); + result.append(tr.isSuspended()); + result.append("\n"); + } + return result.toString(); + } + + private void assertThreadSuspensionState(VirtualMachine vm, boolean expected) + throws InterruptedException { + // If the debugger connects too fast, the VM may not have had time to hit the + // suspension point. We try several times to remedy to this problem. + for (int i = 0; i < 4; i++) { + if (testThreadSuspensionState(vm, expected)) { + return; + } + Thread.sleep(1000); + } + fail("Threads are in unexpected state (expected=" + expected + ")\n" + dumpThreads(vm)); } // App can be started "suspended" which means all its threads will be suspended shorty after diff --git a/hostsidetests/jvmti/Android.bp b/hostsidetests/jvmti/Android.bp new file mode 100644 index 00000000000..8faed40643d --- /dev/null +++ b/hostsidetests/jvmti/Android.bp @@ -0,0 +1,17 @@ +// Copyright (C) 2024 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + default_team: "trendy_team_art_performance", +} diff --git a/hostsidetests/jvmti/run-tests/OWNERS b/hostsidetests/jvmti/OWNERS index ef864121273..131c53fbd08 100644 --- a/hostsidetests/jvmti/run-tests/OWNERS +++ b/hostsidetests/jvmti/OWNERS @@ -1,2 +1,2 @@ -# Bug component: 86431 +# Bug component: 1304319 include platform/art:/OWNERS diff --git a/hostsidetests/jvmti/allocation-tracking/OWNERS b/hostsidetests/jvmti/allocation-tracking/OWNERS deleted file mode 100644 index 2dbff0d6606..00000000000 --- a/hostsidetests/jvmti/allocation-tracking/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -# Bug component: 86431 -include ../run-tests/OWNERS diff --git a/hostsidetests/jvmti/attaching/OWNERS b/hostsidetests/jvmti/attaching/OWNERS deleted file mode 100644 index 2dbff0d6606..00000000000 --- a/hostsidetests/jvmti/attaching/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -# Bug component: 86431 -include ../run-tests/OWNERS diff --git a/hostsidetests/jvmti/base/host/src/android/jvmti/cts/JvmtiHostTest.java b/hostsidetests/jvmti/base/host/src/android/jvmti/cts/JvmtiHostTest.java index cecb6ed547a..881ab719a0d 100644 --- a/hostsidetests/jvmti/base/host/src/android/jvmti/cts/JvmtiHostTest.java +++ b/hostsidetests/jvmti/base/host/src/android/jvmti/cts/JvmtiHostTest.java @@ -128,7 +128,7 @@ public class JvmtiHostTest extends DeviceTestCase implements IBuildReceiver, IAb RemoteAndroidTestRunner runner = new RemoteAndroidTestRunner(mTestPackageName, RUNNER, device.getIDevice()); // set a max deadline limit to avoid hanging forever - runner.setMaxTimeToOutputResponse(5, TimeUnit.MINUTES); + runner.setMaxTimeToOutputResponse(10, TimeUnit.MINUTES); AttachAgent aa = new AttachAgent(device, mTestPackageName, mTestApk); aa.prepare(); diff --git a/hostsidetests/jvmti/redefining/OWNERS b/hostsidetests/jvmti/redefining/OWNERS deleted file mode 100644 index 2dbff0d6606..00000000000 --- a/hostsidetests/jvmti/redefining/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -# Bug component: 86431 -include ../run-tests/OWNERS diff --git a/hostsidetests/jvmti/tagging/OWNERS b/hostsidetests/jvmti/tagging/OWNERS deleted file mode 100644 index 2dbff0d6606..00000000000 --- a/hostsidetests/jvmti/tagging/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -# Bug component: 86431 -include ../run-tests/OWNERS diff --git a/hostsidetests/mediapc/videoencodingquality/app/AndroidManifest.xml b/hostsidetests/mediapc/videoencodingquality/app/AndroidManifest.xml index a3c5336c28c..54e7d959641 100644 --- a/hostsidetests/mediapc/videoencodingquality/app/AndroidManifest.xml +++ b/hostsidetests/mediapc/videoencodingquality/app/AndroidManifest.xml @@ -37,8 +37,5 @@ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" android:targetPackage="android.videoencoding.app" android:label="Video encoding app for android.media" > - <meta-data - android:name="listener" - android:value="com.android.cts.runner.CtsTestRunListener" /> </instrumentation> </manifest> diff --git a/hostsidetests/mediapc/videoencodingquality/app/src/android/videoencoding/app/VideoTranscoderTest.java b/hostsidetests/mediapc/videoencodingquality/app/src/android/videoencoding/app/VideoTranscoderTest.java index e3364bf6827..294fe80f92d 100644 --- a/hostsidetests/mediapc/videoencodingquality/app/src/android/videoencoding/app/VideoTranscoderTest.java +++ b/hostsidetests/mediapc/videoencodingquality/app/src/android/videoencoding/app/VideoTranscoderTest.java @@ -71,7 +71,9 @@ import java.util.ArrayList; */ @RunWith(AndroidJUnit4.class) public class VideoTranscoderTest { - private static final String MEDIA_DIR = "/sdcard/veq/input/"; + private static final String SDCARD_MOUNT_POINT = + Environment.getExternalStorageDirectory().getAbsolutePath(); + private static final String MEDIA_DIR = SDCARD_MOUNT_POINT + "/veq/input/"; public static final String ENC_CONFIG_JSON = "conf-json"; private static final String ENC_CONFIG_FILE; private static String PATH_PREFIX; @@ -186,7 +188,7 @@ public class VideoTranscoderTest { Assert.assertEquals("Apk does not have permissions to write to external storage", PackageManager.PERMISSION_GRANTED, CONTEXT.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)); - File pub = new File("/sdcard/veq/output/"); + File pub = new File(SDCARD_MOUNT_POINT + "/veq/output/"); File dir = buildPath(pub, "output_" + ENC_CONFIG_FILE.substring(0, ENC_CONFIG_FILE.lastIndexOf('.'))); if (!dir.exists()) { diff --git a/hostsidetests/mediapc/videoencodingquality/src/CtsVideoEncodingQualityHostTest.java b/hostsidetests/mediapc/videoencodingquality/src/CtsVideoEncodingQualityHostTest.java index df220112773..1b789dd93fa 100644 --- a/hostsidetests/mediapc/videoencodingquality/src/CtsVideoEncodingQualityHostTest.java +++ b/hostsidetests/mediapc/videoencodingquality/src/CtsVideoEncodingQualityHostTest.java @@ -21,6 +21,7 @@ import android.cts.host.utils.DeviceJUnit4Parameterized; import android.platform.test.annotations.AppModeFull; import com.android.compatibility.common.util.CddTest; +import com.android.ddmlib.IDevice; import com.android.ddmlib.testrunner.RemoteAndroidTestRunner; import com.android.ddmlib.testrunner.TestResult.TestStatus; import com.android.tradefed.config.Option; @@ -98,8 +99,6 @@ public class CtsVideoEncodingQualityHostTest implements IDeviceTest { // Variables related to device-side of the test. These need to kept in sync with definitions of // VideoEncodingApp.apk - private static final String DEVICE_IN_DIR = "/sdcard/veq/input/"; - private static final String DEVICE_OUT_DIR = "/sdcard/veq/output/"; private static final String DEVICE_SIDE_TEST_PACKAGE = "android.videoencoding.app"; private static final String DEVICE_SIDE_TEST_CLASS = "android.videoencoding.app.VideoTranscoderTest"; @@ -135,82 +134,129 @@ public class CtsVideoEncodingQualityHostTest implements IDeviceTest { @Option(name = "quick-check", description = "Run a quick check.") private boolean mQuickCheck = false; - public CtsVideoEncodingQualityHostTest(String jsonName) { + public CtsVideoEncodingQualityHostTest(String jsonName, + @SuppressWarnings("unused") String testLabel) { mJsonName = jsonName; } - private static final List<String> AVC_VBR_B0_PARAMS = Arrays.asList( - "AVICON-MOBILE-Beach-SO04-CRW02-L-420-8bit-SDR-1080p-30fps_hw_avc_vbr_b0.json", - "AVICON-MOBILE-BirthdayHalfway-SI17-CRUW03-L-420-8bit-SDR-1080p" - + "-30fps_hw_avc_vbr_b0.json", - "AVICON-MOBILE-SelfieTeenKitchenSocialMedia-SS01-CF01-P-420-8bit-SDR-1080p" - + "-30fps_hw_avc_vbr_b0.json", - "AVICON-MOBILE-Waterfall-SO05-CRW01-P-420-8bit-SDR-1080p-30fps_hw_avc_vbr_b0.json", - "AVICON-MOBILE-SelfieFamily-SF14-CF01-L-420-8bit-SDR-1080p-30fps_hw_avc_vbr_b0" + private static final List<Object[]> AVC_VBR_B0_PARAMS = Arrays.asList(new Object[][]{ + {"AVICON-MOBILE-Beach-SO04-CRW02-L-420-8bit-SDR-1080p-30fps_hw_avc_vbr_b0.json", + "Beach_SO04_CRW02_L_420_8bit_SDR_1080p_30fps_hw_avc_vbr_b0"}, + {"AVICON-MOBILE-BirthdayHalfway-SI17-CRUW03-L-420-8bit-SDR-1080p-30fps_hw_avc_vbr_b0" + ".json", - "AVICON-MOBILE-River-SO03-CRW01-L-420-8bit-SDR-1080p-30fps_hw_avc_vbr_b0.json", - "AVICON-MOBILE-SelfieGroupGarden-SF15-CF01-P-420-8bit-SDR-1080p" + "BirthdayHalfway_SI17_CRUW03_L_420_8bit_SDR_1080p_30fps_hw_avc_vbr_b0"}, + {"AVICON-MOBILE-SelfieTeenKitchenSocialMedia-SS01-CF01-P-420-8bit-SDR-1080p" + "-30fps_hw_avc_vbr_b0.json", - "AVICON-MOBILE-ConcertNear-SI10-CRW01-L-420-8bit-SDR-1080p-30fps_hw_avc_vbr_b0" + "SelfieTeenKitchenSocialMedia_SS01_CF01_P_420_8bit_SDR_1080p_30fps_hw_avc_" + + "vbr_b0"}, + {"AVICON-MOBILE-Waterfall-SO05-CRW01-P-420-8bit-SDR-1080p-30fps_hw_avc_vbr_b0.json", + "Waterfall_SO05_CRW01_P_420_8bit_SDR_1080p_30fps_hw_avc_vbr_b0"}, + {"AVICON-MOBILE-SelfieFamily-SF14-CF01-L-420-8bit-SDR-1080p-30fps_hw_avc_vbr_b0.json" + , "SelfieFamily_SF14_CF01_L_420_8bit_SDR_1080p_30fps_hw_avc_vbr_b0"}, + {"AVICON-MOBILE-River-SO03-CRW01-L-420-8bit-SDR-1080p-30fps_hw_avc_vbr_b0.json", + "River_SO03_CRW01_L_420_8bit_SDR_1080p_30fps_hw_avc_vbr_b0"}, + {"AVICON-MOBILE-SelfieGroupGarden-SF15-CF01-P-420-8bit-SDR-1080p-30fps_hw_avc_vbr_b0" + ".json", - "AVICON-MOBILE-SelfieCoupleCitySocialMedia-SS02-CF01-P-420-8bit-SDR-1080p" - + "-30fps_hw_avc_vbr_b0.json"); - - private static final List<String> AVC_VBR_B3_PARAMS = Arrays.asList( - "AVICON-MOBILE-Beach-SO04-CRW02-L-420-8bit-SDR-1080p-30fps_hw_avc_vbr_b3.json", - "AVICON-MOBILE-BirthdayHalfway-SI17-CRUW03-L-420-8bit-SDR-1080p-30fps_hw_avc_vbr_b3" + "SelfieGroupGarden_SF15_CF01_P_420_8bit_SDR_1080p_30fps_hw_avc_vbr_b0"}, + {"AVICON-MOBILE-ConcertNear-SI10-CRW01-L-420-8bit-SDR-1080p-30fps_hw_avc_vbr_b0.json" + , "ConcertNear_SI10_CRW01_L_420_8bit_SDR_1080p_30fps_hw_avc_vbr_b0"}, + {"AVICON-MOBILE-SelfieCoupleCitySocialMedia-SS02-CF01-P-420-8bit-SDR" + + "-1080p-30fps_hw_avc_vbr_b0.json", + "SelfieCoupleCitySocialMedia_SS02_CF01_P_420_8bit_SDR_1080p_30fps_hw_avc_" + + "vbr_b0"}}); + + private static final List<Object[]> AVC_VBR_B3_PARAMS = Arrays.asList(new Object[][]{ + {"AVICON-MOBILE-Beach-SO04-CRW02-L-420-8bit-SDR-1080p-30fps_hw_avc_vbr_b3.json", + "Beach_SO04_CRW02_L_420_8bit_SDR_1080p_30fps_hw_avc_vbr_b3"}, + {"AVICON-MOBILE-BirthdayHalfway-SI17-CRUW03-L-420-8bit-SDR-1080p-30fps_hw_avc_vbr_b3" + ".json", - "AVICON-MOBILE-SelfieTeenKitchenSocialMedia-SS01-CF01-P-420-8bit-SDR-1080p" + "BirthdayHalfway_SI17_CRUW03_L_420_8bit_SDR_1080p_30fps_hw_avc_vbr_b3"}, + {"AVICON-MOBILE-SelfieTeenKitchenSocialMedia-SS01-CF01-P-420-8bit-SDR-1080p" + "-30fps_hw_avc_vbr_b3.json", - "AVICON-MOBILE-Waterfall-SO05-CRW01-P-420-8bit-SDR-1080p-30fps_hw_avc_vbr_b3.json", - "AVICON-MOBILE-SelfieFamily-SF14-CF01-L-420-8bit-SDR-1080p-30fps_hw_avc_vbr_b3.json", - "AVICON-MOBILE-River-SO03-CRW01-L-420-8bit-SDR-1080p-30fps_hw_avc_vbr_b3.json", - "AVICON-MOBILE-SelfieGroupGarden-SF15-CF01-P-420-8bit-SDR-1080p-30fps_hw_avc_vbr_b3" + "SelfieTeenKitchenSocialMedia_SS01_CF01_P_420_8bit_SDR_1080p_30fps_hw_avc_" + + "vbr_b3"}, + {"AVICON-MOBILE-Waterfall-SO05-CRW01-P-420-8bit-SDR-1080p-30fps_hw_avc_vbr_b3.json", + "Waterfall_SO05_CRW01_P_420_8bit_SDR_1080p_30fps_hw_avc_vbr_b3"}, + {"AVICON-MOBILE-SelfieFamily-SF14-CF01-L-420-8bit-SDR-1080p-30fps_hw_avc_vbr_b3.json" + , "SelfieFamily_SF14_CF01_L_420_8bit_SDR_1080p_30fps_hw_avc_vbr_b3"}, + {"AVICON-MOBILE-River-SO03-CRW01-L-420-8bit-SDR-1080p-30fps_hw_avc_vbr_b3.json", + "River_SO03_CRW01_L_420_8bit_SDR_1080p_30fps_hw_avc_vbr_b3"}, + {"AVICON-MOBILE-SelfieGroupGarden-SF15-CF01-P-420-8bit-SDR-1080p-30fps_hw_avc_vbr_b3" + ".json", - "AVICON-MOBILE-ConcertNear-SI10-CRW01-L-420-8bit-SDR-1080p-30fps_hw_avc_vbr_b3.json", - "AVICON-MOBILE-SelfieCoupleCitySocialMedia-SS02-CF01-P-420-8bit-SDR-1080p" - + "-30fps_hw_avc_vbr_b3.json"); - - private static final List<String> HEVC_VBR_B0_PARAMS = Arrays.asList( - "AVICON-MOBILE-Beach-SO04-CRW02-L-420-8bit-SDR-1080p-30fps_hw_hevc_vbr_b0.json", - "AVICON-MOBILE-BirthdayHalfway-SI17-CRUW03-L-420-8bit-SDR-1080p-30fps_hw_hevc_vbr_b0" + "SelfieGroupGarden_SF15_CF01_P_420_8bit_SDR_1080p_30fps_hw_avc_vbr_b3"}, + {"AVICON-MOBILE-ConcertNear-SI10-CRW01-L-420-8bit-SDR-1080p-30fps_hw_avc_vbr_b3.json" + , "ConcertNear_SI10_CRW01_L_420_8bit_SDR_1080p_30fps_hw_avc_vbr_b3"}, + {"AVICON-MOBILE-SelfieCoupleCitySocialMedia-SS02-CF01-P-420-8bit-SDR" + + "-1080p-30fps_hw_avc_vbr_b3.json", + "SelfieCoupleCitySocialMedia_SS02_CF01_P_420_8bit_SDR_1080p_30fps_hw_avc_" + + "vbr_b3"}}); + + private static final List<Object[]> HEVC_VBR_B0_PARAMS = Arrays.asList(new Object[][]{ + {"AVICON-MOBILE-Beach-SO04-CRW02-L-420-8bit-SDR-1080p-30fps_hw_hevc_vbr_b0.json", + "Beach_SO04_CRW02_L_420_8bit_SDR_1080p_30fps_hw_hevc_vbr_b0"}, + {"AVICON-MOBILE-BirthdayHalfway-SI17-CRUW03-L-420-8bit-SDR-1080p-30fps_hw_hevc_vbr_b0" + ".json", - "AVICON-MOBILE-SelfieTeenKitchenSocialMedia-SS01-CF01-P-420-8bit-SDR-1080p" + "BirthdayHalfway_SI17_CRUW03_L_420_8bit_SDR_1080p_30fps_hw_hevc_vbr_b0"}, + {"AVICON-MOBILE-SelfieTeenKitchenSocialMedia-SS01-CF01-P-420-8bit-SDR-1080p" + "-30fps_hw_hevc_vbr_b0.json", - "AVICON-MOBILE-Waterfall-SO05-CRW01-P-420-8bit-SDR-1080p-30fps_hw_hevc_vbr_b0.json", - "AVICON-MOBILE-SelfieFamily-SF14-CF01-L-420-8bit-SDR-1080p-30fps_hw_hevc_vbr_b0.json", - "AVICON-MOBILE-River-SO03-CRW01-L-420-8bit-SDR-1080p-30fps_hw_hevc_vbr_b0.json", - "AVICON-MOBILE-SelfieGroupGarden-SF15-CF01-P-420-8bit-SDR-1080p-30fps_hw_hevc_vbr_b0" + "SelfieTeenKitchenSocialMedia_SS01_CF01_P_420_8bit_SDR_1080p_30fps_hw_hevc_" + + "vbr_b0"}, + {"AVICON-MOBILE-Waterfall-SO05-CRW01-P-420-8bit-SDR-1080p-30fps_hw_hevc_vbr_b0.json", + "Waterfall_SO05_CRW01_P_420_8bit_SDR_1080p_30fps_hw_hevc_vbr_b0"}, + {"AVICON-MOBILE-SelfieFamily-SF14-CF01-L-420-8bit-SDR-1080p-30fps_hw_hevc_vbr_b0.json" + , "SelfieFamily_SF14_CF01_L_420_8bit_SDR_1080p_30fps_hw_hevc_vbr_b0"}, + {"AVICON-MOBILE-River-SO03-CRW01-L-420-8bit-SDR-1080p-30fps_hw_hevc_vbr_b0.json", + "River_SO03_CRW01_L_420_8bit_SDR_1080p_30fps_hw_hevc_vbr_b0"}, + {"AVICON-MOBILE-SelfieGroupGarden-SF15-CF01-P-420-8bit-SDR-1080p-30fps_hw_hevc_vbr_b0" + ".json", - "AVICON-MOBILE-ConcertNear-SI10-CRW01-L-420-8bit-SDR-1080p-30fps_hw_hevc_vbr_b0.json", - "AVICON-MOBILE-SelfieCoupleCitySocialMedia-SS02-CF01-P-420-8bit-SDR-1080p" - + "-30fps_hw_hevc_vbr_b0.json"); - - private static final List<String> HEVC_VBR_B3_PARAMS = Arrays.asList( - "AVICON-MOBILE-Beach-SO04-CRW02-L-420-8bit-SDR-1080p-30fps_hw_hevc_vbr_b3.json", - "AVICON-MOBILE-BirthdayHalfway-SI17-CRUW03-L-420-8bit-SDR-1080p-30fps_hw_hevc_vbr_b3" + "SelfieGroupGarden_SF15_CF01_P_420_8bit_SDR_1080p_30fps_hw_hevc_vbr_b0"}, + {"AVICON-MOBILE-ConcertNear-SI10-CRW01-L-420-8bit-SDR-1080p-30fps_hw_hevc_vbr_b0.json" + , "ConcertNear_SI10_CRW01_L_420_8bit_SDR_1080p_30fps_hw_hevc_vbr_b0"}, + {"AVICON-MOBILE-SelfieCoupleCitySocialMedia-SS02-CF01-P-420-8bit-SDR" + + "-1080p-30fps_hw_hevc_vbr_b0.json", + "SelfieCoupleCitySocialMedia_SS02_CF01_P_420_8bit_SDR_1080p_30fps_hw_hevc_" + + "vbr_b0"}}); + + private static final List<Object[]> HEVC_VBR_B3_PARAMS = Arrays.asList(new Object[][]{ + {"AVICON-MOBILE-Beach-SO04-CRW02-L-420-8bit-SDR-1080p-30fps_hw_hevc_vbr_b3.json", + "Beach_SO04_CRW02_L_420_8bit_SDR_1080p_30fps_hw_hevc_vbr_b3"}, + {"AVICON-MOBILE-BirthdayHalfway-SI17-CRUW03-L-420-8bit-SDR-1080p-30fps_hw_hevc_vbr_b3" + ".json", - "AVICON-MOBILE-SelfieTeenKitchenSocialMedia-SS01-CF01-P-420-8bit-SDR-1080p" + "BirthdayHalfway_SI17_CRUW03_L_420_8bit_SDR_1080p_30fps_hw_hevc_vbr_b3"}, + {"AVICON-MOBILE-SelfieTeenKitchenSocialMedia-SS01-CF01-P-420-8bit-SDR-1080p" + "-30fps_hw_hevc_vbr_b3.json", - "AVICON-MOBILE-Waterfall-SO05-CRW01-P-420-8bit-SDR-1080p-30fps_hw_hevc_vbr_b3.json", - "AVICON-MOBILE-SelfieFamily-SF14-CF01-L-420-8bit-SDR-1080p-30fps_hw_hevc_vbr_b3.json", - "AVICON-MOBILE-River-SO03-CRW01-L-420-8bit-SDR-1080p-30fps_hw_hevc_vbr_b3.json", + "SelfieTeenKitchenSocialMedia_SS01_CF01_P_420_8bit_SDR_1080p_30fps_hw_hevc_" + + "vbr_b3"}, + {"AVICON-MOBILE-Waterfall-SO05-CRW01-P-420-8bit-SDR-1080p-30fps_hw_hevc_vbr_b3.json", + "Waterfall_SO05_CRW01_P_420_8bit_SDR_1080p_30fps_hw_hevc_vbr_b3"}, + {"AVICON-MOBILE-SelfieFamily-SF14-CF01-L-420-8bit-SDR-1080p-30fps_hw_hevc_vbr_b3.json" + , "SelfieFamily_SF14_CF01_L_420_8bit_SDR_1080p_30fps_hw_hevc_vbr_b3"}, + {"AVICON-MOBILE-River-SO03-CRW01-L-420-8bit-SDR-1080p-30fps_hw_hevc_vbr_b3.json", + "River_SO03_CRW01_L_420_8bit_SDR_1080p_30fps_hw_hevc_vbr_b3"}, // Abnormal curve, not monotonically increasing. - /*"AVICON-MOBILE-SelfieGroupGarden-SF15-CF01-P-420-8bit-SDR-1080p-30fps_hw_hevc_vbr_b3" - + ".json",*/ - "AVICON-MOBILE-ConcertNear-SI10-CRW01-L-420-8bit-SDR-1080p-30fps_hw_hevc_vbr_b3.json", - "AVICON-MOBILE-SelfieCoupleCitySocialMedia-SS02-CF01-P-420-8bit-SDR-1080p" - + "-30fps_hw_hevc_vbr_b3.json"); - - private static final List<String> QUICK_RUN_PARAMS = Arrays.asList( - "AVICON-MOBILE-SelfieTeenKitchenSocialMedia-SS01-CF01-P-420-8bit-SDR-1080p" + /*{"AVICON-MOBILE-SelfieGroupGarden-SF15-CF01-P-420-8bit-SDR-1080p-30fps_hw_hevc_vbr_b3" + + ".json", + "SelfieGroupGarden_SF15_CF01_P_420_8bit_SDR_1080p_30fps_hw_hevc_vbr_b3"},*/ + {"AVICON-MOBILE-ConcertNear-SI10-CRW01-L-420-8bit-SDR-1080p-30fps_hw_hevc_vbr_b3.json" + , "ConcertNear_SI10_CRW01_L_420_8bit_SDR_1080p_30fps_hw_hevc_vbr_b3"}, + {"AVICON-MOBILE-SelfieCoupleCitySocialMedia-SS02-CF01-P-420-8bit-SDR" + + "-1080p-30fps_hw_hevc_vbr_b3.json", + "SelfieCoupleCitySocialMedia_SS02_CF01_P_420_8bit_SDR_1080p_30fps_hw_hevc_" + + "vbr_b3"}}); + + private static final List<Object[]> QUICK_RUN_PARAMS = Arrays.asList(new Object[][]{ + {"AVICON-MOBILE-SelfieTeenKitchenSocialMedia-SS01-CF01-P-420-8bit-SDR-1080p" + "-30fps_hw_avc_vbr_b0.json", - "AVICON-MOBILE-SelfieTeenKitchenSocialMedia-SS01-CF01-P-420-8bit-SDR-1080p" - + "-30fps_hw_hevc_vbr_b0.json"); + "SelfieTeenKitchenSocialMedia_SS01_CF01_P_420_8bit_SDR_1080p_30fps_hw_avc_" + + "vbr_b0"}, + {"AVICON-MOBILE-SelfieTeenKitchenSocialMedia-SS01-CF01-P-420-8bit-SDR-1080p" + + "-30fps_hw_hevc_vbr_b0.json", + "SelfieTeenKitchenSocialMedia_SS01_CF01_P_420_8bit_SDR_1080p_30fps_hw_hevc_" + + "vbr_b0"}}); - @Parameterized.Parameters(name = "{index}_{0}") - public static List<String> input() { - final List<String> args = new ArrayList<>(); + @Parameterized.Parameters(name = "{index}_{1}") + public static List<Object[]> input() { + final List<Object[]> args = new ArrayList<>(); args.addAll(AVC_VBR_B0_PARAMS); args.addAll(AVC_VBR_B3_PARAMS); args.addAll(HEVC_VBR_B0_PARAMS); @@ -281,17 +327,32 @@ public class CtsVideoEncodingQualityHostTest implements IDeviceTest { Assert.assertEquals("Failed to untar " + fileName, 0, result); // Push input files to device - Assert.assertNotNull("Failed to create directory " + DEVICE_IN_DIR + " on device ", - getDevice().executeAdbCommand("shell", "mkdir", "-p", DEVICE_IN_DIR)); - Assert.assertTrue("Failed to push json files to " + DEVICE_IN_DIR + " on device ", - getDevice().syncFiles(new File(sHostWorkDir.getPath() + "/json/"), DEVICE_IN_DIR)); - Assert.assertTrue("Failed to push mp4 files to " + DEVICE_IN_DIR + " on device ", - getDevice().syncFiles(new File(sHostWorkDir.getPath() + "/samples/"), - DEVICE_IN_DIR)); + String deviceInDir = getDevice().getMountPoint(IDevice.MNT_EXTERNAL_STORAGE) + + "/veq/input/"; + String deviceJsonDir = deviceInDir + "json/"; + String deviceSamplesDir = deviceInDir + "samples/"; + Assert.assertNotNull("Failed to create directory " + deviceJsonDir + " on device ", + getDevice().executeAdbCommand("shell", "mkdir", "-p", deviceJsonDir)); + Assert.assertNotNull("Failed to create directory " + deviceSamplesDir + " on device ", + getDevice().executeAdbCommand("shell", "mkdir", "-p", deviceSamplesDir)); + Assert.assertTrue("Failed to push json files to " + deviceJsonDir + " on device ", + getDevice().pushDir(new File(sHostWorkDir.getPath() + "/json/"), deviceJsonDir)); + Assert.assertTrue("Failed to push mp4 files to " + deviceSamplesDir + " on device ", + getDevice().pushDir(new File(sHostWorkDir.getPath() + "/samples/"), + deviceSamplesDir)); sIsTestSetUpDone = true; } + public static boolean containsJson(String jsonName, List<Object[]> params) { + for (Object[] param : params) { + if (param[0].equals(jsonName)) { + return true; + } + } + return false; + } + /** * Verify the video encoding quality requirements for the performance class 14 devices. */ @@ -299,19 +360,19 @@ public class CtsVideoEncodingQualityHostTest implements IDeviceTest { @Test public void testEncoding() throws Exception { Assume.assumeFalse("Skipping due to quick run mode", - mQuickCheck && !QUICK_RUN_PARAMS.contains(mJsonName)); + mQuickCheck && !containsJson(mJsonName, QUICK_RUN_PARAMS)); Assume.assumeFalse("Skipping avc encoder tests", - mSkipAvc && (AVC_VBR_B0_PARAMS.contains(mJsonName) || AVC_VBR_B3_PARAMS.contains( - mJsonName))); + mSkipAvc && (containsJson(mJsonName, AVC_VBR_B0_PARAMS) || containsJson(mJsonName, + AVC_VBR_B3_PARAMS))); Assume.assumeFalse("Skipping hevc encoder tests", - mSkipHevc && (HEVC_VBR_B0_PARAMS.contains(mJsonName) || HEVC_VBR_B3_PARAMS.contains( - mJsonName))); + mSkipHevc && (containsJson(mJsonName, HEVC_VBR_B0_PARAMS) || containsJson(mJsonName, + HEVC_VBR_B3_PARAMS))); Assume.assumeFalse("Skipping b-frame tests", - mSkipB && (AVC_VBR_B3_PARAMS.contains(mJsonName) || HEVC_VBR_B3_PARAMS.contains( - mJsonName))); + mSkipB && (containsJson(mJsonName, AVC_VBR_B3_PARAMS) || containsJson(mJsonName, + HEVC_VBR_B3_PARAMS))); Assume.assumeFalse("Skipping non b-frame tests", - mSkipP && (AVC_VBR_B0_PARAMS.contains(mJsonName) || HEVC_VBR_B0_PARAMS.contains( - mJsonName))); + mSkipP && (containsJson(mJsonName, AVC_VBR_B0_PARAMS) || containsJson(mJsonName, + HEVC_VBR_B0_PARAMS))); // set up test environment sLock.lock(); @@ -336,7 +397,8 @@ public class CtsVideoEncodingQualityHostTest implements IDeviceTest { } catch (SecurityException e) { LogUtil.CLog.e("Unable to establish output host directory : " + outHostPath.getPath()); } - String outDevPath = DEVICE_OUT_DIR + outDir; + String outDevPath = getDevice().getMountPoint(IDevice.MNT_EXTERNAL_STORAGE) + "/veq/output/" + + outDir; Assert.assertTrue("Failed to pull mp4 files from " + outDevPath + " to " + outHostPath.getPath(), getDevice().pullDir(outDevPath, outHostPath)); getDevice().deleteFile(outDevPath); diff --git a/hostsidetests/rollback/AndroidTest.xml b/hostsidetests/rollback/AndroidTest.xml index 07d21e9fbd8..d757f3e733e 100644 --- a/hostsidetests/rollback/AndroidTest.xml +++ b/hostsidetests/rollback/AndroidTest.xml @@ -20,6 +20,10 @@ <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> <option name="config-descriptor:metadata" key="parameter" value="not_secondary_user" /> <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" /> + <target_preparer class="com.android.tradefed.targetprep.RebootTargetPreparer"> + <!-- flake mitigation, in case device is in bad state--> + <option name="pre-reboot" value="true" /> + </target_preparer> <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> <option name="cleanup-apks" value="true" /> <option name="test-file-name" value="CtsRollbackManagerHostTestHelperApp.apk" /> diff --git a/hostsidetests/scopedstorage/libs/ScopedStorageTestLib/src/android/scopedstorage/cts/lib/TestUtils.java b/hostsidetests/scopedstorage/libs/ScopedStorageTestLib/src/android/scopedstorage/cts/lib/TestUtils.java index b337265999c..c024efcfab4 100644 --- a/hostsidetests/scopedstorage/libs/ScopedStorageTestLib/src/android/scopedstorage/cts/lib/TestUtils.java +++ b/hostsidetests/scopedstorage/libs/ScopedStorageTestLib/src/android/scopedstorage/cts/lib/TestUtils.java @@ -1200,13 +1200,16 @@ public class TestUtils { return packageManager.hasSystemFeature(feature); } - private static void scrollIntoView(UiSelector selector) { + private static void scrollIntoView(UiSelector selector) throws Exception { UiScrollable uiScrollable = new UiScrollable(new UiSelector().scrollable(true)); + uiScrollable.setSwipeDeadZonePercentage(0.25); try { uiScrollable.scrollIntoView(selector); } catch (UiObjectNotFoundException e) { // Scrolling can fail if the UI is not scrollable } + // Sleep for a few moments to let the scroll fully stop. + Thread.sleep(250); } /** diff --git a/hostsidetests/security/Android.bp b/hostsidetests/security/Android.bp index f42b9b434ed..e816d6b108b 100644 --- a/hostsidetests/security/Android.bp +++ b/hostsidetests/security/Android.bp @@ -61,11 +61,6 @@ java_test_host { target_required: ["CtsDeviceInfo"], } -filegroup { - name: "prebuilt_sepolicy_cts_data", - srcs: [":202404_sepolicy_cts_data"], -} - java_genrule_host { name: "CtsSecurityHostTestCases_StaticLibs", tools: [ diff --git a/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java b/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java index 2ffcfce38c6..b4fa803fb77 100644 --- a/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java +++ b/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java @@ -440,10 +440,29 @@ public class SELinuxHostTest extends BaseHostJUnit4Test { * search new paths, hence this may not work on devices launching Android 11 and later. */ private static int getVendorSepolicyVersionFromManifests(ITestDevice device) throws Exception { - String deviceManifestPath = - (device.doesFileExist("/vendor/etc/vintf/manifest.xml")) ? - "/vendor/etc/vintf/manifest.xml" : - "/vendor/manifest.xml"; + String deviceManifestPath = null; + + //check default path /vendor/etc/vintf/manifest.xml, prefer to use by default + if (device.doesFileExist("/vendor/etc/vintf/manifest.xml")) { + deviceManifestPath = "/vendor/etc/vintf/manifest.xml"; + } + + //only if /vendor/etc/vintf/manifest.xml not exist, then check /vendor/etc/vintf/manifest_{vendorSku}.xml + String vendorSku = device.getProperty("ro.boot.product.vendor.sku"); + if (deviceManifestPath == null && vendorSku != null && vendorSku.length() > 0) { + String vendorSkuDeviceManifestPath = "/vendor/etc/vintf/manifest_"+ vendorSku + ".xml"; + if (device.doesFileExist(vendorSkuDeviceManifestPath)) { + deviceManifestPath = vendorSkuDeviceManifestPath; + } + } + + //use /vendor/manifest.xml if above paths not exist + if (deviceManifestPath == null) { + deviceManifestPath = "/vendor/manifest.xml"; + } + + CLog.i("getVendorSepolicyVersionFromManifests " + deviceManifestPath); + File vendorManifestFile = getDeviceFile(device, sCachedDeviceVendorManifest, deviceManifestPath, "manifest.xml"); diff --git a/hostsidetests/statsdatom/src/android/cts/statsdatom/lib/DeviceUtils.java b/hostsidetests/statsdatom/src/android/cts/statsdatom/lib/DeviceUtils.java index 21d8ab7b3af..b73916df81f 100644 --- a/hostsidetests/statsdatom/src/android/cts/statsdatom/lib/DeviceUtils.java +++ b/hostsidetests/statsdatom/src/android/cts/statsdatom/lib/DeviceUtils.java @@ -281,13 +281,22 @@ public final class DeviceUtils { throws DeviceNotAvailableException { String uidLine = device.executeShellCommand("cmd package list packages -U --user " + userId + " " + pkgName); - String[] uidLineArr = uidLine.split(":"); - // Package uid is located at index 2. - assertThat(uidLineArr.length).isGreaterThan(2); - int appUid = Integer.parseInt(uidLineArr[2].trim()); - assertThat(appUid).isGreaterThan(10000); - return appUid; + // Split package list by lines + // Sample packages response: + // package:com.android.server.cts.device.statsd.host uid:1010033 + // package:com.android.server.cts.device.statsd uid:1010034 + final String[] lines = uidLine.split("\\R+"); + for (final String line : lines) { + if (line.startsWith("package:" + pkgName + " ")) { + final int uidIndex = line.lastIndexOf(":") + 1; + final int uid = Integer.parseInt(line.substring(uidIndex).trim()); + assertThat(uid).isGreaterThan(10_000); + return uid; + } + } + throw new Error( + String.format("Could not find installed package: %s", pkgName)); } /** diff --git a/hostsidetests/videoencodingminimum/app/AndroidManifest.xml b/hostsidetests/videoencodingminimum/app/AndroidManifest.xml index 3d252354ef4..2cc1cf8e4a8 100644 --- a/hostsidetests/videoencodingminimum/app/AndroidManifest.xml +++ b/hostsidetests/videoencodingminimum/app/AndroidManifest.xml @@ -37,8 +37,5 @@ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" android:targetPackage="android.videoencodingmin.app" android:label="Video encoding app for android.media" > - <meta-data - android:name="listener" - android:value="com.android.cts.runner.CtsTestRunListener" /> </instrumentation> </manifest> diff --git a/hostsidetests/videoencodingminimum/app/src/android/videoencodingmin/app/VideoTranscoderTest.java b/hostsidetests/videoencodingminimum/app/src/android/videoencodingmin/app/VideoTranscoderTest.java index b2b86117884..fd1fa45dcf9 100644 --- a/hostsidetests/videoencodingminimum/app/src/android/videoencodingmin/app/VideoTranscoderTest.java +++ b/hostsidetests/videoencodingminimum/app/src/android/videoencodingmin/app/VideoTranscoderTest.java @@ -30,6 +30,7 @@ import android.mediav2.common.cts.CodecEncoderSurfaceTestBase; import android.mediav2.common.cts.CodecTestBase; import android.mediav2.common.cts.EncoderConfigParams; import android.mediav2.common.cts.OutputManager; +import android.os.Environment; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.LargeTest; @@ -70,7 +71,9 @@ import java.util.ArrayList; */ @RunWith(AndroidJUnit4.class) public class VideoTranscoderTest { - private static final String MEDIA_DIR = "/sdcard/vqf/input/"; + private static final String SDCARD_MOUNT_POINT = + Environment.getExternalStorageDirectory().getAbsolutePath(); + private static final String MEDIA_DIR = SDCARD_MOUNT_POINT + "/vqf/input/"; public static final String ENC_CONFIG_JSON = "conf-json"; private static final String ENC_CONFIG_FILE; private static String PATH_PREFIX; @@ -185,7 +188,7 @@ public class VideoTranscoderTest { Assert.assertEquals("Apk does not have permissions to write to external storage", PackageManager.PERMISSION_GRANTED, CONTEXT.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)); - File pub = new File("/sdcard/vqf/output/"); + File pub = new File(SDCARD_MOUNT_POINT + "/vqf/output/"); File dir = buildPath(pub, "output_" + ENC_CONFIG_FILE.substring(0, ENC_CONFIG_FILE.lastIndexOf('.'))); if (!dir.exists()) { diff --git a/hostsidetests/videoencodingminimum/src/android/videoqualityfloor/cts/CtsVideoQualityFloorHostTest.java b/hostsidetests/videoencodingminimum/src/android/videoqualityfloor/cts/CtsVideoQualityFloorHostTest.java index 1af53612e20..6fb41c6a1c5 100644 --- a/hostsidetests/videoencodingminimum/src/android/videoqualityfloor/cts/CtsVideoQualityFloorHostTest.java +++ b/hostsidetests/videoencodingminimum/src/android/videoqualityfloor/cts/CtsVideoQualityFloorHostTest.java @@ -20,6 +20,7 @@ import android.cts.host.utils.DeviceJUnit4ClassRunnerWithParameters; import android.cts.host.utils.DeviceJUnit4Parameterized; import android.platform.test.annotations.AppModeFull; +import com.android.ddmlib.IDevice; import com.android.ddmlib.testrunner.RemoteAndroidTestRunner; import com.android.ddmlib.testrunner.TestResult.TestStatus; import com.android.tradefed.config.Option; @@ -71,6 +72,8 @@ public class CtsVideoQualityFloorHostTest implements IDeviceTest { // variables related to host-side of the test private static final int MINIMUM_VALID_SDK = 31; // test is not valid before sdk 31, aka Android 12, aka Android S + private static final float TARGET_VMAF_SCORE = 70.0f; + private static final float TOLERANCE = 0.95f; private static final Lock sLock = new ReentrantLock(); private static final Condition sCondition = sLock.newCondition(); @@ -81,8 +84,6 @@ public class CtsVideoQualityFloorHostTest implements IDeviceTest { // Variables related to device-side of the test. These need to kept in sync with definitions of // VideoEncodingMinApp.apk - private static final String DEVICE_IN_DIR = "/sdcard/vqf/input/"; - private static final String DEVICE_OUT_DIR = "/sdcard/vqf/output/"; private static final String DEVICE_SIDE_TEST_PACKAGE = "android.videoencodingmin.app"; private static final String DEVICE_SIDE_TEST_CLASS = "android.videoencodingmin.app.VideoTranscoderTest"; @@ -99,13 +100,14 @@ public class CtsVideoQualityFloorHostTest implements IDeviceTest { @Option(name = "reset", description = "Start with a fresh directory.") private boolean mReset = false; - public CtsVideoQualityFloorHostTest(String jsonName) { + public CtsVideoQualityFloorHostTest(String jsonName, + @SuppressWarnings("unused") String testLabel) { mJsonName = jsonName; } - @Parameterized.Parameters(name = "{index}_{0}") - public static List<String> input() { - final List<String> args = new ArrayList<>(); + @Parameterized.Parameters(name = "{index}_{1}") + public static List<Object[]> input() { + final List<Object[]> args = new ArrayList<>(); String[] clips = {"Fireworks", "MountainBike", "Motorcycle", "TreesAndGrass"}; String[] resolutions = {"1080p", "720p", "540p", "480p"}; String[] codecInfos = {"avcBaseline3", "avcHigh4", "avcHigh52", "hevcMain3"}; @@ -113,7 +115,10 @@ public class CtsVideoQualityFloorHostTest implements IDeviceTest { for (String clip : clips) { for (String res : resolutions) { for (String info : codecInfos) { - args.add(res + "-" + clip + "-" + info + ".json"); + Object[] testArgs = new Object[2]; + testArgs[0] = res + "-" + clip + "-" + info + ".json"; + testArgs[1] = res + "_" + clip + "_" + info; + args.add(testArgs); } } } @@ -173,14 +178,19 @@ public class CtsVideoQualityFloorHostTest implements IDeviceTest { Assert.assertEquals("Failed to untar " + fileName, 0, result); // Push input files to device - Assert.assertNotNull("Failed to create directory " + DEVICE_IN_DIR + " on device ", - getDevice().executeAdbCommand("shell", "mkdir", "-p", DEVICE_IN_DIR)); - Assert.assertTrue("Failed to push json files to " + DEVICE_IN_DIR + " on device ", - getDevice().syncFiles(new File(sHostWorkDir.getPath() + "/json/"), DEVICE_IN_DIR)); - Assert.assertTrue("Failed to push mp4 files to " + DEVICE_IN_DIR + " on device ", - getDevice().syncFiles(new File(sHostWorkDir.getPath() + "/samples/"), - DEVICE_IN_DIR)); - + String deviceInDir = getDevice().getMountPoint(IDevice.MNT_EXTERNAL_STORAGE) + + "/vqf/input/"; + String deviceJsonDir = deviceInDir + "json/"; + String deviceSamplesDir = deviceInDir + "samples/"; + Assert.assertNotNull("Failed to create directory " + deviceJsonDir + " on device ", + getDevice().executeAdbCommand("shell", "mkdir", "-p", deviceJsonDir)); + Assert.assertNotNull("Failed to create directory " + deviceSamplesDir + " on device ", + getDevice().executeAdbCommand("shell", "mkdir", "-p", deviceSamplesDir)); + Assert.assertTrue("Failed to push json files to " + deviceJsonDir + " on device ", + getDevice().pushDir(new File(sHostWorkDir.getPath() + "/json/"), deviceJsonDir)); + Assert.assertTrue("Failed to push mp4 files to " + deviceSamplesDir + " on device ", + getDevice().pushDir(new File(sHostWorkDir.getPath() + "/samples/"), + deviceSamplesDir)); sIsTestSetUpDone = true; } @@ -212,7 +222,8 @@ public class CtsVideoQualityFloorHostTest implements IDeviceTest { } catch (SecurityException e) { LogUtil.CLog.e("Unable to establish output host directory : " + outHostPath.getPath()); } - String outDevPath = DEVICE_OUT_DIR + outDir; + String outDevPath = getDevice().getMountPoint(IDevice.MNT_EXTERNAL_STORAGE) + "/vqf/output/" + + outDir; Assert.assertTrue("Failed to pull mp4 files from " + outDevPath + " to " + outHostPath.getPath(), getDevice().pullDir(outDevPath, outHostPath)); getDevice().deleteFile(outDevPath); @@ -256,8 +267,9 @@ public class CtsVideoQualityFloorHostTest implements IDeviceTest { if (line.contains(token)) { line = line.substring(line.indexOf(token)); double vmaf_score = Double.parseDouble(line.substring(token.length())); - Assert.assertTrue("Video encoding failed for " + outputName - + " with vmaf score of " + vmaf_score, vmaf_score >= 70); + Assert.assertTrue( + "Video encoding failed for " + outputName + " with vmaf score of " + + vmaf_score, vmaf_score >= TARGET_VMAF_SCORE * TOLERANCE); LogUtil.CLog.i(vmafLine); break; } diff --git a/tests/JobScheduler/OWNERS b/tests/JobScheduler/OWNERS index 99b5cf7bbd9..914e6886a5b 100644 --- a/tests/JobScheduler/OWNERS +++ b/tests/JobScheduler/OWNERS @@ -1,7 +1,4 @@ # Bug component: 330738 set noparent -ctate@google.com -kwekua@google.com -omakoto@google.com -yamasani@google.com
\ No newline at end of file +include platform/frameworks/base:/apex/jobscheduler/OWNERS diff --git a/tests/app/src/android/app/cts/DownloadManagerTest.java b/tests/app/src/android/app/cts/DownloadManagerTest.java index 321ac8bca22..36424a7bfb8 100644 --- a/tests/app/src/android/app/cts/DownloadManagerTest.java +++ b/tests/app/src/android/app/cts/DownloadManagerTest.java @@ -468,11 +468,11 @@ public class DownloadManagerTest extends DownloadManagerTestBase { } } - private String cannonicalizeProcessName(ApplicationInfo ai) { - return cannonicalizeProcessName(ai.processName, ai); + private String canonicalizeProcessName(ApplicationInfo ai) { + return canonicalizeProcessName(ai.processName, ai); } - private String cannonicalizeProcessName(String process, ApplicationInfo ai) { + private String canonicalizeProcessName(String process, ApplicationInfo ai) { if (process == null) { return null; } @@ -502,10 +502,10 @@ public class DownloadManagerTest extends DownloadManagerTestBase { ProviderInfo downloadInfo = pm.resolveContentProvider("downloads", 0); assertNotNull(downloadInfo); String downloadProcess - = cannonicalizeProcessName(downloadInfo.processName, downloadInfo.applicationInfo); + = canonicalizeProcessName(downloadInfo.processName, downloadInfo.applicationInfo); for (PackageInfo pi : mContext.getPackageManager().getInstalledPackages(0)) { - if (downloadProcess.equals(cannonicalizeProcessName(pi.applicationInfo))) { + if (downloadProcess.equals(canonicalizeProcessName(pi.applicationInfo))) { assertTrue("package: " + pi.applicationInfo.packageName + " must set android:usesCleartextTraffic=true" ,(pi.applicationInfo.flags & ApplicationInfo.FLAG_USES_CLEARTEXT_TRAFFIC) diff --git a/tests/camera/src/android/hardware/camera2/cts/PerformanceTest.java b/tests/camera/src/android/hardware/camera2/cts/PerformanceTest.java index fef6849002f..196f2d51353 100644 --- a/tests/camera/src/android/hardware/camera2/cts/PerformanceTest.java +++ b/tests/camera/src/android/hardware/camera2/cts/PerformanceTest.java @@ -984,6 +984,7 @@ public class PerformanceTest { ZoomDirection direction, ZoomRange range) throws Exception { final int ZOOM_STEPS = 5; final float ZOOM_ERROR_MARGIN = 0.05f; + final float ERROR_THRESH_FACTOR = 0.33f; final int ZOOM_IN_MIN_IMPROVEMENT_IN_FRAMES = 1; final int MAX_IMPROVEMENT_VARIATION = 2; for (String id : mTestRule.getCameraIdsUnderTest()) { @@ -1048,6 +1049,10 @@ public class PerformanceTest { for (int j = 0; j < NUM_ZOOM_STEPS; j++) { float zoomFactor = startRatio + (endRatio - startRatio) * (j + 1) / NUM_ZOOM_STEPS; + // The error margin needs to be adjusted based on the zoom step size. + // We take the min of ZOOM_ERROR_MARGIN and 1/3 of zoom ratio step. + float zoomErrorMargin = Math.min(ZOOM_ERROR_MARGIN, + (float) Math.abs(zoomFactor - previousRatio) * ERROR_THRESH_FACTOR); previewBuilder.set(CaptureRequest.CONTROL_ZOOM_RATIO, zoomFactor); int newSequenceId = mTestRule.getCameraSession().setRepeatingRequest( previewBuilder.build(), resultListener, mTestRule.getHandler()); @@ -1069,13 +1074,13 @@ public class PerformanceTest { assertTrue(String.format("Zoom ratio should monotonically increase/decrease" + " or stay the same (previous = %f, current = %f", previousRatio, resultZoomFactor), - Math.abs(previousRatio - resultZoomFactor) < ZOOM_ERROR_MARGIN + Math.abs(previousRatio - resultZoomFactor) < zoomErrorMargin || (direction == ZoomDirection.ZOOM_IN && previousRatio < resultZoomFactor) || (direction == ZoomDirection.ZOOM_OUT && previousRatio > resultZoomFactor)); - if (Math.abs(resultZoomFactor - zoomFactor) < ZOOM_ERROR_MARGIN + if (Math.abs(resultZoomFactor - zoomFactor) < zoomErrorMargin && improvement == 0) { improvement = (int) (lastFrameNumberForRequest + 1 - frameNumber); } diff --git a/tests/core/runner-axt/src/com/android/cts/core/runner/filter/CoreTestModeFilter.java b/tests/core/runner-axt/src/com/android/cts/core/runner/filter/CoreTestModeFilter.java index b297b939f88..e827a500384 100644 --- a/tests/core/runner-axt/src/com/android/cts/core/runner/filter/CoreTestModeFilter.java +++ b/tests/core/runner-axt/src/com/android/cts/core/runner/filter/CoreTestModeFilter.java @@ -53,19 +53,19 @@ public class CoreTestModeFilter implements Predicate<Description> { */ public static Predicate<Description> createInstance(Bundle args) { String mode = args.getString(ARGUMENT_MODE); - if ("mts".equals(mode)) { - // We have to hard-coded the annotation name of NonMts because the annotation definition - // isn't built into the same apk file. - return new CoreTestModeFilter(NonMts.class); - } else if ("presubmit".equals(mode)) { - return new CoreTestModeFilter(FlakyTest.class, LargeTest.class); - } else { - // The default mode is CTS, because most libcore test suites are prefixed with "Cts". + if ("cts".equals(mode)) { // It's okay that ignoredTestsInCts.txt doesn't exist in the test .apk file, and // the created CoreExpectationFilter doesn't skip any test in this case. Set<String> expectationFile = Set.of("/skippedCtsTest.txt"); return new CoreTestModeFilter(NonCts.class) .and(CoreExpectationFilter.createInstance(expectationFile)); + } else if ("presubmit".equals(mode)) { + return new CoreTestModeFilter(FlakyTest.class, LargeTest.class); + } else { + // Use MTS mode by default because MCTS should dynamically download + // the MCTS artifacts in the same version as the ART module installed + // on the device. + return new CoreTestModeFilter(NonMts.class); } } diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/BluetoothTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/BluetoothTest.java index 42d10eb6804..461ae881afc 100644 --- a/tests/devicepolicy/src/android/devicepolicy/cts/BluetoothTest.java +++ b/tests/devicepolicy/src/android/devicepolicy/cts/BluetoothTest.java @@ -413,6 +413,8 @@ public final class BluetoothTest { @Postsubmit(reason = "new test") @ApiTest(apis = "android.os.UserManager#DISALLOW_BLUETOOTH_SHARING") public void share_disallowBluetoothAndSharingRestrictionsAreNotSet_canShare() { + Assume.assumeTrue("We can't test resolving if opp is disabled", OPP_ENABLED); + Poll.forValue("Opp Launcher Component Enabled", () -> TestApis.packages().activity(OPP_LAUNCHER_COMPONENT) .isEnabled(TestApis.users().system())) @@ -420,8 +422,6 @@ public final class BluetoothTest { .errorOnFail() .await(); - Assume.assumeTrue("We can't test resolving if opp is disabled", OPP_ENABLED); - List<ResolveInfo> resolveInfos = sPackageManager.queryIntentActivities( FILE_SHARING_INTENT, /* flags= */ 0); assertThat(resolveInfosContainsActivity(resolveInfos, OPP_LAUNCHER_COMPONENT)).isTrue(); @@ -432,6 +432,8 @@ public final class BluetoothTest { @Postsubmit(reason = "new test") @ApiTest(apis = "android.os.UserManager#DISALLOW_BLUETOOTH_SHARING") public void share_disallowBluetoothSharingRestrictionIsSet_canNotShare() { + Assume.assumeTrue("We can't test resolving if opp is disabled", OPP_ENABLED); + Poll.forValue("Opp Launcher Component Enabled", () -> TestApis.packages().activity(OPP_LAUNCHER_COMPONENT) .isEnabled(TestApis.users().system())) @@ -439,8 +441,6 @@ public final class BluetoothTest { .errorOnFail() .await(); - Assume.assumeTrue("We can't test resolving if opp is disabled", OPP_ENABLED); - List<ResolveInfo> resolveInfos = sPackageManager.queryIntentActivities( FILE_SHARING_INTENT, /* flags= */ 0); assertThat(resolveInfosContainsActivity(resolveInfos, OPP_LAUNCHER_COMPONENT)).isFalse(); @@ -489,6 +489,8 @@ public final class BluetoothTest { @ApiTest(apis = "android.os.UserManager#DISALLOW_BLUETOOTH") @RequireNotHeadlessSystemUserMode(reason = "b/276405672 bluetooth restriction not enforced on secondary users") public void share_disallowBluetoothRestrictionIsSet_canNotShare() { + Assume.assumeTrue("We can't test resolving if opp is disabled", OPP_ENABLED); + Poll.forValue("Opp Launcher Component Enabled", () -> TestApis.packages().activity(OPP_LAUNCHER_COMPONENT) .isEnabled(TestApis.users().system())) @@ -496,8 +498,6 @@ public final class BluetoothTest { .errorOnFail() .await(); - Assume.assumeTrue("We can't test resolving if opp is disabled", OPP_ENABLED); - List<ResolveInfo> resolveInfos = sPackageManager.queryIntentActivities( FILE_SHARING_INTENT, /* flags= */ 0); assertThat(resolveInfosContainsActivity(resolveInfos, OPP_LAUNCHER_COMPONENT)).isFalse(); diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/extensions/util/TestValueCountConsumer.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/extensions/util/TestValueCountConsumer.java index 34276f0298b..cb3159202df 100644 --- a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/extensions/util/TestValueCountConsumer.java +++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/extensions/util/TestValueCountConsumer.java @@ -16,9 +16,13 @@ package android.server.wm.jetpack.extensions.util; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.window.extensions.core.util.function.Consumer; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; @@ -67,6 +71,22 @@ public class TestValueCountConsumer<T> implements Consumer<T> { } /** + * Returns a list that contains the number of values set in + * {@link TestValueCountConsumer#setCount(int)}. + */ + @NonNull + public List<T> waitAndGetAllValues() throws InterruptedException { + List<T> values = new ArrayList<>(); + for (int i = 0; i < mCount; i++) { + T value = mLinkedBlockingQueue.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS); + if (value != null) { + values.add(value); + } + } + return Collections.unmodifiableList(values); + } + + /** * Clears the queue of currently recorded values. */ public void clearQueue() { diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/layout/ExtensionWindowLayoutComponentTest.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/layout/ExtensionWindowLayoutComponentTest.java index 71a93d4c094..2d943da5795 100644 --- a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/layout/ExtensionWindowLayoutComponentTest.java +++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/layout/ExtensionWindowLayoutComponentTest.java @@ -22,6 +22,7 @@ import static android.server.wm.jetpack.extensions.util.ExtensionsUtil.EXTENSION import static android.server.wm.jetpack.extensions.util.ExtensionsUtil.assertEqualWindowLayoutInfo; import static android.server.wm.jetpack.extensions.util.ExtensionsUtil.assumeHasDisplayFeatures; import static android.server.wm.jetpack.extensions.util.ExtensionsUtil.getExtensionWindowLayoutInfo; +import static android.server.wm.jetpack.extensions.util.ExtensionsUtil.getWindowExtensions; import static android.server.wm.jetpack.extensions.util.ExtensionsUtil.isExtensionVersionAtLeast; import static android.server.wm.jetpack.extensions.util.SidecarUtil.assumeSidecarSupportedDevice; import static android.server.wm.jetpack.extensions.util.SidecarUtil.getSidecarInterface; @@ -437,16 +438,21 @@ public class ExtensionWindowLayoutComponentTest extends WindowManagerJetpackTest mWindowLayoutInfo = getExtensionWindowLayoutInfo(configHandlingActivity); assumeHasDisplayFeatures(mWindowLayoutInfo); - final WindowLayoutInfo initialInfo = getExtensionWindowLayoutInfo( - configHandlingActivity); + TestValueCountConsumer<WindowLayoutInfo> consumer = new TestValueCountConsumer<>(); + // We expect 3 values, 1 before entering PiP, one while in PiP, one after exiting PiP. + consumer.setCount(3); + getWindowExtensions().getWindowLayoutComponent().addWindowLayoutInfoListener( + configHandlingActivity, consumer); enterPipActivityHandlesConfigChanges(configHandlingActivity); exitPipActivityHandlesConfigChanges(configHandlingActivity); - final WindowLayoutInfo updatedInfo = getExtensionWindowLayoutInfo( - configHandlingActivity); + List<WindowLayoutInfo> values = consumer.waitAndGetAllValues(); - assertEquals(initialInfo, updatedInfo); + assertEquals(3, values.size()); + assertEquals(mWindowLayoutInfo, values.get(0)); + assertTrue(values.get(1).getDisplayFeatures().isEmpty()); + assertEquals(mWindowLayoutInfo, values.get(2)); } /** 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 e515baed57c..9161b6d1d1b 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 @@ -17,9 +17,11 @@ package android.server.wm.jetpack.utils; import android.app.Activity; +import android.content.res.Configuration; import android.os.Bundle; import android.view.View; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import java.util.concurrent.CountDownLatch; @@ -31,6 +33,7 @@ import java.util.concurrent.TimeUnit; */ public class TestActivity extends Activity implements View.OnLayoutChangeListener { private CountDownLatch mLayoutLatch; + private CountDownLatch mOnConfigurationChangeLatch = new CountDownLatch(1); private static CountDownLatch sResumeLatch = new CountDownLatch(1); @Override @@ -48,6 +51,12 @@ public class TestActivity extends Activity implements View.OnLayoutChangeListene } @Override + public void onConfigurationChanged(@NonNull Configuration newConfig) { + super.onConfigurationChanged(newConfig); + mOnConfigurationChangeLatch.countDown(); + } + + @Override protected void onResume() { super.onResume(); sResumeLatch.countDown(); @@ -76,6 +85,27 @@ public class TestActivity extends Activity implements View.OnLayoutChangeListene } /** + * Resets the configuration change counter. + */ + public void resetOnConfigurationChangeCounter() { + mOnConfigurationChangeLatch = new CountDownLatch(1); + } + + /** + * Waits for a configuration change callback. + * + * @return {@code true} if the configuration change callback is triggered, {@code false} + * otherwise. + */ + public boolean waitForConfigurationChange() { + try { + return mOnConfigurationChangeLatch.await(3, TimeUnit.SECONDS); + } catch (InterruptedException e) { + return false; + } + } + + /** * Resets layout counter when waiting for a layout to happen before calling * {@link #waitForOnResume()}. */ diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/WindowManagerJetpackTestBase.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/WindowManagerJetpackTestBase.java index db0edf4dcb6..adfa8042ae7 100644 --- a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/WindowManagerJetpackTestBase.java +++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/WindowManagerJetpackTestBase.java @@ -281,28 +281,22 @@ public class WindowManagerJetpackTestBase extends ActivityManagerTestBase { if (activity.isInPictureInPictureMode()) { throw new IllegalStateException("Activity must not be in PiP"); } - activity.resetLayoutCounter(); + activity.resetOnConfigurationChangeCounter(); // Change the orientation PictureInPictureParams params = (new PictureInPictureParams.Builder()).build(); activity.enterPictureInPictureMode(params); - // Wait for the activity to layout, which will happen after the orientation change - assertTrue(activity.waitForLayout()); - // Check that orientation matches - assertTrue(activity.isInPictureInPictureMode()); + activity.waitForConfigurationChange(); } public static void exitPipActivityHandlesConfigChanges(TestActivity activity) { if (!activity.isInPictureInPictureMode()) { throw new IllegalStateException("Activity must be in PiP"); } - activity.resetLayoutCounter(); + activity.resetOnConfigurationChangeCounter(); Intent intent = new Intent(activity, activity.getClass()); intent.addFlags(FLAG_ACTIVITY_SINGLE_TOP); activity.startActivity(intent); - // Wait for the activity to layout, which will happen after the orientation change - assertTrue(activity.waitForLayout()); - // Check that orientation matches - assertFalse(activity.isInPictureInPictureMode()); + activity.waitForConfigurationChange(); } public static void setActivityOrientationActivityDoesNotHandleOrientationChanges( diff --git a/tests/framework/base/windowmanager/src/android/server/wm/window/ConfigChangeTests.java b/tests/framework/base/windowmanager/src/android/server/wm/window/ConfigChangeTests.java index 401b1ca9e3d..e596ca6a016 100644 --- a/tests/framework/base/windowmanager/src/android/server/wm/window/ConfigChangeTests.java +++ b/tests/framework/base/windowmanager/src/android/server/wm/window/ConfigChangeTests.java @@ -71,7 +71,7 @@ public class ConfigChangeTests extends ActivityManagerTestBase { @Test public void testRotation90Relaunch() { - assumeTrue("Skipping test: no rotation support", supportsRotation()); + assumeTrue("Skipping test: no rotation support", supportsOrientationRequest()); // Should relaunch on every rotation and receive no onConfigurationChanged() testRotation(TEST_ACTIVITY, 1, 1, 0); @@ -79,7 +79,7 @@ public class ConfigChangeTests extends ActivityManagerTestBase { @Test public void testRotation90NoRelaunch() { - assumeTrue("Skipping test: no rotation support", supportsRotation()); + assumeTrue("Skipping test: no rotation support", supportsOrientationRequest()); // Should receive onConfigurationChanged() on every rotation and no relaunch testRotation(NO_RELAUNCH_ACTIVITY, 1, 0, 1); @@ -87,7 +87,7 @@ public class ConfigChangeTests extends ActivityManagerTestBase { @Test public void testRotation180_RegularActivity() { - assumeTrue("Skipping test: no rotation support", supportsRotation()); + assumeTrue("Skipping test: no rotation support", supportsOrientationRequest()); assumeFalse("Skipping test: display cutout present, can't predict exact lifecycle", hasDisplayCutout()); @@ -97,7 +97,7 @@ public class ConfigChangeTests extends ActivityManagerTestBase { @Test public void testRotation180_NoRelaunchActivity() { - assumeTrue("Skipping test: no rotation support", supportsRotation()); + assumeTrue("Skipping test: no rotation support", supportsOrientationRequest()); assumeFalse("Skipping test: display cutout present, can't predict exact lifecycle", hasDisplayCutout()); @@ -111,7 +111,7 @@ public class ConfigChangeTests extends ActivityManagerTestBase { */ @Test public void testRotation180RelaunchWithCutout() { - assumeTrue("Skipping test: no rotation support", supportsRotation()); + assumeTrue("Skipping test: no rotation support", supportsOrientationRequest()); assumeTrue("Skipping test: no display cutout", hasDisplayCutout()); testRotation180WithCutout(TEST_ACTIVITY, false /* canHandleConfigChange */); @@ -119,7 +119,7 @@ public class ConfigChangeTests extends ActivityManagerTestBase { @Test public void testRotation180NoRelaunchWithCutout() { - assumeTrue("Skipping test: no rotation support", supportsRotation()); + assumeTrue("Skipping test: no rotation support", supportsOrientationRequest()); assumeTrue("Skipping test: no display cutout", hasDisplayCutout()); testRotation180WithCutout(NO_RELAUNCH_ACTIVITY, true /* canHandleConfigChange */); diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodManagerMultiDisplayTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodManagerMultiDisplayTest.java index 14afd328479..14a9f1aebc9 100644 --- a/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodManagerMultiDisplayTest.java +++ b/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodManagerMultiDisplayTest.java @@ -69,6 +69,7 @@ public class InputMethodManagerMultiDisplayTest extends MultiDisplayTestBase { super.setUp(); assumeTrue(mContext.getPackageManager().hasSystemFeature(FEATURE_INPUT_METHODS)); + assumeFalse(isWatch()); mImManager = mContext.getSystemService(InputMethodManager.class); diff --git a/tests/jdwp/Android.bp b/tests/jdwp/Android.bp index dc2db5733ba..899b40b9eb9 100644 --- a/tests/jdwp/Android.bp +++ b/tests/jdwp/Android.bp @@ -14,6 +14,7 @@ package { default_applicable_licenses: ["Android-Apache-2.0"], + default_team: "trendy_team_art_performance", } java_test { diff --git a/tests/jdwp/OWNERS b/tests/jdwp/OWNERS index 6e0629999c7..131c53fbd08 100644 --- a/tests/jdwp/OWNERS +++ b/tests/jdwp/OWNERS @@ -1,2 +1,2 @@ -# Bug component: 86431 -include /hostsidetests/jvmti/run-tests/OWNERS +# Bug component: 1304319 +include platform/art:/OWNERS diff --git a/tests/libcore/Android.bp b/tests/libcore/Android.bp new file mode 100644 index 00000000000..28e5d720ccc --- /dev/null +++ b/tests/libcore/Android.bp @@ -0,0 +1,17 @@ +// Copyright (C) 2024 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + default_team: "trendy_team_java_core_libraries", +} diff --git a/tests/libcore/ojluni/Android.bp b/tests/libcore/ojluni/Android.bp index 1ee4d5a3e5d..9fd72706fb7 100644 --- a/tests/libcore/ojluni/Android.bp +++ b/tests/libcore/ojluni/Android.bp @@ -47,6 +47,7 @@ android_test { test_suites: [ "cts", "general-tests", + "mcts-art", "mts-art", ], data: [ @@ -55,3 +56,95 @@ android_test { per_testcase_directory: true, host_required: ["cts-dalvik-host-test-runner"], } + +test_module_config { + name: "CtsLibcoreOjTestCases_lang_invoke", + base: "CtsLibcoreOjTestCases", + test_suites: ["general-tests"], + include_filters: [ + "test.java.lang.invoke", + "org.openjdk.tests.java.lang.invoke", + ], +} + +test_module_config { + name: "CtsLibcoreOjTestCases_net", + base: "CtsLibcoreOjTestCases", + test_suites: ["general-tests"], + include_filters: ["test.java.net"], +} + +test_module_config { + name: "CtsLibcoreOjTestCases_awt_font", + base: "CtsLibcoreOjTestCases", + test_suites: ["general-tests"], + include_filters: ["test.java.awt.font"], +} + +test_module_config { + name: "CtsLibcoreOjTestCases_nio_file_attribute", + base: "CtsLibcoreOjTestCases", + test_suites: ["general-tests"], + include_filters: ["test.java.nio.file.attribute"], +} + +test_module_config { + name: "CtsLibcoreOjTestCases_security_cert", + base: "CtsLibcoreOjTestCases", + test_suites: ["general-tests"], + include_filters: ["test.java.security.cert"], +} + +test_module_config { + name: "CtsLibcoreOjTestCases_time", + base: "CtsLibcoreOjTestCases", + test_suites: ["general-tests"], + include_filters: [ + "tck.java.time.serial", + "tck.java.time", + "test.java.time", + ], +} + +test_module_config { + name: "CtsLibcoreOjTestCases_time_chrono", + base: "CtsLibcoreOjTestCases", + test_suites: ["general-tests"], + include_filters: [ + "tck.java.time.chrono.serial", + "test.java.time.chrono", + "tck.java.time.chrono", + ], +} + +test_module_config { + name: "CtsLibcoreOjTestCases_time_format", + base: "CtsLibcoreOjTestCases", + test_suites: ["general-tests"], + include_filters: [ + "test.java.time.format", + "tck.java.time.format", + ], +} + +test_module_config { + name: "CtsLibcoreOjTestCases_time_temporal", + base: "CtsLibcoreOjTestCases", + test_suites: ["general-tests"], + include_filters: [ + "tck.java.time.temporal", + "tck.java.time.temporal.serial", + "test.java.time.temporal", + ], +} + +test_module_config { + name: "CtsLibcoreOjTestCases_time_zone", + base: "CtsLibcoreOjTestCases", + test_suites: ["general-tests"], + include_filters: [ + "test.java.time.zone", + "tck.java.time.zone.serial", + "tck.java.time.zone", + ], +} diff --git a/tests/location/common/src/android/location/cts/common/TestMeasurementUtil.java b/tests/location/common/src/android/location/cts/common/TestMeasurementUtil.java index 0a2f63dbac5..bd1c042c0f8 100644 --- a/tests/location/common/src/android/location/cts/common/TestMeasurementUtil.java +++ b/tests/location/common/src/android/location/cts/common/TestMeasurementUtil.java @@ -524,7 +524,7 @@ public final class TestMeasurementUtil { * @param measurement GnssMeasurement * @param softAssert custom SoftAssert * @param timeInNs event time in ns - * */ + */ private static void verifyReceivedSatelliteVehicleTimeInNs(GnssMeasurement measurement, SoftAssert softAssert, long timeInNs) { diff --git a/tests/location/location_gnss/src/android/location/cts/gnss/GnssLocationValuesTest.java b/tests/location/location_gnss/src/android/location/cts/gnss/GnssLocationValuesTest.java index e00e76036ce..f7add97e1b7 100644 --- a/tests/location/location_gnss/src/android/location/cts/gnss/GnssLocationValuesTest.java +++ b/tests/location/location_gnss/src/android/location/cts/gnss/GnssLocationValuesTest.java @@ -205,7 +205,7 @@ public class GnssLocationValuesTest extends GnssTestCase { // For the speed, during the cts test device shouldn't move faster than 1m/s, but allowing up // to 5m/s for possible early fix noise in moderate signal test environments if(location.hasSpeed()) { - softAssert.assertTrue("In the test enviorment, speed should be in the range of [0, 5] m/s", + softAssert.assertTrue("In the test environment, speed should be in the range of [0, 5] m/s", location.getSpeed() >= 0 && location.getSpeed() <= 5); } 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 1502f0ca9fc..3eb430364ed 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 @@ -20,7 +20,7 @@ import java.util.concurrent.TimeUnit; /** * Tests for the ttff (time to the first fix) validating whether TTFF is - * below the expected thresholds in differnt scenario + * below the expected thresholds in different scenario */ public class GnssTtffTests extends GnssTestCase { diff --git a/tests/location/location_gnss/src/android/location/cts/gnss/asn1/base/PerAlignedUtils.java b/tests/location/location_gnss/src/android/location/cts/gnss/asn1/base/PerAlignedUtils.java index 1255aaa05a9..d14de4270ec 100644 --- a/tests/location/location_gnss/src/android/location/cts/gnss/asn1/base/PerAlignedUtils.java +++ b/tests/location/location_gnss/src/android/location/cts/gnss/asn1/base/PerAlignedUtils.java @@ -310,7 +310,7 @@ public class PerAlignedUtils { * Encodes an Asn1Object into a Open type field (X.691-0207, 10.2), used * mostly for encoding Sequence and SetOf extension additions. A decode method * hasn't been added as the extension additions should decoded - * by their relevent Asn1Object decoders. + * by their relevant Asn1Object decoders. */ public static Iterable<BitStream> encodeOpenTypeField( Asn1Object object){ diff --git a/tests/location/location_gnss/src/android/location/cts/gnss/asn1/base/PerUnalignedUtils.java b/tests/location/location_gnss/src/android/location/cts/gnss/asn1/base/PerUnalignedUtils.java index 63f962f9b5b..f7789408ec6 100644 --- a/tests/location/location_gnss/src/android/location/cts/gnss/asn1/base/PerUnalignedUtils.java +++ b/tests/location/location_gnss/src/android/location/cts/gnss/asn1/base/PerUnalignedUtils.java @@ -245,7 +245,7 @@ public class PerUnalignedUtils { * Encodes an Asn1Object into a Open type field (X.691-0207, 10.2), used * mostly for encoding Sequence and SetOf extension additions. A decode method * hasn't been added as the extension additions should decoded - * by their relevent Asn1Object decoders. + * by their relevant Asn1Object decoders. */ public static Iterable<BitStream> encodeOpenTypeField(Asn1Object object){ PacketBuilder packetBuilder = new PacketBuilder(); diff --git a/tests/media/common/src/android/mediav2/common/cts/CodecDecoderBlockModelMultiAccessUnitDrmTestBase.java b/tests/media/common/src/android/mediav2/common/cts/CodecDecoderBlockModelMultiAccessUnitDrmTestBase.java index 9f6f5bf8d57..54c370da0c1 100644 --- a/tests/media/common/src/android/mediav2/common/cts/CodecDecoderBlockModelMultiAccessUnitDrmTestBase.java +++ b/tests/media/common/src/android/mediav2/common/cts/CodecDecoderBlockModelMultiAccessUnitDrmTestBase.java @@ -35,6 +35,8 @@ import android.os.Build; import android.util.Log; import android.util.Pair; +import androidx.test.filters.SdkSuppress; + import org.junit.After; import java.util.ArrayDeque; @@ -45,6 +47,7 @@ import java.util.UUID; * Wrapper class for trying and testing secure mediacodec decoder components in block model large * audio buffer mode */ +@SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName = "VanillaIceCream") @RequiresApi(api = Build.VERSION_CODES.R) public class CodecDecoderBlockModelMultiAccessUnitDrmTestBase extends CodecDecoderBlockModelMultiAccessUnitTestBase { diff --git a/tests/media/common/src/android/mediav2/common/cts/CodecDecoderTestBase.java b/tests/media/common/src/android/mediav2/common/cts/CodecDecoderTestBase.java index f4fd16c5c0c..d0521f883ad 100644 --- a/tests/media/common/src/android/mediav2/common/cts/CodecDecoderTestBase.java +++ b/tests/media/common/src/android/mediav2/common/cts/CodecDecoderTestBase.java @@ -131,14 +131,6 @@ public class CodecDecoderTestBase extends CodecTestBase { boolean selectHBD = doesAnyFormatHaveHDRProfile(mMediaType, formatList); if (!selectHBD && srcFile.contains("10bit")) { selectHBD = true; - if (mMediaType.equals(MediaFormat.MIMETYPE_VIDEO_VP9)) { - // In some cases, webm extractor may not signal profile for 10-bit VP9 - // clips. In such cases, set profile to a 10-bit compatible profile. - // TODO (b/295804596) Remove the following once webm extractor signals - // profile correctly for all 10-bit clips - int[] profileArray = CodecTestBase.PROFILE_HDR_MAP.get(mMediaType); - format.setInteger(MediaFormat.KEY_PROFILE, profileArray[0]); - } } format.setInteger(MediaFormat.KEY_COLOR_FORMAT, getColorFormat(mCodecName, mMediaType, mSurface != null, selectHBD)); diff --git a/tests/media/src/android/mediav2/cts/Av1FilmGrainValidationTest.java b/tests/media/src/android/mediav2/cts/Av1FilmGrainValidationTest.java index a142204320a..091f4b34724 100644 --- a/tests/media/src/android/mediav2/cts/Av1FilmGrainValidationTest.java +++ b/tests/media/src/android/mediav2/cts/Av1FilmGrainValidationTest.java @@ -33,6 +33,7 @@ import android.mediav2.common.cts.OutputManager; import android.util.Log; import android.util.Pair; +import org.junit.Assume; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -163,6 +164,8 @@ public class Av1FilmGrainValidationTest extends CodecDecoderTestBase { */ @Test public void testAv1FilmGrainRequirement() throws Exception { + Assume.assumeTrue("Skipping, Only intended for devices with SDK >= 202404", + BOARD_FIRST_SDK_IS_AT_LEAST_202404); MediaFormat format = setUpSource(mTestFile); mImageSurface = new ImageSurface(); setUpSurface(getWidth(format), getHeight(format), ImageFormat.YUV_420_888, 1, 0, null); diff --git a/tests/media/src/android/mediav2/cts/CodecEncoderBlockModelMultiAccessUnitTest.java b/tests/media/src/android/mediav2/cts/CodecEncoderBlockModelMultiAccessUnitTest.java index 5f57dc753b7..acdbdae1238 100644 --- a/tests/media/src/android/mediav2/cts/CodecEncoderBlockModelMultiAccessUnitTest.java +++ b/tests/media/src/android/mediav2/cts/CodecEncoderBlockModelMultiAccessUnitTest.java @@ -25,7 +25,6 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import static org.junit.Assume.assumeTrue; import android.media.AudioFormat; import android.media.MediaCodec; @@ -96,28 +95,35 @@ public class CodecEncoderBlockModelMultiAccessUnitTest extends CodecEncoderBlock @Parameterized.Parameters(name = "{index}_{0}_{1}_{3}") public static Collection<Object[]> input() { List<Object[]> defArgsList = new ArrayList<>(Arrays.asList(new Object[][]{ - // mediaType, arrays of bit-rates, sample rates, channel counts, pcm encoding - {MediaFormat.MIMETYPE_AUDIO_AAC, new int[]{64000, 128000}, new int[]{8000, 12000, - 16000, 22050, 24000, 32000, 44100, 48000}, new int[]{1, 2}, - AudioFormat.ENCODING_PCM_16BIT}, - {MediaFormat.MIMETYPE_AUDIO_OPUS, new int[]{64000, 128000}, new int[]{8000, 12000, - 16000, 24000, 48000}, new int[]{1, 2}, - AudioFormat.ENCODING_PCM_16BIT}, - {MediaFormat.MIMETYPE_AUDIO_AMR_NB, new int[]{4750, 5150, 5900, 6700, 7400, 7950, - 10200, 12200}, new int[]{8000}, new int[]{1}, - AudioFormat.ENCODING_PCM_16BIT}, - {MediaFormat.MIMETYPE_AUDIO_AMR_WB, new int[]{6600, 8850, 12650, 14250, 15850, - 18250, 19850, 23050, 23850}, new int[]{16000}, new int[]{1}, - AudioFormat.ENCODING_PCM_16BIT}, - {MediaFormat.MIMETYPE_AUDIO_FLAC, new int[]{0, 1, 2, 3, 4, 5, 6, 7, 8}, - new int[]{8000, 16000, 32000, 48000, 96000, 192000}, new int[]{1, 2}, - AudioFormat.ENCODING_PCM_16BIT}, - {MediaFormat.MIMETYPE_AUDIO_FLAC, new int[]{0, 1, 2, 3, 4, 5, 6, 7, 8}, - new int[]{8000, 16000, 32000, 48000, 96000, 192000}, new int[]{1, 2}, - AudioFormat.ENCODING_PCM_FLOAT}, + // mediaType, arrays of bit-rates, sample rate, channel counts, pcm encoding + + // mono testing @ common sample rates, pcm encoding + {MediaFormat.MIMETYPE_AUDIO_AAC, new int[]{64000}, new int[]{8000, 16000}, + new int[]{1}, AudioFormat.ENCODING_PCM_16BIT}, + {MediaFormat.MIMETYPE_AUDIO_OPUS, new int[]{64000}, new int[]{8000, 16000}, + new int[]{1}, AudioFormat.ENCODING_PCM_16BIT}, + {MediaFormat.MIMETYPE_AUDIO_AMR_NB, new int[]{4750, 12200}, new int[]{8000}, + new int[]{1}, AudioFormat.ENCODING_PCM_16BIT}, + {MediaFormat.MIMETYPE_AUDIO_AMR_WB, new int[]{6600, 23850}, new int[]{16000}, + new int[]{1}, AudioFormat.ENCODING_PCM_16BIT}, + {MediaFormat.MIMETYPE_AUDIO_FLAC, new int[]{0, 7}, new int[]{8000, 16000}, + new int[]{1}, AudioFormat.ENCODING_PCM_16BIT}, + {MediaFormat.MIMETYPE_AUDIO_FLAC, new int[]{0, 7}, new int[]{8000, 16000}, + new int[]{1}, AudioFormat.ENCODING_PCM_FLOAT}, + + // stereo testing @ common sample rates, pcm encoding + {MediaFormat.MIMETYPE_AUDIO_AAC, new int[]{128000}, new int[]{44100, 48000}, + new int[]{2}, AudioFormat.ENCODING_PCM_16BIT}, + {MediaFormat.MIMETYPE_AUDIO_OPUS, new int[]{128000}, new int[]{48000}, + new int[]{2}, AudioFormat.ENCODING_PCM_16BIT}, + {MediaFormat.MIMETYPE_AUDIO_FLAC, new int[]{0, 7}, new int[]{48000, 192000}, + new int[]{2}, AudioFormat.ENCODING_PCM_16BIT}, + {MediaFormat.MIMETYPE_AUDIO_FLAC, new int[]{0, 7}, new int[]{48000, 192000}, + new int[]{2}, AudioFormat.ENCODING_PCM_FLOAT}, })); List<Object[]> argsList = flattenParams(defArgsList); - return prepareParamList(argsList, true, true, false, true); + return prepareParamList(argsList, true, true, false, true, ComponentClass.ALL, + new String[]{FEATURE_MultipleFrames}); } public CodecEncoderBlockModelMultiAccessUnitTest(String encoder, String mediaType, @@ -297,11 +303,6 @@ public class CodecEncoderBlockModelMultiAccessUnitTest extends CodecEncoderBlock @LargeTest @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS) public void testSimpleEncode() throws IOException, InterruptedException { - assumeTrue(mCodecName + " does not support FEATURE_MultipleFrames", - isFeatureSupported(mCodecName, mMediaType, FEATURE_MultipleFrames)); - assumeTrue(mCodecName + " is not compatible with LinearBlocks", - MediaCodec.LinearBlock.isCodecCopyFreeCompatible(new String[]{mCodecName})); - CodecEncoderTestBase referenceBase = new CodecEncoderTestBase(mCodecName, mMediaType, new EncoderConfigParams[]{mActiveEncCfg}, mAllTestParams); referenceBase.encodeToMemory(mCodecName, mActiveEncCfg, mActiveRawRes, Integer.MAX_VALUE, diff --git a/tests/media/src/android/mediav2/cts/CodecEncoderMultiAccessUnitTest.java b/tests/media/src/android/mediav2/cts/CodecEncoderMultiAccessUnitTest.java index ed55b72a476..2dfcd087669 100644 --- a/tests/media/src/android/mediav2/cts/CodecEncoderMultiAccessUnitTest.java +++ b/tests/media/src/android/mediav2/cts/CodecEncoderMultiAccessUnitTest.java @@ -24,7 +24,6 @@ import static android.mediav2.cts.CodecDecoderMultiAccessUnitTest.getCompression import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import static org.junit.Assume.assumeTrue; import android.media.AudioFormat; import android.media.MediaCodec; @@ -93,28 +92,35 @@ public class CodecEncoderMultiAccessUnitTest extends CodecEncoderTestBase { @Parameterized.Parameters(name = "{index}_{0}_{1}_{3}") public static Collection<Object[]> input() { List<Object[]> defArgsList = new ArrayList<>(Arrays.asList(new Object[][]{ - // mediaType, arrays of bit-rates, sample rates, channel counts, pcm encoding - {MediaFormat.MIMETYPE_AUDIO_AAC, new int[]{64000, 128000}, new int[]{8000, 12000, - 16000, 22050, 24000, 32000, 44100, 48000}, new int[]{1, 2}, - AudioFormat.ENCODING_PCM_16BIT}, - {MediaFormat.MIMETYPE_AUDIO_OPUS, new int[]{64000, 128000}, new int[]{8000, 12000, - 16000, 24000, 48000}, new int[]{1, 2}, - AudioFormat.ENCODING_PCM_16BIT}, - {MediaFormat.MIMETYPE_AUDIO_AMR_NB, new int[]{4750, 5150, 5900, 6700, 7400, 7950, - 10200, 12200}, new int[]{8000}, new int[]{1}, - AudioFormat.ENCODING_PCM_16BIT}, - {MediaFormat.MIMETYPE_AUDIO_AMR_WB, new int[]{6600, 8850, 12650, 14250, 15850, - 18250, 19850, 23050, 23850}, new int[]{16000}, new int[]{1}, - AudioFormat.ENCODING_PCM_16BIT}, - {MediaFormat.MIMETYPE_AUDIO_FLAC, new int[]{0, 1, 2, 3, 4, 5, 6, 7, 8}, - new int[]{8000, 16000, 32000, 48000, 96000, 192000}, new int[]{1, 2}, - AudioFormat.ENCODING_PCM_16BIT}, - {MediaFormat.MIMETYPE_AUDIO_FLAC, new int[]{0, 1, 2, 3, 4, 5, 6, 7, 8}, - new int[]{8000, 16000, 32000, 48000, 96000, 192000}, new int[]{1, 2}, - AudioFormat.ENCODING_PCM_FLOAT}, + // mediaType, arrays of bit-rates, sample rate, channel counts, pcm encoding + + // mono testing @ common sample rates, pcm encoding + {MediaFormat.MIMETYPE_AUDIO_AAC, new int[]{64000}, new int[]{8000, 16000}, + new int[]{1}, AudioFormat.ENCODING_PCM_16BIT}, + {MediaFormat.MIMETYPE_AUDIO_OPUS, new int[]{64000}, new int[]{8000, 16000}, + new int[]{1}, AudioFormat.ENCODING_PCM_16BIT}, + {MediaFormat.MIMETYPE_AUDIO_AMR_NB, new int[]{4750, 12200}, new int[]{8000}, + new int[]{1}, AudioFormat.ENCODING_PCM_16BIT}, + {MediaFormat.MIMETYPE_AUDIO_AMR_WB, new int[]{6600, 23850}, new int[]{16000}, + new int[]{1}, AudioFormat.ENCODING_PCM_16BIT}, + {MediaFormat.MIMETYPE_AUDIO_FLAC, new int[]{0, 7}, new int[]{8000, 16000}, + new int[]{1}, AudioFormat.ENCODING_PCM_16BIT}, + {MediaFormat.MIMETYPE_AUDIO_FLAC, new int[]{0, 7}, new int[]{8000, 16000}, + new int[]{1}, AudioFormat.ENCODING_PCM_FLOAT}, + + // stereo testing @ common sample rates, pcm encoding + {MediaFormat.MIMETYPE_AUDIO_AAC, new int[]{128000}, new int[]{44100, 48000}, + new int[]{2}, AudioFormat.ENCODING_PCM_16BIT}, + {MediaFormat.MIMETYPE_AUDIO_OPUS, new int[]{128000}, new int[]{48000}, + new int[]{2}, AudioFormat.ENCODING_PCM_16BIT}, + {MediaFormat.MIMETYPE_AUDIO_FLAC, new int[]{0, 7}, new int[]{48000, 192000}, + new int[]{2}, AudioFormat.ENCODING_PCM_16BIT}, + {MediaFormat.MIMETYPE_AUDIO_FLAC, new int[]{0, 7}, new int[]{48000, 192000}, + new int[]{2}, AudioFormat.ENCODING_PCM_FLOAT}, })); List<Object[]> argsList = flattenParams(defArgsList); - return prepareParamList(argsList, true, true, false, true); + return prepareParamList(argsList, true, true, false, true, ComponentClass.ALL, + new String[]{FEATURE_MultipleFrames}); } public CodecEncoderMultiAccessUnitTest(String encoder, String mediaType, @@ -285,9 +291,6 @@ public class CodecEncoderMultiAccessUnitTest extends CodecEncoderTestBase { @LargeTest @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS) public void testSimpleEncode() throws IOException, InterruptedException { - assumeTrue(mCodecName + " does not support FEATURE_MultipleFrames", - isFeatureSupported(mCodecName, mMediaType, FEATURE_MultipleFrames)); - CodecEncoderTestBase cetb = new CodecEncoderTestBase(mCodecName, mMediaType, new EncoderConfigParams[]{mActiveEncCfg}, mAllTestParams); cetb.encodeToMemory(mCodecName, mActiveEncCfg, mActiveRawRes, Integer.MAX_VALUE, true, diff --git a/tests/mediapc/Android.bp b/tests/mediapc/Android.bp index e76b60325fa..756f3ab3af9 100644 --- a/tests/mediapc/Android.bp +++ b/tests/mediapc/Android.bp @@ -27,6 +27,9 @@ android_test { "MediaPerformanceClassCommon", "mediapc-requirements", ], + jni_libs: [ + "libctsmediapc_vulkan_jni", + ], platform_apis: true, srcs: ["src/**/*.java"], // Tag this module as a cts test artifact 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 8c81520f758..bac06fa962c 100644 --- a/tests/mediapc/common/src/android/mediapc/cts/common/PerformanceClassEvaluator.java +++ b/tests/mediapc/common/src/android/mediapc/cts/common/PerformanceClassEvaluator.java @@ -379,52 +379,6 @@ public class PerformanceClassEvaluator { } /** - * [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. For Dolby vision codec, the codec initialization - * latency MUST be 50 ms or less. - */ - public static CodecInitLatencyRequirement createR5_1__H_1_7(String mediaType) { - long latency = mediaType.equals(MediaFormat.MIMETYPE_VIDEO_DOLBY_VISION) ? 50L : 40L; - 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, latency) - .addRequiredValue(Build.VERSION_CODES.UPSIDE_DOWN_CAKE, latency) - .addRequiredValue(Build.VERSION_CODES.VANILLA_ICE_CREAM, latency) - .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) - .addRequiredValue(Build.VERSION_CODES.UPSIDE_DOWN_CAKE, 30L) - .addRequiredValue(Build.VERSION_CODES.VANILLA_ICE_CREAM, 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 @@ -442,25 +396,6 @@ public class PerformanceClassEvaluator { 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) - .addRequiredValue(Build.VERSION_CODES.UPSIDE_DOWN_CAKE, 30L) - .addRequiredValue(Build.VERSION_CODES.VANILLA_ICE_CREAM, 30L) - .build(); - - return new CodecInitLatencyRequirement(RequirementConstants.R5_1__H_1_13, - codec_init_latency); - } } // 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], @@ -1626,7 +1561,7 @@ public class PerformanceClassEvaluator { private static final String TAG = LogicalMultiCameraRequirement.class.getSimpleName(); - private LogicalMultiCameraRequirement(String id, RequiredMeasurement<?> ... reqs) { + private LogicalMultiCameraRequirement(String id, RequiredMeasurement<?>... reqs) { super(id, reqs); } @@ -1654,56 +1589,6 @@ public class PerformanceClassEvaluator { } } - // 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(); - - // Media performance requires 6 GB minimum RAM, but keeping the following to - // 5 GB as activityManager.getMemoryInfo() typically returns around 5.4 GB on a 6 GB device, - // so these values are a bit lower than the required value stated on the Android CDD. - private static final long RS_REQUIRED_MEMORY_MB = Utils.MIN_MEMORY_PERF_CLASS_CANDIDATE_MB; - private static final long TUV_REQUIRED_MEMORY_MB = Utils.MIN_MEMORY_PERF_CLASS_T_MB; - - private MemoryRequirement(String id, RequiredMeasurement<?> ... reqs) { - super(id, reqs); - } - - public void setPhysicalMemory(long physicalMemory) { - this.<Long>setMeasuredValue(RequirementConstants.PHYSICAL_MEMORY, physicalMemory); - } - - /** - * [7.6.1/H-1-1] MUST have at least 6 GB of physical memory. - */ - public static MemoryRequirement createR7_6_1__H_1_1() { - RequiredMeasurement<Long> physical_memory = RequiredMeasurement - .<Long>builder() - .setId(RequirementConstants.PHYSICAL_MEMORY) - .setPredicate(RequirementConstants.LONG_GTE) - .addRequiredValue(Build.VERSION_CODES.R, RS_REQUIRED_MEMORY_MB) - .build(); - - return new MemoryRequirement(RequirementConstants.R7_6_1__H_1_1, 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) - .addRequiredValue(Build.VERSION_CODES.S, RS_REQUIRED_MEMORY_MB) - .addRequiredValue(Build.VERSION_CODES.TIRAMISU, TUV_REQUIRED_MEMORY_MB) - .addRequiredValue(Build.VERSION_CODES.UPSIDE_DOWN_CAKE, TUV_REQUIRED_MEMORY_MB) - .addRequiredValue(Build.VERSION_CODES.VANILLA_ICE_CREAM, TUV_REQUIRED_MEMORY_MB) - .build(); - - return new MemoryRequirement(RequirementConstants.R7_6_1__H_2_1, physical_memory); - } - } - public static class PreviewStabilizationRequirement extends Requirement { private static final String TAG = PreviewStabilizationRequirement.class.getSimpleName(); @@ -2236,22 +2121,6 @@ public class PerformanceClassEvaluator { return new VideoCodecRequirement(RequirementConstants.R5_1__H_1_22, requirement); } - - /** - * [5.12/H-1-2] MUST support RGBA_1010102 color format for all hardware AV1 and HEVC - * encoders present on the device. - */ - public static VideoCodecRequirement createColorFormatSupportReq() { - RequiredMeasurement<Boolean> requirement = RequiredMeasurement - .<Boolean>builder() - .setId(RequirementConstants.RGBA_1010102_COLOR_FORMAT_REQ) - .setPredicate(RequirementConstants.BOOLEAN_EQ) - .addRequiredValue(Build.VERSION_CODES.UPSIDE_DOWN_CAKE, true) - .addRequiredValue(Build.VERSION_CODES.VANILLA_ICE_CREAM, true) - .build(); - - return new VideoCodecRequirement(RequirementConstants.R5_12__H_1_2, requirement); - } } public static class UltraWideZoomRatioRequirement extends Requirement { @@ -2388,14 +2257,6 @@ public class PerformanceClassEvaluator { return this.addRequirement(ConcurrentCodecRequirement.createR5_1__H_1_6_4k()); } - public CodecInitLatencyRequirement addR5_1__H_1_7(String mediaType) { - return this.addRequirement(CodecInitLatencyRequirement.createR5_1__H_1_7(mediaType)); - } - - public CodecInitLatencyRequirement addR5_1__H_1_8() { - return this.addRequirement(CodecInitLatencyRequirement.createR5_1__H_1_8()); - } - public ConcurrentCodecRequirement addR5_1__H_1_9_1080p() { return this.addRequirement(ConcurrentCodecRequirement.createR5_1__H_1_9_1080p()); } @@ -2420,10 +2281,6 @@ public class PerformanceClassEvaluator { 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()); - } - /* Adds requirement 5.1/H-1-14 */ public VideoCodecRequirement addRAV1DecoderReq() { return this.addRequirement(VideoCodecRequirement.createRAV1DecoderReq()); @@ -2502,18 +2359,11 @@ public class PerformanceClassEvaluator { return this.addRequirement(SecureCodecRequirement.createR5_7__H_1_2()); } - - /* Adds requirement 5.12/H-1-2 */ - public VideoCodecRequirement addColorFormatSupportReq() { - return this.addRequirement(VideoCodecRequirement.createColorFormatSupportReq()); - } - /* Adds requirement 5.12/H-1-3 */ public ExtYuvTargetRequirement addExtYUVSupportReq() { return this.addRequirement(ExtYuvTargetRequirement.createExtensionReq()); } - /* Adds requirement 7.5/H-1-1 */ public PrimaryCameraRequirement addPrimaryRearCameraReq() { return this.addRequirement(PrimaryCameraRequirement.createRearPrimaryCamera()); @@ -2586,10 +2436,6 @@ public class PerformanceClassEvaluator { return this.<DensityRequirement>addRequirement(DensityRequirement.createR7_1_1_3__H_1_1()); } - public MemoryRequirement addR7_6_1__H_1_1() { - return this.<MemoryRequirement>addRequirement(MemoryRequirement.createR7_6_1__H_1_1()); - } - public ResolutionRequirement addR7_1_1_1__H_2_1() { return this.<ResolutionRequirement>addRequirement( ResolutionRequirement.createR7_1_1_1__H_2_1()); @@ -2599,11 +2445,6 @@ public class PerformanceClassEvaluator { return this.<DensityRequirement>addRequirement(DensityRequirement.createR7_1_1_3__H_2_1()); } - public MemoryRequirement addR7_6_1__H_2_1() { - 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()); } 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 a9e38017e83..b9c7e15cc06 100644 --- a/tests/mediapc/common/src/android/mediapc/cts/common/RequirementConstants.java +++ b/tests/mediapc/common/src/android/mediapc/cts/common/RequirementConstants.java @@ -66,6 +66,8 @@ public final class RequirementConstants { public static final String R7_1_1_3__H_1_1 = "r7_1_1_3__h_1_1"; // 7.1.1.3/H-1-1 public static final String R7_1_1_3__H_2_1 = "r7_1_1_3__h_2_1"; // 7.1.1.3/H-2-1 public static final String R7_1_1_3__H_3_1 = "r7_1_1_3__H_3_1"; // 7.1.1.3/H-3-1 + public static final String R7_1_4_1__H_1_2 = "r7_1_4_1__h_1_2"; // 7.1.4.1/H-1-2 + public static final String R7_1_4_1__H_1_3 = "r7_1_4_1__h_1_3"; // 7.1.4.1/H-1-3 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 @@ -116,6 +118,8 @@ public final class RequirementConstants { public static final String DISPLAY_DENSITY = "display_density_dpi"; public static final String DISPLAY_LUMINANCE_NITS = "display_luminance_nits"; public static final String DYNAMIC_COLOR_ASPECTS = "dynamic_color_aspects"; + public static final String EGL_EXT_PROTECTED_CONTENT = "egl_ext_protected_content"; + public static final String EGL_IMG_CONTEXT_PRIORITY = "egl_img_context_priority"; public static final String EXT_YUV_EXTENSION = "ext_yuv_target_supported"; public static final String FILESYSTEM_IO_RATE = "filesystem_io_rate_mbps"; public static final String FRAMES_DROPPED = "frame_drops_per_30sec"; @@ -183,6 +187,7 @@ public final class RequirementConstants { public static final String TEST_RESOLUTION = "resolution"; // keep-sorted end + public enum Result { NA, MET, UNMET } diff --git a/tests/mediapc/common/src/android/mediapc/cts/common/Requirements.java.tmpl b/tests/mediapc/common/src/android/mediapc/cts/common/Requirements.java.tmpl index 4ccf08e4aae..b2794b97921 100644 --- a/tests/mediapc/common/src/android/mediapc/cts/common/Requirements.java.tmpl +++ b/tests/mediapc/common/src/android/mediapc/cts/common/Requirements.java.tmpl @@ -1,3 +1,83 @@ +{{- /* + * CompMethod generates comparison method constant from a RequiredMeasurement + */}} +{{- define "CompMethod" -}} + {{- $c :=.GetComparison }} + {{- $mt :=.GetMeasurementType }} + {{- if eq $mt.String "MEASUREMENT_TYPE_BOOL" -}}BOOLEAN + {{- else if eq $mt.String "MEASUREMENT_TYPE_DOUBLE" -}}DOUBLE + {{- else if eq $mt.String "MEASUREMENT_TYPE_INT" -}}INTEGER + {{- else if eq $mt.String "MEASUREMENT_TYPE_STRING" -}}STRING + {{- else if eq $mt.String "MEASUREMENT_TYPE_LONG" -}}LONG + {{- else }}{{$mt}} + {{- end }}_ + {{- if eq $c.String "COMPARISON_EQUAL" -}}EQ + {{- else if eq $c.String "COMPARISON_LESS_THAN" -}}LT + {{- else if eq $c.String "COMPARISON_LESS_THAN_OR_EQUAL" -}}LTE + {{- else if eq $c.String "COMPARISON_GREATER_THAN" -}}GTE + {{- else if eq $c.String "COMPARISON_GREATER_THAN_OR_EQUAL" -}}GTE + {{- else if eq $c.String "COMPARISON_INFO_ONLY" -}}INFO + {{- else }}{{$c}} + {{- end }} +{{- end -}} + + +{{- /* + * JavaClass generates Java class name from a MeasurementType + */}} +{{- define "JavaClass" -}} + {{- if eq .String "MEASUREMENT_TYPE_BOOL" -}}Boolean + {{- else if eq .String "MEASUREMENT_TYPE_DOUBLE" -}}Double + {{- else if eq .String "MEASUREMENT_TYPE_INT" -}}Integer + {{- else if eq .String "MEASUREMENT_TYPE_STRING" -}}String + {{- else if eq .String "MEASUREMENT_TYPE_LONG" -}}Long + {{- else }}{{.}} + {{- end }} +{{- end -}} + +{{- /* + * JavaClass generates Java type from a MeasurementType + */}} +{{- define "JavaType" -}} + {{- if eq .String "MEASUREMENT_TYPE_BOOL" -}}boolean + {{- else if eq .String "MEASUREMENT_TYPE_DOUBLE" -}}double + {{- else if eq .String "MEASUREMENT_TYPE_INT" -}}int + {{- else if eq .String "MEASUREMENT_TYPE_STRING" -}}String + {{- else if eq .String "MEASUREMENT_TYPE_LONG" -}}long + {{- else }}{{.}} + {{- end }} +{{- end -}} + +{{- /* + * VersionCode generates the android.Build VersionCode constant from an mpc value. + * It is an error if the mpc value listed here. + */}} +{{- define "VersionCode" -}} + VERSION_CODES. + {{- if eq . 30}}R + {{- else if eq . 31}}S + {{- else if eq . 33}}TIRAMISU + {{- else if eq . 34}}UPSIDE_DOWN_CAKE + {{- else if eq . 35}}VANILLA_ICE_CREAM + {{- else }}{{.}} + {{- end }} +{{- end -}} + + +{{- /* + * MeasurementValue gets value from RequiredValue base on the MeasurementType + * It is an error if the MeasurementType is not listed here. + */}} +{{- define "MeasurementValue" -}} + {{- if eq .MeasurementType.String "MEASUREMENT_TYPE_BOOL" -}}{{.RequiredValue.GetBoolValue}} + {{- else if eq .MeasurementType.String "MEASUREMENT_TYPE_DOUBLE" -}}{{printf "%f" .RequiredValue.GetDoubleValue}} + {{- else if eq .MeasurementType.String "MEASUREMENT_TYPE_INT" -}}{{.RequiredValue.GetIntValue}} + {{- else if eq .MeasurementType.String "MEASUREMENT_TYPE_STRING" -}}"{{.RequiredValue.GetStringValue}}" + {{- else if eq .MeasurementType.String "MEASUREMENT_TYPE_LONG" -}}{{.RequiredValue.GetLongValue}}L + {{- else }}{{.MeasurementType}} + {{- end }} +{{- end -}} + /* * Copyright (C) 2024 The Android Open Source Project * @@ -16,6 +96,8 @@ package android.mediapc.cts.common; +import android.os.Build.VERSION_CODES; + /** * Requirements Classes. */ @@ -24,14 +106,34 @@ public final class Requirements { {{- range $r := .ReqList.GetRequirements }} {{- if $r.GetName }} /** - * Create {{UpperCamelCase $r.GetName}}Requirement for requirement {{ $r.GetId }}. + * Add a new {{UpperCamelCase $r.GetName}}Requirement for requirement {{ $r.GetId }} to a + * {@code PerformanceClassEvaluator} instance. + {{- with $r.GetDescription}} + * + * {{.}}{{end}} + */ + public static {{UpperCamelCase $r.GetName}}Requirement add{{ SafeReqID $r.GetId | UpperCase}}( + PerformanceClassEvaluator pce) { + return pce.addRequirement({{UpperCamelCase $r.GetName}}Requirement.create()); + } + + {{- range $v_id, $v := $r.GetVariants }} + + /** + * Add a new {{UpperCamelCase $r.GetName}}Requirement for requirement {{ $r.GetId }} + * to a {@code PerformanceClassEvaluator} instance + * {{$v.GetDescription}}. {{- with $r.GetDescription}} * * {{.}}{{end}} */ - public static {{UpperCamelCase $r.GetName}}Requirement create{{ SafeReqID $r.GetId | UpperCase}}() { - return {{UpperCamelCase $r.GetName}}Requirement.create(); - } + public static {{UpperCamelCase $r.GetName}}Requirement add{{ SafeReqID $r.GetId | UpperCase}} + {{- UpperCamelCase $v_id}}( + PerformanceClassEvaluator pce) { + return pce.addRequirement({{UpperCamelCase $r.GetName}}Requirement.create + {{- UpperCamelCase $v_id}}()); + } + {{- end }}{{/* range $v_id, $v */}} /** * {{ $r.GetId }} {{$r.GetName}} @@ -47,80 +149,77 @@ public final class Requirements { * * {{.}}{{end}} */ - public static {{UpperCamelCase $r.GetName}}Requirement create() { + private static {{UpperCamelCase $r.GetName}}Requirement create() { {{- range $m_id, $m := $r.GetMeasurements }} {{- $mt := $m.GetMeasurementType}} {{- $c := $m.GetComparison}} var {{LowerCamelCase $m_id}} = RequiredMeasurement - .< - {{- if eq $mt.String "MEASUREMENT_TYPE_BOOL" -}}Boolean - {{- else if eq $mt.String "MEASUREMENT_TYPE_DOUBLE" -}}Double - {{- else if eq $mt.String "MEASUREMENT_TYPE_INT" -}}Integer - {{- else if eq $mt.String "MEASUREMENT_TYPE_STRING" -}}String - {{- else if eq $mt.String "MEASUREMENT_TYPE_LONG" -}}Long - {{- else }}{{$mt}} - {{- end }}>builder() + .<{{template "JavaClass" $mt}}>builder() .setId("{{$m_id}}") - .setPredicate(RequirementConstants. - {{- if eq $mt.String "MEASUREMENT_TYPE_BOOL" -}}BOOLEAN - {{- else if eq $mt.String "MEASUREMENT_TYPE_DOUBLE" -}}DOUBLE - {{- else if eq $mt.String "MEASUREMENT_TYPE_INT" -}}INTEGER - {{- else if eq $mt.String "MEASUREMENT_TYPE_STRING" -}}STRING - {{- else if eq $mt.String "MEASUREMENT_TYPE_LONG" -}}LONG - {{- else }}{{$mt}} - {{- end }}_ - {{- if eq $c.String "COMPARISON_EQUAL" -}}EQ - {{- else if eq $c.String "COMPARISON_LESS_THAN" -}}LT - {{- else if eq $c.String "COMPARISON_LESS_THAN_OR_EQUAL" -}}LTE - {{- else if eq $c.String "COMPARISON_GREATER_THAN" -}}GTE - {{- else if eq $c.String "COMPARISON_GREATER_THAN_OR_EQUAL" -}}GTE - {{- else if eq $c.String "COMPARISON_INFO_ONLY" -}}INFO - {{- else }}{{$c}} - {{- end }}) + .setPredicate(RequirementConstants.{{template "CompMethod" $m}}) {{- range $mpc, $s := $r.GetSpecs }} {{- with index $s.GetRequiredValues $m_id}} - .addRequiredValue({{$mpc}} - {{- if eq $mt.String "MEASUREMENT_TYPE_BOOL" -}}, {{.GetBoolValue}} - {{- else if eq $mt.String "MEASUREMENT_TYPE_DOUBLE" -}}, {{printf "%f" .GetDoubleValue}} - {{- else if eq $mt.String "MEASUREMENT_TYPE_INT" -}}, {{.GetIntValue}} - {{- else if eq $mt.String "MEASUREMENT_TYPE_STRING" -}}, "{{.GetStringValue}}" - {{- else if eq $mt.String "MEASUREMENT_TYPE_LONG" -}}, {{.GetLongValue}}L - {{- else }}{{$mt}} - {{- end }}) + .addRequiredValue({{template "VersionCode" $mpc}}, {{template + "MeasurementValue" Dict "RequiredValue" . "MeasurementType" $mt}}) {{- end}} {{- end}} .build(); {{- end }} - return new {{UpperCamelCase $r.GetName}}Requirement( + return new {{UpperCamelCase $r.GetName}}Requirement( "{{SafeReqID $r.GetId }}" {{- range $m_id, $m := $r.GetMeasurements }}, {{LowerCamelCase $m_id}} {{- end}}); } + {{- range $v_id, $v := $r.GetVariants }} + /** + * {{ $r.GetId }} {{$r.GetName}}{{- with $v.GetDescription}} {{.}}{{end}} + {{- with $r.GetDescription}} + * + * {{.}}{{end}} + */ + private static {{UpperCamelCase $r.GetName}}Requirement create{{UpperCamelCase $v_id}}() { + {{- range $m_id, $m := $r.GetMeasurements }} + {{- $mt := $m.GetMeasurementType}} + {{- $c := $m.GetComparison}} + var {{LowerCamelCase $m_id}} = RequiredMeasurement + .<{{template "JavaClass" $mt}}>builder() + .setId("{{$m_id}}") + .setPredicate(RequirementConstants.{{template "CompMethod" $m}}) + {{- range $mpc, $s := $r.GetSpecs }} + {{- $vs := index $s.GetVariantSpecs $v_id}} + {{- if $vs }} + {{- with index $vs.GetRequiredValues $m_id}} + .addRequiredValue({{template "VersionCode" $mpc}}, {{template + "MeasurementValue" Dict "RequiredValue" . "MeasurementType" $mt}}) + {{- end}}{{/* if $vs */}} + {{- end}}{{/* with rv */}} + {{- end}}{{/* range $mpc, $s */}} + .build(); + {{- end}}{{/* range $m_id, $m */}} + return new {{UpperCamelCase $r.GetName}}Requirement( + "{{SafeReqID $r.GetId }}" + {{- range $m_id, $m := $r.GetMeasurements }}, + {{LowerCamelCase $m_id}} + {{- end}}); + } + {{- end }}{{/* range $v_id, $v */}} - {{- range $m_id, $m := $r.GetMeasurements }} - {{- $mt := $m.GetMeasurementType}} - /** {{$m.GetDescription}} */ - public void set{{UpperCamelCase $m_id}}( - {{- if eq $mt.String "MEASUREMENT_TYPE_BOOL" -}}boolean - {{- else if eq $mt.String "MEASUREMENT_TYPE_DOUBLE" -}}double - {{- else if eq $mt.String "MEASUREMENT_TYPE_INT" -}}int - {{- else if eq $mt.String "MEASUREMENT_TYPE_STRING" -}}String - {{- else if eq $mt.String "MEASUREMENT_TYPE_LONG" -}}long - {{- else }}{{$mt}} - {{- end }} v) { - this.setMeasuredValue("{{$m_id}}", v); - } - {{- end }}{{/* range $m_id, $m */}} - - private {{UpperCamelCase $r.GetName}}Requirement(String id, RequiredMeasurement<?>... reqs) { - super(id, reqs); - } + {{- range $m_id, $m := $r.GetMeasurements }} + {{- $mt := $m.GetMeasurementType}} + /** {{$m.GetDescription}} */ + public void set{{UpperCamelCase $m_id}}({{template "JavaType" $mt}} v) { + this.setMeasuredValue("{{$m_id}}", v); + } + {{- end }}{{/* range $m_id, $m */}} + private {{UpperCamelCase $r.GetName}}Requirement(String id, RequiredMeasurement<?>... reqs) { + super(id, reqs); + } } {{- end }} {{/* if $r.GetName */}} {{- end }} {{/* range $r */}} - private Requirements () {} + private Requirements () {} } diff --git a/tests/mediapc/common/tests/src/android/mediapc/cts/common/RequirementsTest.java b/tests/mediapc/common/tests/src/android/mediapc/cts/common/RequirementsTest.java index 54084f0553c..aa6cc552d8e 100644 --- a/tests/mediapc/common/tests/src/android/mediapc/cts/common/RequirementsTest.java +++ b/tests/mediapc/common/tests/src/android/mediapc/cts/common/RequirementsTest.java @@ -22,7 +22,9 @@ import android.mediapc.cts.common.Requirements.HDRDisplayRequirement; import android.mediapc.cts.common.Requirements.SequentialWriteRequirement; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TestName; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -30,11 +32,14 @@ import org.junit.runners.JUnit4; @RunWith(JUnit4.class) public class RequirementsTest { + @Rule + public final TestName mTestName = new TestName(); // HDRDisplayRequirement has two required measurements. @Test public void hdrDisplay_noHdr_1000nits() { - var req = HDRDisplayRequirement.create(); + var pce = new PerformanceClassEvaluator(mTestName); + HDRDisplayRequirement req = Requirements.addR7_1_1_3__H_3_1(pce); req.setIsHdr(false); req.setDisplayLuminanceNits(1000); @@ -44,7 +49,8 @@ public class RequirementsTest { @Test public void hdrDisplay_900nits() { - var req = HDRDisplayRequirement.create(); + var pce = new PerformanceClassEvaluator(mTestName); + HDRDisplayRequirement req = Requirements.addR7_1_1_3__H_3_1(pce); req.setIsHdr(true); req.setDisplayLuminanceNits(900); @@ -54,7 +60,8 @@ public class RequirementsTest { @Test public void hdrDisplay_1000nits() { - var req = HDRDisplayRequirement.create(); + var pce = new PerformanceClassEvaluator(mTestName); + HDRDisplayRequirement req = Requirements.addR7_1_1_3__H_3_1(pce); req.setIsHdr(true); req.setDisplayLuminanceNits(1000); @@ -66,7 +73,8 @@ public class RequirementsTest { // SequentialWriteRequirement has more than one MPC level. @Test public void sequentialWrite_90mbps() { - var req = SequentialWriteRequirement.create(); + var pce = new PerformanceClassEvaluator(mTestName); + SequentialWriteRequirement req = Requirements.addR8_2__H_1_1(pce); req.setFilesystemIoRateMbps(90); var pc = req.computePerformanceClass(); @@ -75,7 +83,8 @@ public class RequirementsTest { @Test public void sequentialWrite_100mbps() { - var req = SequentialWriteRequirement.create(); + var pce = new PerformanceClassEvaluator(mTestName); + SequentialWriteRequirement req = Requirements.addR8_2__H_1_1(pce); req.setFilesystemIoRateMbps(100); var pc = req.computePerformanceClass(); @@ -84,7 +93,8 @@ public class RequirementsTest { @Test public void sequentialWrite_125mbps() { - var req = SequentialWriteRequirement.create(); + var pce = new PerformanceClassEvaluator(mTestName); + SequentialWriteRequirement req = Requirements.addR8_2__H_1_1(pce); req.setFilesystemIoRateMbps(125); var pc = req.computePerformanceClass(); @@ -93,7 +103,8 @@ public class RequirementsTest { @Test public void sequentialWrite_150mbps() { - var req = SequentialWriteRequirement.create(); + var pce = new PerformanceClassEvaluator(mTestName); + SequentialWriteRequirement req = Requirements.addR8_2__H_1_1(pce); req.setFilesystemIoRateMbps(150); var pc = req.computePerformanceClass(); diff --git a/tests/mediapc/jni/Android.bp b/tests/mediapc/jni/Android.bp new file mode 100644 index 00000000000..f5997bbbf52 --- /dev/null +++ b/tests/mediapc/jni/Android.bp @@ -0,0 +1,40 @@ +// Copyright (C) 2024 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +cc_test_library { + name: "libctsmediapc_vulkan_jni", + shared_libs: [ + "libandroid", + "liblog", + "libvulkan", + ], + srcs: [ + "VulkanDeviceInfo.cpp", + ], + static_libs: ["libvkjson_ndk"], + header_libs: [ + "jni_headers", + "liblog_headers", + ], + stl: "libc++_static", + cflags: [ + "-Werror", + "-Wall", + ], + sdk_version: "29", +} diff --git a/tests/mediapc/jni/VulkanDeviceInfo.cpp b/tests/mediapc/jni/VulkanDeviceInfo.cpp new file mode 100644 index 00000000000..6833c507e70 --- /dev/null +++ b/tests/mediapc/jni/VulkanDeviceInfo.cpp @@ -0,0 +1,46 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <android/log.h> +#include <jni.h> +#include <vkjson.h> + +namespace { + +jstring GetVkJSON(JNIEnv* env, jclass /*clazz*/) { + std::string vkjson(VkJsonInstanceToJson(VkJsonGetInstance())); + return env->NewStringUTF(vkjson.c_str()); +} + +static JNINativeMethod gMethods[] = { + {"nativeGetVkJSON", "()Ljava/lang/String;", (void*) GetVkJSON}, +}; + +} // anonymous namespace + +extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void*) { + JNIEnv* env; + if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { + return JNI_ERR; + } + jclass clazz = env->FindClass("android/mediapc/cts/VulkanTest"); + int status = env->RegisterNatives(clazz, gMethods, sizeof(gMethods) / sizeof(JNINativeMethod)); + if (status != JNI_OK) { + return JNI_ERR; + } + return JNI_VERSION_1_6; +} diff --git a/tests/mediapc/requirements/Android.bp b/tests/mediapc/requirements/Android.bp index 7e3bcb29d62..78ebcf2f02c 100644 --- a/tests/mediapc/requirements/Android.bp +++ b/tests/mediapc/requirements/Android.bp @@ -48,6 +48,13 @@ bootstrap_go_package { ], } +// Regenerate the go proto srcs with these commands +// m aprotoc protoc-go-gen && \ +// aprotoc \ +// --go_out=paths=source_relative:. \ +// cts/tests/mediapc/requirements/mpc.proto \ +// cts/tests/mediapc/requirements/requirements.proto + bootstrap_go_package { name: "requirements_go_proto", pkgPath: "cts/test/mediapc/requirements/requirements_go_proto", diff --git a/tests/mediapc/requirements/mpc.pb.go b/tests/mediapc/requirements/mpc.pb.go index 64bfcdfd9c2..0f684e6acfb 100644 --- a/tests/mediapc/requirements/mpc.pb.go +++ b/tests/mediapc/requirements/mpc.pb.go @@ -4,7 +4,7 @@ // 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 +// 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, @@ -56,6 +56,9 @@ const ( // As specified in // https://source.android.com/docs/compatibility/14/android-14-cdd#227_handheld_media_performance_class MediaPerformanceClass_MEDIA_PERFORMANCE_CLASS_14 MediaPerformanceClass = 34 + // As specified in + // Link pending + MediaPerformanceClass_MEDIA_PERFORMANCE_CLASS_15 MediaPerformanceClass = 35 ) // Enum value maps for MediaPerformanceClass. @@ -67,6 +70,7 @@ var ( 31: "MEDIA_PERFORMANCE_CLASS_12", 33: "MEDIA_PERFORMANCE_CLASS_13", 34: "MEDIA_PERFORMANCE_CLASS_14", + 35: "MEDIA_PERFORMANCE_CLASS_15", } MediaPerformanceClass_value = map[string]int32{ "MEDIA_PERFORMANCE_CLASS_UNSPECIFIED": 0, @@ -75,6 +79,7 @@ var ( "MEDIA_PERFORMANCE_CLASS_12": 31, "MEDIA_PERFORMANCE_CLASS_13": 33, "MEDIA_PERFORMANCE_CLASS_14": 34, + "MEDIA_PERFORMANCE_CLASS_15": 35, } ) @@ -122,7 +127,7 @@ var file_cts_tests_mediapc_requirements_mpc_proto_rawDesc = []byte{ 0x61, 0x70, 0x63, 0x2f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x6d, 0x70, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x6f, 0x72, - 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x2a, 0xee, 0x01, 0x0a, 0x15, 0x4d, + 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x2a, 0x8e, 0x02, 0x0a, 0x15, 0x4d, 0x65, 0x64, 0x69, 0x61, 0x50, 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x12, 0x27, 0x0a, 0x23, 0x4d, 0x45, 0x44, 0x49, 0x41, 0x5f, 0x50, 0x45, 0x52, 0x46, 0x4f, 0x52, 0x4d, 0x41, 0x4e, 0x43, 0x45, 0x5f, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, @@ -137,7 +142,9 @@ var file_cts_tests_mediapc_requirements_mpc_proto_rawDesc = []byte{ 0x45, 0x44, 0x49, 0x41, 0x5f, 0x50, 0x45, 0x52, 0x46, 0x4f, 0x52, 0x4d, 0x41, 0x4e, 0x43, 0x45, 0x5f, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x31, 0x33, 0x10, 0x21, 0x12, 0x1e, 0x0a, 0x1a, 0x4d, 0x45, 0x44, 0x49, 0x41, 0x5f, 0x50, 0x45, 0x52, 0x46, 0x4f, 0x52, 0x4d, 0x41, 0x4e, 0x43, 0x45, - 0x5f, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x31, 0x34, 0x10, 0x22, 0x42, 0x2e, 0x50, 0x01, 0x5a, + 0x5f, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x31, 0x34, 0x10, 0x22, 0x12, 0x1e, 0x0a, 0x1a, 0x4d, + 0x45, 0x44, 0x49, 0x41, 0x5f, 0x50, 0x45, 0x52, 0x46, 0x4f, 0x52, 0x4d, 0x41, 0x4e, 0x43, 0x45, + 0x5f, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x31, 0x35, 0x10, 0x23, 0x42, 0x2e, 0x50, 0x01, 0x5a, 0x2a, 0x63, 0x74, 0x73, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x70, 0x63, 0x2f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x6d, 0x70, 0x63, 0x5f, 0x67, 0x6f, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, diff --git a/tests/mediapc/requirements/requirements.pb.go b/tests/mediapc/requirements/requirements.pb.go index 5d60dbdd4ad..599b68c870f 100644 --- a/tests/mediapc/requirements/requirements.pb.go +++ b/tests/mediapc/requirements/requirements.pb.go @@ -313,6 +313,10 @@ type Requirement struct { // measurement_id to RequiredMeasurements // The measurement_id is a field name safe string. Measurements map[string]*RequiredMeasurement `protobuf:"bytes,6,rep,name=measurements" json:"measurements,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + // variant_id to Variant + // + // The variant_id is a field name safe string. + Variants map[string]*Variant `protobuf:"bytes,57,rep,name=variants" json:"variants,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` } func (x *Requirement) Reset() { @@ -389,6 +393,13 @@ func (x *Requirement) GetMeasurements() map[string]*RequiredMeasurement { return nil } +func (x *Requirement) GetVariants() map[string]*Variant { + if x != nil { + return x.Variants + } + return nil +} + type RequirementSpec struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -404,6 +415,9 @@ type RequirementSpec struct { // The measurement_id must match the id of a RequiredMeasurement in the parent // Requirement. RequiredValues map[string]*RequiredValue `protobuf:"bytes,4,rep,name=required_values,json=requiredValues" json:"required_values,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + // variant_id to VariantSpec + // The variant_id is a field name safe string. + VariantSpecs map[string]*VariantSpec `protobuf:"bytes,5,rep,name=variant_specs,json=variantSpecs" json:"variant_specs,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` } func (x *RequirementSpec) Reset() { @@ -459,6 +473,13 @@ func (x *RequirementSpec) GetRequiredValues() map[string]*RequiredValue { return nil } +func (x *RequirementSpec) GetVariantSpecs() map[string]*VariantSpec { + if x != nil { + return x.VariantSpecs + } + return nil +} + // A required measurement needed verifiy a MPC requirement. type RequiredMeasurement struct { state protoimpl.MessageState @@ -665,6 +686,108 @@ func (*RequiredValue_BoolValue) isRequiredValue_Value() {} func (*RequiredValue_LongValue) isRequiredValue_Value() {} +// Variants are used when an alternative set of required values should apply +type Variant struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // When the variant should be used. + Description *string `protobuf:"bytes,1,opt,name=description" json:"description,omitempty"` +} + +func (x *Variant) Reset() { + *x = Variant{} + if protoimpl.UnsafeEnabled { + mi := &file_cts_tests_mediapc_requirements_requirements_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Variant) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Variant) ProtoMessage() {} + +func (x *Variant) ProtoReflect() protoreflect.Message { + mi := &file_cts_tests_mediapc_requirements_requirements_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Variant.ProtoReflect.Descriptor instead. +func (*Variant) Descriptor() ([]byte, []int) { + return file_cts_tests_mediapc_requirements_requirements_proto_rawDescGZIP(), []int{5} +} + +func (x *Variant) GetDescription() string { + if x != nil && x.Description != nil { + return *x.Description + } + return "" +} + +// The set values required for a variant +type VariantSpec struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // measurement_id to RequiredMeasurements + // + // The measurement_id is a field name safe string. + // The measurement_id must match the id of a RequiredMeasurement in the parent + // Requirement. + RequiredValues map[string]*RequiredValue `protobuf:"bytes,4,rep,name=required_values,json=requiredValues" json:"required_values,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` +} + +func (x *VariantSpec) Reset() { + *x = VariantSpec{} + if protoimpl.UnsafeEnabled { + mi := &file_cts_tests_mediapc_requirements_requirements_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *VariantSpec) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*VariantSpec) ProtoMessage() {} + +func (x *VariantSpec) ProtoReflect() protoreflect.Message { + mi := &file_cts_tests_mediapc_requirements_requirements_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use VariantSpec.ProtoReflect.Descriptor instead. +func (*VariantSpec) Descriptor() ([]byte, []int) { + return file_cts_tests_mediapc_requirements_requirements_proto_rawDescGZIP(), []int{6} +} + +func (x *VariantSpec) GetRequiredValues() map[string]*RequiredValue { + if x != nil { + return x.RequiredValues + } + return nil +} + var File_cts_tests_mediapc_requirements_requirements_proto protoreflect.FileDescriptor var file_cts_tests_mediapc_requirements_requirements_proto_rawDesc = []byte{ @@ -685,7 +808,7 @@ var file_cts_tests_mediapc_requirements_requirements_proto_rawDesc = []byte{ 0x74, 0x73, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x0c, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1d, 0x0a, 0x08, 0x61, 0x6c, 0x6c, 0x5f, 0x6d, 0x70, 0x63, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x03, 0x42, 0x02, - 0x10, 0x01, 0x52, 0x07, 0x61, 0x6c, 0x6c, 0x4d, 0x70, 0x63, 0x73, 0x22, 0xe4, 0x04, 0x0a, 0x0b, + 0x10, 0x01, 0x52, 0x07, 0x61, 0x6c, 0x6c, 0x4d, 0x70, 0x63, 0x73, 0x22, 0xbb, 0x06, 0x0a, 0x0b, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, @@ -708,113 +831,161 @@ var file_cts_tests_mediapc_requirements_requirements_proto_rawDesc = []byte{ 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4d, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0c, 0x6d, 0x65, 0x61, 0x73, 0x75, 0x72, - 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x1a, 0x76, 0x0a, 0x0a, 0x53, 0x70, 0x65, 0x63, 0x73, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x52, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3c, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, + 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x62, 0x0a, 0x08, 0x76, 0x61, 0x72, 0x69, 0x61, 0x6e, + 0x74, 0x73, 0x18, 0x39, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x46, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, + 0x69, 0x64, 0x2e, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, + 0x61, 0x6e, 0x63, 0x65, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x2e, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, + 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x2e, 0x56, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x52, 0x08, 0x76, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x74, 0x73, 0x1a, 0x76, 0x0a, 0x0a, 0x53, 0x70, + 0x65, 0x63, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x52, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3c, 0x2e, 0x61, 0x6e, 0x64, 0x72, + 0x6f, 0x69, 0x64, 0x2e, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x6f, 0x72, + 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x2e, 0x72, 0x65, 0x71, 0x75, 0x69, + 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, + 0x65, 0x6e, 0x74, 0x53, 0x70, 0x65, 0x63, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, + 0x38, 0x01, 0x1a, 0x81, 0x01, 0x0a, 0x11, 0x4d, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x56, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x40, 0x2e, 0x61, 0x6e, 0x64, 0x72, + 0x6f, 0x69, 0x64, 0x2e, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x6f, 0x72, + 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x2e, 0x72, 0x65, 0x71, 0x75, 0x69, + 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, + 0x4d, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x71, 0x0a, 0x0d, 0x56, 0x61, 0x72, 0x69, 0x61, 0x6e, + 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x4a, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, + 0x69, 0x64, 0x2e, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, + 0x61, 0x6e, 0x63, 0x65, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x2e, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, + 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x56, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x74, 0x52, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xea, 0x04, 0x0a, 0x0f, 0x52, 0x65, + 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x70, 0x65, 0x63, 0x12, 0x47, 0x0a, + 0x03, 0x6d, 0x70, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x35, 0x2e, 0x61, 0x6e, 0x64, + 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x6f, + 0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x2e, 0x4d, 0x65, 0x64, 0x69, + 0x61, 0x50, 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x43, 0x6c, 0x61, 0x73, + 0x73, 0x52, 0x03, 0x6d, 0x70, 0x63, 0x12, 0x24, 0x0a, 0x0d, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x73, + 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x79, 0x0a, 0x0f, + 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, + 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x50, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x2e, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x53, - 0x70, 0x65, 0x63, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x81, - 0x01, 0x0a, 0x11, 0x4d, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x56, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x40, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, - 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x63, - 0x65, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x2e, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, - 0x6e, 0x74, 0x73, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x4d, 0x65, 0x61, 0x73, - 0x75, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, - 0x38, 0x01, 0x22, 0xfa, 0x02, 0x0a, 0x0f, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, - 0x6e, 0x74, 0x53, 0x70, 0x65, 0x63, 0x12, 0x47, 0x0a, 0x03, 0x6d, 0x70, 0x63, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x35, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x6d, 0x65, - 0x64, 0x69, 0x61, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x63, - 0x6c, 0x61, 0x73, 0x73, 0x2e, 0x4d, 0x65, 0x64, 0x69, 0x61, 0x50, 0x65, 0x72, 0x66, 0x6f, 0x72, - 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x52, 0x03, 0x6d, 0x70, 0x63, 0x12, - 0x24, 0x0a, 0x0d, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x79, 0x0a, 0x0f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, - 0x64, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x50, + 0x70, 0x65, 0x63, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x56, 0x61, 0x6c, 0x75, + 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0e, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, + 0x64, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x73, 0x0a, 0x0d, 0x76, 0x61, 0x72, 0x69, 0x61, + 0x6e, 0x74, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x4e, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x2e, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x52, 0x65, 0x71, - 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x70, 0x65, 0x63, 0x2e, 0x52, 0x65, 0x71, - 0x75, 0x69, 0x72, 0x65, 0x64, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x52, 0x0e, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, - 0x1a, 0x7d, 0x0a, 0x13, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x56, 0x61, 0x6c, 0x75, - 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x50, 0x0a, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3a, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, - 0x69, 0x64, 0x2e, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, - 0x61, 0x6e, 0x63, 0x65, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x2e, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, - 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x56, - 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, - 0x89, 0x02, 0x0a, 0x13, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x4d, 0x65, 0x61, 0x73, - 0x75, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, - 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x67, 0x0a, 0x10, 0x6d, 0x65, 0x61, - 0x73, 0x75, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x3c, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x6d, 0x65, - 0x64, 0x69, 0x61, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x63, - 0x6c, 0x61, 0x73, 0x73, 0x2e, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, - 0x73, 0x2e, 0x4d, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, - 0x65, 0x52, 0x0f, 0x6d, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, - 0x70, 0x65, 0x12, 0x57, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x37, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, - 0x2e, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x6e, - 0x63, 0x65, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x2e, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, - 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x52, - 0x0a, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x22, 0xd9, 0x01, 0x0a, 0x0d, - 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x0e, 0x0a, - 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x23, 0x0a, - 0x0c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0b, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, - 0x75, 0x65, 0x12, 0x1d, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x03, 0x48, 0x00, 0x52, 0x08, 0x69, 0x6e, 0x74, 0x56, 0x61, 0x6c, 0x75, - 0x65, 0x12, 0x23, 0x0a, 0x0c, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x48, 0x00, 0x52, 0x0b, 0x64, 0x6f, 0x75, 0x62, 0x6c, - 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1f, 0x0a, 0x0a, 0x62, 0x6f, 0x6f, 0x6c, 0x5f, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x09, 0x62, 0x6f, - 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1f, 0x0a, 0x0a, 0x6c, 0x6f, 0x6e, 0x67, 0x5f, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x48, 0x00, 0x52, 0x09, 0x6c, - 0x6f, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x07, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x2a, 0x6c, 0x0a, 0x05, 0x47, 0x72, 0x6f, 0x75, 0x70, - 0x12, 0x15, 0x0a, 0x11, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, - 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x47, 0x52, 0x4f, 0x55, 0x50, - 0x5f, 0x4d, 0x45, 0x44, 0x49, 0x41, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x47, 0x52, 0x4f, 0x55, - 0x50, 0x5f, 0x43, 0x41, 0x4d, 0x45, 0x52, 0x41, 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x47, 0x52, - 0x4f, 0x55, 0x50, 0x5f, 0x48, 0x41, 0x52, 0x44, 0x57, 0x41, 0x52, 0x45, 0x10, 0x03, 0x12, 0x15, - 0x0a, 0x11, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x50, 0x45, 0x52, 0x46, 0x4f, 0x52, 0x4d, 0x41, - 0x4e, 0x43, 0x45, 0x10, 0x04, 0x2a, 0xd8, 0x01, 0x0a, 0x0a, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, - 0x69, 0x73, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x16, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x52, 0x49, 0x53, - 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, - 0x12, 0x14, 0x0a, 0x10, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x52, 0x49, 0x53, 0x4f, 0x4e, 0x5f, 0x45, - 0x51, 0x55, 0x41, 0x4c, 0x10, 0x01, 0x12, 0x18, 0x0a, 0x14, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x52, - 0x49, 0x53, 0x4f, 0x4e, 0x5f, 0x4c, 0x45, 0x53, 0x53, 0x5f, 0x54, 0x48, 0x41, 0x4e, 0x10, 0x02, - 0x12, 0x21, 0x0a, 0x1d, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x52, 0x49, 0x53, 0x4f, 0x4e, 0x5f, 0x4c, - 0x45, 0x53, 0x53, 0x5f, 0x54, 0x48, 0x41, 0x4e, 0x5f, 0x4f, 0x52, 0x5f, 0x45, 0x51, 0x55, 0x41, - 0x4c, 0x10, 0x03, 0x12, 0x1b, 0x0a, 0x17, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x52, 0x49, 0x53, 0x4f, - 0x4e, 0x5f, 0x47, 0x52, 0x45, 0x41, 0x54, 0x45, 0x52, 0x5f, 0x54, 0x48, 0x41, 0x4e, 0x10, 0x04, - 0x12, 0x24, 0x0a, 0x20, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x52, 0x49, 0x53, 0x4f, 0x4e, 0x5f, 0x47, - 0x52, 0x45, 0x41, 0x54, 0x45, 0x52, 0x5f, 0x54, 0x48, 0x41, 0x4e, 0x5f, 0x4f, 0x52, 0x5f, 0x45, - 0x51, 0x55, 0x41, 0x4c, 0x10, 0x05, 0x12, 0x18, 0x0a, 0x14, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x52, - 0x49, 0x53, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x10, 0x06, - 0x2a, 0xbd, 0x01, 0x0a, 0x0f, 0x4d, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, - 0x54, 0x79, 0x70, 0x65, 0x12, 0x20, 0x0a, 0x1c, 0x4d, 0x45, 0x41, 0x53, 0x55, 0x52, 0x45, 0x4d, - 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, - 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x19, 0x0a, 0x15, 0x4d, 0x45, 0x41, 0x53, 0x55, 0x52, - 0x45, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x42, 0x4f, 0x4f, 0x4c, 0x10, - 0x01, 0x12, 0x1b, 0x0a, 0x17, 0x4d, 0x45, 0x41, 0x53, 0x55, 0x52, 0x45, 0x4d, 0x45, 0x4e, 0x54, - 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x4f, 0x55, 0x42, 0x4c, 0x45, 0x10, 0x02, 0x12, 0x18, - 0x0a, 0x14, 0x4d, 0x45, 0x41, 0x53, 0x55, 0x52, 0x45, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, - 0x50, 0x45, 0x5f, 0x49, 0x4e, 0x54, 0x10, 0x03, 0x12, 0x1b, 0x0a, 0x17, 0x4d, 0x45, 0x41, 0x53, - 0x55, 0x52, 0x45, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x54, 0x52, - 0x49, 0x4e, 0x47, 0x10, 0x04, 0x12, 0x19, 0x0a, 0x15, 0x4d, 0x45, 0x41, 0x53, 0x55, 0x52, 0x45, - 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4c, 0x4f, 0x4e, 0x47, 0x10, 0x05, - 0x42, 0x37, 0x50, 0x01, 0x5a, 0x33, 0x63, 0x74, 0x73, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x6d, - 0x65, 0x64, 0x69, 0x61, 0x70, 0x63, 0x2f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, - 0x6e, 0x74, 0x73, 0x2f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, - 0x5f, 0x67, 0x6f, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x70, 0x65, 0x63, 0x2e, 0x56, 0x61, 0x72, + 0x69, 0x61, 0x6e, 0x74, 0x53, 0x70, 0x65, 0x63, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0c, + 0x76, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x74, 0x53, 0x70, 0x65, 0x63, 0x73, 0x1a, 0x7d, 0x0a, 0x13, + 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x50, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3a, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x6d, + 0x65, 0x64, 0x69, 0x61, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, + 0x63, 0x6c, 0x61, 0x73, 0x73, 0x2e, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x73, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x79, 0x0a, 0x11, 0x56, + 0x61, 0x72, 0x69, 0x61, 0x6e, 0x74, 0x53, 0x70, 0x65, 0x63, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x4e, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x38, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x6d, 0x65, 0x64, 0x69, + 0x61, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x63, 0x6c, 0x61, + 0x73, 0x73, 0x2e, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2e, + 0x56, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x74, 0x53, 0x70, 0x65, 0x63, 0x52, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x89, 0x02, 0x0a, 0x13, 0x52, 0x65, 0x71, 0x75, 0x69, + 0x72, 0x65, 0x64, 0x4d, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x0e, + 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x20, + 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x67, 0x0a, 0x10, 0x6d, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x3c, 0x2e, 0x61, 0x6e, 0x64, + 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x6f, + 0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x2e, 0x72, 0x65, 0x71, 0x75, + 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x4d, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0f, 0x6d, 0x65, 0x61, 0x73, 0x75, 0x72, + 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x57, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, + 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x37, 0x2e, + 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2e, 0x70, 0x65, + 0x72, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x2e, 0x72, + 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x43, 0x6f, 0x6d, 0x70, + 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x52, 0x0a, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, + 0x6f, 0x6e, 0x22, 0xd9, 0x01, 0x0a, 0x0d, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x02, 0x69, 0x64, 0x12, 0x23, 0x0a, 0x0c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0b, 0x73, 0x74, + 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1d, 0x0a, 0x09, 0x69, 0x6e, 0x74, + 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x48, 0x00, 0x52, 0x08, + 0x69, 0x6e, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x23, 0x0a, 0x0c, 0x64, 0x6f, 0x75, 0x62, + 0x6c, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x48, 0x00, + 0x52, 0x0b, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1f, 0x0a, + 0x0a, 0x62, 0x6f, 0x6f, 0x6c, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x08, 0x48, 0x00, 0x52, 0x09, 0x62, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1f, + 0x0a, 0x0a, 0x6c, 0x6f, 0x6e, 0x67, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x03, 0x48, 0x00, 0x52, 0x09, 0x6c, 0x6f, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, + 0x07, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x22, 0x2b, + 0x0a, 0x07, 0x56, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, + 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x83, 0x02, 0x0a, 0x0b, + 0x56, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x74, 0x53, 0x70, 0x65, 0x63, 0x12, 0x75, 0x0a, 0x0f, 0x72, + 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x04, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x4c, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x6d, + 0x65, 0x64, 0x69, 0x61, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, + 0x63, 0x6c, 0x61, 0x73, 0x73, 0x2e, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x73, 0x2e, 0x56, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x74, 0x53, 0x70, 0x65, 0x63, 0x2e, 0x52, + 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x52, 0x0e, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x56, 0x61, 0x6c, 0x75, + 0x65, 0x73, 0x1a, 0x7d, 0x0a, 0x13, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x56, 0x61, + 0x6c, 0x75, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x50, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3a, 0x2e, 0x61, 0x6e, 0x64, + 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x6f, + 0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x2e, 0x72, 0x65, 0x71, 0x75, + 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, + 0x64, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, + 0x01, 0x2a, 0x6c, 0x0a, 0x05, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x15, 0x0a, 0x11, 0x47, 0x52, + 0x4f, 0x55, 0x50, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, + 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x4d, 0x45, 0x44, 0x49, 0x41, + 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x43, 0x41, 0x4d, 0x45, + 0x52, 0x41, 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x48, 0x41, + 0x52, 0x44, 0x57, 0x41, 0x52, 0x45, 0x10, 0x03, 0x12, 0x15, 0x0a, 0x11, 0x47, 0x52, 0x4f, 0x55, + 0x50, 0x5f, 0x50, 0x45, 0x52, 0x46, 0x4f, 0x52, 0x4d, 0x41, 0x4e, 0x43, 0x45, 0x10, 0x04, 0x2a, + 0xd8, 0x01, 0x0a, 0x0a, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x12, 0x1a, + 0x0a, 0x16, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x52, 0x49, 0x53, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x53, + 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x14, 0x0a, 0x10, 0x43, 0x4f, + 0x4d, 0x50, 0x41, 0x52, 0x49, 0x53, 0x4f, 0x4e, 0x5f, 0x45, 0x51, 0x55, 0x41, 0x4c, 0x10, 0x01, + 0x12, 0x18, 0x0a, 0x14, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x52, 0x49, 0x53, 0x4f, 0x4e, 0x5f, 0x4c, + 0x45, 0x53, 0x53, 0x5f, 0x54, 0x48, 0x41, 0x4e, 0x10, 0x02, 0x12, 0x21, 0x0a, 0x1d, 0x43, 0x4f, + 0x4d, 0x50, 0x41, 0x52, 0x49, 0x53, 0x4f, 0x4e, 0x5f, 0x4c, 0x45, 0x53, 0x53, 0x5f, 0x54, 0x48, + 0x41, 0x4e, 0x5f, 0x4f, 0x52, 0x5f, 0x45, 0x51, 0x55, 0x41, 0x4c, 0x10, 0x03, 0x12, 0x1b, 0x0a, + 0x17, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x52, 0x49, 0x53, 0x4f, 0x4e, 0x5f, 0x47, 0x52, 0x45, 0x41, + 0x54, 0x45, 0x52, 0x5f, 0x54, 0x48, 0x41, 0x4e, 0x10, 0x04, 0x12, 0x24, 0x0a, 0x20, 0x43, 0x4f, + 0x4d, 0x50, 0x41, 0x52, 0x49, 0x53, 0x4f, 0x4e, 0x5f, 0x47, 0x52, 0x45, 0x41, 0x54, 0x45, 0x52, + 0x5f, 0x54, 0x48, 0x41, 0x4e, 0x5f, 0x4f, 0x52, 0x5f, 0x45, 0x51, 0x55, 0x41, 0x4c, 0x10, 0x05, + 0x12, 0x18, 0x0a, 0x14, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x52, 0x49, 0x53, 0x4f, 0x4e, 0x5f, 0x49, + 0x4e, 0x46, 0x4f, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x10, 0x06, 0x2a, 0xbd, 0x01, 0x0a, 0x0f, 0x4d, + 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x20, + 0x0a, 0x1c, 0x4d, 0x45, 0x41, 0x53, 0x55, 0x52, 0x45, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, + 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, + 0x12, 0x19, 0x0a, 0x15, 0x4d, 0x45, 0x41, 0x53, 0x55, 0x52, 0x45, 0x4d, 0x45, 0x4e, 0x54, 0x5f, + 0x54, 0x59, 0x50, 0x45, 0x5f, 0x42, 0x4f, 0x4f, 0x4c, 0x10, 0x01, 0x12, 0x1b, 0x0a, 0x17, 0x4d, + 0x45, 0x41, 0x53, 0x55, 0x52, 0x45, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, + 0x44, 0x4f, 0x55, 0x42, 0x4c, 0x45, 0x10, 0x02, 0x12, 0x18, 0x0a, 0x14, 0x4d, 0x45, 0x41, 0x53, + 0x55, 0x52, 0x45, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, 0x4e, 0x54, + 0x10, 0x03, 0x12, 0x1b, 0x0a, 0x17, 0x4d, 0x45, 0x41, 0x53, 0x55, 0x52, 0x45, 0x4d, 0x45, 0x4e, + 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x54, 0x52, 0x49, 0x4e, 0x47, 0x10, 0x04, 0x12, + 0x19, 0x0a, 0x15, 0x4d, 0x45, 0x41, 0x53, 0x55, 0x52, 0x45, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x54, + 0x59, 0x50, 0x45, 0x5f, 0x4c, 0x4f, 0x4e, 0x47, 0x10, 0x05, 0x42, 0x37, 0x50, 0x01, 0x5a, 0x33, + 0x63, 0x74, 0x73, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x70, 0x63, + 0x2f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x72, 0x65, + 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x5f, 0x67, 0x6f, 0x5f, 0x70, 0x72, + 0x6f, 0x74, 0x6f, } var ( @@ -830,7 +1001,7 @@ func file_cts_tests_mediapc_requirements_requirements_proto_rawDescGZIP() []byte } var file_cts_tests_mediapc_requirements_requirements_proto_enumTypes = make([]protoimpl.EnumInfo, 3) -var file_cts_tests_mediapc_requirements_requirements_proto_msgTypes = make([]protoimpl.MessageInfo, 8) +var file_cts_tests_mediapc_requirements_requirements_proto_msgTypes = make([]protoimpl.MessageInfo, 13) var file_cts_tests_mediapc_requirements_requirements_proto_goTypes = []interface{}{ (Group)(0), // 0: android.media.performanceclass.requirements.Group (Comparison)(0), // 1: android.media.performanceclass.requirements.Comparison @@ -840,28 +1011,39 @@ var file_cts_tests_mediapc_requirements_requirements_proto_goTypes = []interface (*RequirementSpec)(nil), // 5: android.media.performanceclass.requirements.RequirementSpec (*RequiredMeasurement)(nil), // 6: android.media.performanceclass.requirements.RequiredMeasurement (*RequiredValue)(nil), // 7: android.media.performanceclass.requirements.RequiredValue - nil, // 8: android.media.performanceclass.requirements.Requirement.SpecsEntry - nil, // 9: android.media.performanceclass.requirements.Requirement.MeasurementsEntry - nil, // 10: android.media.performanceclass.requirements.RequirementSpec.RequiredValuesEntry - (mpc_go_proto.MediaPerformanceClass)(0), // 11: android.media.performanceclass.MediaPerformanceClass + (*Variant)(nil), // 8: android.media.performanceclass.requirements.Variant + (*VariantSpec)(nil), // 9: android.media.performanceclass.requirements.VariantSpec + nil, // 10: android.media.performanceclass.requirements.Requirement.SpecsEntry + nil, // 11: android.media.performanceclass.requirements.Requirement.MeasurementsEntry + nil, // 12: android.media.performanceclass.requirements.Requirement.VariantsEntry + nil, // 13: android.media.performanceclass.requirements.RequirementSpec.RequiredValuesEntry + nil, // 14: android.media.performanceclass.requirements.RequirementSpec.VariantSpecsEntry + nil, // 15: android.media.performanceclass.requirements.VariantSpec.RequiredValuesEntry + (mpc_go_proto.MediaPerformanceClass)(0), // 16: android.media.performanceclass.MediaPerformanceClass } var file_cts_tests_mediapc_requirements_requirements_proto_depIdxs = []int32{ 4, // 0: android.media.performanceclass.requirements.RequirementList.requirements:type_name -> android.media.performanceclass.requirements.Requirement 0, // 1: android.media.performanceclass.requirements.Requirement.group:type_name -> android.media.performanceclass.requirements.Group - 8, // 2: android.media.performanceclass.requirements.Requirement.specs:type_name -> android.media.performanceclass.requirements.Requirement.SpecsEntry - 9, // 3: android.media.performanceclass.requirements.Requirement.measurements:type_name -> android.media.performanceclass.requirements.Requirement.MeasurementsEntry - 11, // 4: android.media.performanceclass.requirements.RequirementSpec.mpc:type_name -> android.media.performanceclass.MediaPerformanceClass - 10, // 5: android.media.performanceclass.requirements.RequirementSpec.required_values:type_name -> android.media.performanceclass.requirements.RequirementSpec.RequiredValuesEntry - 2, // 6: android.media.performanceclass.requirements.RequiredMeasurement.measurement_type:type_name -> android.media.performanceclass.requirements.MeasurementType - 1, // 7: android.media.performanceclass.requirements.RequiredMeasurement.comparison:type_name -> android.media.performanceclass.requirements.Comparison - 5, // 8: android.media.performanceclass.requirements.Requirement.SpecsEntry.value:type_name -> android.media.performanceclass.requirements.RequirementSpec - 6, // 9: android.media.performanceclass.requirements.Requirement.MeasurementsEntry.value:type_name -> android.media.performanceclass.requirements.RequiredMeasurement - 7, // 10: android.media.performanceclass.requirements.RequirementSpec.RequiredValuesEntry.value:type_name -> android.media.performanceclass.requirements.RequiredValue - 11, // [11:11] is the sub-list for method output_type - 11, // [11:11] is the sub-list for method input_type - 11, // [11:11] is the sub-list for extension type_name - 11, // [11:11] is the sub-list for extension extendee - 0, // [0:11] is the sub-list for field type_name + 10, // 2: android.media.performanceclass.requirements.Requirement.specs:type_name -> android.media.performanceclass.requirements.Requirement.SpecsEntry + 11, // 3: android.media.performanceclass.requirements.Requirement.measurements:type_name -> android.media.performanceclass.requirements.Requirement.MeasurementsEntry + 12, // 4: android.media.performanceclass.requirements.Requirement.variants:type_name -> android.media.performanceclass.requirements.Requirement.VariantsEntry + 16, // 5: android.media.performanceclass.requirements.RequirementSpec.mpc:type_name -> android.media.performanceclass.MediaPerformanceClass + 13, // 6: android.media.performanceclass.requirements.RequirementSpec.required_values:type_name -> android.media.performanceclass.requirements.RequirementSpec.RequiredValuesEntry + 14, // 7: android.media.performanceclass.requirements.RequirementSpec.variant_specs:type_name -> android.media.performanceclass.requirements.RequirementSpec.VariantSpecsEntry + 2, // 8: android.media.performanceclass.requirements.RequiredMeasurement.measurement_type:type_name -> android.media.performanceclass.requirements.MeasurementType + 1, // 9: android.media.performanceclass.requirements.RequiredMeasurement.comparison:type_name -> android.media.performanceclass.requirements.Comparison + 15, // 10: android.media.performanceclass.requirements.VariantSpec.required_values:type_name -> android.media.performanceclass.requirements.VariantSpec.RequiredValuesEntry + 5, // 11: android.media.performanceclass.requirements.Requirement.SpecsEntry.value:type_name -> android.media.performanceclass.requirements.RequirementSpec + 6, // 12: android.media.performanceclass.requirements.Requirement.MeasurementsEntry.value:type_name -> android.media.performanceclass.requirements.RequiredMeasurement + 8, // 13: android.media.performanceclass.requirements.Requirement.VariantsEntry.value:type_name -> android.media.performanceclass.requirements.Variant + 7, // 14: android.media.performanceclass.requirements.RequirementSpec.RequiredValuesEntry.value:type_name -> android.media.performanceclass.requirements.RequiredValue + 9, // 15: android.media.performanceclass.requirements.RequirementSpec.VariantSpecsEntry.value:type_name -> android.media.performanceclass.requirements.VariantSpec + 7, // 16: android.media.performanceclass.requirements.VariantSpec.RequiredValuesEntry.value:type_name -> android.media.performanceclass.requirements.RequiredValue + 17, // [17:17] is the sub-list for method output_type + 17, // [17:17] is the sub-list for method input_type + 17, // [17:17] is the sub-list for extension type_name + 17, // [17:17] is the sub-list for extension extendee + 0, // [0:17] is the sub-list for field type_name } func init() { file_cts_tests_mediapc_requirements_requirements_proto_init() } @@ -930,6 +1112,30 @@ func file_cts_tests_mediapc_requirements_requirements_proto_init() { return nil } } + file_cts_tests_mediapc_requirements_requirements_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Variant); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_cts_tests_mediapc_requirements_requirements_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*VariantSpec); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } file_cts_tests_mediapc_requirements_requirements_proto_msgTypes[4].OneofWrappers = []interface{}{ (*RequiredValue_StringValue)(nil), @@ -944,7 +1150,7 @@ func file_cts_tests_mediapc_requirements_requirements_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_cts_tests_mediapc_requirements_requirements_proto_rawDesc, NumEnums: 3, - NumMessages: 8, + NumMessages: 13, NumExtensions: 0, NumServices: 0, }, diff --git a/tests/mediapc/requirements/requirements.proto b/tests/mediapc/requirements/requirements.proto index 3fa6fed5a0f..676c0c55b99 100644 --- a/tests/mediapc/requirements/requirements.proto +++ b/tests/mediapc/requirements/requirements.proto @@ -40,9 +40,15 @@ message Requirement { optional Group group = 4; map<int64, RequirementSpec> specs = 5; + // measurement_id to RequiredMeasurements // The measurement_id is a field name safe string. map<string, RequiredMeasurement> measurements = 6; + + // variant_id to Variant + // + // The variant_id is a field name safe string. + map<string, Variant> variants = 57; } message RequirementSpec { @@ -58,6 +64,10 @@ message RequirementSpec { // The measurement_id must match the id of a RequiredMeasurement in the parent // Requirement. map<string, RequiredValue> required_values = 4; + + // variant_id to VariantSpec + // The variant_id is a field name safe string. + map<string, VariantSpec> variant_specs = 5; } enum Group { @@ -91,6 +101,22 @@ message RequiredValue { } } +// Variants are used when an alternative set of required values should apply +message Variant { + // When the variant should be used. + optional string description = 1; +} + +// The set values required for a variant +message VariantSpec { + // measurement_id to RequiredMeasurements + // + // The measurement_id is a field name safe string. + // The measurement_id must match the id of a RequiredMeasurement in the parent + // Requirement. + map<string, RequiredValue> required_values = 4; +} + enum Comparison { COMPARISON_UNSPECIFIED = 0; COMPARISON_EQUAL = 1; diff --git a/tests/mediapc/requirements/requirements.txtpb b/tests/mediapc/requirements/requirements.txtpb index 39aa216909f..3cc6a9a7fc4 100644 --- a/tests/mediapc/requirements/requirements.txtpb +++ b/tests/mediapc/requirements/requirements.txtpb @@ -252,7 +252,9 @@ requirements { requirements { id: "5.1/H-1-7" name: "Video encoder init latency" - description: "Maximum codec initialization latency for a 1080p or smaller video encoding session for all hardware video encoders when under load." + description: + "Maximum codec initialization latency for a 1080p or smaller video " + "encoding session for all hardware video encoders when under load." measurements: { key: "codec_initialization_latency_ms" @@ -262,69 +264,157 @@ requirements { comparison: COMPARISON_LESS_THAN_OR_EQUAL } } + variants: { + key: "dolby" + value: { + description: "When the codec is Dolby Vision" + } + } specs { key: 30 value { mpc: MEDIA_PERFORMANCE_CLASS_11 - specification: "MUST have a codec initialization latency of 65 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." + specification: + "MUST have a codec initialization latency of 65 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." required_values: { key: "codec_initialization_latency_ms" value { long_value: 65 } } + variant_specs: { + key: "dolby" + value: { + required_values: { + key: "codec_initialization_latency_ms" + value { + long_value: 65 + } + } + } + } } } specs { key: 31 value { mpc: MEDIA_PERFORMANCE_CLASS_12 - specification: "MUST have a codec initialization latency of 50 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." + specification: + "MUST have a codec initialization latency of 50 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." required_values: { key: "codec_initialization_latency_ms" value { long_value: 50 } } + variant_specs: { + key: "dolby" + value: { + required_values: { + key: "codec_initialization_latency_ms" + value { + long_value: 50 + } + } + } + } } } specs { key: 33 value { mpc: MEDIA_PERFORMANCE_CLASS_13 - specification: "MUST have a codec initialization latency of 40 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." + specification: + "MUST have a codec initialization latency of 40 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." required_values: { key: "codec_initialization_latency_ms" value { long_value: 40 } } + variant_specs: { + key: "dolby" + value: { + required_values: { + key: "codec_initialization_latency_ms" + value { + long_value: 50 + } + } + } + } } } specs { key: 34 value { mpc: MEDIA_PERFORMANCE_CLASS_14 - specification: "MUST have a codec initialization latency of 40 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. For Dolby vision codec, the codec initialization latency MUST be 50 ms or less." + specification: + "MUST have a codec initialization latency of 40 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. For Dolby vision " + "codec, the codec initialization latency MUST be 50 ms or less." required_values: { key: "codec_initialization_latency_ms" value { long_value: 40 } } + variant_specs: { + key: "dolby" + value: { + required_values: { + key: "codec_initialization_latency_ms" + value { + long_value: 50 + } + } + } + } } } specs { key: 35 value { mpc: MEDIA_PERFORMANCE_CLASS_15 - specification: "MUST have a codec initialization latency of 40 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. For Dolby vision codec, the codec initialization latency MUST be 50 ms or less." + specification: + "MUST have a codec initialization latency of 40 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. For Dolby vision " + "codec, the codec initialization latency MUST be 50 ms or less." required_values: { key: "codec_initialization_latency_ms" value { long_value: 40 } } + variant_specs: { + key: "dolby" + value: { + required_values: { + key: "codec_initialization_latency_ms" + value { + long_value: 50 + } + } + } + } } } } @@ -508,12 +598,27 @@ requirements { } requirements { id: "5.1/H-1-13" - description: "- Decoder initialization latency" + name: "Audio decoder init latency" + description: "Maximum codec initialization latency for a 128 kbps or lower bitrate audio decoding session for all audio decoders when under load." + measurements: { + key: "codec_initialization_latency_ms" + value: { + description: "Codec initialization latency in milliseconds" + measurement_type: MEASUREMENT_TYPE_LONG + comparison: COMPARISON_LESS_THAN_OR_EQUAL + } + } specs { key: 33 value { mpc: MEDIA_PERFORMANCE_CLASS_13 - specification: "MUST have an audio decoder initialization latency of 30 ms or less." + specification: "MUST have a codec initialization latency of 30 ms or less for a 128 kbps or lower bitrate audio decoding session for all audio decoders 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 playback initialization." + required_values: { + key: "codec_initialization_latency_ms" + value { + long_value: 30 + } + } } } specs { @@ -521,13 +626,25 @@ requirements { value { mpc: MEDIA_PERFORMANCE_CLASS_14 specification: "MUST have a codec initialization latency of 30 ms or less for a 128 kbps or lower bitrate audio decoding session for all audio decoders 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 playback initialization." + required_values: { + key: "codec_initialization_latency_ms" + value { + long_value: 30 + } + } } } specs { key: 35 value { mpc: MEDIA_PERFORMANCE_CLASS_15 - specification: "same" + specification: "MUST have a codec initialization latency of 30 ms or less for a 128 kbps or lower bitrate audio decoding session for all audio decoders 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 playback initialization." + required_values: { + key: "codec_initialization_latency_ms" + value { + long_value: 30 + } + } } } } @@ -719,19 +836,40 @@ requirements { } requirements { id: "5.12/H-1-2" - description: "- RGBA_1010102 color format" + name: "RGBA 1010102 color format" + description: "RGBA_1010102 color format support for hardware encoders." + measurements: { + key: "rgba_1010102_color_format" + value: { + description: "Is RGBA_1010102 supported." + measurement_type: MEASUREMENT_TYPE_BOOL + comparison: COMPARISON_EQUAL + } + } specs { key: 34 value { mpc: MEDIA_PERFORMANCE_CLASS_14 specification: "MUST support RGBA_1010102 color format for all hardware AV1 and HEVC encoders present on the device." + required_values: { + key: "rgba_1010102_color_format" + value { + bool_value: true + } + } } } specs { key: 35 value { mpc: MEDIA_PERFORMANCE_CLASS_15 - specification: "same" + specification: "MUST support RGBA_1010102 color format for all hardware AV1 and HEVC encoders present on the device." + required_values: { + key: "rgba_1010102_color_format" + value { + bool_value: true + } + } } } } @@ -1981,11 +2119,60 @@ requirements { } requirements { id: "7.1.4.1/H-1-3" + name: "Vulkan" + description: "Must support vulkan physical device protected memory and global priority." + measurements: [ + { + key: "vk_ext_global_priority" + value: { + description: "Is VK_EXT_global_priority supported." + measurement_type: MEASUREMENT_TYPE_BOOL + comparison: COMPARISON_EQUAL + } + }, + { + key: "vk_physical_device_protected_memory" + value: { + description: "Is VkPhysicalDeviceProtectedMemoryFeatures.protectedMemory supported." + measurement_type: MEASUREMENT_TYPE_BOOL + comparison: COMPARISON_EQUAL + } + }, + { + key: "vk_non_cpu_device_count" + value: { + description: "The count of non VK_PHYSICAL_DEVICE_TYPE_CPU Vulcan Devices." + measurement_type: MEASUREMENT_TYPE_INT + comparison: COMPARISON_GREATER_THAN_OR_EQUAL + } + } + ] specs { key: 35 value { mpc: MEDIA_PERFORMANCE_CLASS_15 specification: "MUST support VkPhysicalDeviceProtectedMemoryFeatures.protectedMemory and VK_KHR_global_priority." + required_values: [ + { + key: "vk_ext_global_priority" + value: { + bool_value: true + + } + }, + { + key: "vk_physical_device_protected_memory" + value: { + bool_value: true + } + }, + { + key: "vk_non_cpu_device_count" + value: { + int_value: 1 + } + } + ] } } } diff --git a/tests/mediapc/requirements/requirementsdata_test.go b/tests/mediapc/requirements/requirementsdata_test.go index 1535fb37df7..eeef93027e3 100644 --- a/tests/mediapc/requirements/requirementsdata_test.go +++ b/tests/mediapc/requirements/requirementsdata_test.go @@ -45,7 +45,7 @@ func TestUniqueRequirementIDs(t *testing.T) { func TestUniqueRequirementNames(t *testing.T) { reqList := mustUnmarshalRequirementList(t) - // Requirment names must be unique + // Requirement names must be unique nameToID := make(map[string]string) // name to id for _, req := range reqList.GetRequirements() { if req.HasName() { diff --git a/tests/mediapc/requirements/templatefns.go b/tests/mediapc/requirements/templatefns.go index 1b743d935f6..59491d37cab 100644 --- a/tests/mediapc/requirements/templatefns.go +++ b/tests/mediapc/requirements/templatefns.go @@ -16,6 +16,7 @@ package templatefns import ( + "fmt" "strings" "text/template" "unicode" @@ -26,15 +27,17 @@ import ( func Funcs() template.FuncMap { // These function are made available in templates by calling their key values, e.g. {{SnakeCase "HelloWorld"}}. return template.FuncMap{ - // Case conversion functions. - "LowerCase": strings.ToLower, - "UpperCase": strings.ToUpper, - "TitleCase": titleCase, - "SnakeCase": snakeCase, + // go/keep-sorted start + "Dict": dict, "KebabCase": kebabCase, - "UpperCamelCase": upperCamelCase, "LowerCamelCase": lowerCamelCase, + "LowerCase": strings.ToLower, "SafeReqID": safeReqID, + "SnakeCase": snakeCase, + "TitleCase": titleCase, + "UpperCamelCase": upperCamelCase, + "UpperCase": strings.ToUpper, + // go/keep-sorted end } } @@ -118,3 +121,36 @@ func safeReqID(s string) string { } return "r" + strings.ToLower(f(f(f(s, "/", "__"), ".", "_"), "-", "_")) } + +// dict converts a list of key-value pairs into a map. +// If there is an odd number of values, the last value is nil. +// The last key is preserved so in the template it can be referenced like {{$myDict.key}}. +func dict(v ...any) map[string]any { + dict := map[string]any{} + lenv := len(v) + for i := 0; i < lenv; i += 2 { + key := toString(v[i]) + if i+1 >= lenv { + dict[key] = nil + continue + } + dict[key] = v[i+1] + } + return dict +} + +// toString converts a value to a string. +func toString(v any) string { + switch v := v.(type) { + case string: + return v + case []byte: + return string(v) + case error: + return v.Error() + case fmt.Stringer: + return v.String() + default: + return fmt.Sprintf("%v", v) + } +} diff --git a/tests/mediapc/requirements/templatefns_test.go b/tests/mediapc/requirements/templatefns_test.go index 9f60de3586b..b0eb431abbe 100644 --- a/tests/mediapc/requirements/templatefns_test.go +++ b/tests/mediapc/requirements/templatefns_test.go @@ -15,6 +15,7 @@ package templatefns import ( + "errors" "testing" ) @@ -144,11 +145,34 @@ func TestLowerCamelCase(t *testing.T) { } } -func TestSafeReqID(t *testing.T) { - want := "r5_1__h_1_1" - id := "5.1/H-1-1" - got := safeReqID(id) - if got != want { - t.Fatalf("safeReqID(%q) = %q, want %q", id, got, want) +func TestDict(t *testing.T) { + errorValue := errors.New("error_value") + var dictTest = []struct { + values []any + want map[string]any + }{ + { + values: []any{1, 2, 3}, + want: map[string]any{ + "1": 2, + "3": nil, + }, + }, + { + values: []any{"foo", "bar"}, + want: map[string]any{"foo": "bar"}, + }, + { + values: []any{errors.New("error_key"), errorValue}, + want: map[string]any{"error_key": errorValue}, + }, + } + for _, tt := range dictTest { + got := dict(tt.values...) + for k, v := range tt.want { + if got[k] != v { + t.Fatalf("dict(%v)[%q] = %q, want %q", tt.values, k, got[k], v) + } + } } } diff --git a/tests/mediapc/src/android/mediapc/cts/CodecInitializationLatencyTest.java b/tests/mediapc/src/android/mediapc/cts/CodecInitializationLatencyTest.java index 1948e4f4d96..0f795ae95d3 100644 --- a/tests/mediapc/src/android/mediapc/cts/CodecInitializationLatencyTest.java +++ b/tests/mediapc/src/android/mediapc/cts/CodecInitializationLatencyTest.java @@ -40,6 +40,7 @@ import android.media.MediaCodecInfo; import android.media.MediaFormat; import android.media.MediaRecorder; import android.mediapc.cts.common.PerformanceClassEvaluator; +import android.mediapc.cts.common.Requirements; import android.mediapc.cts.common.Utils; import android.os.SystemClock; import android.util.Log; @@ -358,12 +359,27 @@ public class CodecInitializationLatencyTest { 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(mMime) - : isAudio ? pce.addR5_1__H_1_13() : pce.addR5_1__H_1_12(); - - r5_1__H_1_Latency.setCodecInitLatencyMs(initializationLatency); - + if (isEncoder) { + if (isAudio) { + Requirements.addR5_1__H_1_8(pce).setCodecInitializationLatencyMs( + initializationLatency); + } else { + if (mMime.equals(MediaFormat.MIMETYPE_VIDEO_DOLBY_VISION)) { + Requirements.addR5_1__H_1_7Dolby(pce).setCodecInitializationLatencyMs( + initializationLatency); + } else { + Requirements.addR5_1__H_1_7(pce).setCodecInitializationLatencyMs( + initializationLatency); + } + } + } else { + if (isAudio) { + Requirements.addR5_1__H_1_13(pce).setCodecInitializationLatencyMs( + initializationLatency); + } else { + pce.addR5_1__H_1_12().setCodecInitLatencyMs(initializationLatency); + } + } pce.submitAndCheck(); } diff --git a/tests/mediapc/src/android/mediapc/cts/PerformanceClassTest.java b/tests/mediapc/src/android/mediapc/cts/PerformanceClassTest.java index 9b7c54994cd..401278d4b15 100644 --- a/tests/mediapc/src/android/mediapc/cts/PerformanceClassTest.java +++ b/tests/mediapc/src/android/mediapc/cts/PerformanceClassTest.java @@ -29,7 +29,9 @@ import android.media.MediaFormat; import android.media.UnsupportedSchemeException; import android.mediapc.cts.common.PerformanceClassEvaluator; import android.mediapc.cts.common.Requirements; +import android.mediapc.cts.common.Requirements.Android11MemoryRequirement; import android.mediapc.cts.common.Requirements.HDRDisplayRequirement; +import android.mediapc.cts.common.Requirements.MemoryRequirement; import android.mediapc.cts.common.Utils; import android.util.Log; @@ -199,11 +201,11 @@ public class PerformanceClassTest { Log.i(TAG, String.format("Total device memory = %,d MB", totalMemoryMb)); PerformanceClassEvaluator pce = new PerformanceClassEvaluator(this.mTestName); - PerformanceClassEvaluator.MemoryRequirement r7_6_1_h_1_1 = pce.addR7_6_1__H_1_1(); - PerformanceClassEvaluator.MemoryRequirement r7_6_1_h_2_1 = pce.addR7_6_1__H_2_1(); + Android11MemoryRequirement r7_6_1_h_1_1 = Requirements.addR7_6_1__H_1_1(pce); + MemoryRequirement r7_6_1_h_2_1 = Requirements.addR7_6_1__H_2_1(pce); - r7_6_1_h_1_1.setPhysicalMemory(totalMemoryMb); - r7_6_1_h_2_1.setPhysicalMemory(totalMemoryMb); + r7_6_1_h_1_1.setPhysicalMemoryMb(totalMemoryMb); + r7_6_1_h_2_1.setPhysicalMemoryMb(totalMemoryMb); pce.submitAndCheck(); } @@ -212,8 +214,7 @@ public class PerformanceClassTest { @CddTest(requirements = {"2.2.7.3/7.1.1.3/H-3-1"}) public void testDisplayHdr() { PerformanceClassEvaluator pce = new PerformanceClassEvaluator(this.mTestName); - HDRDisplayRequirement req = pce.addRequirement( - Requirements.createR7_1_1_3__H_3_1()); + HDRDisplayRequirement req = Requirements.addR7_1_1_3__H_3_1(pce); req.setIsHdr(Utils.IS_HDR); req.setDisplayLuminanceNits(Utils.HDR_DISPLAY_AVERAGE_LUMINANCE); diff --git a/tests/mediapc/src/android/mediapc/cts/VideoCodecRequirementsTest.java b/tests/mediapc/src/android/mediapc/cts/VideoCodecRequirementsTest.java index e627ff1bdde..a2167eba77f 100644 --- a/tests/mediapc/src/android/mediapc/cts/VideoCodecRequirementsTest.java +++ b/tests/mediapc/src/android/mediapc/cts/VideoCodecRequirementsTest.java @@ -51,6 +51,8 @@ import android.media.MediaFormat; import android.media.MediaRecorder; import android.media.codec.Flags; import android.mediapc.cts.common.PerformanceClassEvaluator; +import android.mediapc.cts.common.Requirements; +import android.mediapc.cts.common.Requirements.RGBA1010102ColorFormatRequirement; import android.mediapc.cts.common.Utils; import android.platform.test.annotations.RequiresFlagsEnabled; import android.util.Log; @@ -454,9 +456,8 @@ public class VideoCodecRequirementsTest { } PerformanceClassEvaluator pce = new PerformanceClassEvaluator(this.mTestName); - PerformanceClassEvaluator.VideoCodecRequirement colorFormatSupportReq = - pce.addColorFormatSupportReq(); - colorFormatSupportReq.setColorFormatSupportReq(isSupported); + RGBA1010102ColorFormatRequirement colorFormatSupportReq = Requirements.addR5_12__H_1_2(pce); + colorFormatSupportReq.setRgba1010102ColorFormat(isSupported); pce.submitAndCheck(); } diff --git a/tests/mediapc/src/android/mediapc/cts/VulkanTest.java b/tests/mediapc/src/android/mediapc/cts/VulkanTest.java new file mode 100644 index 00000000000..cf1e7af8401 --- /dev/null +++ b/tests/mediapc/src/android/mediapc/cts/VulkanTest.java @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.mediapc.cts; + +import android.mediapc.cts.common.PerformanceClassEvaluator; +import android.mediapc.cts.common.Requirements; +import android.mediapc.cts.common.Requirements.VulkanRequirement; +import android.util.Log; + +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import com.android.compatibility.common.util.CddTest; + +import com.google.common.collect.ImmutableList; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestName; +import org.junit.runner.RunWith; + +/** + * Verify Vulkan MPC requirements. + */ +@RunWith(AndroidJUnit4.class) +public class VulkanTest { + + private static final String LOG_TAG = VulkanTest.class.getSimpleName(); + private static final int VK_PHYSICAL_DEVICE_TYPE_CPU = 4; + + + private static final String VK_EXT_GLOBAL_PRIORITY_EXTENSION_NAME = "VK_EXT_global_priority"; + private static final int VK_EXT_GLOBAL_PRIORITY_SPEC_VERSION = 1; + + private ImmutableList<JSONObject> mVulkanDevices; + private static native String nativeGetVkJSON(); + + @Rule + public final TestName mTestName = new TestName(); + + static { + System.loadLibrary("ctsmediapc_vulkan_jni"); + } + + /** + * Test specific setup + */ + @Before + public void setUp() throws Exception { + JSONObject instance = new JSONObject(nativeGetVkJSON()); + final JSONArray vkjson = instance.getJSONArray("devices"); + var builder = ImmutableList.<JSONObject>builder(); + for (int i = 0; i < vkjson.length(); i++) { + builder.add(vkjson.getJSONObject(i)); + } + mVulkanDevices = builder.build(); + } + + /** + * <b>7.1.4.1/H-1-3</b> MUST support + * {@code VkPhysicalDeviceProtectedMemoryFeatures.protectedMemory} and + * {@code VK_EXT_global_priority}. + */ + @CddTest(requirements = {"7.1.4.1/H-1-3"}) + @Test + public void checkVulkanProtectedMemoryAndGlobalPrioritySupport() throws Exception { + + PerformanceClassEvaluator pce = new PerformanceClassEvaluator(this.mTestName); + VulkanRequirement req = Requirements.addR7_1_4_1__H_1_3(pce); + + var filteredDevices = mVulkanDevices.stream().filter(this::notCpuDevice).toList(); + final boolean extGlobalPriority = filteredDevices.stream().allMatch( + device -> hasExtension(device, VK_EXT_GLOBAL_PRIORITY_EXTENSION_NAME, + VK_EXT_GLOBAL_PRIORITY_SPEC_VERSION)); + final boolean hasProtectedMemory = filteredDevices.stream().allMatch( + this::hasProtectedMemory); + + req.setVkNonCpuDeviceCount(filteredDevices.size()); + req.setVkPhysicalDeviceProtectedMemory(hasProtectedMemory); + req.setVkExtGlobalPriority(extGlobalPriority); + + pce.submitAndCheck(); + } + + private boolean notCpuDevice(JSONObject device) { + try { + return device.getJSONObject("properties").getInt("deviceType") + != VK_PHYSICAL_DEVICE_TYPE_CPU; + } catch (JSONException e) { + Log.e(LOG_TAG, "Failed to get device type of %s".formatted(device), e); + return false; + } + } + + private static boolean hasExtension(JSONObject device, String name, int minVersion) { + try { + JSONArray extensions = device.getJSONArray("extensions"); + for (int i = 0; i < extensions.length(); i++) { + JSONObject ext = extensions.getJSONObject(i); + if (ext.getString("extensionName").equals(name) + && ext.getInt("specVersion") + >= minVersion) { + return true; + } + } + } catch (Exception e) { + Log.e(LOG_TAG, + "Failed to get extension %s v%s from %s".formatted(name, minVersion, device), + e); + } + return false; + } + + private boolean hasProtectedMemory(JSONObject device) { + try { + return device.getJSONObject("protectedMemoryFeatures") + .getInt("protectedMemory") == 1; + } catch (Exception e) { + Log.e(LOG_TAG, "Failed to test for protect memory of %s".formatted(device), e); + } + return false; + } +} diff --git a/tests/rollback/src/com/android/cts/rollback/RollbackManagerTest.java b/tests/rollback/src/com/android/cts/rollback/RollbackManagerTest.java index 1392bfdce40..f10115f0d97 100644 --- a/tests/rollback/src/com/android/cts/rollback/RollbackManagerTest.java +++ b/tests/rollback/src/com/android/cts/rollback/RollbackManagerTest.java @@ -677,7 +677,7 @@ public class RollbackManagerTest { */ @Test public void testRollbackExpiresAfterLifetime() throws Exception { - long expirationTime = TimeUnit.SECONDS.toMillis(30); + long expirationTime = TimeUnit.SECONDS.toMillis(6); DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK_BOOT, RollbackManager.PROPERTY_ROLLBACK_LIFETIME_MILLIS, Long.toString(expirationTime), false /* makeDefault*/); @@ -691,8 +691,8 @@ public class RollbackManagerTest { Thread.sleep(expirationTime / 2); assertThat(RollbackUtils.getAvailableRollback(TestApp.A)).isNotNull(); - // Check that the data has expired after the expiration time (with a buffer of 1 second) - Thread.sleep(expirationTime / 2 + TimeUnit.SECONDS.toMillis(1)); + // Check that the data has expired after the expiration time (with a buffer of 3 second) + Thread.sleep(expirationTime / 2 + TimeUnit.SECONDS.toMillis(3)); assertThat(RollbackUtils.getAvailableRollback(TestApp.A)).isNull(); } finally { // Restore default config values @@ -763,7 +763,7 @@ public class RollbackManagerTest { @Test @RequiresFlagsEnabled(Flags.FLAG_ROLLBACK_LIFETIME) public void testRollbackExpiresWhenLifetimeStays() throws Exception { - long expirationTime = TimeUnit.SECONDS.toMillis(5); + long expirationTime = TimeUnit.SECONDS.toMillis(6); Install.single(TestApp.A1).commit(); Install.single(TestApp.A2).setEnableRollback() .setRollbackLifetimeMillis(expirationTime).commit(); @@ -777,7 +777,7 @@ public class RollbackManagerTest { Thread.sleep(expirationTime / 2); assertThat(RollbackUtils.getAvailableRollback(TestApp.A)).isNotNull(); // The rollback now should expire - Thread.sleep(expirationTime / 2 + TimeUnit.SECONDS.toMillis(1)); + Thread.sleep(expirationTime / 2 + TimeUnit.SECONDS.toMillis(3)); assertThat(RollbackUtils.getAvailableRollback(TestApp.A)).isNull(); } finally { // Restore default config values @@ -793,7 +793,7 @@ public class RollbackManagerTest { */ @Test public void testTimeChangeDoesNotAffectLifetime() throws Exception { - long expirationTime = TimeUnit.SECONDS.toMillis(30); + long expirationTime = TimeUnit.SECONDS.toMillis(6); DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK_BOOT, RollbackManager.PROPERTY_ROLLBACK_LIFETIME_MILLIS, Long.toString(expirationTime), false /* makeDefault*/); @@ -811,7 +811,7 @@ public class RollbackManagerTest { Thread.sleep(expirationTime / 2); assertThat(RollbackUtils.getAvailableRollback(TestApp.A)).isNotNull(); // The rollback now should expire - Thread.sleep(expirationTime / 2 + TimeUnit.SECONDS.toMillis(1)); + Thread.sleep(expirationTime / 2 + TimeUnit.SECONDS.toMillis(3)); assertThat(RollbackUtils.getAvailableRollback(TestApp.A)).isNull(); } finally { RollbackUtils.forwardTimeBy(-expirationTime); @@ -831,7 +831,7 @@ public class RollbackManagerTest { @Test @RequiresFlagsEnabled(Flags.FLAG_ROLLBACK_LIFETIME) public void testTimeChangeDoesNotAffectLifetimeMillis() throws Exception { - long expirationTime = TimeUnit.SECONDS.toMillis(10); + long expirationTime = TimeUnit.SECONDS.toMillis(6); Install.single(TestApp.A1).commit(); Install.single(TestApp.A2).setEnableRollback() @@ -846,7 +846,7 @@ public class RollbackManagerTest { Thread.sleep(expirationTime / 2); assertThat(RollbackUtils.getAvailableRollback(TestApp.A)).isNotNull(); // The rollback now should expire - Thread.sleep(expirationTime / 2 + TimeUnit.SECONDS.toMillis(1)); + Thread.sleep(expirationTime / 2 + TimeUnit.SECONDS.toMillis(3)); assertThat(RollbackUtils.getAvailableRollback(TestApp.A)).isNull(); } finally { RollbackUtils.forwardTimeBy(-expirationTime); diff --git a/tests/signature/api-check/hidden-api-killswitch-debug-class/AndroidTest.xml b/tests/signature/api-check/hidden-api-killswitch-debug-class/AndroidTest.xml index bbbd3910616..0f465e79fde 100644 --- a/tests/signature/api-check/hidden-api-killswitch-debug-class/AndroidTest.xml +++ b/tests/signature/api-check/hidden-api-killswitch-debug-class/AndroidTest.xml @@ -29,7 +29,7 @@ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> <option name="class" value="android.signature.cts.api.DebugClassKillswitchTest" /> <option name="runtime-hint" value="60s" /> - <option name="shell-timeout" value="45m" /> + <option name="shell-timeout" value="65m" /> </test> <!-- Controller that will skip the module if a native bridge situation is detected --> 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 ee3903fdaa8..b3c26b37551 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 @@ -44,22 +44,22 @@ public abstract class BaseKillswitchTest extends AbstractApiTest { private final static Predicate<DexMember> FIELD_FILTER = dexMember -> (dexMember instanceof DexField); - @Test(timeout = 2400000) + @Test(timeout = 3600000) public void testKillswitchMechanismMethodsThroughReflection() { doTestKillswitchMechanism(METHOD_FILTER, /* reflection= */ true, /* jni= */ false); } - @Test(timeout = 2400000) + @Test(timeout = 3600000) public void testKillswitchMechanismMethodsThroughJni() { doTestKillswitchMechanism(METHOD_FILTER, /* reflection= */ false, /* jni= */ true); } - @Test(timeout = 2400000) + @Test(timeout = 3600000) public void testKillswitchMechanismFieldsThroughReflection() { doTestKillswitchMechanism(FIELD_FILTER, /* reflection= */ true, /* jni= */ false); } - @Test(timeout = 2400000) + @Test(timeout = 3600000) public void testKillswitchMechanismFieldsThroughJni() { doTestKillswitchMechanism(FIELD_FILTER, /* reflection= */ false, /* jni= */ true); } diff --git a/tests/surfacecontrol/AndroidManifest.xml b/tests/surfacecontrol/AndroidManifest.xml index ccfa2894d3d..20dcfd75274 100644 --- a/tests/surfacecontrol/AndroidManifest.xml +++ b/tests/surfacecontrol/AndroidManifest.xml @@ -110,7 +110,6 @@ android:label="HandleConfigurationActivity" android:rotationAnimation="jumpcut" android:configChanges="orientation|screenSize|screenLayout|smallestScreenSize" - android:theme="@android:style/Theme.Material.Dialog.NoActionBar" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN"/> diff --git a/tests/surfacecontrol/src/android/view/surfacecontrol/cts/AttachedSurfaceControlTest.java b/tests/surfacecontrol/src/android/view/surfacecontrol/cts/AttachedSurfaceControlTest.java index c2eefe74ca2..c99eeae9ffc 100644 --- a/tests/surfacecontrol/src/android/view/surfacecontrol/cts/AttachedSurfaceControlTest.java +++ b/tests/surfacecontrol/src/android/view/surfacecontrol/cts/AttachedSurfaceControlTest.java @@ -18,6 +18,7 @@ package android.view.surfacecontrol.cts; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static android.server.wm.BuildUtils.HW_TIMEOUT_MULTIPLIER; +import static android.server.wm.CtsWindowInfoUtils.waitForWindowOnTop; import static android.view.cts.surfacevalidator.BitmapPixelChecker.validateScreenshot; import static com.google.common.truth.Truth.assertThat; @@ -57,6 +58,8 @@ import androidx.lifecycle.Lifecycle; import androidx.test.core.app.ActivityScenario; import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; +import androidx.test.rule.ActivityTestRule; import com.android.compatibility.common.util.SystemUtil; import com.android.window.flags.Flags; @@ -89,6 +92,12 @@ public class AttachedSurfaceControlTest { @Rule public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + @Rule + public ActivityTestRule<HandleConfigurationActivity> mActivityRule = new ActivityTestRule<>( + HandleConfigurationActivity.class); + + private HandleConfigurationActivity mActivity; + private static class TransformHintListener implements AttachedSurfaceControl.OnBufferTransformHintChangedListener { Activity activity; @@ -110,6 +119,14 @@ public class AttachedSurfaceControlTest { + " expected=" + expectedOrientation + " transformHint=" + hint); Assert.assertEquals("Failed to switch orientation hint=" + hint, orientation, expectedOrientation); + + // Check the callback value matches the call to get the transform hint. + int actualTransformHint = + activity.getWindow().getRootSurfaceControl().getBufferTransformHint(); + Assert.assertEquals( + "Callback " + hint + " doesn't match transform hint=" + actualTransformHint, + hint, + actualTransformHint); hintConsumer.accept(hint); latch.countDown(); activity.getWindow().getRootSurfaceControl() @@ -118,9 +135,11 @@ public class AttachedSurfaceControlTest { } @Before - public void setup() { + public void setup() throws InterruptedException { mOrientationSession = new IgnoreOrientationRequestSession(false /* enable */); mWmState = new WindowManagerStateHelper(); + mActivity = mActivityRule.getActivity(); + waitForWindowOnTop(mActivity.getWindow()); } private void supportRotationCheck() { @@ -147,47 +166,31 @@ public class AttachedSurfaceControlTest { final int[] transformHintResult = new int[2]; final CountDownLatch[] firstCallback = new CountDownLatch[1]; final CountDownLatch[] secondCallback = new CountDownLatch[1]; - try (ActivityScenario<HandleConfigurationActivity> scenario = - ActivityScenario.launch(HandleConfigurationActivity.class)) { - scenario.moveToState(Lifecycle.State.RESUMED); - scenario.onActivity(activity -> { - mWmState.computeState(); - assumeFalse("Skipping test: display area is ignoring orientation request", - mWmState.isTaskDisplayAreaIgnoringOrientationRequest( - activity.getComponentName())); - int requestedOrientation = getRequestedOrientation(activity); - TransformHintListener listener = new TransformHintListener(activity, - requestedOrientation, hint -> transformHintResult[0] = hint); - firstCallback[0] = listener.latch; - activity.getWindow().getRootSurfaceControl() - .addOnBufferTransformHintChangedListener(listener); - setRequestedOrientation(activity, requestedOrientation); - }); - // Check we get a callback since the orientation has changed and we expect transform - // hint to change. - Assert.assertTrue(firstCallback[0].await(3, TimeUnit.SECONDS)); - - // Check the callback value matches the call to get the transform hint. - scenario.onActivity(activity -> Assert.assertEquals(transformHintResult[0], - activity.getWindow().getRootSurfaceControl().getBufferTransformHint())); - - scenario.onActivity(activity -> { - int requestedOrientation = getRequestedOrientation(activity); - TransformHintListener listener = new TransformHintListener(activity, - requestedOrientation, hint -> transformHintResult[1] = hint); - secondCallback[0] = listener.latch; - activity.getWindow().getRootSurfaceControl() - .addOnBufferTransformHintChangedListener(listener); - setRequestedOrientation(activity, requestedOrientation); - }); - // Check we get a callback since the orientation has changed and we expect transform - // hint to change. - Assert.assertTrue(secondCallback[0].await(3, TimeUnit.SECONDS)); - - // Check the callback value matches the call to get the transform hint. - scenario.onActivity(activity -> Assert.assertEquals(transformHintResult[1], - activity.getWindow().getRootSurfaceControl().getBufferTransformHint())); - } + mWmState.computeState(); + assumeFalse("Skipping test: display area is ignoring orientation request", + mWmState.isTaskDisplayAreaIgnoringOrientationRequest( + mActivity.getComponentName())); + int requestedOrientation = getRequestedOrientation(mActivity); + TransformHintListener listener = new TransformHintListener(mActivity, + requestedOrientation, hint -> transformHintResult[0] = hint); + firstCallback[0] = listener.latch; + mActivity.getWindow().getRootSurfaceControl() + .addOnBufferTransformHintChangedListener(listener); + setRequestedOrientation(mActivity, requestedOrientation); + // Check we get a callback since the orientation has changed and we expect transform + // hint to change. + Assert.assertTrue(firstCallback[0].await(10, TimeUnit.SECONDS)); + + requestedOrientation = getRequestedOrientation(mActivity); + TransformHintListener secondListener = new TransformHintListener(mActivity, + requestedOrientation, hint -> transformHintResult[1] = hint); + secondCallback[0] = secondListener.latch; + mActivity.getWindow().getRootSurfaceControl() + .addOnBufferTransformHintChangedListener(secondListener); + setRequestedOrientation(mActivity, requestedOrientation); + // Check we get a callback since the orientation has changed and we expect transform + // hint to change. + Assert.assertTrue(secondCallback[0].await(10, TimeUnit.SECONDS)); // If the app orientation was changed, we should get a different transform hint Assert.assertNotEquals(transformHintResult[0], transformHintResult[1]); @@ -217,51 +220,41 @@ public class AttachedSurfaceControlTest { final int[] transformHintResult = new int[2]; final CountDownLatch[] firstCallback = new CountDownLatch[1]; final CountDownLatch[] secondCallback = new CountDownLatch[1]; - try (ActivityScenario<HandleConfigurationActivity> scenario = - ActivityScenario.launch(HandleConfigurationActivity.class)) { - scenario.moveToState(Lifecycle.State.RESUMED); - scenario.onActivity(activity -> { - mWmState.computeState(); - assumeFalse("Skipping test: display area is ignoring orientation request", - mWmState.isTaskDisplayAreaIgnoringOrientationRequest( - activity.getComponentName())); - if (activity.getResources().getConfiguration().orientation - == ORIENTATION_LANDSCAPE) { - return; - } - TransformHintListener listener = new TransformHintListener(activity, - ORIENTATION_LANDSCAPE, hint -> transformHintResult[0] = hint); - firstCallback[0] = listener.latch; - activity.getWindow().getRootSurfaceControl() - .addOnBufferTransformHintChangedListener(listener); - setRequestedOrientation(activity, ORIENTATION_LANDSCAPE); - }); - - // If the device is already in landscape, do nothing. - if (firstCallback[0] != null) { - Assert.assertTrue(firstCallback[0].await(3, TimeUnit.SECONDS)); - scenario.onActivity(activity -> Assert.assertEquals(transformHintResult[0], - activity.getWindow().getRootSurfaceControl().getBufferTransformHint())); - } - - scenario.onActivity(activity -> { - TransformHintListener listener = new TransformHintListener(activity, - ORIENTATION_LANDSCAPE, hint -> transformHintResult[1] = hint); - secondCallback[0] = listener.latch; - activity.getWindow().getRootSurfaceControl() - .addOnBufferTransformHintChangedListener(listener); - activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE); + mWmState.computeState(); + assumeFalse("Skipping test: display area is ignoring orientation request", + mWmState.isTaskDisplayAreaIgnoringOrientationRequest( + mActivity.getComponentName())); + if (mActivity.getResources().getConfiguration().orientation + != ORIENTATION_LANDSCAPE) { + Log.d(TAG, "Request landscape orientation"); + TransformHintListener listener = new TransformHintListener(mActivity, + ORIENTATION_LANDSCAPE, hint -> { + transformHintResult[0] = hint; + Log.d(TAG, "firstListener fired with hint =" + hint); }); - // Check we get a callback since the orientation has changed and we expect transform - // hint to change. - Assert.assertTrue(secondCallback[0].await(3, TimeUnit.SECONDS)); - - // Check the callback value matches the call to get the transform hint. - scenario.onActivity(activity -> Assert.assertEquals(transformHintResult[1], - activity.getWindow().getRootSurfaceControl().getBufferTransformHint())); + firstCallback[0] = listener.latch; + mActivity.getWindow().getRootSurfaceControl() + .addOnBufferTransformHintChangedListener(listener); + setRequestedOrientation(mActivity, ORIENTATION_LANDSCAPE); + Assert.assertTrue(firstCallback[0].await(10, TimeUnit.SECONDS)); + } else { + transformHintResult[0] = + mActivity.getWindow().getRootSurfaceControl().getBufferTransformHint(); + Log.d(TAG, "Skipped request landscape orientation: hint=" + transformHintResult[0]); } - // If the app orientation was changed, we should get a different transform hint + TransformHintListener secondListener = new TransformHintListener(mActivity, + ORIENTATION_LANDSCAPE, hint -> { + transformHintResult[1] = hint; + Log.d(TAG, "secondListener fired with hint =" + hint); + }); + secondCallback[0] = secondListener.latch; + mActivity.getWindow().getRootSurfaceControl() + .addOnBufferTransformHintChangedListener(secondListener); + Log.d(TAG, "Requesting reverse landscape"); + mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE); + + Assert.assertTrue(secondCallback[0].await(10, TimeUnit.SECONDS)); Assert.assertNotEquals(transformHintResult[0], transformHintResult[1]); } diff --git a/tests/tests/app/Android.bp b/tests/tests/app/Android.bp index 2022e5f36b2..d03334b0239 100644 --- a/tests/tests/app/Android.bp +++ b/tests/tests/app/Android.bp @@ -18,6 +18,7 @@ package { android_test { name: "CtsAndroidAppTestCases", + team: "trendy_team_platform_security", defaults: ["cts_defaults"], platform_apis: true, libs: [ diff --git a/tests/tests/appop/src/android/app/appops/cts/AttributionTest.kt b/tests/tests/appop/src/android/app/appops/cts/AttributionTest.kt index 48d3a1d843d..310195c7e77 100644 --- a/tests/tests/appop/src/android/app/appops/cts/AttributionTest.kt +++ b/tests/tests/appop/src/android/app/appops/cts/AttributionTest.kt @@ -111,6 +111,7 @@ class AttributionTest { .isEqualTo(before.attributedOpEntries[ATTRIBUTION_3]!! .getLastAccessTime(OP_FLAGS_ALL)) } + runCommand("pm uninstall $PKG") } @Test diff --git a/tests/tests/appop/src/android/app/appops/cts/RuntimeMessageCollectionTest.kt b/tests/tests/appop/src/android/app/appops/cts/RuntimeMessageCollectionTest.kt index 6bbabda646c..d7ad0aaac40 100644 --- a/tests/tests/appop/src/android/app/appops/cts/RuntimeMessageCollectionTest.kt +++ b/tests/tests/appop/src/android/app/appops/cts/RuntimeMessageCollectionTest.kt @@ -22,6 +22,7 @@ import androidx.test.platform.app.InstrumentationRegistry import com.google.common.truth.Truth.assertThat import org.junit.Assert.fail import org.junit.Before +import org.junit.After; import org.junit.Test import java.lang.Thread.sleep @@ -49,6 +50,11 @@ class RuntimeMessageCollectionTest { installApk("CtsAppToCollect.apk") } + @After + fun uninstallTestApp() { + runCommand("pm uninstall $APP_PKG") + } + @Test fun collectAsyncStackTrace() { installApk("CtsAppToCollect.apk") diff --git a/tests/tests/bluetooth/AndroidTest.xml b/tests/tests/bluetooth/AndroidTest.xml index 58b6c1dc3c9..6adb8db6bb6 100644 --- a/tests/tests/bluetooth/AndroidTest.xml +++ b/tests/tests/bluetooth/AndroidTest.xml @@ -27,6 +27,12 @@ <option name="test-file-name" value="CtsBluetoothTestCases.apk" /> </target_preparer> <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> + <option name="throw-if-cmd-fail" value="true" /> + <option name="run-command" value="setprop persist.log.tag.bluetooth VERBOSE" /> + <!-- In order for the setprop to take effect everywhere, we need to toggle OFF Bluetooth --> + <option name="run-command" value="settings put global ble_scan_always_enabled 0"/> + <option name="run-command" value="cmd bluetooth_manager disable" /> + <option name="run-command" value="cmd bluetooth_manager wait-for-state:STATE_OFF" /> <option name="run-command" value="cmd bluetooth_manager enable" /> <option name="run-command" value="cmd bluetooth_manager wait-for-state:STATE_ON" /> <option name="teardown-command" value="cmd bluetooth_manager disable" /> diff --git a/tests/tests/bluetooth/OWNERS b/tests/tests/bluetooth/OWNERS index 536cb7e9cb5..8ac713e3dc3 100644 --- a/tests/tests/bluetooth/OWNERS +++ b/tests/tests/bluetooth/OWNERS @@ -1,6 +1,7 @@ # Bug component: 27441 -licorne@google.com -sattiraju@google.com +muhammadfalam@google.com + +girardier@google.com siyuanh@google.com wescande@google.com diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothAdapterTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothAdapterTest.java index d274cbe8d33..c1a96b66bb1 100644 --- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothAdapterTest.java +++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothAdapterTest.java @@ -514,18 +514,10 @@ public class BluetoothAdapterTest { assertFalse(mAdapter.registerBluetoothConnectionCallback(executor, null)); assertFalse(mAdapter.unregisterBluetoothConnectionCallback(null)); - assertTrue(BTAdapterUtils.enableAdapter(mAdapter, mContext)); - - // Verify throws SecurityException without permission.BLUETOOTH_PRIVILEGED - assertThrows(SecurityException.class, - () -> mAdapter.registerBluetoothConnectionCallback(executor, callback)); + assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext)); - mUiAutomation.dropShellPermissionIdentity(); - // Verify throws SecurityException without permission.BLUETOOTH_CONNECT - assertThrows(SecurityException.class, () -> - mAdapter.registerBluetoothConnectionCallback(executor, callback)); - assertThrows(SecurityException.class, () -> - mAdapter.unregisterBluetoothConnectionCallback(callback)); + assertFalse(mAdapter.registerBluetoothConnectionCallback(executor, callback)); + assertTrue(mAdapter.unregisterBluetoothConnectionCallback(callback)); } @Test diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/HearingAidProfileTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/HearingAidProfileTest.java index 96b0c847d97..a1370aaa254 100644 --- a/tests/tests/bluetooth/src/android/bluetooth/cts/HearingAidProfileTest.java +++ b/tests/tests/bluetooth/src/android/bluetooth/cts/HearingAidProfileTest.java @@ -20,20 +20,24 @@ import static android.Manifest.permission.BLUETOOTH_CONNECT; import static android.Manifest.permission.BLUETOOTH_PRIVILEGED; import static android.Manifest.permission.BLUETOOTH_SCAN; -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 com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + import static org.junit.Assert.assertThrows; -import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.verify; import android.app.UiAutomation; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHearingAid; import android.bluetooth.BluetoothHearingAid.AdvertisementServiceData; -import android.bluetooth.BluetoothManager; import android.bluetooth.BluetoothProfile; +import android.bluetooth.test_utils.BlockingBluetoothAdapter; +import android.bluetooth.test_utils.EnableBluetoothRule; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -45,137 +49,98 @@ import android.util.Log; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.platform.app.InstrumentationRegistry; +import com.android.compatibility.common.util.AdoptShellPermissionsRule; import com.android.compatibility.common.util.CddTest; -import org.junit.After; import org.junit.Assume; import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; -import java.util.ArrayList; -import java.util.Arrays; +import java.time.Duration; import java.util.List; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.Condition; -import java.util.concurrent.locks.ReentrantLock; - -/** - * Unit test cases for {@link BluetoothHearingAid}. - * <p> - * To run the test, use adb shell am instrument -e class 'android.bluetooth.HearingAidProfileTest' - * -w 'com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner' - */ + +/** Unit test cases for {@link BluetoothHearingAid}. */ @RunWith(AndroidJUnit4.class) public class HearingAidProfileTest { - private static final String TAG = "HearingAidProfileTest"; - - private static final int WAIT_FOR_INTENT_TIMEOUT_MS = 10000; // ms to wait for intent callback - private static final int PROXY_CONNECTION_TIMEOUT_MS = 500; // ms timeout for Proxy Connect - // ADAPTER_ENABLE_TIMEOUT_MS = AdapterState.BLE_START_TIMEOUT_DELAY + - // AdapterState.BREDR_START_TIMEOUT_DELAY - private static final int ADAPTER_ENABLE_TIMEOUT_MS = 8000; - // ADAPTER_DISABLE_TIMEOUT_MS = AdapterState.BLE_STOP_TIMEOUT_DELAY + - // AdapterState.BREDR_STOP_TIMEOUT_DELAY - private static final int ADAPTER_DISABLE_TIMEOUT_MS = 5000; - private static final String FAKE_REMOTE_ADDRESS = "00:11:22:AA:BB:CC"; - - private Context mContext; - private BluetoothHearingAid mService; - private BluetoothAdapter mBluetoothAdapter; - private BroadcastReceiver mIntentReceiver; - private UiAutomation mUiAutomation;; - - private Condition mConditionProfileConnection; - private ReentrantLock mProfileConnectionlock; - private boolean mIsProfileReady; - private AdvertisementServiceData mAdvertisementData; + private static final String TAG = HearingAidProfileTest.class.getSimpleName(); - private static List<Integer> mValidConnectionStates = new ArrayList<Integer>( - Arrays.asList(BluetoothProfile.STATE_CONNECTING, BluetoothProfile.STATE_CONNECTED, - BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.STATE_DISCONNECTING)); + @ClassRule public static final EnableBluetoothRule sEnableBluetooth = new EnableBluetoothRule(); - private List<BluetoothDevice> mIntentCallbackDeviceList; + @Rule + public final AdoptShellPermissionsRule mPermissionRule = + new AdoptShellPermissionsRule(sUiAutomation, BLUETOOTH_CONNECT); - @Before - public void setUp() throws Exception { - mContext = InstrumentationRegistry.getInstrumentation().getContext(); + private static final BluetoothAdapter sBluetoothAdapter = BlockingBluetoothAdapter.getAdapter(); + private static final Context sContext = + InstrumentationRegistry.getInstrumentation().getContext(); + private static final UiAutomation sUiAutomation = + InstrumentationRegistry.getInstrumentation().getUiAutomation(); - Assume.assumeTrue(TestUtils.isBleSupported(mContext)); - Assume.assumeTrue(TestUtils.isProfileEnabled(BluetoothProfile.HEARING_AID)); + private static final Duration PROXY_CONNECTION_TIMEOUT = Duration.ofMillis(500); + private static final Duration WAIT_FOR_INTENT_TIMEOUT = Duration.ofSeconds(1); + private static final BluetoothDevice sFakeDevice = + sBluetoothAdapter.getRemoteDevice("42:11:22:AA:BB:CC"); - mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation(); - mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT); + private static List<Integer> sValidConnectionStates = + List.of( + BluetoothProfile.STATE_CONNECTING, + BluetoothProfile.STATE_CONNECTED, + BluetoothProfile.STATE_DISCONNECTED, + BluetoothProfile.STATE_DISCONNECTING); - BluetoothManager manager = (BluetoothManager) mContext.getSystemService( - Context.BLUETOOTH_SERVICE); - mBluetoothAdapter = manager.getAdapter(); + private BluetoothHearingAid mService; - assertTrue(BTAdapterUtils.enableAdapter(mBluetoothAdapter, mContext)); - mProfileConnectionlock = new ReentrantLock(); - mConditionProfileConnection = mProfileConnectionlock.newCondition(); - mIsProfileReady = false; - mService = null; - mBluetoothAdapter.getProfileProxy(mContext, new HearingAidsServiceListener(), - BluetoothProfile.HEARING_AID); + private static final AdvertisementServiceData sAdvertisementData; + static { Parcel parcel = Parcel.obtain(); parcel.writeInt(0b110); // CSIP supported, MODE_BINAURAL, SIDE_LEFT parcel.writeInt(1); parcel.setDataPosition(0); - mAdvertisementData = AdvertisementServiceData.CREATOR.createFromParcel(parcel); - assertNotNull(mAdvertisementData); + sAdvertisementData = AdvertisementServiceData.CREATOR.createFromParcel(parcel); } - @After - public void tearDown() { - if (mUiAutomation != null) { - mUiAutomation.dropShellPermissionIdentity(); - } - } + @Mock BluetoothProfile.ServiceListener mServiceListener; - @CddTest(requirements = {"7.4.3/C-2-1", "7.4.3/C-3-2"}) - @Test - public void closeProfileProxy() { - assertTrue(waitForProfileConnect()); - assertNotNull(mService); - assertTrue(mIsProfileReady); + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + Assume.assumeTrue(TestUtils.isBleSupported(sContext)); + Assume.assumeTrue(TestUtils.isProfileEnabled(BluetoothProfile.HEARING_AID)); - mBluetoothAdapter.closeProfileProxy(BluetoothProfile.HEARING_AID, mService); - assertTrue(waitForProfileDisconnect()); - assertFalse(mIsProfileReady); + assertThat( + sBluetoothAdapter.getProfileProxy( + sContext, mServiceListener, BluetoothProfile.HEARING_AID)) + .isTrue(); + + ArgumentCaptor<BluetoothProfile> captor = ArgumentCaptor.forClass(BluetoothProfile.class); + verify(mServiceListener, timeout(PROXY_CONNECTION_TIMEOUT.toMillis())) + .onServiceConnected(eq(BluetoothProfile.HEARING_AID), captor.capture()); + mService = (BluetoothHearingAid) captor.getValue(); + assertThat(mService).isNotNull(); } - /** - * Basic test case to make sure that Hearing Aid Profile Proxy can connect. - */ @CddTest(requirements = {"7.4.3/C-2-1", "7.4.3/C-3-2"}) - @MediumTest @Test - public void getProxyServiceConnect() { - waitForProfileConnect(); - assertTrue(mIsProfileReady); - assertNotNull(mService); + public void closeProfileProxy() { + sBluetoothAdapter.closeProfileProxy(BluetoothProfile.HEARING_AID, mService); + verify(mServiceListener, timeout(PROXY_CONNECTION_TIMEOUT.toMillis())) + .onServiceDisconnected(eq(BluetoothProfile.HEARING_AID)); } - /** - * Basic test case to make sure that a fictional device is disconnected. - */ + /** Basic test case to make sure that a fictional device is disconnected. */ @CddTest(requirements = {"7.4.3/C-2-1", "7.4.3/C-3-2"}) @MediumTest @Test public void getConnectionState() { - waitForProfileConnect(); - assertTrue(mIsProfileReady); - assertNotNull(mService); - - // Create a fake device - BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(FAKE_REMOTE_ADDRESS); - assertNotNull(device); - - int connectionState = mService.getConnectionState(device); - // Fake device should be disconnected - assertEquals(connectionState, BluetoothProfile.STATE_DISCONNECTED); + assertThat(mService.getConnectionState(sFakeDevice)) + .isEqualTo(BluetoothProfile.STATE_DISCONNECTED); } /** @@ -186,74 +151,37 @@ public class HearingAidProfileTest { @MediumTest @Test public void setVolume() { - waitForProfileConnect(); - assertTrue(mIsProfileReady); - assertNotNull(mService); - // This should throw a SecurityException because no BLUETOOTH_PRIVILEGED permission assertThrows(SecurityException.class, () -> mService.setVolume(42)); } - /** - * Basic test case to make sure that a fictional device is unknown side. - */ + /** Basic test case to make sure that a fictional device is unknown side. */ @CddTest(requirements = {"7.4.3/C-2-1", "7.4.3/C-3-2"}) @MediumTest @Test public void getDeviceSide() { - waitForProfileConnect(); - assertTrue(mIsProfileReady); - assertNotNull(mService); - - // Create a fake device - final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(FAKE_REMOTE_ADDRESS); - assertNotNull(device); - - final int side = mService.getDeviceSide(device); - // Fake device should be no value, unknown side - assertEquals(BluetoothHearingAid.SIDE_UNKNOWN, side); + assertThat(mService.getDeviceSide(sFakeDevice)).isEqualTo(BluetoothHearingAid.SIDE_UNKNOWN); } - /** - * Basic test case to make sure that a fictional device is unknown mode. - */ + /** Basic test case to make sure that a fictional device is unknown mode. */ @CddTest(requirements = {"7.4.3/C-2-1", "7.4.3/C-3-2"}) @MediumTest @Test public void getDeviceMode() { - waitForProfileConnect(); - assertTrue(mIsProfileReady); - assertNotNull(mService); - - // Create a fake device - final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(FAKE_REMOTE_ADDRESS); - assertNotNull(device); - - final int mode = mService.getDeviceMode(device); - // Fake device should be no value, unknown mode - assertEquals(BluetoothHearingAid.MODE_UNKNOWN, mode); + assertThat(mService.getDeviceMode(sFakeDevice)).isEqualTo(BluetoothHearingAid.MODE_UNKNOWN); } /** - * Basic test case to make sure that a fictional device's ASHA Advertisement Service Data - * is null. + * Basic test case to make sure that a fictional device's ASHA Advertisement Service Data is + * null. */ @CddTest(requirements = {"7.4.3/C-2-1", "7.4.3/C-3-2"}) @MediumTest @Test public void getAdvertisementServiceData() { - waitForProfileConnect(); - assertTrue(mIsProfileReady); - assertNotNull(mService); - - // Create a fake device - final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(FAKE_REMOTE_ADDRESS); - assertNotNull(device); - TestUtils.adoptPermissionAsShellUid(BLUETOOTH_SCAN, BLUETOOTH_PRIVILEGED); - // Fake device should have no service data - assertNull(mService.getAdvertisementServiceData(device)); + assertThat(mService.getAdvertisementServiceData(sFakeDevice)).isNull(); } /** @@ -264,16 +192,7 @@ public class HearingAidProfileTest { @MediumTest @Test public void getAdvertisementDeviceMode() { - waitForProfileConnect(); - assertTrue(mIsProfileReady); - assertNotNull(mService); - - // Create a fake advertisement data - AdvertisementServiceData data = mAdvertisementData; - - final int mode = data.getDeviceMode(); - // Fake device should be MODE_BINAURAL - assertEquals(BluetoothHearingAid.MODE_BINAURAL, mode); + assertThat(sAdvertisementData.getDeviceMode()).isEqualTo(BluetoothHearingAid.MODE_BINAURAL); } /** @@ -284,37 +203,18 @@ public class HearingAidProfileTest { @MediumTest @Test public void getAdvertisementDeviceSide() { - waitForProfileConnect(); - assertTrue(mIsProfileReady); - assertNotNull(mService); - - // Create a fake advertisement data - AdvertisementServiceData data = mAdvertisementData; - - final int side = data.getDeviceSide(); - // Fake device should be SIDE_LEFT - assertEquals(BluetoothHearingAid.SIDE_LEFT, side); + assertThat(sAdvertisementData.getDeviceSide()).isEqualTo(BluetoothHearingAid.SIDE_LEFT); } /** - * Basic test case to make sure that a fictional device's truncated HiSyncId is the - * expected value. + * Basic test case to make sure that a fictional device's truncated HiSyncId is the expected + * value. */ @CddTest(requirements = {"7.4.3/C-2-1", "7.4.3/C-3-2"}) @MediumTest @Test public void getTruncatedHiSyncId() { - waitForProfileConnect(); - assertTrue(mIsProfileReady); - assertNotNull(mService); - - // Create a fake advertisement data - AdvertisementServiceData data = mAdvertisementData; - assertNotNull(data); - - final int id = data.getTruncatedHiSyncId(); - // Fake device should be supported - assertEquals(1, id); + assertThat(sAdvertisementData.getTruncatedHiSyncId()).isEqualTo(1); } /** @@ -325,17 +225,7 @@ public class HearingAidProfileTest { @MediumTest @Test public void isCsipSupported() { - waitForProfileConnect(); - assertTrue(mIsProfileReady); - assertNotNull(mService); - - // Create a fake advertisement data - AdvertisementServiceData data = mAdvertisementData; - assertNotNull(data); - - final boolean supported = data.isCsipSupported(); - // Fake device should be supported - assertEquals(true, supported); + assertThat(sAdvertisementData.isCsipSupported()).isTrue(); } /** @@ -346,14 +236,6 @@ public class HearingAidProfileTest { @MediumTest @Test public void isLikelyPairOfBluetoothHearingAid() { - waitForProfileConnect(); - assertTrue(mIsProfileReady); - assertNotNull(mService); - - // Create a fake advertisement data - final AdvertisementServiceData data = mAdvertisementData; - assertNotNull(data); - // Create another fake advertisement data Parcel parcel = Parcel.obtain(); parcel.writeInt(0b111); // CSIP supported, MODE_BINAURAL, SIDE_RIGHT @@ -361,31 +243,21 @@ public class HearingAidProfileTest { parcel.setDataPosition(0); AdvertisementServiceData dataOtherSide = AdvertisementServiceData.CREATOR.createFromParcel(parcel); - assertNotNull(dataOtherSide); + assertThat(dataOtherSide).isNotNull(); - final boolean isLikelyPair = data.isInPairWith(dataOtherSide); // two devices should be a pair - assertEquals(true, isLikelyPair); + assertThat(sAdvertisementData.isInPairWith(dataOtherSide)).isTrue(); } - /** - * Basic test case to get the list of connected Hearing Aid devices. - */ + /** Basic test case to get the list of connected Hearing Aid devices. */ @CddTest(requirements = {"7.4.3/C-2-1", "7.4.3/C-3-2"}) @MediumTest @Test public void getConnectedDevices() { - waitForProfileConnect(); - assertTrue(mIsProfileReady); - assertNotNull(mService); - - List<BluetoothDevice> deviceList; - - deviceList = mService.getConnectedDevices(); + List<BluetoothDevice> deviceList = mService.getConnectedDevices(); Log.d(TAG, "getConnectedDevices(): size=" + deviceList.size()); for (BluetoothDevice device : deviceList) { - int connectionState = mService.getConnectionState(device); - checkValidConnectionState(connectionState); + assertThat(mService.getConnectionState(device)).isIn(sValidConnectionStates); } } @@ -397,18 +269,15 @@ public class HearingAidProfileTest { @MediumTest @Test public void getDevicesMatchingConnectionStates() { - waitForProfileConnect(); - assertTrue(mIsProfileReady); - assertNotNull(mService); - - for (int connectionState : mValidConnectionStates) { - List<BluetoothDevice> deviceList; - - deviceList = mService.getDevicesMatchingConnectionStates(new int[]{connectionState}); - assertNotNull(deviceList); - Log.d(TAG, "getDevicesMatchingConnectionStates(" + connectionState + "): size=" - + deviceList.size()); - checkDeviceListAndStates(deviceList, connectionState); + for (int connectionState : sValidConnectionStates) { + List<BluetoothDevice> deviceList = + mService.getDevicesMatchingConnectionStates(new int[] {connectionState}); + assertThat(deviceList).isNotNull(); + for (BluetoothDevice device : deviceList) { + assertWithMessage("Mismatched connection state for device=" + device) + .that(mService.getConnectionState(device)) + .isEqualTo(connectionState); + } } } @@ -420,158 +289,35 @@ public class HearingAidProfileTest { @MediumTest @Test public void getConnectionStateChangedIntent() { - waitForProfileConnect(); - assertTrue(mIsProfileReady); - assertNotNull(mService); - - // Find out how many Hearing Aid bonded devices - List<BluetoothDevice> bondedDeviceList = new ArrayList(); - int numDevices = 0; - for (int connectionState : mValidConnectionStates) { - List<BluetoothDevice> deviceList; - - deviceList = mService.getDevicesMatchingConnectionStates(new int[]{connectionState}); - bondedDeviceList.addAll(deviceList); - numDevices += deviceList.size(); - } - - if (numDevices <= 0) return; - Log.d(TAG, "Number Hearing Aids devices bonded=" + numDevices); - - mIntentCallbackDeviceList = new ArrayList(); - - // Set up the Connection State Changed receiver - IntentFilter filter = new IntentFilter(); - filter.addAction(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED); - mIntentReceiver = new HearingAidIntentReceiver(); - mContext.registerReceiver(mIntentReceiver, filter); - - Log.d(TAG, "getConnectionStateChangedIntent: disable adapter and wait"); - assertTrue(BTAdapterUtils.disableAdapter(mBluetoothAdapter, mContext)); - - Log.d(TAG, "getConnectionStateChangedIntent: enable adapter and wait"); - assertTrue(BTAdapterUtils.enableAdapter(mBluetoothAdapter, mContext)); - - int sanityCount = WAIT_FOR_INTENT_TIMEOUT_MS; - while ((numDevices != mIntentCallbackDeviceList.size()) && (sanityCount > 0)) { - final int SLEEP_QUANTUM_MS = 100; - sleep(SLEEP_QUANTUM_MS); - sanityCount -= SLEEP_QUANTUM_MS; - } - - // Tear down - mContext.unregisterReceiver(mIntentReceiver); - - Log.d(TAG, "getConnectionStateChangedIntent: number of bonded device=" - + numDevices + ", mIntentCallbackDeviceList.size()=" - + mIntentCallbackDeviceList.size()); - for (BluetoothDevice device : mIntentCallbackDeviceList) { - assertTrue(bondedDeviceList.contains(device)); - } - } - - private boolean waitForProfileConnect() { - mProfileConnectionlock.lock(); + List<BluetoothDevice> bondedDeviceList = + mService.getDevicesMatchingConnectionStates( + sValidConnectionStates.stream().mapToInt(Integer::intValue).toArray()); + + int numDevices = bondedDeviceList.size(); + Assume.assumeTrue(numDevices > 0); + + BroadcastReceiver mockReceiver = mock(BroadcastReceiver.class); + sContext.registerReceiver( + mockReceiver, + new IntentFilter(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED)); + ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class); try { - // Wait for the Adapter to be disabled - while (!mIsProfileReady) { - if (!mConditionProfileConnection.await( - PROXY_CONNECTION_TIMEOUT_MS, TimeUnit.MILLISECONDS)) { - // Timeout - Log.e(TAG, "Timeout while waiting for Profile Connect"); - break; - } // else spurious wakeups - } - } catch(InterruptedException e) { - Log.e(TAG, "waitForProfileConnect: interrrupted"); - } finally { - mProfileConnectionlock.unlock(); - } - return mIsProfileReady; - } + assertThat(BlockingBluetoothAdapter.disable(true)).isTrue(); + assertThat(BlockingBluetoothAdapter.enable()).isTrue(); - private boolean waitForProfileDisconnect() { - mConditionProfileConnection = mProfileConnectionlock.newCondition(); - mProfileConnectionlock.lock(); - try { - while (mIsProfileReady) { - if (!mConditionProfileConnection.await( - PROXY_CONNECTION_TIMEOUT_MS, TimeUnit.MILLISECONDS)) { - // Timeout - Log.e(TAG, "Timeout while waiting for Profile Disconnect"); - break; - } // else spurious wakeups - } - } catch (InterruptedException e) { - Log.e(TAG, "waitForProfileDisconnect: interrrupted"); + verify(mockReceiver, timeout(WAIT_FOR_INTENT_TIMEOUT.toMillis()).times(numDevices)) + .onReceive(any(), captor.capture()); } finally { - mProfileConnectionlock.unlock(); - } - return !mIsProfileReady; - } - - private final class HearingAidsServiceListener - implements BluetoothProfile.ServiceListener { - - public void onServiceConnected(int profile, BluetoothProfile proxy) { - mProfileConnectionlock.lock(); - mService = (BluetoothHearingAid) proxy; - mIsProfileReady = true; - try { - mConditionProfileConnection.signal(); - } finally { - mProfileConnectionlock.unlock(); - } - } - - public void onServiceDisconnected(int profile) { - mProfileConnectionlock.lock(); - mIsProfileReady = false; - mService = null; - try { - mConditionProfileConnection.signal(); - } finally { - mProfileConnectionlock.unlock(); - } - } - } - - private class HearingAidIntentReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - if (BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED.equals(intent.getAction())) { - int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1); - int previousState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1); - BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); - - Log.d(TAG,"HearingAidIntentReceiver.onReceive: device=" + device - + ", state=" + state + ", previousState=" + previousState); - - checkValidConnectionState(state); - checkValidConnectionState(previousState); - - mIntentCallbackDeviceList.add(device); - } + sContext.unregisterReceiver(mockReceiver); } - } - private void checkDeviceListAndStates(List<BluetoothDevice> deviceList, int connectionState) { - Log.d(TAG, "checkDeviceListAndStates(): size=" + deviceList.size() - + ", connectionState=" + connectionState); - for (BluetoothDevice device : deviceList) { - int deviceConnectionState = mService.getConnectionState(device); - assertEquals("Mismatched connection state for " + device, - connectionState, deviceConnectionState); + for (Intent intent : captor.getAllValues()) { + assertThat(intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1)) + .isIn(sValidConnectionStates); + assertThat(intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1)) + .isIn(sValidConnectionStates); + assertThat((BluetoothDevice) intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)) + .isIn(bondedDeviceList); } } - - private void checkValidConnectionState(int connectionState) { - assertTrue(mValidConnectionStates.contains(connectionState)); - } - - private static void sleep(long t) { - try { - Thread.sleep(t); - } catch (InterruptedException e) {} - } } diff --git a/tests/tests/display/src/android/display/cts/VirtualDisplayTest.java b/tests/tests/display/src/android/display/cts/VirtualDisplayTest.java index a93a6a683b2..2f7f7d97ba5 100644 --- a/tests/tests/display/src/android/display/cts/VirtualDisplayTest.java +++ b/tests/tests/display/src/android/display/cts/VirtualDisplayTest.java @@ -560,7 +560,7 @@ public class VirtualDisplayTest { } private final class RotationChangeWaiter { - private static final int DISPLAY_CHANGE_TIMEOUT_SECS = 1; + private static final int DISPLAY_CHANGE_TIMEOUT_SECS = 3; private final Display mDisplay; private int mCurrentRotation; diff --git a/tests/tests/graphics/jni/ImageReaderTestHelpers.cpp b/tests/tests/graphics/jni/ImageReaderTestHelpers.cpp index 3bda711a941..15fbe3cc52b 100644 --- a/tests/tests/graphics/jni/ImageReaderTestHelpers.cpp +++ b/tests/tests/graphics/jni/ImageReaderTestHelpers.cpp @@ -41,7 +41,7 @@ ImageReaderHelper::~ImageReaderHelper() { int ImageReaderHelper::initImageReader() { if (mImgReader != nullptr || mImgReaderAnw != nullptr) { - ALOGE("Cannot re-initalize image reader, mImgReader=%p, mImgReaderAnw=%p", + ALOGE("Cannot reinitialize image reader, mImgReader=%p, mImgReaderAnw=%p", mImgReader, mImgReaderAnw); return -1; } @@ -84,9 +84,9 @@ int ImageReaderHelper::getBufferFromCurrentImage(AHardwareBuffer **outBuffer) { if (ret != AMEDIA_OK || outImage == nullptr) { // When the BufferQueue is in async mode, it is still possible that // AImageReader_acquireNextImage returns nothing after onFrameAvailable. - ALOGW("Failed to acquire image, ret=%d, outIamge=%p.", ret, outImage); + ALOGW("Failed to acquire image, ret=%d, outImage=%p.", ret, outImage); } else { - // Any exisitng in mAcquiredImage will be deleted and released + // Any existing in mAcquiredImage will be deleted and released // automatically. mAcquiredImage.reset(outImage); } diff --git a/tests/tests/graphics/jni/ImageReaderTestHelpers.h b/tests/tests/graphics/jni/ImageReaderTestHelpers.h index dcd08d95c1d..2196aca57f8 100644 --- a/tests/tests/graphics/jni/ImageReaderTestHelpers.h +++ b/tests/tests/graphics/jni/ImageReaderTestHelpers.h @@ -48,7 +48,7 @@ private: uint32_t mMaxImages; std::mutex mMutex; - // Number of images that's avaiable for acquire. + // Number of images that are available to be acquired. size_t mAvailableImages{0}; // Although AImageReader supports acquiring multiple images at a time, we // don't really need it in this test. We only acquire one image that a time. diff --git a/tests/tests/graphics/res/drawable/vector_icon_render_order_1.xml b/tests/tests/graphics/res/drawable/vector_icon_render_order_1.xml index d4472e2ec6c..97dfac993e8 100644 --- a/tests/tests/graphics/res/drawable/vector_icon_render_order_1.xml +++ b/tests/tests/graphics/res/drawable/vector_icon_render_order_1.xml @@ -38,7 +38,7 @@ android:pathData="@string/rectangle200" /> <group - android:name="ThridLevelGroup1" + android:name="ThirdLevelGroup1" android:translateX="-100.0" android:translateY="50.0" > <path @@ -47,7 +47,7 @@ android:pathData="@string/rectangle200" /> </group> <group - android:name="ThridLevelGroup2" + android:name="ThirdLevelGroup2" android:translateX="100.0" android:translateY="50.0" > <path @@ -66,7 +66,7 @@ android:pathData="@string/rectangle200" /> <group - android:name="ThridLevelGroup3" + android:name="ThirdLevelGroup3" android:translateX="-100.0" android:translateY="50.0" > <path @@ -75,7 +75,7 @@ android:pathData="@string/rectangle200" /> </group> <group - android:name="ThridLevelGroup4" + android:name="ThirdLevelGroup4" android:translateX="100.0" android:translateY="50.0" > <path diff --git a/tests/tests/graphics/res/drawable/vector_icon_render_order_2.xml b/tests/tests/graphics/res/drawable/vector_icon_render_order_2.xml index 6fcb3552057..964047a8e83 100644 --- a/tests/tests/graphics/res/drawable/vector_icon_render_order_2.xml +++ b/tests/tests/graphics/res/drawable/vector_icon_render_order_2.xml @@ -33,7 +33,7 @@ android:pathData="@string/rectangle200" /> <group - android:name="ThridLevelGroup1" + android:name="ThirdLevelGroup1" android:translateX="-100.0" android:translateY="50.0" > <path @@ -42,7 +42,7 @@ android:pathData="@string/rectangle200" /> </group> <group - android:name="ThridLevelGroup2" + android:name="ThirdLevelGroup2" android:translateX="100.0" android:translateY="50.0" > <path @@ -61,7 +61,7 @@ android:pathData="@string/rectangle200" /> <group - android:name="ThridLevelGroup3" + android:name="ThirdLevelGroup3" android:translateX="-100.0" android:translateY="50.0" > <path @@ -70,7 +70,7 @@ android:pathData="@string/rectangle200" /> </group> <group - android:name="ThridLevelGroup4" + android:name="ThirdLevelGroup4" android:translateX="100.0" android:translateY="50.0" > <path diff --git a/tests/tests/graphics/src/android/graphics/cts/NinePatchTest.java b/tests/tests/graphics/src/android/graphics/cts/NinePatchTest.java index f829e808097..52ae7c248dc 100644 --- a/tests/tests/graphics/src/android/graphics/cts/NinePatchTest.java +++ b/tests/tests/graphics/src/android/graphics/cts/NinePatchTest.java @@ -47,8 +47,8 @@ public class NinePatchTest { private static final int ALPHA_OPAQUE = 0xFF; private static final String NAME = "TESTNAME"; private static final int WIDTH = 80; - private static final int HEIGTH = 120; - private static final int[] COLOR = new int[WIDTH * HEIGTH]; + private static final int HEIGHT = 120; + private static final int[] COLOR = new int[WIDTH * HEIGHT]; private NinePatch mNinePatch; private Bitmap mBitmap; diff --git a/tests/tests/graphics/src/android/graphics/cts/PaintTest.java b/tests/tests/graphics/src/android/graphics/cts/PaintTest.java index 86e7400bfea..d4e4d522c56 100644 --- a/tests/tests/graphics/src/android/graphics/cts/PaintTest.java +++ b/tests/tests/graphics/src/android/graphics/cts/PaintTest.java @@ -1810,7 +1810,7 @@ public class PaintTest { } @Test - public void testEqualsForTextMeasurment() { + public void testEqualsForTextMeasurement() { Paint p1 = new Paint(); Paint p2 = new Paint(); @@ -1818,7 +1818,7 @@ public class PaintTest { } @Test - public void testEqualsForTextMeasurment_textSize() { + public void testEqualsForTextMeasurement_textSize() { Paint p1 = new Paint(); Paint p2 = new Paint(); @@ -1829,7 +1829,7 @@ public class PaintTest { } @Test - public void testEqualsForTextMeasurment_textSkew() { + public void testEqualsForTextMeasurement_textSkew() { Paint p1 = new Paint(); Paint p2 = new Paint(); @@ -1840,7 +1840,7 @@ public class PaintTest { } @Test - public void testEqualsForTextMeasurment_textScale() { + public void testEqualsForTextMeasurement_textScale() { Paint p1 = new Paint(); Paint p2 = new Paint(); @@ -1851,7 +1851,7 @@ public class PaintTest { } @Test - public void testEqualsForTextMeasurment_letterSpacing() { + public void testEqualsForTextMeasurement_letterSpacing() { Paint p1 = new Paint(); Paint p2 = new Paint(); @@ -1862,7 +1862,7 @@ public class PaintTest { } @Test - public void testEqualsForTextMeasurment_localeList() { + public void testEqualsForTextMeasurement_localeList() { Paint p1 = new Paint(); Paint p2 = new Paint(); @@ -1876,7 +1876,7 @@ public class PaintTest { } @Test - public void testEqualsForTextMeasurment_typeface() { + public void testEqualsForTextMeasurement_typeface() { Paint p1 = new Paint(); Paint p2 = new Paint(); diff --git a/tests/tests/graphics/src/android/graphics/cts/TypefaceTest.java b/tests/tests/graphics/src/android/graphics/cts/TypefaceTest.java index dd4f3e14e57..c3335b45631 100644 --- a/tests/tests/graphics/src/android/graphics/cts/TypefaceTest.java +++ b/tests/tests/graphics/src/android/graphics/cts/TypefaceTest.java @@ -564,7 +564,7 @@ public class TypefaceTest { @Test public void testTypeface_SupportedCmapEncodingTest() { - // We support the following combinations of cmap platfrom/endcoding pairs. + // We support the following combinations of cmap platform/endcoding pairs. String[] fontPaths = { // Platform ID == 0, Encoding ID == 0 "fonts/cmap_selection/CmapPlatform0Encoding0.ttf", @@ -673,7 +673,7 @@ public class TypefaceTest { assertEquals(GLYPH_3EM_WIDTH, measureText("b", italicFamily), 0f); assertEquals(GLYPH_1EM_WIDTH, measureText("c", italicFamily), 0f); - // Draw with the italic font which weigth is 700. + // Draw with the italic font and weight set to 700. final Typeface boldItalicFamily = Typeface.create(family, 700 /* weight */, true /* italic */); assertEquals(GLYPH_1EM_WIDTH, measureText("a", boldItalicFamily), 0f); diff --git a/tests/tests/graphics/src/android/graphics/cts/VulkanFeaturesTest.java b/tests/tests/graphics/src/android/graphics/cts/VulkanFeaturesTest.java index 969fcf89900..f87c5c8a041 100644 --- a/tests/tests/graphics/src/android/graphics/cts/VulkanFeaturesTest.java +++ b/tests/tests/graphics/src/android/graphics/cts/VulkanFeaturesTest.java @@ -235,6 +235,7 @@ public class VulkanFeaturesTest { private JSONObject mVkJSON = null; private JSONObject mVulkanDevices[]; private JSONObject mBestDevice = null; + private boolean mIsTV = false; @Before public void setup() throws Throwable { @@ -262,6 +263,8 @@ public class VulkanFeaturesTest { if (DEBUG) { Log.d(TAG, feature.name + "=" + feature.version); } + } else if (PackageManager.FEATURE_LEANBACK.equals(feature.name)) { + mIsTV = true; } } } @@ -501,6 +504,7 @@ public class VulkanFeaturesTest { @Test public void testAndroidBaselineProfile2021Support() throws JSONException { assumeTrue("Skipping because Vulkan is not supported", mVulkanHardwareVersion != null); + assumeTrue("Skipping because ABP is not required of TV devices", !mIsTV); if (!hasOnlyCpuDevice()) { assertEquals("This device must support the ABP 2021.", "", nativeGetABPSupport()); diff --git a/tests/tests/graphics/src/android/graphics/cts/YuvImageTest.java b/tests/tests/graphics/src/android/graphics/cts/YuvImageTest.java index 744fd8b4528..ecdc5bd4cf6 100644 --- a/tests/tests/graphics/src/android/graphics/cts/YuvImageTest.java +++ b/tests/tests/graphics/src/android/graphics/cts/YuvImageTest.java @@ -183,7 +183,7 @@ public class YuvImageTest { // test if handling compression parameters correctly verifyParameters(); - // test various cases by varing + // test various cases by varying // <ImageFormat, Bitmap, HasPaddings, Rect> for (int i = 0; i < JPEG_FORMATS.length; ++i) { for (int j = 0; j < mTestBitmaps.length; ++j) { @@ -524,7 +524,7 @@ public class YuvImageTest { } // Compress rect1 in testBitmap and rect2 in image. - // Then, compare the two resutls to check their MSE. + // Then, compare the two results to check their MSE. private void compressRects(Bitmap testBitmap, YuvImage image, Rect rect1, Rect rect2) { Bitmap expected = null; diff --git a/tests/tests/graphics/src/android/graphics/cts/utils/CamUtils.java b/tests/tests/graphics/src/android/graphics/cts/utils/CamUtils.java index fe129e868aa..75af2705240 100644 --- a/tests/tests/graphics/src/android/graphics/cts/utils/CamUtils.java +++ b/tests/tests/graphics/src/android/graphics/cts/utils/CamUtils.java @@ -34,7 +34,7 @@ import android.graphics.Color; * * <p>XYZ is commonly used as an intermediate color space for converting between one color space to * another. For example, to convert RGB to L*a*b*, first RGB is converted to XYZ, then XYZ is - * convered to L*a*b*. + * converted to L*a*b*. * * <p>sRGB is a "specification originated from work in 1990s through cooperation by Hewlett-Packard * and Microsoft, and it was designed to be a standard definition of RGB for the internet, which it diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/PictureDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/PictureDrawableTest.java index 1142c20190d..9bb4962133c 100644 --- a/tests/tests/graphics/src/android/graphics/drawable/cts/PictureDrawableTest.java +++ b/tests/tests/graphics/src/android/graphics/drawable/cts/PictureDrawableTest.java @@ -65,7 +65,7 @@ public class PictureDrawableTest { // Check the color has been set. assertEquals(0xff0f0b0c, destBitmap.getPixel(10, 10)); pictureDrawable.draw(canvas); - // Check the target pixle's color, ensure it has been changed. + // Check the target pixel's color, ensure it has been changed. assertEquals(0xff0a0c0b, destBitmap.getPixel(10, 10)); } diff --git a/tests/tests/graphics/src/android/graphics/text/cts/LineBreakerTest.java b/tests/tests/graphics/src/android/graphics/text/cts/LineBreakerTest.java index dc90130d513..d1dd3419e93 100644 --- a/tests/tests/graphics/src/android/graphics/text/cts/LineBreakerTest.java +++ b/tests/tests/graphics/src/android/graphics/text/cts/LineBreakerTest.java @@ -72,7 +72,7 @@ public class LineBreakerTest { } @Test - public void testSetBreakStrategy_shoulNotThrowExceptions() { + public void testSetBreakStrategy_shouldNotThrowExceptions() { assertNotNull(new LineBreaker.Builder().setBreakStrategy(BREAK_STRATEGY_SIMPLE).build()); assertNotNull(new LineBreaker.Builder().setBreakStrategy(BREAK_STRATEGY_HIGH_QUALITY) .build()); diff --git a/tests/tests/hibernation/src/android/hibernation/cts/AppHibernationUtils.kt b/tests/tests/hibernation/src/android/hibernation/cts/AppHibernationUtils.kt index c241120cb00..a1335370ee8 100644 --- a/tests/tests/hibernation/src/android/hibernation/cts/AppHibernationUtils.kt +++ b/tests/tests/hibernation/src/android/hibernation/cts/AppHibernationUtils.kt @@ -295,8 +295,12 @@ fun openUnusedAppsNotification() { val notifSelector = By.textContains("unused app") if (hasFeatureWatch()) { val uiAutomation = InstrumentationRegistry.getInstrumentation().uiAutomation - expandNotificationsWatch(UiAutomatorUtils2.getUiDevice()) - waitFindObject(uiAutomation, notifSelector).click() + val clickRunnable = object : Runnable { + override fun run () { + waitFindObject(uiAutomation, notifSelector).click() + } + } + expandAndClickNotificationWatch(UiAutomatorUtils2.getUiDevice(), clickRunnable) // In wear os, notification has one additional button to open it waitFindObject(uiAutomation, By.textContains("Open")).click() } else { @@ -341,12 +345,27 @@ fun hasFeatureAutomotive(): Boolean { PackageManager.FEATURE_AUTOMOTIVE) } -private fun expandNotificationsWatch(uiDevice: UiDevice) { +private fun expandAndClickNotificationWatch(uiDevice: UiDevice, clickRunnable: Runnable) { with(uiDevice) { wakeUp() - // Swipe up from bottom to reveal notifications val x = displayWidth / 2 + // Swipe up from bottom to reveal notifications swipe(x, displayHeight, x, 0, 1) + try { + clickRunnable.run() + return + } catch (e: Exception) { + // TODO(b/338772456) we catch the exception here since som watches have their + // notifications tray on the horizontal swipe. Find a way to find a solution that does + // not require vertical/horizontal swipe retries. + } + // Upwards swipe did not find notifications. Undo the upwards swipe, and try sideways. + swipe(x, 0, x, displayHeight, 5) + val y = displayHeight / 2 + swipe(0, y, displayWidth, y, 5) + + clickRunnable.run() + } } diff --git a/tests/tests/hibernation/src/android/hibernation/cts/AutoRevokeTest.kt b/tests/tests/hibernation/src/android/hibernation/cts/AutoRevokeTest.kt index f3448bfd8d1..9045ff5f1a2 100644 --- a/tests/tests/hibernation/src/android/hibernation/cts/AutoRevokeTest.kt +++ b/tests/tests/hibernation/src/android/hibernation/cts/AutoRevokeTest.kt @@ -636,13 +636,18 @@ class AutoRevokeTest { rowItem.findObject(uninstallSelector).click() } else { rowItem.click() - waitFindObject(By.text("Uninstall")).click() + try { + waitFindObject(By.text("Uninstall")).click() + } catch (e: Exception) { + // Some watch implementations do not have the "Uninstall" text and directly go + // to the uninstall confirmation screen, so it's ok to let this exception go. + } } } private fun clickUninstallOk() { val uninstallSelector = if (hasFeatureWatch()) { - By.desc("OK") + By.res(Pattern.compile(".*(button1|positive_button)")) } else { By.text("OK") } diff --git a/tests/tests/jni/Android.bp b/tests/tests/jni/Android.bp index 5aa391f2d4e..8055b1e5294 100644 --- a/tests/tests/jni/Android.bp +++ b/tests/tests/jni/Android.bp @@ -14,6 +14,9 @@ package { default_applicable_licenses: ["Android-Apache-2.0"], + // Team assignment based on libnativeloader and linker namespace ownership. + // Other JNI issues are owned by trendy_team_art_performance. + default_team: "trendy_team_art_mainline", } android_test { diff --git a/tests/tests/jvmti/Android.bp b/tests/tests/jvmti/Android.bp new file mode 100644 index 00000000000..8faed40643d --- /dev/null +++ b/tests/tests/jvmti/Android.bp @@ -0,0 +1,17 @@ +// Copyright (C) 2024 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + default_team: "trendy_team_art_performance", +} diff --git a/tests/tests/jvmti/OWNERS b/tests/tests/jvmti/OWNERS new file mode 100644 index 00000000000..131c53fbd08 --- /dev/null +++ b/tests/tests/jvmti/OWNERS @@ -0,0 +1,2 @@ +# Bug component: 1304319 +include platform/art:/OWNERS diff --git a/tests/tests/jvmti/attaching/OWNERS b/tests/tests/jvmti/attaching/OWNERS deleted file mode 100644 index 6e0629999c7..00000000000 --- a/tests/tests/jvmti/attaching/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -# Bug component: 86431 -include /hostsidetests/jvmti/run-tests/OWNERS diff --git a/tests/tests/keystore/CtsKeystorePerformanceTestManifest.xml b/tests/tests/keystore/CtsKeystorePerformanceTestManifest.xml index f7d7bd6a556..7971309e28e 100644 --- a/tests/tests/keystore/CtsKeystorePerformanceTestManifest.xml +++ b/tests/tests/keystore/CtsKeystorePerformanceTestManifest.xml @@ -25,8 +25,6 @@ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" android:targetPackage="android.keystore.cts.performance" android:label="CTS tests of android.keystore.cts.performance"> - <meta-data android:name="listener" - android:value="com.android.cts.runner.CtsTestRunListener" /> </instrumentation> </manifest> diff --git a/tests/tests/keystore/CtsKeystoreWycheproofTestManifest.xml b/tests/tests/keystore/CtsKeystoreWycheproofTestManifest.xml index 12e24f20986..803f96241c0 100644 --- a/tests/tests/keystore/CtsKeystoreWycheproofTestManifest.xml +++ b/tests/tests/keystore/CtsKeystoreWycheproofTestManifest.xml @@ -25,8 +25,6 @@ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" android:targetPackage="com.google.security.wycheproof" android:label="CTS tests of com.google.security.wycheproof"> - <meta-data android:name="listener" - android:value="com.android.cts.runner.CtsTestRunListener" /> </instrumentation> </manifest> diff --git a/tests/tests/keystore/src/android/keystore/cts/CipherTest.java b/tests/tests/keystore/src/android/keystore/cts/CipherTest.java index 9de76e00ab5..3e7aecbd794 100644 --- a/tests/tests/keystore/src/android/keystore/cts/CipherTest.java +++ b/tests/tests/keystore/src/android/keystore/cts/CipherTest.java @@ -40,6 +40,7 @@ import android.platform.test.annotations.RequiresFlagsDisabled; import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; +import android.security.keystore.KeyPermanentlyInvalidatedException; import android.security.keystore.KeyProperties; import android.security.keystore.KeyProtection; import android.server.wm.ActivityManagerTestBase; @@ -1366,6 +1367,32 @@ public class CipherTest { } @Test + public void testAuthBoundKeysKeyPermanentlyInvalidatedException() throws Exception { + assumeTrue(TestUtils.hasSecureLockScreen(getContext())); + + ImportedKey key = null; + try (DeviceLockSession dl = new DeviceLockSession()) { + KeyProtection importParams = + TestUtils.getMinimalWorkingImportParametersForCipheringWith(BASIC_ALGORITHMS[0], + KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT, + /* ivProvidedWhenEncrypting= */ false, + /* isUnlockedDeviceRequired= */ false, + /* isUserAuthRequired= */ true); + key = importDefaultKatKey(BASIC_ALGORITHMS[0], importParams); + assertTrue(TestUtils.keyExists(key.getAlias())); + } // DeviceLockSession#close() removes the secure lock screen. + + // Try to use the key after removal of secure screen lock screen. + KatVector testVector = KAT_VECTORS.get(BASIC_ALGORITHMS[0]); + Cipher cipher = Cipher.getInstance(BASIC_ALGORITHMS[0]); + Key encryptionKey = key.getKeystoreBackedEncryptionKey(); + // Removing the secure lock screen should have invalidated the auth-bound keys. + assertThrows(KeyPermanentlyInvalidatedException.class, () -> { + cipher.init(Cipher.ENCRYPT_MODE, encryptionKey); + }); + } + + @Test public void testInitDecryptFailsWhenNotAuthorizedToDecrypt() throws Exception { for (String transformation : EXPECTED_ALGORITHMS) { try { diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java index 6903e949696..5a42aa97837 100644 --- a/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java +++ b/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java @@ -32,9 +32,7 @@ import static android.keystore.cts.AuthorizationList.KM_PURPOSE_SIGN; import static android.keystore.cts.AuthorizationList.KM_PURPOSE_VERIFY; import static android.keystore.cts.RootOfTrust.KM_VERIFIED_BOOT_VERIFIED; import static android.security.keymaster.KeymasterDefs.KM_PURPOSE_AGREE_KEY; -import static android.security.keystore.KeyProperties.DIGEST_NONE; import static android.security.keystore.KeyProperties.DIGEST_SHA256; -import static android.security.keystore.KeyProperties.DIGEST_SHA512; import static android.security.keystore.KeyProperties.ENCRYPTION_PADDING_NONE; import static android.security.keystore.KeyProperties.ENCRYPTION_PADDING_RSA_OAEP; import static android.security.keystore.KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1; @@ -89,6 +87,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.bedstead.nene.TestApis; import com.android.bedstead.nene.permissions.PermissionContext; +import com.android.compatibility.common.util.CddTest; import com.android.compatibility.common.util.PropertyUtil; import com.google.common.collect.ImmutableSet; @@ -113,9 +112,11 @@ import java.security.cert.CertificateException; import java.security.cert.CertificateParsingException; import java.security.cert.X509Certificate; import java.security.spec.ECGenParameterSpec; +import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.HashSet; +import java.util.List; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -152,6 +153,7 @@ public class KeyAttestationTest { private static final int KM_ERROR_CANNOT_ATTEST_IDS = -66; private static final int KM_ERROR_INVALID_INPUT_LENGTH = -21; + private static final int KM_ERROR_UNKNOWN_ERROR = -1000; private static final int KM_ERROR_PERMISSION_DENIED = 6; private Context getContext() { @@ -281,16 +283,45 @@ public class KeyAttestationTest { } } + private void assertAttestationKeyMintError(KeyStoreException keyStoreException, + boolean devicePropertiesAttestation) { + int errorCode = keyStoreException.getErrorCode(); + List<Integer> expectedErrs = new ArrayList<Integer>(); + expectedErrs.add(KM_ERROR_INVALID_INPUT_LENGTH); + if (devicePropertiesAttestation) { + expectedErrs.add(KM_ERROR_CANNOT_ATTEST_IDS); + } + if (TestUtils.getVendorApiLevel() < 35) { + // b/337427860, some devices returns UNKNOWN_ERROR if large challenge is + // passed. So allow an extra error code for earlier devices. + expectedErrs.add(KM_ERROR_UNKNOWN_ERROR); + } + String assertMessage = String.format( + "The KeyMint implementation may only return INVALID_INPUT_LENGTH or " + + "CANNOT_ATTEST_IDSs as errors when the attestation challenge is " + + "too large (error code was %d, attestation properties %b)", + errorCode, devicePropertiesAttestation); + assertTrue(assertMessage, expectedErrs.contains(errorCode)); + } + private void assertPublicAttestationError(KeyStoreException keyStoreException, boolean devicePropertiesAttestation) { // Assert public failure information. int errorCode = keyStoreException.getNumericErrorCode(); + List<Integer> expectedErrs = new ArrayList<Integer>(); + expectedErrs.add(KeyStoreException.ERROR_INCORRECT_USAGE); + if (devicePropertiesAttestation) { + expectedErrs.add(KeyStoreException.ERROR_ID_ATTESTATION_FAILURE); + } + if (TestUtils.getVendorApiLevel() < 35) { + // b/337427860, some devices returns UNKNOWN_ERROR if large challenge is + // passed. So allow an extra error code for earlier devices. + expectedErrs.add(KeyStoreException.ERROR_KEYMINT_FAILURE); + } String assertMessage = String.format( "Error code was %d, device properties attestation? %b", errorCode, devicePropertiesAttestation); - assertTrue(assertMessage, KeyStoreException.ERROR_INCORRECT_USAGE == errorCode - || (devicePropertiesAttestation - && KeyStoreException.ERROR_ID_ATTESTATION_FAILURE == errorCode)); + assertTrue(assertMessage, expectedErrs.contains(errorCode)); assertFalse("Unexpected transient failure.", keyStoreException.isTransientFailure()); } @@ -319,16 +350,7 @@ public class KeyAttestationTest { fail("Attestation challenges larger than 128 bytes should be rejected"); } catch (ProviderException e) { KeyStoreException cause = (KeyStoreException) e.getCause(); - int errorCode = cause.getErrorCode(); - String assertMessage = String.format( - "The KeyMint implementation may only return INVALID_INPUT_LENGTH or " - + "CANNOT_ATTEST_IDSs as errors when the attestation challenge is " - + "too large (error code was %d, attestation properties %b)", - errorCode, devicePropertiesAttestation); - assertTrue(assertMessage, KM_ERROR_INVALID_INPUT_LENGTH == cause.getErrorCode() - || (devicePropertiesAttestation - && KM_ERROR_CANNOT_ATTEST_IDS == cause.getErrorCode()) - ); + assertAttestationKeyMintError(cause, devicePropertiesAttestation); assertPublicAttestationError(cause, devicePropertiesAttestation); } } @@ -355,7 +377,7 @@ public class KeyAttestationTest { Date consumptionEnd = new Date(now.getTime() + CONSUMPTION_TIME_OFFSET); KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN) .setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1")) - .setDigests(DIGEST_NONE, DIGEST_SHA256, DIGEST_SHA512) + .setDigests(TestUtils.getDigestsForKeyMintImplementation(isStrongBox)) .setAttestationChallenge(null) .setKeyValidityStart(now) .setKeyValidityForOriginationEnd(originationEnd) @@ -397,18 +419,13 @@ public class KeyAttestationTest { KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN) .setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1")) + .setDigests(TestUtils.getDigestsForKeyMintImplementation(expectStrongBox)) .setAttestationChallenge(new byte[128]) .setKeyValidityStart(now) .setKeyValidityForOriginationEnd(originationEnd) .setKeyValidityForConsumptionEnd(consumptionEnd) .setIsStrongBoxBacked(expectStrongBox); - if (expectStrongBox) { - builder.setDigests(DIGEST_NONE, DIGEST_SHA256); - } else { - builder.setDigests(DIGEST_NONE, DIGEST_SHA256, DIGEST_SHA512); - } - generateKeyPair(KEY_ALGORITHM_EC, builder.build()); KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); @@ -428,6 +445,7 @@ public class KeyAttestationTest { @RestrictedBuildTest @RequiresDevice @Test + @CddTest(requirements = {"9.10/C-0-1", "9.10/C-1-3"}) public void testEcAttestation_DeviceLocked() throws Exception { testEcAttestation_DeviceLocked(false /* expectStrongBox */); } @@ -435,6 +453,7 @@ public class KeyAttestationTest { @RestrictedBuildTest @RequiresDevice @Test + @CddTest(requirements = {"9.10/C-0-1", "9.10/C-1-3"}) public void testEcAttestation_DeviceLockedStrongbox() throws Exception { if (!TestUtils.hasStrongBox(getContext())) return; @@ -445,6 +464,7 @@ public class KeyAttestationTest { public void testAttestationKmVersionMatchesFeatureVersion() throws Exception { if (getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_PC)) return; + assumeTrue("Device does not support attestation", TestUtils.isAttestationSupported()); testAttestationKmVersionMatchesFeatureVersion(false); } @@ -453,6 +473,7 @@ public class KeyAttestationTest { public void testAttestationKmVersionMatchesFeatureVersionStrongBox() throws Exception { if (getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_PC)) return; + assumeTrue("Device does not support attestation", TestUtils.isAttestationSupported()); int keyStoreFeatureVersionStrongBox = TestUtils.getFeatureVersionKeystoreStrongBox(getContext()); @@ -467,7 +488,8 @@ public class KeyAttestationTest { } private void testAttestationKmVersionMatchesFeatureVersion(boolean isStrongBox) - throws Exception { + throws Exception { + assumeTrue("Device does not support attestation", TestUtils.isAttestationSupported()); String keystoreAlias = "test_key"; Date now = new Date(); @@ -505,7 +527,7 @@ public class KeyAttestationTest { // Feature Version is required on devices launching with Android 12 (API Level // 31) but may be reported on devices launching with an earlier version. If it's // present, it must match what is reported in attestation. - if (PropertyUtil.getFirstApiLevel() >= 31) { + if (TestUtils.getVendorApiLevel() >= 31) { assertNotEquals(0, keyStoreFeatureVersion); } if (keyStoreFeatureVersion != 0) { @@ -534,7 +556,7 @@ public class KeyAttestationTest { String keystoreAlias = "test_key"; KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN) .setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1")) - .setDigests(DIGEST_NONE, DIGEST_SHA256, DIGEST_SHA512) + .setDigests(TestUtils.getDigestsForKeyMintImplementation(isStrongBox)) .setAttestationChallenge(new byte[128]) .setUniqueIdIncluded(true) .setIsStrongBoxBacked(isStrongBox) @@ -579,7 +601,7 @@ public class KeyAttestationTest { String keystoreAlias = "test_key"; KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN) .setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1")) - .setDigests(DIGEST_NONE, DIGEST_SHA256, DIGEST_SHA512) + .setDigests(TestUtils.getDigestsForKeyMintImplementation(isStrongBox)) .setAttestationChallenge(new byte[128]) .setUniqueIdIncluded(true) .setIsStrongBoxBacked(isStrongBox) @@ -756,16 +778,7 @@ public class KeyAttestationTest { fail("Attestation challenges larger than 128 bytes should be rejected"); } catch(ProviderException e){ KeyStoreException cause = (KeyStoreException) e.getCause(); - int errorCode = cause.getErrorCode(); - String assertMessage = String.format( - "The KeyMint implementation may only return INVALID_INPUT_LENGTH or " - + "CANNOT_ATTEST_IDSs as errors when the attestation challenge is " - + "too large (error code was %d, attestation properties %b)", - errorCode, devicePropertiesAttestation); - assertTrue(assertMessage, KM_ERROR_INVALID_INPUT_LENGTH == cause.getErrorCode() - || (devicePropertiesAttestation - && KM_ERROR_CANNOT_ATTEST_IDS == cause.getErrorCode()) - ); + assertAttestationKeyMintError(cause, devicePropertiesAttestation); assertPublicAttestationError(cause, devicePropertiesAttestation); } } @@ -791,7 +804,7 @@ public class KeyAttestationTest { Date originationEnd = new Date(now.getTime() + ORIGINATION_TIME_OFFSET); Date consumptionEnd = new Date(now.getTime() + CONSUMPTION_TIME_OFFSET); KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN) - .setDigests(DIGEST_NONE, DIGEST_SHA256, DIGEST_SHA512) + .setDigests(TestUtils.getDigestsForKeyMintImplementation(isStrongBox)) .setAttestationChallenge(null) .setKeyValidityStart(now) .setKeyValidityForOriginationEnd(originationEnd) @@ -829,22 +842,16 @@ public class KeyAttestationTest { Date now = new Date(); Date originationEnd = new Date(now.getTime() + ORIGINATION_TIME_OFFSET); Date consumptionEnd = new Date(now.getTime() + CONSUMPTION_TIME_OFFSET); - KeyGenParameterSpec.Builder builder = - new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN) - .setDigests(DIGEST_NONE, DIGEST_SHA256, DIGEST_SHA512) - .setAttestationChallenge("challenge".getBytes()) - .setKeyValidityStart(now) - .setKeyValidityForOriginationEnd(originationEnd) - .setKeyValidityForConsumptionEnd(consumptionEnd) - .setIsStrongBoxBacked(expectStrongBox); - - if (expectStrongBox) { - builder.setDigests(DIGEST_NONE, DIGEST_SHA256); - } else { - builder.setDigests(DIGEST_NONE, DIGEST_SHA256, DIGEST_SHA512); - } + KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN) + .setDigests(TestUtils.getDigestsForKeyMintImplementation(expectStrongBox)) + .setAttestationChallenge("challenge".getBytes()) + .setKeyValidityStart(now) + .setKeyValidityForOriginationEnd(originationEnd) + .setKeyValidityForConsumptionEnd(consumptionEnd) + .setIsStrongBoxBacked(expectStrongBox) + .build(); - generateKeyPair(KEY_ALGORITHM_RSA, builder.build()); + generateKeyPair(KEY_ALGORITHM_RSA, spec); KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); keyStore.load(null); @@ -863,11 +870,13 @@ public class KeyAttestationTest { @RestrictedBuildTest @RequiresDevice // Emulators have no place to store the needed key @Test + @CddTest(requirements = {"9.10/C-0-1", "9.10/C-1-3"}) public void testRsaAttestation_DeviceLocked() throws Exception { testRsaAttestation_DeviceLocked(false /* expectStrongbox */); } @RestrictedBuildTest + @CddTest(requirements = {"9.10/C-0-1", "9.10/C-1-3"}) @RequiresDevice // Emulators have no place to store the needed key @Test public void testRsaAttestation_DeviceLockedStrongbox() throws Exception { @@ -1126,7 +1135,7 @@ public class KeyAttestationTest { KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(keystoreAlias, purposes) .setKeySize(keySize) - .setDigests(DIGEST_NONE, DIGEST_SHA256, DIGEST_SHA512) + .setDigests(TestUtils.getDigestsForKeyMintImplementation(isStrongBox)) .setAttestationChallenge(challenge) .setDevicePropertiesAttestationIncluded(devicePropertiesAttestation) .setIsStrongBoxBacked(isStrongBox); @@ -1201,7 +1210,7 @@ public class KeyAttestationTest { KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(keystoreAlias, purposes) .setAlgorithmParameterSpec(new ECGenParameterSpec(ecCurve)) - .setDigests(DIGEST_NONE, DIGEST_SHA256, DIGEST_SHA512) + .setDigests(TestUtils.getDigestsForKeyMintImplementation(isStrongBox)) .setAttestationChallenge(challenge) .setDevicePropertiesAttestationIncluded(devicePropertiesAttestation) .setIsStrongBoxBacked(isStrongBox); @@ -1314,8 +1323,11 @@ public class KeyAttestationTest { Date startTime, boolean includesValidityDates, boolean devicePropertiesAttestation, Attestation attestation) throws NoSuchAlgorithmException, NameNotFoundException { - checkKeyIndependentAttestationInfo(challenge, purposes, - ImmutableSet.of(KM_DIGEST_NONE, KM_DIGEST_SHA_2_256, KM_DIGEST_SHA_2_512), + Set digests = ImmutableSet.of(KM_DIGEST_NONE, KM_DIGEST_SHA_2_256, KM_DIGEST_SHA_2_512); + if (attestation.getAttestationSecurityLevel() == KM_SECURITY_LEVEL_STRONG_BOX) { + digests = ImmutableSet.of(KM_DIGEST_NONE, KM_DIGEST_SHA_2_256); + } + checkKeyIndependentAttestationInfo(challenge, purposes, digests, startTime, includesValidityDates, devicePropertiesAttestation, attestation); } diff --git a/tests/tests/keystore/src/android/keystore/cts/util/TestUtils.java b/tests/tests/keystore/src/android/keystore/cts/util/TestUtils.java index 22045526055..a30074c5ab7 100644 --- a/tests/tests/keystore/src/android/keystore/cts/util/TestUtils.java +++ b/tests/tests/keystore/src/android/keystore/cts/util/TestUtils.java @@ -16,6 +16,10 @@ package android.keystore.cts.util; +import static android.security.keystore.KeyProperties.DIGEST_NONE; +import static android.security.keystore.KeyProperties.DIGEST_SHA256; +import static android.security.keystore.KeyProperties.DIGEST_SHA512; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -121,6 +125,20 @@ public class TestUtils { return getFeatureVersionKeystore(appContext); } + /** + * This function returns the valid digest algorithms supported for a Strongbox or default + * KeyMint implementation. The isStrongbox parameter specifies the underlying KeyMint + * implementation. If true, it indicates Strongbox KeyMint, otherwise TEE/Software KeyMint + * is assumed. + */ + public static @KeyProperties.DigestEnum String[] getDigestsForKeyMintImplementation( + boolean isStrongbox) { + if (isStrongbox) { + return new String[]{DIGEST_NONE, DIGEST_SHA256}; + } + return new String[]{DIGEST_NONE, DIGEST_SHA256, DIGEST_SHA512}; + } + // Returns 0 if not implemented. Otherwise returns the feature version. // public static int getFeatureVersionKeystore(Context appContext) { diff --git a/tests/tests/libcorefileio/AndroidManifest.xml b/tests/tests/libcorefileio/AndroidManifest.xml index c6a95b5af0b..4e0ad520dad 100644 --- a/tests/tests/libcorefileio/AndroidManifest.xml +++ b/tests/tests/libcorefileio/AndroidManifest.xml @@ -22,18 +22,11 @@ <application> <uses-library android:name="android.test.runner"/> + <!-- Use ':' to indicate that lockHoldingService needs to run in a separate process. + See https://developer.android.com/guide/topics/manifest/service-element#proc --> <service android:name="android.cts.LockHoldingService" android:process=":lockHoldingService" android:permission="android.permission.WRITE_EXTERNAL_STORAGE"/> - <receiver android:name="android.cts.FileChannelInterProcessLockTest$IntentReceiver" - android:exported="true"> - - <intent-filter> - <action android:name="android.cts.CtsLibcoreFileIOTestCases"> - </action> - </intent-filter> - - </receiver> </application> <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" diff --git a/tests/tests/libcorefileio/src/android/cts/FileChannelInterProcessLockTest.java b/tests/tests/libcorefileio/src/android/cts/FileChannelInterProcessLockTest.java index e5b25c93b9a..bc990e27166 100644 --- a/tests/tests/libcorefileio/src/android/cts/FileChannelInterProcessLockTest.java +++ b/tests/tests/libcorefileio/src/android/cts/FileChannelInterProcessLockTest.java @@ -16,12 +16,19 @@ package android.cts; -import android.content.BroadcastReceiver; +import static java.util.concurrent.TimeUnit.SECONDS; + +import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.ServiceConnection; import android.os.Bundle; -import android.os.Debug; import android.os.Environment; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; import android.test.AndroidTestCase; import java.io.File; @@ -33,8 +40,6 @@ import java.nio.file.StandardOpenOption; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; -import static java.util.concurrent.TimeUnit.SECONDS; - @SuppressWarnings("deprecation") public class FileChannelInterProcessLockTest extends AndroidTestCase { @@ -55,11 +60,11 @@ public class FileChannelInterProcessLockTest extends AndroidTestCase { * the service. This provides ample amount of time for the service to receive the request from * the test, then act, and respond back. */ - final static int MAX_WAIT_TIME = 20; + final static int MAX_WAIT_TIME = 10; @Override public void tearDown() throws Exception { - stopService(); + IpcChannel.unbindService(); super.tearDown(); } @@ -419,15 +424,12 @@ public class FileChannelInterProcessLockTest extends AndroidTestCase { ChannelType localChannelType, ChannelType remoteChannelType, boolean expectToGetLock) throws Exception { try { - IntentReceiver.resetReceiverState(); - // Request that the remote lock be obtained. - getContext().startService(new Intent(getContext(), LockHoldingService.class) - .putExtra(LockHoldingService.LOCK_TYPE_KEY, remoteLockType) - .putExtra(LockHoldingService.CHANNEL_TYPE_KEY, remoteChannelType)); + IpcChannel.bindService(getContext()); + IpcChannel.requestRemoteLock(remoteLockType, remoteChannelType); // Wait for a signal that the remote lock is definitely held. - assertTrue(IntentReceiver.lockHeldLatch.await(MAX_WAIT_TIME, SECONDS)); + assertTrue(IpcChannel.lockHeldLatch.await(MAX_WAIT_TIME, SECONDS)); // Try to acquire the local lock in all cases and check whether it could be acquired or // not as expected. @@ -440,7 +442,7 @@ public class FileChannelInterProcessLockTest extends AndroidTestCase { } // Release the remote lock. } finally { - stopService(); + IpcChannel.unbindService(); } } @@ -458,30 +460,24 @@ public class FileChannelInterProcessLockTest extends AndroidTestCase { ChannelType localChannelType, ChannelType remoteChannelType, boolean expectToWait) throws Exception { try { - IntentReceiver.resetReceiverState(); - // The amount of time the remote service should hold lock. - long remoteLockHoldTimeMillis = 7000; + long remoteLockHoldTimeMillis = 5000; // The amount of time test should get to try to acquire the lock. long sufficientOverlappingTimeInMillis = 2000; // This is the allowable delta in the time between the time recorded after the service // released the lock and the time recorded after the test obtained the lock. - long lockReleasedAndReacquiredTimeDeltaInMillis = 1000; + long lockReleasedAndReacquiredTimeDeltaInMillis = 500; // Tell the service to acquire a remote lock. - Intent sendIntent = new Intent(getContext(), LockHoldingService.class) - .putExtra(LockHoldingService.TIME_TO_HOLD_LOCK_KEY, remoteLockHoldTimeMillis) - .putExtra(LockHoldingService.LOCK_TYPE_KEY, remoteLockType) - .putExtra(LockHoldingService.CHANNEL_TYPE_KEY, remoteChannelType) - .putExtra(LockHoldingService.LOCK_BEHAVIOR_RELEASE_AND_NOTIFY_KEY, true); - - getContext().startService(sendIntent); + IpcChannel.bindService(getContext()); + IpcChannel.requestRemoteLockAndRelease(remoteLockType, + remoteChannelType, remoteLockHoldTimeMillis); // Wait for the service to hold the lock and notify for the same. assertTrue("No remote lock held notification", - IntentReceiver.lockHeldLatch.await(MAX_WAIT_TIME, SECONDS)); + IpcChannel.lockHeldLatch.await(MAX_WAIT_TIME, SECONDS)); long localLockNotObtainedTime = System.currentTimeMillis(); @@ -491,9 +487,9 @@ public class FileChannelInterProcessLockTest extends AndroidTestCase { // Wait until the remote lock has definitely been released. assertTrue("No remote lock release notification", - IntentReceiver.lockReleasedLatch.await(MAX_WAIT_TIME, SECONDS)); + IpcChannel.lockReleasedLatch.await(MAX_WAIT_TIME, SECONDS)); - Bundle remoteLockReleasedBundle = IntentReceiver.lockReleasedBundle; + Bundle remoteLockReleasedBundle = IpcChannel.lockReleasedBundle; long remoteLockNotReleasedTime = remoteLockReleasedBundle.getLong(LockHoldingService.LOCK_NOT_YET_RELEASED_TIMESTAMP); long remoteLockReleasedTime = @@ -506,11 +502,10 @@ public class FileChannelInterProcessLockTest extends AndroidTestCase { // but we can't be sure and the test may not be valid. This is why we hold the lock // remotely for a long time compared to the delays we expect for intents to propagate // between processes. - assertTrue(String.format("Remote lock release start (%d), " + - "too soon after local lock notification time (%d). " + + assertTrue(String.format("Remote lock release start " + + "too soon (%d ms) after lock notification time. " + "Need at least %d ms", - remoteLockReleasedTime, - localLockNotObtainedTime, + remoteLockNotReleasedTime - localLockNotObtainedTime, sufficientOverlappingTimeInMillis ), remoteLockNotReleasedTime - localLockNotObtainedTime > @@ -522,10 +517,10 @@ public class FileChannelInterProcessLockTest extends AndroidTestCase { // service. The localLockObtainedTime is captured after the lock was obtained by this // thread. Therefore, there is a degree of slop inherent in the two times. We assert // that they are "close" to each other, but we cannot assert any ordering. - assertTrue(String.format("Local lock obtained (%d) too long " + - "from remote lock release time (%d). " + + assertTrue(String.format("Local lock obtained too long (%d ms) " + + "from remote lock release time. " + "Expected at most %d ms.", - localLockObtainedTime, remoteLockReleasedTime, + localLockObtainedTime - remoteLockReleasedTime, lockReleasedAndReacquiredTimeDeltaInMillis), Math.abs(localLockObtainedTime - remoteLockReleasedTime) < lockReleasedAndReacquiredTimeDeltaInMillis); @@ -545,22 +540,8 @@ public class FileChannelInterProcessLockTest extends AndroidTestCase { // Asserting if the fileLock is valid. assertTrue(fileLock.isValid()); } finally { - stopService(); - } - } - - /** - * Requests and waits for the service to stop - */ - void stopService() throws Exception { - getContext().stopService(new Intent(getContext(), LockHoldingService.class)); - // onStopLatch can be null if we never start the service, possibly because of - // an earlier failure in the test. - if (IntentReceiver.onStopLatch != null) { - assertTrue(IntentReceiver.onStopLatch.await(MAX_WAIT_TIME, SECONDS)); + IpcChannel.unbindService(); } - - deleteDir(getContext()); } static enum LockType { @@ -748,57 +729,136 @@ public class FileChannelInterProcessLockTest extends AndroidTestCase { } /** - * Listens to broadcasts sent by the LockHoldingService and records information / provides - * latches so the test code can synchronize until it is informed the service has acted on - * requests it has sent. + * Handles the comms with the LockHoldingService. + * + * Binds to the service and sends requests and receives callbacks from it. + * + * It records information / provides latches so the test code can synchronize until it is + * informed the service has acted on requests it has sent. */ - public static class IntentReceiver extends BroadcastReceiver { + public static class IpcChannel { - static CountDownLatch onStartLatch; - - static CountDownLatch onStopLatch; + static Context context; + static CountDownLatch onBindLatch; + static CountDownLatch onUnbindLatch; static CountDownLatch lockHeldLatch; - static volatile Bundle lockHeldBundle; - static CountDownLatch lockReleasedLatch; - static volatile Bundle lockReleasedBundle; + static volatile boolean bound = false; + static Messenger messenger; + static Messenger responseMessenger; + static ServiceConnection serviceConnection; + + static Handler responseMessageHandler; + + private static Context getContext() { + return context; + } + /** - * Reset the IntentReceiver for a new test. Assumes no intents will be received from prior - * tests. + * Reset the IpcChannel for a new test. Assumes no intents will be received from prior + * tests. */ - public static synchronized void resetReceiverState() { - onStartLatch = new CountDownLatch(1); - onStopLatch = new CountDownLatch(1); + static synchronized void resetReceiverState(Context testContext) { + context = testContext; + bound = false; + onBindLatch = new CountDownLatch(1); + onUnbindLatch = new CountDownLatch(1); lockHeldLatch = new CountDownLatch(1); lockReleasedLatch = new CountDownLatch(1); lockHeldBundle = null; lockReleasedBundle = null; + + responseMessageHandler = new Handler(Looper.getMainLooper()) { + @Override + public void handleMessage(Message msg) { + Bundle bundle = msg.getData(); + if (bundle.getBoolean(LockHoldingService.NOTIFICATION_LOCK_HELD)) { + lockHeldBundle = bundle; + lockHeldLatch.countDown(); + } else if (bundle.getBoolean(LockHoldingService.NOTIFICATION_LOCK_RELEASED)) { + lockReleasedBundle = bundle; + lockReleasedLatch.countDown(); + } else if (bundle.getBoolean(LockHoldingService.NOTIFICATION_READY_FOR_SHUTDOWN)) { + onUnbindLatch.countDown(); + } else { + super.handleMessage(msg); + } + } + }; + responseMessenger = new Messenger(responseMessageHandler); + + serviceConnection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName className, IBinder service) { + messenger = new Messenger(service); + bound = true; + onBindLatch.countDown(); + } + + @Override + public void onBindingDied(ComponentName className) { + getContext().unbindService(this); + bound = false; + } + + @Override + public void onServiceDisconnected(ComponentName className) { + getContext().unbindService(this); + bound = false; + } + }; } - @Override - public void onReceive(Context context, Intent intent) { - String msg = intent.getStringExtra(LockHoldingService.NOTIFICATION_KEY); - switch (msg) { - case LockHoldingService.NOTIFICATION_START: - onStartLatch.countDown(); - break; - case LockHoldingService.NOTIFICATION_STOP: - onStopLatch.countDown(); - break; - case LockHoldingService.NOTIFICATION_LOCK_HELD: - lockHeldBundle = intent.getExtras(); - lockHeldLatch.countDown(); - break; - case LockHoldingService.NOTIFICATION_LOCK_RELEASED: - lockReleasedBundle = intent.getExtras(); - lockReleasedLatch.countDown(); - break; + static void bindService(Context testContext) throws Exception { + resetReceiverState(testContext); + getContext().bindService(new Intent(getContext(), LockHoldingService.class), + serviceConnection, Context.BIND_AUTO_CREATE); + assertTrue(onBindLatch.await(MAX_WAIT_TIME, SECONDS)); + } + + static void requestRemoteLock(LockType lockType, ChannelType channelType) throws Exception { + Message msg = Message.obtain(null, + LockHoldingService.LOCK_BEHAVIOUR_ACQUIRE_ONLY_AND_NOTIFY, 0, 0); + Bundle bundle = msg.getData(); + bundle.putSerializable(LockHoldingService.LOCK_TYPE_KEY, lockType); + bundle.putSerializable(LockHoldingService.CHANNEL_TYPE_KEY, channelType); + msg.replyTo = responseMessenger; + messenger.send(msg); + } + + static void requestRemoteLockAndRelease(LockType lockType, ChannelType channelType, + long lockHoldTimeMillis) throws Exception { + Message msg = Message.obtain(null, + LockHoldingService.LOCK_BEHAVIOR_RELEASE_AND_NOTIFY, 0, 0); + Bundle bundle = msg.getData(); + bundle.putSerializable(LockHoldingService.LOCK_TYPE_KEY, lockType); + bundle.putSerializable(LockHoldingService.CHANNEL_TYPE_KEY, channelType); + bundle.putLong(LockHoldingService.TIME_TO_HOLD_LOCK_KEY, lockHoldTimeMillis); + msg.replyTo = responseMessenger; + messenger.send(msg); + } + + /** + * Requests and waits for the service to stop + */ + static void unbindService() throws Exception { + if (bound) { + Message msg = Message.obtain(null, LockHoldingService.PREPARE_FOR_SHUTDOWN, 0, 0); + msg.replyTo = responseMessenger; + messenger.send(msg); + + assertTrue(onUnbindLatch.await(MAX_WAIT_TIME, SECONDS)); + getContext().unbindService(serviceConnection); + bound = false; } + messenger = null; + deleteDir(getContext()); } } + } diff --git a/tests/tests/libcorefileio/src/android/cts/LockHoldingService.java b/tests/tests/libcorefileio/src/android/cts/LockHoldingService.java index 0df884683ff..61822353f5c 100644 --- a/tests/tests/libcorefileio/src/android/cts/LockHoldingService.java +++ b/tests/tests/libcorefileio/src/android/cts/LockHoldingService.java @@ -16,18 +16,23 @@ package android.cts; +import static android.cts.FileChannelInterProcessLockTest.ChannelType; +import static android.cts.FileChannelInterProcessLockTest.LockType; + import android.app.Service; import android.content.Intent; +import android.os.Bundle; +import android.os.Handler; import android.os.IBinder; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; import android.util.Log; import java.io.IOException; import java.nio.channels.FileLock; import java.util.concurrent.ExecutionException; -import static android.cts.FileChannelInterProcessLockTest.ChannelType; -import static android.cts.FileChannelInterProcessLockTest.LockType; - /** * A Service that listens for commands from the FileChannelInterProcessLockTest to acquire locks of * different types. It exists to test the behavior when file locks are acquired/released across @@ -51,16 +56,6 @@ public class LockHoldingService extends Service { static final String NOTIFICATION_KEY = "notification"; /** - * The value for the notification sent to the test after the service starts. - */ - static final String NOTIFICATION_START = "onStart"; - - /** - * The value for the notification sent to the test just before the service stops. - */ - static final String NOTIFICATION_STOP = "onStop"; - - /** * The value for the notification sent to the test after the lock is acquired. */ static final String NOTIFICATION_LOCK_HELD = "lockHeld"; @@ -71,6 +66,11 @@ public class LockHoldingService extends Service { static final String NOTIFICATION_LOCK_RELEASED = "lockReleased"; /** + * The value for the notification sent to the test after the lock is released for shutdown + */ + static final String NOTIFICATION_READY_FOR_SHUTDOWN = "readyForShutdown"; + + /** * The key of the Bundle extra used to send time for which the service should wait before * releasing the lock. */ @@ -87,72 +87,86 @@ public class LockHoldingService extends Service { static final String CHANNEL_TYPE_KEY = "channelType"; /** - * The key of the Bundle extra used to let he service know whether to release the lock after - * some time. + * The message code used to let he service know to release the lock after some time. */ - static final String LOCK_BEHAVIOR_RELEASE_AND_NOTIFY_KEY = "releaseAndNotify"; + static final int LOCK_BEHAVIOR_RELEASE_AND_NOTIFY = 1; - static final String ACTION_TYPE_FOR_INTENT_COMMUNICATION - = "android.cts.CtsLibcoreFileIOTestCases"; + /** + * The message code used to let he service know to lock without releasing. + */ + static final int LOCK_BEHAVIOUR_ACQUIRE_ONLY_AND_NOTIFY = 2; + + /** + * The message code used to let the service know to release the lock before test end, if still + * held. + */ + static final int PREPARE_FOR_SHUTDOWN = 3; final String LOG_MESSAGE_TAG = "CtsLibcoreFileIOTestCases"; private FileLock fileLock = null; - public IBinder onBind(Intent intent) { - return null; - } - - @Override - public int onStartCommand(Intent intent, int flags, int startID) { - try { - if (intent.getBooleanExtra(LOCK_BEHAVIOR_RELEASE_AND_NOTIFY_KEY, false)) { - acquireLockAndThenWaitThenRelease(intent); - } else { - acquireLock(intent); + private class LockHoldingHandler extends Handler { + @Override + public void handleMessage(Message msg) { + try { + switch (msg.what) { + case LOCK_BEHAVIOR_RELEASE_AND_NOTIFY: + acquireLockAndThenWaitThenRelease(msg); + break; + case LOCK_BEHAVIOUR_ACQUIRE_ONLY_AND_NOTIFY: + acquireLock(msg); + break; + case PREPARE_FOR_SHUTDOWN: + prepareForShutdown(msg); + break; + default: + super.handleMessage(msg); + } + } catch (Exception e) { + Log.e(LOG_MESSAGE_TAG, "Exception acquire lock", e); } - } catch (Exception e) { - Log.e(LOG_MESSAGE_TAG, "Exception acquire lock", e); } - return START_STICKY; + } + + private Messenger messenger; + + public IBinder onBind(Intent intent) { + messenger = new Messenger(new LockHoldingHandler()); + return messenger.getBinder(); } /** * Acquires the lock asked by the test indefinitely. */ - private void acquireLock(Intent intent) throws IOException, - InterruptedException, ExecutionException { - LockType lockType = (LockType) intent.getSerializableExtra(LOCK_TYPE_KEY); - ChannelType channelType = (ChannelType) intent.getSerializableExtra(CHANNEL_TYPE_KEY); + private void acquireLock(Message msg) throws IOException, + InterruptedException, ExecutionException, RemoteException { + Bundle bundle = msg.getData(); + LockType lockType = (LockType) bundle.get(LOCK_TYPE_KEY); + ChannelType channelType = (ChannelType) bundle.get(CHANNEL_TYPE_KEY); // Acquire the lock based on the information contained in the intent received. this.fileLock = FileChannelInterProcessLockTest.acquire(this, lockType, channelType); - Intent responseIntent = new Intent() - .setPackage("android.libcorefileio.cts") - .putExtra(NOTIFICATION_KEY, NOTIFICATION_LOCK_HELD) - .setAction(ACTION_TYPE_FOR_INTENT_COMMUNICATION); - sendBroadcast(responseIntent); + + notifyLockHeld(msg); } /** - * Acquires and holds the lock for a time specified by the test. Sends a broadcast message after + * Acquires and holds the lock for a time specified by the test. Sends a response message after * releasing the lock. */ - private void acquireLockAndThenWaitThenRelease(Intent intent) - throws IOException, InterruptedException, ExecutionException { - long lockHoldTimeMillis = intent.getLongExtra(TIME_TO_HOLD_LOCK_KEY, 0); + private void acquireLockAndThenWaitThenRelease(Message msg) + throws IOException, InterruptedException, ExecutionException, RemoteException { + Bundle bundle = msg.getData(); + long lockHoldTimeMillis = bundle.getLong(TIME_TO_HOLD_LOCK_KEY, 0); + LockType lockType = (LockType) bundle.get(LOCK_TYPE_KEY); + ChannelType channelType = (ChannelType) bundle.get(CHANNEL_TYPE_KEY); // Acquire the lock. - LockType lockType = (LockType) intent.getSerializableExtra(LOCK_TYPE_KEY); - ChannelType channelType = (ChannelType) intent.getSerializableExtra(CHANNEL_TYPE_KEY); this.fileLock = FileChannelInterProcessLockTest.acquire(this, lockType, channelType); // Signal the lock is now held. - Intent heldIntent = new Intent() - .setPackage("android.libcorefileio.cts") - .putExtra(NOTIFICATION_KEY, NOTIFICATION_LOCK_HELD) - .setAction(ACTION_TYPE_FOR_INTENT_COMMUNICATION); - sendBroadcast(heldIntent); + notifyLockHeld(msg); Thread.sleep(lockHoldTimeMillis); @@ -164,17 +178,35 @@ public class LockHoldingService extends Service { long lockReleasedTimestamp = System.currentTimeMillis(); // Signal the lock is released and some information about timing. - Intent releaseIntent = new Intent() - .setPackage("android.libcorefileio.cts") - .putExtra(NOTIFICATION_KEY, NOTIFICATION_LOCK_RELEASED) - .putExtra(LOCK_NOT_YET_RELEASED_TIMESTAMP, lockNotReleasedTimestamp) - .putExtra(LOCK_DEFINITELY_RELEASED_TIMESTAMP, lockReleasedTimestamp) - .setAction(ACTION_TYPE_FOR_INTENT_COMMUNICATION); - sendBroadcast(releaseIntent); + notifyLockReleased(msg, lockNotReleasedTimestamp, lockReleasedTimestamp); } - @Override - public void onDestroy() { + private void notifyLockHeld(Message msg) throws RemoteException { + Message rsp = msg.obtain(); + Bundle rspBundle = rsp.getData(); + rspBundle.putBoolean(NOTIFICATION_LOCK_HELD, true); + msg.replyTo.send(rsp); + } + + private void notifyLockReleased(Message msg, long lockNotReleasedTimestamp, + long lockReleasedTimestamp) throws RemoteException { + Message rsp = msg.obtain(); + Bundle rspBundle = rsp.getData(); + rspBundle.putBoolean(NOTIFICATION_LOCK_RELEASED, true); + rspBundle.putLong(LOCK_NOT_YET_RELEASED_TIMESTAMP, lockNotReleasedTimestamp); + rspBundle.putLong(LOCK_DEFINITELY_RELEASED_TIMESTAMP, lockReleasedTimestamp); + msg.replyTo.send(rsp); + } + + private void prepareForShutdown(Message msg) throws RemoteException { + doReleaseLock(); + Message rsp = msg.obtain(); + Bundle rspBundle = rsp.getData(); + rspBundle.putBoolean(NOTIFICATION_READY_FOR_SHUTDOWN, true); + msg.replyTo.send(rsp); + } + + private void doReleaseLock() { try { if (fileLock != null) { fileLock.release(); @@ -182,10 +214,11 @@ public class LockHoldingService extends Service { } catch (IOException e) { Log.e(LOG_MESSAGE_TAG, e.getMessage()); } - Intent intent = new Intent() - .setPackage("android.libcorefileio.cts") - .putExtra(NOTIFICATION_KEY, NOTIFICATION_STOP) - .setAction(ACTION_TYPE_FOR_INTENT_COMMUNICATION); - sendBroadcast(intent); + } + + @Override + public boolean onUnbind(Intent intent) { + doReleaseLock(); + return false; } } diff --git a/tests/tests/libnativehelper/Android.bp b/tests/tests/libnativehelper/Android.bp index 9705db0cfb0..1761035744e 100644 --- a/tests/tests/libnativehelper/Android.bp +++ b/tests/tests/libnativehelper/Android.bp @@ -14,6 +14,7 @@ package { default_applicable_licenses: ["Android-Apache-2.0"], + default_team: "trendy_team_art_mainline", } android_test { diff --git a/tests/tests/media/audio/src/android/media/audio/cts/AudioPlaybackConfigurationTest.java b/tests/tests/media/audio/src/android/media/audio/cts/AudioPlaybackConfigurationTest.java index 0c5e0895679..9a7b3bc65f0 100644 --- a/tests/tests/media/audio/src/android/media/audio/cts/AudioPlaybackConfigurationTest.java +++ b/tests/tests/media/audio/src/android/media/audio/cts/AudioPlaybackConfigurationTest.java @@ -493,6 +493,10 @@ public class AudioPlaybackConfigurationTest extends CtsAndroidTestCase { "android.media.AudioManager.AudioPlaybackCallback#isMuted", "android.media.AudioManager.AudioPlaybackCallback#getMutedBy"}) public void testAudioTrackMuteFromAppOpsNotification() throws Exception { + if (isWatch()) { + Log.w(TAG, "Skip testAudioTrackMuteFromAppOpsNotification for Wear"); + return; + } if (!isValidPlatform("testAudioTrackMuteFromAppOpsNotification")) return; if (hasAudioSilentProperty()) { Log.w(TAG, "Device has ro.audio.silent set, skipping " @@ -509,6 +513,10 @@ public class AudioPlaybackConfigurationTest extends CtsAndroidTestCase { "android.media.AudioManager.AudioPlaybackCallback#isMuted", "android.media.AudioManager.AudioPlaybackCallback#getMutedBy"}) public void testMediaPlayerMuteFromAppOpsNotification() throws Exception { + if (isWatch()) { + Log.w(TAG, "Skip testMediaPlayerMuteFromAppOpsNotification for Wear"); + return; + } if (!isValidPlatform("testMediaPlayerMuteFromAppOpsNotification")) return; if (hasAudioSilentProperty()) { Log.w(TAG, "Device has ro.audio.silent set, skipping " @@ -700,6 +708,9 @@ public class AudioPlaybackConfigurationTest extends CtsAndroidTestCase { assertTrue("onPlaybackConfigChanged play, format and device expected", callback.waitForCallbacks(3, TEST_TIMING_TOLERANCE_MS + PLAY_ROUTING_TIMING_TOLERANCE_MS)); + if (mAt != null) { + Thread.sleep(TEST_TIMING_TOLERANCE_MS + PLAY_ROUTING_TIMING_TOLERANCE_MS); + } } else { Thread.sleep(TEST_TIMING_TOLERANCE_MS + PLAY_ROUTING_TIMING_TOLERANCE_MS); } @@ -1022,4 +1033,8 @@ public class AudioPlaybackConfigurationTest extends CtsAndroidTestCase { } return true; } + + private boolean isWatch() { + return getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH); + } } diff --git a/tests/tests/media/audio/src/android/media/audio/cts/RemoteSubmixTest.java b/tests/tests/media/audio/src/android/media/audio/cts/RemoteSubmixTest.java index 9a30b672ab3..91e2bb9adaa 100644 --- a/tests/tests/media/audio/src/android/media/audio/cts/RemoteSubmixTest.java +++ b/tests/tests/media/audio/src/android/media/audio/cts/RemoteSubmixTest.java @@ -49,6 +49,8 @@ import org.junit.Rule; import org.junit.Test; import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.Map; /** * Validate that there is no discontinuity in the AudioRecord data from Remote Submix. @@ -157,7 +159,22 @@ public class RemoteSubmixTest { AudioRecord audioRecord = createPlaybackCaptureRecord(); ByteBuffer rawBuffer = null; + int[] streams = {AudioManager.STREAM_RING, AudioManager.STREAM_NOTIFICATION, + AudioManager.STREAM_SYSTEM}; + + // Get current device stream volume level + Map<Integer, Integer> streamVolume = new HashMap<Integer, Integer>(); + for (int stream : streams) { + streamVolume.put(stream, mAudioManager.getStreamVolume(stream)); + } try { + for (int stream : streams) { + // Mute device streams + mAudioManager.adjustStreamVolume( + stream, AudioManager.ADJUST_MUTE, 0 /*no flag used*/); + assertEquals(mAudioManager.getStreamVolume(stream), 0); + } + audioRecord.startRecording(); mediaPlayer.start(); @@ -187,6 +204,12 @@ public class RemoteSubmixTest { .executeShellCommand("wm dismiss-keyguard"); } + for (Map.Entry<Integer, Integer> map : streamVolume.entrySet()) { + // Restore device stream volume + mAudioManager.setStreamVolume(map.getKey(), map.getValue(), 0 /*no flag used*/); + assertEquals(mAudioManager.getStreamVolume(map.getKey()), (int) map.getValue()); + } + audioRecord.release(); mediaPlayer.release(); } 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 660c97cbaff..f4ace49e12d 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 @@ -630,7 +630,12 @@ public class RoutingTest extends AndroidTestCase { assertNull(mediaPlayer.getPreferredDevice()); // resets to default + mediaPlayer.pause(); + //Wait for state to change before setPreferDevice + SystemClock.sleep(200); assertTrue(mediaPlayer.setPreferredDevice(null)); + mediaPlayer.start(); + assertTrue(mediaPlayer.isPlaying()); // test each device AudioDeviceInfo[] deviceList = mAudioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS); @@ -639,12 +644,22 @@ public class RoutingTest extends AndroidTestCase { // Device with type as TYPE_TELEPHONY requires a privileged permission. continue; } + mediaPlayer.pause(); + //Wait for state to change before setPreferDevice + SystemClock.sleep(200); assertTrue(mediaPlayer.setPreferredDevice(deviceList[index])); + mediaPlayer.start(); + assertTrue(mediaPlayer.isPlaying()); assertTrue(mediaPlayer.getPreferredDevice() == deviceList[index]); } // Check defaults again + mediaPlayer.pause(); + //Wait for state to change before setPreferDevice + SystemClock.sleep(200); assertTrue(mediaPlayer.setPreferredDevice(null)); + mediaPlayer.start(); + assertTrue(mediaPlayer.isPlaying()); assertNull(mediaPlayer.getPreferredDevice()); mediaPlayer.stop(); @@ -737,7 +752,12 @@ public class RoutingTest extends AndroidTestCase { for (AudioDeviceInfo device : devices) { if (routedDevice.getId() != device.getId() && device.getType() != AudioDeviceInfo.TYPE_TELEPHONY) { + mediaPlayer.pause(); + //Wait for state to change before setPreferDevice + SystemClock.sleep(200); mediaPlayer.setPreferredDevice(device); + mediaPlayer.start(); + assertTrue(mediaPlayer.isPlaying()); listener.setCallExpected(true); listener.await(WAIT_ROUTING_CHANGE_TIME_MS); break; diff --git a/tests/tests/media/codec/src/android/media/codec/cts/VideoCodecTest.java b/tests/tests/media/codec/src/android/media/codec/cts/VideoCodecTest.java index 697d96ba5f9..ef7f205cac1 100644 --- a/tests/tests/media/codec/src/android/media/codec/cts/VideoCodecTest.java +++ b/tests/tests/media/codec/src/android/media/codec/cts/VideoCodecTest.java @@ -74,7 +74,7 @@ public class VideoCodecTest extends VideoCodecTestBase { // The tolerance varies by the bitrate, because lower bitrates interact with // video quality standards introduced in Android 12. private static final double[] MAX_CBR_BITRATE_VARIATIONS = { 0.20, 0.20, 0.20, 0.20 }; - private static final double[] MAX_VBR_BITRATE_VARIATIONS = { 0.30, 0.20, 0.20, 0.20 }; + private static final double[] MAX_VBR_BITRATE_VARIATIONS = { 0.50, 0.30, 0.30, 0.30 }; // Average PSNR values for reference Google Video codec for the above bitrates. private static final double[] REFERENCE_AVERAGE_PSNR = { 33.1, 35.2, 36.6, 37.8 }; // Minimum PSNR values for reference Google Video codec for the above bitrates. diff --git a/tests/tests/media/common/src/android/media/cts/MediaProjectionActivity.java b/tests/tests/media/common/src/android/media/cts/MediaProjectionActivity.java index 34de83f39b5..918abea259b 100644 --- a/tests/tests/media/common/src/android/media/cts/MediaProjectionActivity.java +++ b/tests/tests/media/common/src/android/media/cts/MediaProjectionActivity.java @@ -44,6 +44,8 @@ import android.view.WindowManager; import androidx.annotation.Nullable; +import com.android.compatibility.common.util.UiAutomatorUtils; + import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -175,18 +177,44 @@ public class MediaProjectionActivity extends Activity { pressStartRecording(isWatch); } - private static boolean selectEntireScreenOption(String entireScreenString) { - UiObject2 spinner = waitForObject(By.res(SPINNER_RESOURCE_ID)); - if (spinner == null) { - Log.e(TAG, "Couldn't find spinner to select projection mode, now scrolling"); - scrollToGivenResource(SPINNER_RESOURCE_ID); + @Nullable + private static UiObject2 findUiObject(String resourceId) { + return findUiObject(By.res(resourceId)); + } + + @Nullable + private static UiObject2 findUiObject(BySelector selector) { + // Check if the View can be found on the current screen. + UiObject2 obj = waitForObject(selector); - spinner = waitForObject(By.res(SPINNER_RESOURCE_ID)); - if (spinner == null) { - Log.e(TAG, "Couldn't find spinner to select projection mode, even after scrolling"); - return false; + // If the View is not found on the current screen. Try scrolling around to find it. + if (obj == null) { + Log.w(TAG, "Couldn't find " + selector + ", now scrolling to it."); + scrollToGivenResource(SPINNER_RESOURCE_ID); + obj = waitForObject(selector); + } + if (obj == null) { + Log.w(TAG, "Still couldn't find " + selector + ", now scrolling screen height."); + try { + obj = UiAutomatorUtils.waitFindObjectOrNull(selector); + } catch (UiObjectNotFoundException e) { + Log.e(TAG, "Error in looking for " + selector, e); } } + + if (obj == null) { + Log.e(TAG, "Unable to find " + selector); + } + + return obj; + } + + private static boolean selectEntireScreenOption(String entireScreenString) { + UiObject2 spinner = findUiObject(SPINNER_RESOURCE_ID); + if (spinner == null) { + Log.e(TAG, "Couldn't find spinner to select projection mode, even after scrolling"); + return false; + } spinner.click(); UiObject2 entireScreenOption = waitForObject(By.text(entireScreenString)); @@ -221,20 +249,8 @@ public class MediaProjectionActivity extends Activity { private static void pressStartRecording(boolean isWatch) { // May need to scroll down to the start button on small screen devices. - UiObject2 startRecordingButton = waitForObject(By.res(ACCEPT_RESOURCE_ID)); - if (startRecordingButton == null) { - Log.e(TAG, "Couldn't find start recording button, now attempting to scroll to it"); - scrollToGivenResource(ACCEPT_RESOURCE_ID); - - startRecordingButton = waitForObject(By.res(ACCEPT_RESOURCE_ID)); - if (startRecordingButton == null) { - Log.e(TAG, "Couldn't find start recording button, even after scrolling"); - } else { - Log.d(TAG, "found permission dialog after scrolling down, clicked"); - startRecordingButton.click(); - } - } else { - Log.d(TAG, "found permission dialog after searching all windows, clicked"); + UiObject2 startRecordingButton = findUiObject(ACCEPT_RESOURCE_ID); + if (startRecordingButton != null) { startRecordingButton.click(); } } 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 552cf65372a..6b6fc428562 100644 --- a/tests/tests/media/common/src/android/media/cts/TestUtils.java +++ b/tests/tests/media/common/src/android/media/cts/TestUtils.java @@ -25,15 +25,15 @@ import android.content.pm.ApplicationInfo; 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 androidx.test.platform.app.InstrumentationRegistry; import org.junit.Assert; import org.junit.AssumptionViolatedException; +import java.util.Locale; import java.util.Objects; /** @@ -154,15 +154,113 @@ public final class TestUtils { } /* - * Report whether we are in MTS mode (vs in CTS) mode. - * Some tests (or parts of tests) are restricted to a particular mode. + * decide whether we are in CTS, MCTS, or MTS mode. + * return the appropriate constant value */ - public static boolean isMtsMode() { + public static final int TESTMODE_CTS = 0; + public static final int TESTMODE_MCTS = 1; + public static final int TESTMODE_MTS = 2; + + /** + * Report the current testing mode, as an enumeration. + * Testing mode is determined by argument 'media-testing-mode' + * which specifies 'cts', 'mcts', or 'mts' + * If missing, we use the older boolean "mts-media" to generate either 'cts' or 'mts' + * + * This is most often specified in a CtsMedia* app's AndroidTest.xml, using + * a line like: + * <test class="com.android.tradefed.testtype.AndroidJUnitTest" > + * ... + * <option name="instrumentation-arg" key="media-testing-mode" value="CTS" /> + * </test> + * + * @return {@code} one of the values TESTMODE_CTS, TESTMODE_MCTS, or TESTMODE_MTS. + * + */ + public static int currentTestMode() { Bundle bundle = InstrumentationRegistry.getArguments(); - // null if not set - boolean isMTS = TextUtils.equals("true", bundle.getString("mts-media")); + String value = bundle.getString("media-testing-mode"); + if (value == null) { + value = bundle.getString("mts-media"); + if (value == null || !value.equals("true")) { + value = "CTS"; + } else { + value = "MTS"; + } + } + int mode; + value = value.toUpperCase(Locale.ROOT); + if (value.equals("CTS")) { + mode = TESTMODE_CTS; + } else if (value.equals("MCTS")) { + mode = TESTMODE_MCTS; + } else if (value.equals("MTS")) { + mode = TESTMODE_MTS; + } else { + mode = TESTMODE_CTS; + } + return mode; + } - return isMTS; + /** + * Report the current testing mode, as a string. + * Testing mode is determined by argument 'media-testing-mode' + * which specifies 'cts', 'mcts', or 'mts' + * If missing, we use the older boolean "mts-media" to generate either 'cts' or 'mts' + * + * @return {@code} "CTS", "MCTS", or "MTS" corresponding to the mode. + */ + public static String currentTestModeName() { + Bundle bundle = InstrumentationRegistry.getArguments(); + String value = bundle.getString("media-testing-mode"); + if (value == null) { + value = bundle.getString("mts-media"); + if (value == null || !value.equals("true")) { + value = "CTS"; + } else { + value = "MTS"; + } + } + value = value.toUpperCase(Locale.ROOT); + if (value.equals("CTS")) { + return "CTS"; + } else if (value.equals("MCTS")) { + return "MCTS"; + } else if (value.equals("MTS")) { + return "MTS"; + } else { + // same default as currentTestMode() + return "CTS"; + } + } + + /** + * Report whether this test run should evaluate module functionality. + * Some tests (or parts of tests) are restricted to a particular mode. + * + * @return {@code} true is the current test mode is MCTS or MTS. + */ + public static boolean isTestingModules() { + int mode = currentTestMode(); + switch (mode) { + case TESTMODE_MCTS: + case TESTMODE_MTS: + return true; + default: + break; + } + return false; + } + + /** + * Report whether we are in MTS mode (vs CTS or MCTS) mode. + * Some tests (or parts of tests) are restricted to a particular mode. + * + * @return {@code} true is the current test mode is MTS. + */ + public static boolean isMtsMode() { + int mode = currentTestMode(); + return mode == TESTMODE_MTS; } /* @@ -180,18 +278,22 @@ public final class TestUtils { */ 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 (isMainlineCodec(name)) { - return true; + int mode = currentTestMode(); + boolean result = false; + switch (mode) { + case TESTMODE_CTS: + result = !isMainlineCodec(name); + break; + case TESTMODE_MCTS: + case TESTMODE_MTS: + result = isMainlineCodec(name); + break; } - Log.d(TAG, "Test mode MTS does not test codec " + name); - return false; + Log.d(TAG, "codec " + name + (result ? " is " : " is not ") + + "tested in mode " + currentTestModeName()); + return result; } /* diff --git a/tests/tests/media/decoder/src/android/media/decoder/cts/AdaptivePlaybackTest.java b/tests/tests/media/decoder/src/android/media/decoder/cts/AdaptivePlaybackTest.java index fece96896f6..857acc739c7 100644 --- a/tests/tests/media/decoder/src/android/media/decoder/cts/AdaptivePlaybackTest.java +++ b/tests/tests/media/decoder/src/android/media/decoder/cts/AdaptivePlaybackTest.java @@ -1446,45 +1446,44 @@ class Media { Log.i(TAG, "format=" + media.getFormat()); ArrayList<ByteBuffer> csds = new ArrayList<ByteBuffer>(); - // CSD in VP9 can not be combined with frame data - if (!media.getMime().equals(MediaFormat.MIMETYPE_VIDEO_VP9)) { - for (String tag: new String[] { "csd-0", "csd-1" }) { - if (media.getFormat().containsKey(tag)) { - ByteBuffer csd = media.getFormat().getByteBuffer(tag); - Log.i(TAG, tag + "=" + AdaptivePlaybackTest.byteBufferToString(csd, 0, 16)); - csds.add(csd); - } + int csdSize = 0; + for (String tag: new String[] { "csd-0", "csd-1" }) { + if (media.getFormat().containsKey(tag)) { + ByteBuffer csd = media.getFormat().getByteBuffer(tag); + Log.i(TAG, tag + "=" + AdaptivePlaybackTest.byteBufferToString(csd, 0, 16)); + csds.add(csd); + csdSize += csd.capacity(); } } - + int ix = 0; + if (csdSize > 0) { + ByteBuffer csdBuf = ByteBuffer.allocate(csdSize); + for (ByteBuffer csd: csds) { + csd.clear(); + csdBuf.put(csd); + csd.clear(); + Log.i(TAG, "csd[" + csd.capacity() + "]"); + } + Log.i(TAG, "frame-" + ix + "[" + csdSize + "]"); + csds.clear(); + media.mFrames[ix++] = new Frame(0, MediaCodec.BUFFER_FLAG_CODEC_CONFIG, csdBuf); + } int maxInputSize = 0; ByteBuffer readBuf = ByteBuffer.allocate(2000000); - for (int ix = 0; ix < numFrames; ix++) { + while (ix < numFrames) { int sampleSize = extractor.readSampleData(readBuf, 0 /* offset */); if (sampleSize < 0) { throw new IllegalArgumentException("media is too short at " + ix + " frames"); } else { readBuf.position(0).limit(sampleSize); - for (ByteBuffer csd: csds) { - sampleSize += csd.capacity(); - } - if (maxInputSize < sampleSize) { maxInputSize = sampleSize; } ByteBuffer buf = ByteBuffer.allocate(sampleSize); - for (ByteBuffer csd: csds) { - csd.clear(); - buf.put(csd); - csd.clear(); - Log.i(TAG, "csd[" + csd.capacity() + "]"); - } - Log.i(TAG, "frame-" + ix + "[" + sampleSize + "]"); - csds.clear(); buf.put(readBuf); - media.mFrames[ix] = new Frame( + media.mFrames[ix++] = new Frame( extractor.getSampleTime(), extractor.getSampleFlags(), buf); diff --git a/tests/tests/media/decoder/src/android/media/decoder/cts/DecodeAccuracyTest.java b/tests/tests/media/decoder/src/android/media/decoder/cts/DecodeAccuracyTest.java index bf4faccbaa1..b78e96c116d 100644 --- a/tests/tests/media/decoder/src/android/media/decoder/cts/DecodeAccuracyTest.java +++ b/tests/tests/media/decoder/src/android/media/decoder/cts/DecodeAccuracyTest.java @@ -62,7 +62,7 @@ public class DecodeAccuracyTest extends DecodeAccuracyTestBase { private static final String TAG = DecodeAccuracyTest.class.getSimpleName(); private static final Field[] fields = R.raw.class.getFields(); - private static final int ALLOWED_GREATEST_PIXEL_DIFFERENCE = 90; + private static final int ALLOWED_GREATEST_PIXEL_DIFFERENCE = 105; private static final int OFFSET = 10; private static final long PER_TEST_TIMEOUT_MS = 60000; private static final String[] VIDEO_FILES = { diff --git a/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderLowLatencyTest.java b/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderLowLatencyTest.java new file mode 100644 index 00000000000..2233df45da3 --- /dev/null +++ b/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderLowLatencyTest.java @@ -0,0 +1,276 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.decoder.cts; + +import static android.media.decoder.cts.DecoderTest.getAssetFileDescriptorFor; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import android.content.res.AssetFileDescriptor; +import android.media.MediaCodec; +import android.media.MediaCodecInfo; +import android.media.MediaCodecList; +import android.media.MediaExtractor; +import android.media.MediaFormat; +import android.media.cts.MediaCodecWrapper; +import android.media.cts.MediaHeavyPresubmitTest; +import android.media.cts.MediaTestBase; +import android.media.cts.NdkMediaCodec; +import android.media.cts.SdkMediaCodec; +import android.os.Build; +import android.platform.test.annotations.AppModeFull; +import android.util.Log; +import android.view.Surface; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SdkSuppress; + +import com.android.compatibility.common.util.DeviceReportLog; +import com.android.compatibility.common.util.MediaUtils; +import com.android.compatibility.common.util.NonMainlineTest; +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.Test; +import org.junit.runner.RunWith; + +import java.nio.ByteBuffer; + +@MediaHeavyPresubmitTest +@AppModeFull(reason = "There should be no instant apps specific behavior related to decoders") +@RunWith(AndroidJUnit4.class) +public class DecoderLowLatencyTest extends MediaTestBase { + private static final String TAG = "DecoderLowLatencyTest"; + private static final String REPORT_LOG_NAME = "CtsMediaDecoderTestCases"; + + @Before + @Override + public void setUp() throws Throwable { + super.setUp(); + } + + @After + @Override + public void tearDown() { + super.tearDown(); + } + + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R) + @Test + public void testLowLatencyVp9At1280x720() throws Exception { + testLowLatencyVideo( + "video_1280x720_webm_vp9_csd_309kbps_25fps_vorbis_stereo_128kbps_48000hz.webm", 300, + false /* useNdk */); + testLowLatencyVideo( + "video_1280x720_webm_vp9_csd_309kbps_25fps_vorbis_stereo_128kbps_48000hz.webm", 300, + true /* useNdk */); + } + + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R) + @Test + public void testLowLatencyVp9At1920x1080() throws Exception { + testLowLatencyVideo( + "bbb_s2_1920x1080_webm_vp9_0p41_10mbps_60fps_vorbis_6ch_384kbps_22050hz.webm", 300, + false /* useNdk */); + testLowLatencyVideo( + "bbb_s2_1920x1080_webm_vp9_0p41_10mbps_60fps_vorbis_6ch_384kbps_22050hz.webm", 300, + true /* useNdk */); + } + + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R) + @Test + public void testLowLatencyVp9At3840x2160() throws Exception { + testLowLatencyVideo( + "bbb_s2_3840x2160_webm_vp9_0p51_20mbps_60fps_vorbis_6ch_384kbps_32000hz.webm", 300, + false /* useNdk */); + testLowLatencyVideo( + "bbb_s2_3840x2160_webm_vp9_0p51_20mbps_60fps_vorbis_6ch_384kbps_32000hz.webm", 300, + true /* useNdk */); + } + + @NonMainlineTest + @Test + public void testLowLatencyAVCAt1280x720() throws Exception { + testLowLatencyVideo( + "video_1280x720_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4", 300, + false /* useNdk */); + testLowLatencyVideo( + "video_1280x720_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4", 300, + true /* useNdk */); + } + + @NonMainlineTest + @Test + public void testLowLatencyHEVCAt480x360() throws Exception { + testLowLatencyVideo( + "video_480x360_mp4_hevc_650kbps_30fps_aac_stereo_128kbps_48000hz.mp4", 300, + false /* useNdk */); + testLowLatencyVideo( + "video_480x360_mp4_hevc_650kbps_30fps_aac_stereo_128kbps_48000hz.mp4", 300, + true /* useNdk */); + } + + private void testLowLatencyVideo(String testVideo, int frameCount, boolean useNdk) + throws Exception { + AssetFileDescriptor fd = getAssetFileDescriptorFor(testVideo); + MediaExtractor extractor = new MediaExtractor(); + extractor.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength()); + 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; + } + } + + assertTrue("No video track was found", trackIndex >= 0); + + extractor.selectTrack(trackIndex); + format.setFeatureEnabled(MediaCodecInfo.CodecCapabilities.FEATURE_LowLatency, + true /* enable */); + + MediaCodecList mcl = new MediaCodecList(MediaCodecList.ALL_CODECS); + String decoderName = mcl.findDecoderForFormat(format); + if (decoderName == null) { + MediaUtils.skipTest("no low latency decoder for " + format); + return; + } + String entry = (useNdk ? "NDK" : "SDK"); + Log.v(TAG, "found " + entry + " decoder " + decoderName + " for format: " + format); + + Surface surface = getActivity().getSurfaceHolder().getSurface(); + MediaCodecWrapper decoder = null; + if (useNdk) { + decoder = new NdkMediaCodec(decoderName); + } else { + decoder = new SdkMediaCodec(MediaCodec.createByCodecName(decoderName)); + } + format.removeFeature(MediaCodecInfo.CodecCapabilities.FEATURE_LowLatency); + format.setInteger(MediaFormat.KEY_LOW_LATENCY, 1); + decoder.configure(format, 0 /* flags */, surface); + decoder.start(); + + if (!useNdk) { + decoder.getInputBuffers(); + } + ByteBuffer[] codecOutputBuffers = decoder.getOutputBuffers(); + String decoderOutputFormatString = null; + + // start decoding + final long kTimeOutUs = 1000000; // 1 second + MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); + int bufferCounter = 0; + long[] latencyMs = new long[frameCount]; + boolean waitingForOutput = false; + long startTimeMs = System.currentTimeMillis(); + while (bufferCounter < frameCount) { + if (!waitingForOutput) { + int inputBufferId = decoder.dequeueInputBuffer(kTimeOutUs); + if (inputBufferId < 0) { + Log.v(TAG, "no input buffer"); + break; + } + + ByteBuffer dstBuf = decoder.getInputBuffer(inputBufferId); + + int sampleSize = extractor.readSampleData(dstBuf, 0 /* offset */); + long presentationTimeUs = 0; + if (sampleSize < 0) { + Log.v(TAG, "had input EOS, early termination at frame " + bufferCounter); + break; + } else { + presentationTimeUs = extractor.getSampleTime(); + } + + startTimeMs = System.currentTimeMillis(); + decoder.queueInputBuffer( + inputBufferId, + 0 /* offset */, + sampleSize, + presentationTimeUs, + 0 /* flags */); + + extractor.advance(); + waitingForOutput = true; + } + + int outputBufferId = decoder.dequeueOutputBuffer(info, kTimeOutUs); + + if (outputBufferId >= 0) { + waitingForOutput = false; + //Log.d(TAG, "got output, size " + info.size + ", time " + info.presentationTimeUs); + latencyMs[bufferCounter++] = System.currentTimeMillis() - startTimeMs; + // TODO: render the frame and find the rendering time to calculate the total delay + decoder.releaseOutputBuffer(outputBufferId, false /* render */); + } else if (outputBufferId == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { + codecOutputBuffers = decoder.getOutputBuffers(); + Log.d(TAG, "output buffers have changed."); + } else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { + decoderOutputFormatString = decoder.getOutputFormatString(); + Log.d(TAG, "output format has changed to " + decoderOutputFormatString); + } else { + fail("No output buffer returned without frame delay, status " + outputBufferId); + } + } + + assertTrue("No INFO_OUTPUT_FORMAT_CHANGED from decoder", decoderOutputFormatString != null); + + long latencyMean = 0; + long latencyMax = 0; + int maxIndex = 0; + for (int i = 0; i < bufferCounter; ++i) { + latencyMean += latencyMs[i]; + if (latencyMs[i] > latencyMax) { + latencyMax = latencyMs[i]; + maxIndex = i; + } + } + if (bufferCounter > 0) { + latencyMean /= bufferCounter; + } + Log.d(TAG, entry + " latency average " + latencyMean + " ms, max " + latencyMax + + " ms at frame " + maxIndex); + + DeviceReportLog log = new DeviceReportLog(REPORT_LOG_NAME, "video_decoder_latency"); + String mime = format.getString(MediaFormat.KEY_MIME); + int width = format.getInteger(MediaFormat.KEY_WIDTH); + int height = format.getInteger(MediaFormat.KEY_HEIGHT); + log.addValue("codec_name", decoderName, ResultType.NEUTRAL, ResultUnit.NONE); + log.addValue("mime_type", mime, ResultType.NEUTRAL, ResultUnit.NONE); + log.addValue("width", width, ResultType.NEUTRAL, ResultUnit.NONE); + log.addValue("height", height, ResultType.NEUTRAL, ResultUnit.NONE); + log.addValue("video_res", testVideo, ResultType.NEUTRAL, ResultUnit.NONE); + log.addValue("decode_to", surface == null ? "buffer" : "surface", + ResultType.NEUTRAL, ResultUnit.NONE); + + log.addValue("average_latency", latencyMean, ResultType.LOWER_BETTER, ResultUnit.MS); + log.addValue("max_latency", latencyMax, ResultType.LOWER_BETTER, ResultUnit.MS); + + log.submit(getInstrumentation()); + + decoder.stop(); + decoder.release(); + extractor.release(); + } +} 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 b29e4161c9a..c93c0f081df 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 @@ -3839,6 +3839,21 @@ public class DecoderTest extends MediaTestBase { Thread.sleep(200); mMediaCodecPlayer.stopDrainingAudioOutputBuffers(false); + // Wait until underrun recovers, otherwise false detection of end of playback occurs + { + long underrunRecoveryTimeoutMs = 200; + long startTimeMs = System.currentTimeMillis(); + AudioTimestamp previousTimestamp; + do { + assertTrue(String.format("No underrun recovery after %d milliseconds", + underrunRecoveryTimeoutMs), + System.currentTimeMillis() - startTimeMs < underrunRecoveryTimeoutMs); + previousTimestamp = mMediaCodecPlayer.getTimestamp(); + Thread.sleep(50); + } while (mMediaCodecPlayer.getTimestamp().framePosition + == previousTimestamp.framePosition); + } + // Sleep till framePosition stabilizes, i.e. playback is complete { long endOfPlayackTimeoutMs = 20000; @@ -4503,206 +4518,4 @@ public class DecoderTest extends MediaTestBase { PackageManager pm = mContext.getPackageManager(); return pm.hasSystemFeature(PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE); } - - @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R) - @Test - public void testLowLatencyVp9At1280x720() throws Exception { - testLowLatencyVideo( - "video_1280x720_webm_vp9_csd_309kbps_25fps_vorbis_stereo_128kbps_48000hz.webm", 300, - false /* useNdk */); - testLowLatencyVideo( - "video_1280x720_webm_vp9_csd_309kbps_25fps_vorbis_stereo_128kbps_48000hz.webm", 300, - true /* useNdk */); - } - - @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R) - @Test - public void testLowLatencyVp9At1920x1080() throws Exception { - testLowLatencyVideo( - "bbb_s2_1920x1080_webm_vp9_0p41_10mbps_60fps_vorbis_6ch_384kbps_22050hz.webm", 300, - false /* useNdk */); - testLowLatencyVideo( - "bbb_s2_1920x1080_webm_vp9_0p41_10mbps_60fps_vorbis_6ch_384kbps_22050hz.webm", 300, - true /* useNdk */); - } - - @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R) - @Test - public void testLowLatencyVp9At3840x2160() throws Exception { - testLowLatencyVideo( - "bbb_s2_3840x2160_webm_vp9_0p51_20mbps_60fps_vorbis_6ch_384kbps_32000hz.webm", 300, - false /* useNdk */); - testLowLatencyVideo( - "bbb_s2_3840x2160_webm_vp9_0p51_20mbps_60fps_vorbis_6ch_384kbps_32000hz.webm", 300, - true /* useNdk */); - } - - @NonMainlineTest - @Test - public void testLowLatencyAVCAt1280x720() throws Exception { - testLowLatencyVideo( - "video_1280x720_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4", 300, - false /* useNdk */); - testLowLatencyVideo( - "video_1280x720_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4", 300, - true /* useNdk */); - } - - @NonMainlineTest - @Test - public void testLowLatencyHEVCAt480x360() throws Exception { - testLowLatencyVideo( - "video_480x360_mp4_hevc_650kbps_30fps_aac_stereo_128kbps_48000hz.mp4", 300, - false /* useNdk */); - testLowLatencyVideo( - "video_480x360_mp4_hevc_650kbps_30fps_aac_stereo_128kbps_48000hz.mp4", 300, - true /* useNdk */); - } - - private void testLowLatencyVideo(String testVideo, int frameCount, boolean useNdk) - throws Exception { - AssetFileDescriptor fd = getAssetFileDescriptorFor(testVideo); - MediaExtractor extractor = new MediaExtractor(); - extractor.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength()); - 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; - } - } - - assertTrue("No video track was found", trackIndex >= 0); - - extractor.selectTrack(trackIndex); - format.setFeatureEnabled(MediaCodecInfo.CodecCapabilities.FEATURE_LowLatency, - true /* enable */); - - MediaCodecList mcl = new MediaCodecList(MediaCodecList.ALL_CODECS); - String decoderName = mcl.findDecoderForFormat(format); - if (decoderName == null) { - MediaUtils.skipTest("no low latency decoder for " + format); - return; - } - String entry = (useNdk ? "NDK" : "SDK"); - Log.v(TAG, "found " + entry + " decoder " + decoderName + " for format: " + format); - - Surface surface = getActivity().getSurfaceHolder().getSurface(); - MediaCodecWrapper decoder = null; - if (useNdk) { - decoder = new NdkMediaCodec(decoderName); - } else { - decoder = new SdkMediaCodec(MediaCodec.createByCodecName(decoderName)); - } - format.removeFeature(MediaCodecInfo.CodecCapabilities.FEATURE_LowLatency); - format.setInteger(MediaFormat.KEY_LOW_LATENCY, 1); - decoder.configure(format, 0 /* flags */, surface); - decoder.start(); - - if (!useNdk) { - decoder.getInputBuffers(); - } - ByteBuffer[] codecOutputBuffers = decoder.getOutputBuffers(); - String decoderOutputFormatString = null; - - // start decoding - final long kTimeOutUs = 1000000; // 1 second - MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); - int bufferCounter = 0; - long[] latencyMs = new long[frameCount]; - boolean waitingForOutput = false; - long startTimeMs = System.currentTimeMillis(); - while (bufferCounter < frameCount) { - if (!waitingForOutput) { - int inputBufferId = decoder.dequeueInputBuffer(kTimeOutUs); - if (inputBufferId < 0) { - Log.v(TAG, "no input buffer"); - break; - } - - ByteBuffer dstBuf = decoder.getInputBuffer(inputBufferId); - - int sampleSize = extractor.readSampleData(dstBuf, 0 /* offset */); - long presentationTimeUs = 0; - if (sampleSize < 0) { - Log.v(TAG, "had input EOS, early termination at frame " + bufferCounter); - break; - } else { - presentationTimeUs = extractor.getSampleTime(); - } - - startTimeMs = System.currentTimeMillis(); - decoder.queueInputBuffer( - inputBufferId, - 0 /* offset */, - sampleSize, - presentationTimeUs, - 0 /* flags */); - - extractor.advance(); - waitingForOutput = true; - } - - int outputBufferId = decoder.dequeueOutputBuffer(info, kTimeOutUs); - - if (outputBufferId >= 0) { - waitingForOutput = false; - //Log.d(TAG, "got output, size " + info.size + ", time " + info.presentationTimeUs); - latencyMs[bufferCounter++] = System.currentTimeMillis() - startTimeMs; - // TODO: render the frame and find the rendering time to calculate the total delay - decoder.releaseOutputBuffer(outputBufferId, false /* render */); - } else if (outputBufferId == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { - codecOutputBuffers = decoder.getOutputBuffers(); - Log.d(TAG, "output buffers have changed."); - } else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { - decoderOutputFormatString = decoder.getOutputFormatString(); - Log.d(TAG, "output format has changed to " + decoderOutputFormatString); - } else { - fail("No output buffer returned without frame delay, status " + outputBufferId); - } - } - - assertTrue("No INFO_OUTPUT_FORMAT_CHANGED from decoder", decoderOutputFormatString != null); - - long latencyMean = 0; - long latencyMax = 0; - int maxIndex = 0; - for (int i = 0; i < bufferCounter; ++i) { - latencyMean += latencyMs[i]; - if (latencyMs[i] > latencyMax) { - latencyMax = latencyMs[i]; - maxIndex = i; - } - } - if (bufferCounter > 0) { - latencyMean /= bufferCounter; - } - Log.d(TAG, entry + " latency average " + latencyMean + " ms, max " + latencyMax + - " ms at frame " + maxIndex); - - DeviceReportLog log = new DeviceReportLog(REPORT_LOG_NAME, "video_decoder_latency"); - String mime = format.getString(MediaFormat.KEY_MIME); - int width = format.getInteger(MediaFormat.KEY_WIDTH); - int height = format.getInteger(MediaFormat.KEY_HEIGHT); - log.addValue("codec_name", decoderName, ResultType.NEUTRAL, ResultUnit.NONE); - log.addValue("mime_type", mime, ResultType.NEUTRAL, ResultUnit.NONE); - log.addValue("width", width, ResultType.NEUTRAL, ResultUnit.NONE); - log.addValue("height", height, ResultType.NEUTRAL, ResultUnit.NONE); - log.addValue("video_res", testVideo, ResultType.NEUTRAL, ResultUnit.NONE); - log.addValue("decode_to", surface == null ? "buffer" : "surface", - ResultType.NEUTRAL, ResultUnit.NONE); - - log.addValue("average_latency", latencyMean, ResultType.LOWER_BETTER, ResultUnit.MS); - log.addValue("max_latency", latencyMax, ResultType.LOWER_BETTER, ResultUnit.MS); - - log.submit(getInstrumentation()); - - decoder.stop(); - decoder.release(); - extractor.release(); - } } diff --git a/tests/tests/media/decoder/src/android/media/decoder/cts/ImageReaderDecoderTest.java b/tests/tests/media/decoder/src/android/media/decoder/cts/ImageReaderDecoderTest.java index 5a9921bb18a..92da87697be 100644 --- a/tests/tests/media/decoder/src/android/media/decoder/cts/ImageReaderDecoderTest.java +++ b/tests/tests/media/decoder/src/android/media/decoder/cts/ImageReaderDecoderTest.java @@ -364,13 +364,6 @@ public class ImageReaderDecoderTest { mediaFormat = mExtractor.getTrackFormat(0); mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, colorFormat); - if (mMime.equals(MediaFormat.MIMETYPE_VIDEO_VP9) && video.contains("10bit")) { - // TODO: b/295804596 - parse color profiles in vp9 - // In some cases, webm extractor may not signal - // profile for 10-bit VP9 clips. In such cases, set profile to a - // 10-bit compatible profile. - mediaFormat.setInteger(MediaFormat.KEY_PROFILE, CodecProfileLevel.VP9Profile2); - } MediaCodecInfo info = mDecoder.getCodecInfo(); CodecCapabilities caps = info.getCapabilitiesForType(mMime); diff --git a/tests/tests/media/extractor/AndroidTest.xml b/tests/tests/media/extractor/AndroidTest.xml index e88c69c9c6e..6df771dbf43 100644 --- a/tests/tests/media/extractor/AndroidTest.xml +++ b/tests/tests/media/extractor/AndroidTest.xml @@ -41,7 +41,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="CtsMediaExtractorTestCases-2.1" /> + <option name="media-folder-name" value="CtsMediaExtractorTestCases-2.2" /> <option name="dynamic-config-module" value="CtsMediaExtractorTestCases" /> </target_preparer> <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> diff --git a/tests/tests/media/extractor/DynamicConfig.xml b/tests/tests/media/extractor/DynamicConfig.xml index 0056677a140..766524c8926 100644 --- a/tests/tests/media/extractor/DynamicConfig.xml +++ b/tests/tests/media/extractor/DynamicConfig.xml @@ -15,6 +15,6 @@ <dynamicConfig> <entry key="media_files_url"> - <value>https://dl.google.com/android/xts/cts/tests/tests/media/extractor/CtsMediaExtractorTestCases-2.1.zip</value> + <value>https://dl.google.com/android/xts/cts/tests/tests/media/extractor/CtsMediaExtractorTestCases-2.2.zip</value> </entry> </dynamicConfig> diff --git a/tests/tests/media/extractor/copy_media.sh b/tests/tests/media/extractor/copy_media.sh index 8b83bdf18b7..b4e5ba03762 100755 --- a/tests/tests/media/extractor/copy_media.sh +++ b/tests/tests/media/extractor/copy_media.sh @@ -17,4 +17,4 @@ [ -z "$MEDIA_ROOT_DIR" ] && MEDIA_ROOT_DIR=$(dirname $0)/.. source $MEDIA_ROOT_DIR/common/copy_media_utils.sh get_adb_options "$@" -copy_media "extractor" "CtsMediaExtractorTestCases-2.1" +copy_media "extractor" "CtsMediaExtractorTestCases-2.2" diff --git a/tests/tests/media/extractor/src/android/media/extractor/cts/MediaExtractorTest.java b/tests/tests/media/extractor/src/android/media/extractor/cts/MediaExtractorTest.java index d4e3ff4a21b..51775516924 100644 --- a/tests/tests/media/extractor/src/android/media/extractor/cts/MediaExtractorTest.java +++ b/tests/tests/media/extractor/src/android/media/extractor/cts/MediaExtractorTest.java @@ -372,6 +372,22 @@ public class MediaExtractorTest { assertEquals("video/av01", mimeType); } + // DolbyVisionMediaExtractor for trimmed Dolby Vision file. + // Please check https://issuetracker.google.com/329121650 for details. + @Test + public void testTrimmedDolbyVisionFileTrackDuration() throws Exception { + TestMediaDataSource dataSource = setDataSource("video_dovi_dvhe_08_trimmed.mp4"); + + final int trackCount = mExtractor.getTrackCount(); + assertTrue("There should be at least one track", 0 < trackCount); + + long expectedTrackDurationUs = 3_026_666; + for (int i = 0; i < trackCount; i++) { + MediaFormat trackFormat = mExtractor.getTrackFormat(i); + assertEquals(expectedTrackDurationUs, trackFormat.getLong(MediaFormat.KEY_DURATION)); + } + } + //MPEG-H 3D Audio single stream (mha1) @Test public void testMpegh3dAudioMediaExtractorMha1() throws Exception { diff --git a/tests/tests/media/extractor/src/android/media/extractor/cts/WorkDir.java b/tests/tests/media/extractor/src/android/media/extractor/cts/WorkDir.java index 8b847e26e05..31c069e2378 100644 --- a/tests/tests/media/extractor/src/android/media/extractor/cts/WorkDir.java +++ b/tests/tests/media/extractor/src/android/media/extractor/cts/WorkDir.java @@ -20,6 +20,6 @@ import android.media.cts.WorkDirBase; class WorkDir extends WorkDirBase { public static final String getMediaDirString() { - return getMediaDirString("CtsMediaExtractorTestCases-2.1"); + return getMediaDirString("CtsMediaExtractorTestCases-2.2"); } } diff --git a/tests/tests/media/misc/AndroidTest.xml b/tests/tests/media/misc/AndroidTest.xml index ed3133fe29c..546a15bac8d 100644 --- a/tests/tests/media/misc/AndroidTest.xml +++ b/tests/tests/media/misc/AndroidTest.xml @@ -41,7 +41,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="CtsMediaMiscTestCases-2.2" /> + <option name="media-folder-name" value="CtsMediaMiscTestCases-2.3" /> <option name="dynamic-config-module" value="CtsMediaMiscTestCases" /> </target_preparer> <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> diff --git a/tests/tests/media/misc/DynamicConfig.xml b/tests/tests/media/misc/DynamicConfig.xml index ed21ce3cd35..2eec4491804 100644 --- a/tests/tests/media/misc/DynamicConfig.xml +++ b/tests/tests/media/misc/DynamicConfig.xml @@ -15,6 +15,6 @@ <dynamicConfig> <entry key="media_files_url"> - <value>https://dl.google.com/android/xts/cts/tests/tests/media/misc/CtsMediaMiscTestCases-2.2.zip</value> + <value>https://dl.google.com/android/xts/cts/tests/tests/media/misc/CtsMediaMiscTestCases-2.3.zip</value> </entry> </dynamicConfig> diff --git a/tests/tests/media/misc/copy_media.sh b/tests/tests/media/misc/copy_media.sh index c8381975d7b..16538a1bf95 100755 --- a/tests/tests/media/misc/copy_media.sh +++ b/tests/tests/media/misc/copy_media.sh @@ -17,4 +17,4 @@ [ -z "$MEDIA_ROOT_DIR" ] && MEDIA_ROOT_DIR=$(dirname $0)/.. source $MEDIA_ROOT_DIR/common/copy_media_utils.sh get_adb_options "$@" -copy_media "misc" "CtsMediaMiscTestCases-2.2" +copy_media "misc" "CtsMediaMiscTestCases-2.3" diff --git a/tests/tests/media/misc/src/android/media/misc/cts/MediaMetadataRetrieverTest.java b/tests/tests/media/misc/src/android/media/misc/cts/MediaMetadataRetrieverTest.java index 8c5bf2fd5eb..020fd09895e 100644 --- a/tests/tests/media/misc/src/android/media/misc/cts/MediaMetadataRetrieverTest.java +++ b/tests/tests/media/misc/src/android/media/misc/cts/MediaMetadataRetrieverTest.java @@ -1242,17 +1242,15 @@ public class MediaMetadataRetrieverTest { @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName = "VanillaIceCream") public void testGetImageAtIndexAvifWithCrop() throws Exception { - // sample_1960x1120_crop_20_20_1920_1080.avif is a 1960x1120 AVIF image with the crop window - // set to left: 20 top: 20 crop width: 1920 crop height: 1080. The cropped image should + // sample_720x480_crop_20_20_680_440.avif is a 720x480 AVIF image with the crop window + // set to left: 20 top: 20 crop width: 680 crop height: 440. The cropped image should // contain the same pixels as other sample AVIF images. So in order to verify the cropping, // it is sufficient to pass checkColor to true to testGetImage. If the cropping was // incorrect, then checking of color bars will fail. The expected width and height should be // that of the cropped image. - if (!MediaUtils.canDecodeVideo(MediaFormat.MIMETYPE_VIDEO_AV1, 1960, 1120, 30)) { - MediaUtils.skipTest("No AV1 codec for 1960x1120"); - return; - } - testGetImage("sample_1960x1120_crop_20_20_1920_1080.avif", 1920, 1080, "image/avif", + assumeTrue("No AV1 codec for 720x480", + MediaUtils.canDecodeVideo(MediaFormat.MIMETYPE_VIDEO_AV1, 720, 480, 30)); + testGetImage("sample_720x480_crop_20_20_680_440.avif", 680, 440, "image/avif", 0 /*rotation*/, 1 /*imageCount*/, 0 /*primary*/, false /*useGrid*/, true /*checkColor*/); } diff --git a/tests/tests/media/misc/src/android/media/misc/cts/WorkDir.java b/tests/tests/media/misc/src/android/media/misc/cts/WorkDir.java index 6cbab7c5376..e8e3e183aed 100644 --- a/tests/tests/media/misc/src/android/media/misc/cts/WorkDir.java +++ b/tests/tests/media/misc/src/android/media/misc/cts/WorkDir.java @@ -20,6 +20,6 @@ import android.media.cts.WorkDirBase; class WorkDir extends WorkDirBase { public static final String getMediaDirString() { - return getMediaDirString("CtsMediaMiscTestCases-2.2"); + return getMediaDirString("CtsMediaMiscTestCases-2.3"); } } diff --git a/tests/tests/mediacujtest/common/Android.bp b/tests/tests/mediacujtest/common/Android.bp index 5f95f923101..4a9ed91a8b9 100644 --- a/tests/tests/mediacujtest/common/Android.bp +++ b/tests/tests/mediacujtest/common/Android.bp @@ -30,6 +30,7 @@ android_library { "androidx.media3.media3-exoplayer", "androidx.media3.media3-ui", "permission-test-util-lib", + "cts-wm-util", ], libs: [ "auto_value_annotations", @@ -38,6 +39,6 @@ android_library { "src/**/*.java", ], plugins: ["auto_value_plugin"], + platform_apis: true, min_sdk_version: "30", - sdk_version: "current", } diff --git a/tests/tests/mediacujtest/common/res/layout/activity_main.xml b/tests/tests/mediacujtest/common/res/layout/activity_main.xml index c34567d791e..17a17d6e355 100644 --- a/tests/tests/mediacujtest/common/res/layout/activity_main.xml +++ b/tests/tests/mediacujtest/common/res/layout/activity_main.xml @@ -16,6 +16,7 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" + xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> @@ -23,5 +24,12 @@ <androidx.media3.ui.PlayerView android:id="@+id/exoplayer" android:layout_width="match_parent" - android:layout_height="match_parent" /> + android:layout_height="match_parent"> + <ImageButton + android:id="@+id/lock_controller" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + app:srcCompat="@android:drawable/ic_lock_idle_lock" /> + </androidx.media3.ui.PlayerView> + </LinearLayout> diff --git a/tests/tests/mediacujtest/common/src/android/media/cujcommon/cts/CujTestBase.java b/tests/tests/mediacujtest/common/src/android/media/cujcommon/cts/CujTestBase.java index 6181f8eef38..3b246cb14f5 100644 --- a/tests/tests/mediacujtest/common/src/android/media/cujcommon/cts/CujTestBase.java +++ b/tests/tests/mediacujtest/common/src/android/media/cujcommon/cts/CujTestBase.java @@ -19,6 +19,7 @@ package android.media.cujcommon.cts; import static org.junit.Assert.assertEquals; import android.app.Activity; +import android.app.ActivityTaskManager; import android.content.Context; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; @@ -93,6 +94,13 @@ public class CujTestBase { } /** + * Whether the device supports split-screen feature. + */ + public static boolean deviceSupportSplitScreenMode(final Activity activity) { + return ActivityTaskManager.supportsSplitScreenMultiWindow(activity); + } + + /** * Whether the device is a watch. */ public static boolean isWatchDevice(final Activity activity) { diff --git a/tests/tests/mediacujtest/common/src/android/media/cujcommon/cts/DeviceLockTestPlayerListener.java b/tests/tests/mediacujtest/common/src/android/media/cujcommon/cts/DeviceLockTestPlayerListener.java new file mode 100644 index 00000000000..ceb7ecd88d2 --- /dev/null +++ b/tests/tests/mediacujtest/common/src/android/media/cujcommon/cts/DeviceLockTestPlayerListener.java @@ -0,0 +1,119 @@ +/** + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.cujcommon.cts; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.os.Looper; +import android.server.wm.UiDeviceUtils; +import android.view.Display; + +import androidx.annotation.NonNull; +import androidx.media3.common.Player; + +import java.util.Timer; +import java.util.TimerTask; + +public class DeviceLockTestPlayerListener extends PlayerListener { + + private static final int LOCK_DURATION_MS = 5000; + private static final int DELAY_MS = 2000; + + private final boolean mIsAudioOnlyClip; + + private Display mDisplay; + private boolean mIsPlayerPlaying; + + public DeviceLockTestPlayerListener(long sendMessagePosition, boolean isAudioOnlyClip) { + super(); + this.mSendMessagePosition = sendMessagePosition; + this.mIsAudioOnlyClip = isAudioOnlyClip; + } + + @Override + public void onIsPlayingChanged(boolean isPlaying) { + super.onIsPlayingChanged(isPlaying); + mIsPlayerPlaying = isPlaying; + } + + @Override + public TestType getTestType() { + return TestType.DEVICE_LOCK_TEST; + } + + @Override + public void onEventsPlaybackStateChanged(@NonNull Player player) { + if (player.getPlaybackState() == Player.STATE_READY) { + // At the first media transition player is not ready. So, add duration of + // first clip when player is ready + mExpectedTotalTime += player.getDuration() + LOCK_DURATION_MS; + // Register the screen receiver to listen for screen on and off events + mDisplay = mActivity.getDisplay(); + } + } + + @Override + public void onEventsMediaItemTransition(@NonNull Player player) { + mActivity.mPlayer.createMessage((messageType, payload) -> { + // Lock the device + UiDeviceUtils.pressSleepButton(); + // Unlock the device after LOCK_DURATION + Timer timer = new Timer(); + timer.schedule(new Task(), LOCK_DURATION_MS); + }).setLooper(Looper.getMainLooper()).setPosition(mSendMessagePosition) + .setDeleteAfterDelivery(true) + .send(); + mActivity.mPlayer.createMessage((messageType, payload) -> { + // Verify that the screen is on and player is playing while device is in unlocked state + assertTrue(isDisplayOn()); + assertTrue(mIsPlayerPlaying); + }).setLooper(Looper.getMainLooper()) + .setPosition(mIsAudioOnlyClip ? (mSendMessagePosition + LOCK_DURATION_MS + DELAY_MS) + : (mSendMessagePosition + DELAY_MS)) + .setDeleteAfterDelivery(true) + .send(); + } + + private boolean isDisplayOn() { + return mDisplay != null && mDisplay.getState() == Display.STATE_ON; + } + + class Task extends TimerTask { + + @Override + public void run() { + unlockPhone(); + } + + private void unlockPhone() { + // Verify that the screen is off when device is in locked state + assertFalse(isDisplayOn()); + if (mIsAudioOnlyClip) { + // In case of audio only clip, verify that the player is playing while device is in + // locked state + assertTrue(mIsPlayerPlaying); + } else { + // Otherwise verify that the player is not playing while device is in locked state + assertFalse(mIsPlayerPlaying); + } + // Unlock the device + UiDeviceUtils.pressWakeupButton(); + UiDeviceUtils.pressUnlockButton(); + } + } +} diff --git a/tests/tests/mediacujtest/common/src/android/media/cujcommon/cts/LockPlaybackControllerTestPlayerListener.java b/tests/tests/mediacujtest/common/src/android/media/cujcommon/cts/LockPlaybackControllerTestPlayerListener.java new file mode 100644 index 00000000000..40ca776f502 --- /dev/null +++ b/tests/tests/mediacujtest/common/src/android/media/cujcommon/cts/LockPlaybackControllerTestPlayerListener.java @@ -0,0 +1,131 @@ +/** + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.cujcommon.cts; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.os.Looper; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.media3.common.Player; +import androidx.media3.common.Player.PositionInfo; + +public class LockPlaybackControllerTestPlayerListener extends PlayerListener { + + private static final int MESSAGE_INTERVAL_MS = 2000; + + private boolean mRewindDone; + private boolean mIsControllerLocked; + + public LockPlaybackControllerTestPlayerListener(long sendMessagePosition) { + super(); + this.mSendMessagePosition = sendMessagePosition; + } + + @Override + public void onPositionDiscontinuity(PositionInfo oldPosition, PositionInfo newPosition, + int reason) { + super.onPositionDiscontinuity(oldPosition, newPosition, reason); + // Verify that the position has changed due to seek + assertEquals(Player.DISCONTINUITY_REASON_SEEK, reason); + // Update the variable if rewind has been executed + mRewindDone = (newPosition.positionMs < oldPosition.positionMs); + // Add change in duration due to seek + mExpectedTotalTime += (mSendMessagePosition - newPosition.positionMs); + } + + @Override + public TestType getTestType() { + return TestType.LOCK_PLAYBACK_CONTROLLER_TEST; + } + + @Override + public void onEventsPlaybackStateChanged(@NonNull Player player) { + if (player.getPlaybackState() == Player.STATE_READY) { + // At the first media transition player is not ready. So, add duration of + // first clip when player is ready + mExpectedTotalTime += player.getDuration(); + // Set the controller show timeout to 0 so that controller UI stays eternally + mActivity.mExoplayerView.setControllerShowTimeoutMs(0); + mActivity.mExoplayerView.showController(); + activateLockButton(mActivity); + } + } + + @Override + public void onEventsMediaItemTransition(@NonNull Player player) { + mActivity.mPlayer.createMessage((messageType, payload) -> { + // Rewind by 5 sec + mActivity.mExoRewindButton.performClick(); + }).setLooper(Looper.getMainLooper()) + .setPosition(mSendMessagePosition) + .setDeleteAfterDelivery(true) + .send(); + mActivity.mPlayer.createMessage((messageType, payload) -> { + // Verify that the rewind has been executed + assertTrue(mRewindDone); + mRewindDone = false; + // Lock the playback controller + mActivity.mLockControllerButton.performClick(); + }).setLooper(Looper.getMainLooper()) + .setPosition(mSendMessagePosition + MESSAGE_INTERVAL_MS) + .setDeleteAfterDelivery(true) + .send(); + mActivity.mPlayer.createMessage((messageType, payload) -> { + // Verify that the controller UI is locked + assertTrue(mIsControllerLocked); + // Try to rewind while the controller UI is locked + mActivity.mExoRewindButton.performClick(); + }).setLooper(Looper.getMainLooper()) + .setPosition(mSendMessagePosition + 2 * MESSAGE_INTERVAL_MS) + .setDeleteAfterDelivery(true) + .send(); + mActivity.mPlayer.createMessage((messageType, payload) -> { + // Verify that the rewind has not been executed + assertFalse(mRewindDone); + // Unlock the playback controller + mActivity.mLockControllerButton.performClick(); + }).setLooper(Looper.getMainLooper()) + .setPosition(mSendMessagePosition + 3 * MESSAGE_INTERVAL_MS) + .setDeleteAfterDelivery(true) + .send(); + } + + /** + * By default, the lock controller button is invisible. So we need to make it visible and set its + * onClick listener. + * + * @param activity MainActivity + */ + private void activateLockButton(MainActivity activity) { + activity.mLockControllerButton.setVisibility(View.VISIBLE); + activity.mLockControllerButton.setOnClickListener(view -> { + if (mIsControllerLocked) { + mIsControllerLocked = false; + activity.mExoplayerView.setUseController(true); + activity.mExoplayerView.showController(); + } else { + mIsControllerLocked = true; + activity.mExoplayerView.hideController(); + activity.mExoplayerView.setUseController(false); + } + }); + } +} diff --git a/tests/tests/mediacujtest/common/src/android/media/cujcommon/cts/MainActivity.java b/tests/tests/mediacujtest/common/src/android/media/cujcommon/cts/MainActivity.java index 1c7dee3f969..1a97ce80181 100644 --- a/tests/tests/mediacujtest/common/src/android/media/cujcommon/cts/MainActivity.java +++ b/tests/tests/mediacujtest/common/src/android/media/cujcommon/cts/MainActivity.java @@ -24,14 +24,19 @@ import android.net.Uri; import android.os.Bundle; import android.view.MotionEvent; import android.view.ScaleGestureDetector; +import android.view.View; import android.view.WindowManager; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; +import androidx.media3.common.C; import androidx.media3.common.MediaItem; +import androidx.media3.common.Tracks.Group; import androidx.media3.exoplayer.ExoPlayer; import androidx.media3.ui.PlayerView; +import com.google.common.collect.ImmutableList; + import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -47,6 +52,8 @@ public class MainActivity extends AppCompatActivity { protected boolean mIsInPipMode; protected boolean mConfiguredSplitScreenMode; protected boolean mIsInMultiWindowMode; + protected View mExoRewindButton; + protected View mLockControllerButton; @Override protected void onCreate(Bundle savedInstanceState) { @@ -62,6 +69,9 @@ public class MainActivity extends AppCompatActivity { protected void buildPlayer() { mPlayer = new ExoPlayer.Builder(this).build(); mExoplayerView = findViewById(R.id.exoplayer); + mLockControllerButton = findViewById(R.id.lock_controller); + mLockControllerButton.setVisibility(View.INVISIBLE); + mExoRewindButton = findViewById(androidx.media3.ui.R.id.exo_rew_with_amount); mExoplayerView.setPlayer(mPlayer); } @@ -94,8 +104,15 @@ public class MainActivity extends AppCompatActivity { */ @Override protected void onStop() { - mPlayer.stop(); - super.onStop(); + // When activity is stopped, don't pause the playback if it is an audio only clip + ImmutableList<Group> currentTrackGroups = mPlayer.getCurrentTracks().getGroups(); + if ((currentTrackGroups.size() == 1) && (currentTrackGroups.get(0).getType() + == C.TRACK_TYPE_AUDIO)) { + super.onStop(); + } else { + mPlayer.stop(); + super.onStop(); + } } /** @@ -121,7 +138,7 @@ public class MainActivity extends AppCompatActivity { if (mScaleGestureDetector != null) { mScaleGestureDetector.onTouchEvent(event); } - return true; + return super.dispatchTouchEvent(event); } /** diff --git a/tests/tests/mediacujtest/common/src/android/media/cujcommon/cts/PlayerListener.java b/tests/tests/mediacujtest/common/src/android/media/cujcommon/cts/PlayerListener.java index b02da44b74a..1a6fe308bd4 100644 --- a/tests/tests/mediacujtest/common/src/android/media/cujcommon/cts/PlayerListener.java +++ b/tests/tests/mediacujtest/common/src/android/media/cujcommon/cts/PlayerListener.java @@ -45,7 +45,7 @@ public abstract class PlayerListener implements Player.Listener { public static int CURRENT_MEDIA_INDEX = 0; // Enum Declared for Test Type - protected enum TestType { + public enum TestType { PLAYBACK_TEST, SEEK_TEST, ORIENTATION_TEST, @@ -58,7 +58,9 @@ public abstract class PlayerListener implements Player.Listener { PINCH_TO_ZOOM_TEST, SPEED_CHANGE_TEST, PIP_MODE_TEST, - SPLIT_SCREEN_TEST + SPLIT_SCREEN_TEST, + DEVICE_LOCK_TEST, + LOCK_PLAYBACK_CONTROLLER_TEST } public static boolean mPlaybackEnded; diff --git a/tests/tests/mediacujtest/common/src/android/media/cujcommon/cts/SplitScreenTestPlayerListener.java b/tests/tests/mediacujtest/common/src/android/media/cujcommon/cts/SplitScreenTestPlayerListener.java index da1f9766af2..8c9e5213281 100644 --- a/tests/tests/mediacujtest/common/src/android/media/cujcommon/cts/SplitScreenTestPlayerListener.java +++ b/tests/tests/mediacujtest/common/src/android/media/cujcommon/cts/SplitScreenTestPlayerListener.java @@ -20,7 +20,6 @@ import static org.junit.Assert.assertTrue; import android.content.Intent; import android.os.Looper; -import android.provider.Settings; import androidx.annotation.NonNull; import androidx.media3.common.Player; @@ -52,7 +51,7 @@ public class SplitScreenTestPlayerListener extends PlayerListener { public void onEventsMediaItemTransition(@NonNull Player player) { mActivity.mPlayer.createMessage((messageType, payload) -> { // Switch to split screen mode - Intent intent = new Intent(Settings.ACTION_SETTINGS); + Intent intent = new Intent(Intent.ACTION_MAIN); intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT | Intent.FLAG_ACTIVITY_NEW_TASK); mActivity.startActivity(intent); mActivity.mConfiguredSplitScreenMode = true; diff --git a/tests/tests/mediacujtest/largetest/AndroidManifest.xml b/tests/tests/mediacujtest/largetest/AndroidManifest.xml index 8b2be7a6481..a58d3dd0d5e 100644 --- a/tests/tests/mediacujtest/largetest/AndroidManifest.xml +++ b/tests/tests/mediacujtest/largetest/AndroidManifest.xml @@ -40,9 +40,6 @@ android:label="Media E2E tests InstrumentationRunner" android:name="androidx.test.runner.AndroidJUnitRunner" android:targetPackage="android.media.cujlargetest.cts"> - <meta-data - android:name="listener" - android:value="com.android.cts.runner.CtsTestRunListener" /> </instrumentation> </manifest> diff --git a/tests/tests/mediacujtest/smalltest/AndroidManifest.xml b/tests/tests/mediacujtest/smalltest/AndroidManifest.xml index a748dbeb9ea..3d988702325 100644 --- a/tests/tests/mediacujtest/smalltest/AndroidManifest.xml +++ b/tests/tests/mediacujtest/smalltest/AndroidManifest.xml @@ -38,8 +38,5 @@ android:label="Media E2E tests InstrumentationRunner" android:name="androidx.test.runner.AndroidJUnitRunner" android:targetPackage="android.media.cujsmalltest.cts"> - <meta-data - android:name="listener" - android:value="com.android.cts.runner.CtsTestRunListener" /> </instrumentation> </manifest> diff --git a/tests/tests/mediacujtest/smalltest/res/raw/ElephantsDream_2ch_48Khz_15s.mp3 b/tests/tests/mediacujtest/smalltest/res/raw/ElephantsDream_2ch_48Khz_15s.mp3 Binary files differnew file mode 100644 index 00000000000..101c302d92f --- /dev/null +++ b/tests/tests/mediacujtest/smalltest/res/raw/ElephantsDream_2ch_48Khz_15s.mp3 diff --git a/tests/tests/mediacujtest/smalltest/src/android/media/cujsmalltest/cts/CtsMediaShortFormPlaybackTest.java b/tests/tests/mediacujtest/smalltest/src/android/media/cujsmalltest/cts/CtsMediaShortFormPlaybackTest.java index bb95b35a1f8..693026375f8 100644 --- a/tests/tests/mediacujtest/smalltest/src/android/media/cujsmalltest/cts/CtsMediaShortFormPlaybackTest.java +++ b/tests/tests/mediacujtest/smalltest/src/android/media/cujsmalltest/cts/CtsMediaShortFormPlaybackTest.java @@ -18,10 +18,13 @@ package android.media.cujsmalltest.cts; import android.media.cujcommon.cts.CujTestBase; import android.media.cujcommon.cts.CujTestParam; +import android.media.cujcommon.cts.DeviceLockTestPlayerListener; +import android.media.cujcommon.cts.LockPlaybackControllerTestPlayerListener; import android.media.cujcommon.cts.OrientationTestPlayerListener; import android.media.cujcommon.cts.PinchToZoomTestPlayerListener; import android.media.cujcommon.cts.PipModeTestPlayerListener; import android.media.cujcommon.cts.PlaybackTestPlayerListener; +import android.media.cujcommon.cts.PlayerListener.TestType; import android.media.cujcommon.cts.ScrollTestPlayerListener; import android.media.cujcommon.cts.SeekTestPlayerListener; import android.media.cujcommon.cts.SplitScreenTestPlayerListener; @@ -79,6 +82,8 @@ public class CtsMediaShortFormPlaybackTest extends CujTestBase { "android.resource://android.media.cujsmalltest.cts/raw/tearsofsteel_srt_subtitles_eng_fre_5sec"; private static final String MKV_TEARS_OF_STEEL_ASSET_SSA_SUBTITLES_ENG_FRENCH_URI_STRING = "android.resource://android.media.cujsmalltest.cts/raw/tearsofsteel_ssa_subtitles_eng_fre_5sec"; + private static final String MP3_ELEPHANTSDREAM_2CH_48KHZ_URI_STRING = + "android.resource://android.media.cujsmalltest.cts/raw/ElephantsDream_2ch_48Khz_15s"; CujTestParam mCujTestParam; private final String mTestType; @@ -143,6 +148,18 @@ public class CtsMediaShortFormPlaybackTest extends CujTestBase { .setTimeoutMilliSeconds(45000) .setPlayerListener(new SplitScreenTestPlayerListener(5000)).build(), "Hevc_720p_15sec_SplitScreenTest"}, + {CujTestParam.builder().setMediaUrls(prepareHevc_720p_15sec_SingleVideoList()) + .setTimeoutMilliSeconds(50000) + .setPlayerListener(new DeviceLockTestPlayerListener(3000, false)).build(), + "Hevc_720p_15sec_DeviceLockTest"}, + {CujTestParam.builder().setMediaUrls(prepareMp3_15secAudioListForDeviceLockTest()) + .setTimeoutMilliSeconds(45000) + .setPlayerListener(new DeviceLockTestPlayerListener(3000, true)).build(), + "Mp3_15sec_DeviceLockTest"}, + {CujTestParam.builder().setMediaUrls(prepareMp3_15secAudioListForDeviceLockTest()) + .setTimeoutMilliSeconds(50000) + .setPlayerListener(new LockPlaybackControllerTestPlayerListener(6000)).build(), + "Hevc_720p_15sec_LockPlaybackTest"}, })); return exhaustiveArgsList; } @@ -256,6 +273,14 @@ public class CtsMediaShortFormPlaybackTest extends CujTestBase { return videoInput; } + /** + * Prepare Mp3 15sec audio list for Device Lock Test. + */ + public static List<String> prepareMp3_15secAudioListForDeviceLockTest() { + List<String> audioInput = Arrays.asList( + MP3_ELEPHANTSDREAM_2CH_48KHZ_URI_STRING); + return audioInput; + } // Test to Verify video playback with and without seek @ApiTest(apis = {"android.media.MediaCodec#configure", @@ -284,9 +309,17 @@ public class CtsMediaShortFormPlaybackTest extends CujTestBase { deviceSupportPipMode(mActivity)); } if (mCujTestParam.playerListener().isSplitScreenTest()) { - Assume.assumeFalse("Skipping " + mTestType + " on television", isTelevisionDevice(mActivity)); + Assume.assumeTrue("Skipping " + mTestType + " as device doesn't support split screen feature", + deviceSupportSplitScreenMode(mActivity)); + } + if (mCujTestParam.playerListener().getTestType() + .equals(TestType.LOCK_PLAYBACK_CONTROLLER_TEST)) { Assume.assumeFalse("Skipping " + mTestType + " on watch", isWatchDevice(mActivity)); } + if (mCujTestParam.playerListener().getTestType().equals(TestType.DEVICE_LOCK_TEST)) { + Assume.assumeFalse("Skipping " + mTestType + " on watch", isWatchDevice(mActivity)); + Assume.assumeFalse("Skipping " + mTestType + " on television", isTelevisionDevice(mActivity)); + } play(mCujTestParam.mediaUrls(), mCujTestParam.timeoutMilliSeconds()); } } diff --git a/tests/tests/mediaediting/AndroidManifest.xml b/tests/tests/mediaediting/AndroidManifest.xml index 789cd48a110..72e76c1a2fe 100644 --- a/tests/tests/mediaediting/AndroidManifest.xml +++ b/tests/tests/mediaediting/AndroidManifest.xml @@ -31,8 +31,6 @@ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" android:targetPackage="android.media.mediaediting.cts" android:label="Media editing tests InstrumentationRunner"> - <meta-data android:name="listener" - android:value="com.android.cts.runner.CtsTestRunListener"/> </instrumentation> </manifest> diff --git a/tests/tests/mediaediting/src/android/media/mediaediting/cts/AndroidTestUtil.java b/tests/tests/mediaediting/src/android/media/mediaediting/cts/AndroidTestUtil.java index 6517f2f9fcc..d3749c967e1 100644 --- a/tests/tests/mediaediting/src/android/media/mediaediting/cts/AndroidTestUtil.java +++ b/tests/tests/mediaediting/src/android/media/mediaediting/cts/AndroidTestUtil.java @@ -628,7 +628,9 @@ public final class AndroidTestUtil { if (image == null) { break; } - bitmaps.add(BitmapPixelTestUtil.createGrayscaleArgb8888BitmapFromYuv420888Image(image)); + bitmaps.add( + BitmapPixelTestUtil.createGrayscaleBitmapFromYuv420888Image( + image, Bitmap.Config.ARGB_8888)); image.close(); } } diff --git a/tests/tests/mediaediting/src/android/media/mediaediting/cts/TranscodeQualityTest.java b/tests/tests/mediaediting/src/android/media/mediaediting/cts/TranscodeQualityTest.java index 94ae747478e..f72b1b6300c 100644 --- a/tests/tests/mediaediting/src/android/media/mediaediting/cts/TranscodeQualityTest.java +++ b/tests/tests/mediaediting/src/android/media/mediaediting/cts/TranscodeQualityTest.java @@ -196,6 +196,12 @@ public final class TranscodeQualityTest { .setRequestCalculateSsim(true) .build() .run(testId, editedMediaItem); + + if (!isWithinCddRequirements) { + Assume.assumeFalse("Skipping transcodeTest for " + testId, + result.fallbackDetails != null + && result.fallbackDetails.fallbackOutputHeight != height); + } assertThat(result.ssim).isGreaterThan(EXPECTED_MINIMUM_SSIM); } } diff --git a/tests/tests/networksecurityconfig/manifest.xml b/tests/tests/networksecurityconfig/manifest.xml index 2950baf7109..93250d75ff3 100644 --- a/tests/tests/networksecurityconfig/manifest.xml +++ b/tests/tests/networksecurityconfig/manifest.xml @@ -28,7 +28,5 @@ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" android:targetPackage="android.security.net.config.cts" android:label=""> - <meta-data android:name="listener" - android:value="com.android.cts.runner.CtsTestRunListener" /> </instrumentation> </manifest> diff --git a/tests/tests/notification/OWNERS b/tests/tests/notification/OWNERS index ad9dd5955b7..4628f8c5f64 100644 --- a/tests/tests/notification/OWNERS +++ b/tests/tests/notification/OWNERS @@ -1,4 +1 @@ -# Bug component: 181562 -juliacr@google.com -jeffdq@google.com -dsandler@android.com
\ No newline at end of file +include platform/frameworks/base:/services/core/java/com/android/server/notification/OWNERS
\ No newline at end of file diff --git a/tests/tests/os/src/android/os/cts/FileObserverTest.java b/tests/tests/os/src/android/os/cts/FileObserverTest.java index 26ea8952bc0..96e5e0282b5 100644 --- a/tests/tests/os/src/android/os/cts/FileObserverTest.java +++ b/tests/tests/os/src/android/os/cts/FileObserverTest.java @@ -205,19 +205,27 @@ public class FileObserverTest extends AndroidTestCase { private void verifyTriggeredEventsOnFile(MockFileObserver fileObserver, File testFile, boolean isEmulated) throws Exception { + // We create, write, close the file + // The effects of this vary - create first truncates the existing file, + // then opens it, then modifies it, then closes it + // Prior to kernel 6.6, this produced a modify/open/modify/close-write + // In 6.6, the behavior was changed to combine the two modifies + // See: + // https://lore.kernel.org/all/CAL=UVf5hZNVUPv4WdLsyMw5X8kP-3=gwU9mymWS_3APTVuSacQ@mail.gmail.com/T/ + // though unfortunately the reply from the maintainer seems to have been lost final FileOutputStream out = new FileOutputStream(testFile); out.write(FILE_DATA); // modify, open, write, modify out.close(); // close_write final int[] expected = { - FileObserver.MODIFY, FileObserver.OPEN, FileObserver.MODIFY, FileObserver.CLOSE_WRITE }; final FileEvent[] moveEvents = waitForEvent(fileObserver); + assertEventsContains(testFile, expected, moveEvents); } diff --git a/tests/tests/packagewatchdog/OWNERS b/tests/tests/packagewatchdog/OWNERS index 7448086fe69..57af335fefd 100644 --- a/tests/tests/packagewatchdog/OWNERS +++ b/tests/tests/packagewatchdog/OWNERS @@ -1,3 +1,2 @@ # Bug component: 557916 -gavincorkery@google.com -olilan@google.com +include platform/frameworks/base:/services/core/java/com/android/server/crashrecovery/OWNERS diff --git a/tests/tests/security/res/raw/blocklist_diginotar.pem b/tests/tests/security/res/raw/blocklist_diginotar.pem new file mode 100644 index 00000000000..b972b4bc50c --- /dev/null +++ b/tests/tests/security/res/raw/blocklist_diginotar.pem @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFijCCA3KgAwIBAgIQDHbanJEMTiye/hXQWJM8TDANBgkqhkiG9w0BAQUFADBf +MQswCQYDVQQGEwJOTDESMBAGA1UEChMJRGlnaU5vdGFyMRowGAYDVQQDExFEaWdp +Tm90YXIgUm9vdCBDQTEgMB4GCSqGSIb3DQEJARYRaW5mb0BkaWdpbm90YXIubmww +HhcNMDcwNTE2MTcxOTM2WhcNMjUwMzMxMTgxOTIxWjBfMQswCQYDVQQGEwJOTDES +MBAGA1UEChMJRGlnaU5vdGFyMRowGAYDVQQDExFEaWdpTm90YXIgUm9vdCBDQTEg +MB4GCSqGSIb3DQEJARYRaW5mb0BkaWdpbm90YXIubmwwggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQCssFjBAL3YIQgLK5r+blYwBZ8bd5AQQVzDDYcRd46B +8cp86Yxq7Th0Nbva3/m7wAk3tJZzgX0zGpg595NvlX89ubF1h7pRSOiLcD6VBMXY +tsMW2YiwsYcdcNqGtA8Ui3rPENF0NqISe3eGSnnme98CEWilToauNFibJBN4ViIl +HgGLS1Fx+4LMWZZpiFpoU8W5DQI3y0u8ZkqQfioLBQftFl9VkHXYRskbg+IIvvEj +zJkd1ioPgyAVWCeCLvriIsJJsbkBgWqdbZ1Ad2h2TiEqbYRAhU52mXyC8/O3AlnU +JgEbjt+tUwbRrhjd4rI6y9eIOI6sWym5GdOY+RgDz0iChmYLG2kPyes4iHomGgVM +ktck1JbyrFIto0fVUvY//s6EBnCmqj6i8rZWNBhXouSBbefK8GrTx5FrAoNBfBXv +a5pkXuPQPOWx63tdhvvL5ndJzaNl3Pe5nLjkC1+Tz8wwGjIczhxjlaX56uF0i57p +K6kwe6AYHw4YC+VbqdPRbB4HZ4+RS6mKvNJmqpMBiLKR+jFc1abBUggJzQpjotMi +puih2TkGl/VujQKQjBR7P4DNG5y6xFhyI6+2Vp/GekIzKQc/gsnmHwUNzUwoNovT +yD4cxojvXu6JZOkd69qJfjKmadHdzIif0dDJZiHcBmfFlHqabWJMfczgZICynkeO +owIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNV +HQ4EFgQUiGi/4I41xDs4a2L3KDuEgcgM100wDQYJKoZIhvcNAQEFBQADggIBADsC +jcs8MOhuoK3yc7NfniUTBAXT9uOLuwt5zlPe5JbF0a9zvNXD0EBVfEB/zRtfCdXy +fJ9oHbtdzno5wozWmHvFg1Wo1X1AyuAe94leY12hE8JdiraKfADzI8PthV9xdvBo +Y6pFITlIYXg23PFDk9Qlx/KAZeFTAnVR/Ho67zerhChXDNjU1JlWbOOi/lmEtDHo +M/hklJRRl6s5xUvt2t2AC298KQ3EjopyDedTFLJgQT2EkTFoPSdE2+Xe9PpjRchM +Ppj1P0G6Tss3DbpmmPHdy59c91Q2gmssvBNhl0L4eLvMyKKfyvBovWsdst+Nbwed +2o5nx0ceyrm/KkKRt2NTZvFCo+H0Wk1Ya7XkpDOtXHAd3ODy63MUkZoDweoAZbwH +/M8SESIsrqC9OuCiKthZ6SnTGDWkrBFfGbW1G/8iSlzGeuQX7yCpp/Q/rYqnmgQl +nQ7KN+ZQ/YxCKQSa7LnPS3K94gg2ryMvYuXKAdNw23yCIywWMQzGNgeQerEfZ1jE +O1hZibCMjFCz2IbLaKPECudpSyDOwR5WS5WpI2jYMNjD67BVUc3l/Su49bsRn1NU +9jQZjHkJNsphFyUXC4KYcwx3dMPVDceoEkzHp1RxRy4sGn3J4ys7SN4nhKdjNrN9 +j6BkOSQNPXuHr2ZcdBtLc7LljPCGmbjlxd+Ewbfr +-----END CERTIFICATE----- diff --git a/tests/tests/security/res/raw/blacklist_test_chain.pem b/tests/tests/security/res/raw/blocklist_test_chain.pem index bc7931e9c17..bc7931e9c17 100644 --- a/tests/tests/security/res/raw/blacklist_test_chain.pem +++ b/tests/tests/security/res/raw/blocklist_test_chain.pem diff --git a/tests/tests/security/res/raw/blacklist_test_valid_ca.pem b/tests/tests/security/res/raw/blocklist_test_valid_ca.pem index 43aafeddc6f..43aafeddc6f 100644 --- a/tests/tests/security/res/raw/blacklist_test_valid_ca.pem +++ b/tests/tests/security/res/raw/blocklist_test_valid_ca.pem diff --git a/tests/tests/security/res/raw/blacklist_test_valid_chain.pem b/tests/tests/security/res/raw/blocklist_test_valid_chain.pem index f572627362f..f572627362f 100644 --- a/tests/tests/security/res/raw/blacklist_test_valid_chain.pem +++ b/tests/tests/security/res/raw/blocklist_test_valid_chain.pem diff --git a/tests/tests/security/res/raw/test_blacklist_ca.pem b/tests/tests/security/res/raw/test_blocklist_ca.pem index 74e1c6de00a..74e1c6de00a 100644 --- a/tests/tests/security/res/raw/test_blacklist_ca.pem +++ b/tests/tests/security/res/raw/test_blocklist_ca.pem diff --git a/tests/tests/security/src/android/security/cts/CertBlacklistTest.java b/tests/tests/security/src/android/security/cts/CertBlocklistTest.java index d7a199e29a5..fde5bc9b4bb 100644 --- a/tests/tests/security/src/android/security/cts/CertBlacklistTest.java +++ b/tests/tests/security/src/android/security/cts/CertBlocklistTest.java @@ -16,70 +16,80 @@ package android.security.cts; -import android.content.Context; import android.test.AndroidTestCase; import java.io.InputStream; -import java.util.Collection; +import java.security.KeyStore; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; -import java.security.KeyStore; +import java.util.Collection; + import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; /** - * End to end version of org.conscrypt.CertBlacklistTest that tests the platform default + * End to end version of org.conscrypt.CertBlocklistTest that tests the platform default * {@link X509TrustManager}. * - * The test blacklisted CA's private key can be found in - * external/conscrypt/src/test/resources/blacklist_ca_key.pem + * The test blocklisted CA's private key can be found in + * external/conscrypt/src/test/resources/blocklist_ca_key.pem */ -public class CertBlacklistTest extends AndroidTestCase { +public class CertBlocklistTest extends AndroidTestCase { - private static final int BLACKLIST_CA = R.raw.test_blacklist_ca; - private static final int BLACKLISTED_CHAIN = R.raw.blacklist_test_chain; - private static final int BLACKLIST_FALLBACK_VALID_CA = R.raw.blacklist_test_valid_ca; - private static final int BLACKLISTED_VALID_CHAIN = R.raw.blacklist_test_valid_chain; + private static final int BLOCKLIST_CA = R.raw.test_blocklist_ca; + private static final int BLOCKLIST_DIGINOTAR = R.raw.blocklist_diginotar; + private static final int BLOCKLISTED_CHAIN = R.raw.blocklist_test_chain; + private static final int BLOCKLIST_FALLBACK_VALID_CA = R.raw.blocklist_test_valid_ca; + private static final int BLOCKLISTED_VALID_CHAIN = R.raw.blocklist_test_valid_chain; + + /** + * Checks that the blocklisted CA is rejected even if it used as a root of trust + */ + public void testBlocklistedCaUntrusted() throws Exception { + X509Certificate blocklistedCa = loadCertificate(BLOCKLIST_CA); + assertUntrusted(new X509Certificate[] {blocklistedCa}, getTrustManager(blocklistedCa)); + } /** - * Checks that the blacklisted CA is rejected even if it used as a root of trust + * Checks that a known compromised CA certificate is blocked. */ - public void testBlacklistedCaUntrusted() throws Exception { - X509Certificate blacklistedCa = loadCertificate(BLACKLIST_CA); - assertUntrusted(new X509Certificate[] {blacklistedCa}, getTrustManager(blacklistedCa)); + public void testBlocklistedDiginotar() throws Exception { + // Public Key SHA1 = 410f36363258f30b347d12ce4863e433437806a8 + X509Certificate blocklistedCa = loadCertificate(BLOCKLIST_DIGINOTAR); + assertUntrusted(new X509Certificate[] {blocklistedCa}, getTrustManager(blocklistedCa)); } /** - * Checks that a chain that is rooted in a blacklisted trusted CA is rejected. + * Checks that a chain that is rooted in a blocklisted trusted CA is rejected. */ - public void testBlacklistedRootOfTrust() throws Exception { - // Chain is leaf -> blacklisted - X509Certificate[] chain = loadCertificates(BLACKLISTED_CHAIN); - X509Certificate blacklistedCa = loadCertificate(BLACKLIST_CA); - assertUntrusted(chain, getTrustManager(blacklistedCa)); + public void testBlocklistedRootOfTrust() throws Exception { + // Chain is leaf -> blocklisted + X509Certificate[] chain = loadCertificates(BLOCKLISTED_CHAIN); + X509Certificate blocklistedCa = loadCertificate(BLOCKLIST_CA); + assertUntrusted(chain, getTrustManager(blocklistedCa)); } /** - * Tests that the path building correctly routes around a blacklisted cert where there are + * Tests that the path building correctly routes around a blocklisted cert where there are * other valid paths available. This prevents breakage where a cert was cross signed by a - * blacklisted CA but is still valid due to also being cross signed by CAs that remain trusted. + * blocklisted CA but is still valid due to also being cross signed by CAs that remain trusted. * Path: * - * leaf -> intermediate -> blacklisted_ca + * leaf -> intermediate -> blocklisted_ca * \ * -------> trusted_ca */ - public void testBlacklistedIntermediateFallback() throws Exception { - X509Certificate[] chain = loadCertificates(BLACKLISTED_VALID_CHAIN); - X509Certificate blacklistedCa = loadCertificate(BLACKLIST_CA); - X509Certificate validCa = loadCertificate(BLACKLIST_FALLBACK_VALID_CA); - assertTrusted(chain, getTrustManager(blacklistedCa, validCa)); + public void testBlocklistedIntermediateFallback() throws Exception { + X509Certificate[] chain = loadCertificates(BLOCKLISTED_VALID_CHAIN); + X509Certificate blocklistedCa = loadCertificate(BLOCKLIST_CA); + X509Certificate validCa = loadCertificate(BLOCKLIST_FALLBACK_VALID_CA); + assertTrusted(chain, getTrustManager(blocklistedCa, validCa)); // Check that without the trusted_ca the chain is invalid (since it only chains to a - // blacklisted ca) - assertUntrusted(chain, getTrustManager(blacklistedCa)); + // blocklisted ca) + assertUntrusted(chain, getTrustManager(blocklistedCa)); } private X509Certificate loadCertificate(int resId) throws Exception { diff --git a/tests/tests/settings/src/android/settings/cts/WifiSliceTest.java b/tests/tests/settings/src/android/settings/cts/WifiSliceTest.java index 78461cebfad..2c156e99d40 100644 --- a/tests/tests/settings/src/android/settings/cts/WifiSliceTest.java +++ b/tests/tests/settings/src/android/settings/cts/WifiSliceTest.java @@ -18,7 +18,9 @@ package android.settings.cts; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.provider.Settings.Secure; + import static com.google.common.truth.Truth.assertThat; + import static org.junit.Assume.assumeFalse; import android.app.slice.Slice; @@ -153,8 +155,18 @@ public class WifiSliceTest { final ResolveInfo info = pm.resolveActivity(requestDefaultAssistant, 0); if (info != null) { + final String packageName; + if (!TextUtils.isEmpty(mAssistant)) { + packageName = ComponentName.unflattenFromString(mAssistant).getPackageName(); + Log.i(TAG, "Default assistant: " + packageName); + } else { + packageName = info.activityInfo.packageName; + Log.i(TAG, "Set assistant: " + packageName); + Secure.putString(mContext.getContentResolver(), ASSISTANT, + new ComponentName(packageName, info.activityInfo.name).flattenToString()); + } final int testPid = Process.myPid(); - final int testUid = pm.getPackageUid(info.activityInfo.packageName, 0); + final int testUid = pm.getPackageUid(packageName, 0); assertThat(mSliceManager.checkSlicePermission(WIFI_SLICE_URI, testPid, testUid)) .isEqualTo(PERMISSION_GRANTED); diff --git a/tests/tests/telecom/src/android/telecom/cts/BackgroundCallAudioTest.java b/tests/tests/telecom/src/android/telecom/cts/BackgroundCallAudioTest.java index f0a6d890b01..046429ec17e 100644 --- a/tests/tests/telecom/src/android/telecom/cts/BackgroundCallAudioTest.java +++ b/tests/tests/telecom/src/android/telecom/cts/BackgroundCallAudioTest.java @@ -226,7 +226,7 @@ public class BackgroundCallAudioTest extends BaseTelecomTestWithMockServices { } public void testAudioProcessingFromCallScreeningAllowPlaceEmergencyCall() throws Exception { - if (!mShouldTestTelecom) { + if (!mShouldTestTelecom || !TestUtils.hasTelephonyFeature(mContext)) { return; } setupForEmergencyCalling(TEST_EMERGENCY_NUMBER); @@ -271,7 +271,7 @@ public class BackgroundCallAudioTest extends BaseTelecomTestWithMockServices { } public void testAudioProcessingFromIncomingActivePlaceEmergencyCall() throws Exception { - if (!mShouldTestTelecom) { + if (!mShouldTestTelecom || !TestUtils.hasTelephonyFeature(mContext)) { return; } setupForEmergencyCalling(TEST_EMERGENCY_NUMBER); @@ -379,7 +379,7 @@ public class BackgroundCallAudioTest extends BaseTelecomTestWithMockServices { } public void testAudioProcessOutgoingActiveEmergencyCallPlaced() throws Exception { - if (!mShouldTestTelecom) { + if (!mShouldTestTelecom || !TestUtils.hasTelephonyFeature(mContext)) { return; } setupForEmergencyCalling(TEST_EMERGENCY_NUMBER); diff --git a/tests/tests/telecom/src/android/telecom/cts/CallRedirectionServiceTest.java b/tests/tests/telecom/src/android/telecom/cts/CallRedirectionServiceTest.java index 39b8774b8f8..961ebb7a5d0 100644 --- a/tests/tests/telecom/src/android/telecom/cts/CallRedirectionServiceTest.java +++ b/tests/tests/telecom/src/android/telecom/cts/CallRedirectionServiceTest.java @@ -218,7 +218,7 @@ public class CallRedirectionServiceTest extends BaseTelecomTestWithMockServices } public void testCantRedirectEmergencyCall() throws Exception { - if (!shouldTestTelecom(mContext)) { + if (!shouldTestTelecom(mContext) || !TestUtils.hasTelephonyFeature(mContext)) { return; } Bundle extras = new Bundle(); diff --git a/tests/tests/telecom/src/android/telecom/cts/EmergencyCallTests.java b/tests/tests/telecom/src/android/telecom/cts/EmergencyCallTests.java index 117ab1a16a3..1eaf6e33e68 100644 --- a/tests/tests/telecom/src/android/telecom/cts/EmergencyCallTests.java +++ b/tests/tests/telecom/src/android/telecom/cts/EmergencyCallTests.java @@ -56,7 +56,7 @@ public class EmergencyCallTests extends BaseTelecomTestWithMockServices { // Sets up this package as default dialer in super. super.setUp(); NewOutgoingCallBroadcastReceiver.reset(); - if (!mShouldTestTelecom) return; + if (!mShouldTestTelecom || !TestUtils.hasTelephonyFeature(mContext)) return; setupConnectionService(null, FLAG_REGISTER | FLAG_ENABLE); setupForEmergencyCalling(TEST_EMERGENCY_NUMBER); TestUtils.executeShellCommand(getInstrumentation(), @@ -93,7 +93,7 @@ public class EmergencyCallTests extends BaseTelecomTestWithMockServices { * invocations to induce the failure method. */ public void testEmergencyCallFailureDueToInvalidPhoneAccounts() throws Exception { - if (!mShouldTestTelecom) return; + if (!mShouldTestTelecom || !TestUtils.hasTelephonyFeature(mContext)) return; // needed in order to call mTelecomManager.getPhoneAccountsForPackage() InstrumentationRegistry.getInstrumentation().getUiAutomation() @@ -135,7 +135,7 @@ public class EmergencyCallTests extends BaseTelecomTestWithMockServices { * Place an outgoing emergency call and ensure it is started successfully. */ public void testStartEmergencyCall() throws Exception { - if (!mShouldTestTelecom) return; + if (!mShouldTestTelecom || !TestUtils.hasTelephonyFeature(mContext)) return; Connection conn = placeAndVerifyEmergencyCall(true /*supportsHold*/); Call eCall = getInCallService().getLastCall(); assertCallState(eCall, Call.STATE_DIALING); @@ -187,7 +187,7 @@ public class EmergencyCallTests extends BaseTelecomTestWithMockServices { * Place an outgoing emergency call fail it to trigger persisting of diagnostic data */ public void testEmergencyCallFailureCreatesDropboxEntries() throws Exception { - if (!mShouldTestTelecom || !TestUtils.hasTelephonyFeature(mContext)) return; + if (!mShouldTestTelecom || !TestUtils.hasTelephonyFeature(mContext)) return; long startTime = System.currentTimeMillis() - DAYS_BACK_TO_SEARCH_EMERGENCY_DROP_BOX_ENTRIES_IN_MS; mContext.registerReceiver(new BroadcastReceiver() { @@ -235,7 +235,7 @@ public class EmergencyCallTests extends BaseTelecomTestWithMockServices { * will automatically be rejected as well. */ public void testOngoingEmergencyCallAndReceiveIncomingCall() throws Exception { - if (!mShouldTestTelecom) return; + if (!mShouldTestTelecom || !TestUtils.hasTelephonyFeature(mContext)) return; Connection eConnection = placeAndVerifyEmergencyCall(true /*supportsHold*/); assertIsInCall(true); @@ -259,7 +259,7 @@ public class EmergencyCallTests extends BaseTelecomTestWithMockServices { * rejected and logged as a new missed call. */ public void testIncomingRingingCallAndPlaceEmergencyCall() throws Exception { - if (!mShouldTestTelecom) return; + if (!mShouldTestTelecom || !TestUtils.hasTelephonyFeature(mContext)) return; Uri normalCallNumber = createRandomTestNumber(); addAndVerifyNewIncomingCall(normalCallNumber, null); @@ -294,7 +294,7 @@ public class EmergencyCallTests extends BaseTelecomTestWithMockServices { * as a new missed call. */ public void testActiveCallAndIncomingRingingCallAndPlaceEmergencyCall() throws Exception { - if (!mShouldTestTelecom) return; + if (!mShouldTestTelecom || !TestUtils.hasTelephonyFeature(mContext)) return; Uri normalOutgoingCallNumber = createRandomTestNumber(); Bundle extras = new Bundle(); @@ -331,7 +331,7 @@ public class EmergencyCallTests extends BaseTelecomTestWithMockServices { } public void testEmergencyCallAndNoAdditionalCallPermitted() throws Exception { - if (!mShouldTestTelecom) return; + if (!mShouldTestTelecom || !TestUtils.hasTelephonyFeature(mContext)) return; Connection eConnection = placeAndVerifyEmergencyCall(true); Call eCall = getInCallService().getLastCall(); diff --git a/tests/tests/telecom/src/android/telecom/cts/ExtendedInCallServiceTest.java b/tests/tests/telecom/src/android/telecom/cts/ExtendedInCallServiceTest.java index 4890bdf6a4a..1e2f40bb4ca 100644 --- a/tests/tests/telecom/src/android/telecom/cts/ExtendedInCallServiceTest.java +++ b/tests/tests/telecom/src/android/telecom/cts/ExtendedInCallServiceTest.java @@ -863,7 +863,7 @@ public class ExtendedInCallServiceTest extends BaseTelecomTestWithMockServices { } public void testOnCannedTextResponsesLoaded() { - if (!mShouldTestTelecom) { + if (!mShouldTestTelecom || !TestUtils.hasTelephonyFeature(mContext)) { return; } diff --git a/tests/tests/telecom/src/android/telecom/cts/ExternalCallTest.java b/tests/tests/telecom/src/android/telecom/cts/ExternalCallTest.java index 57ec66c1baf..ec68a42f6d1 100644 --- a/tests/tests/telecom/src/android/telecom/cts/ExternalCallTest.java +++ b/tests/tests/telecom/src/android/telecom/cts/ExternalCallTest.java @@ -118,7 +118,7 @@ public class ExternalCallTest extends BaseTelecomTestWithMockServices { * CAPABILITY_CAN_PLACE_CALL capability is removed. */ public void testPullCallCapabilityRemovedInEmergencyCall() throws Exception { - if (!mShouldTestTelecom) { + if (!mShouldTestTelecom || !TestUtils.hasTelephonyFeature(mContext)) { return; } setupForEmergencyCalling(TEST_EMERGENCY_NUMBER); @@ -140,7 +140,7 @@ public class ExternalCallTest extends BaseTelecomTestWithMockServices { * ongoing emergency call, the request is not completed. */ public void testTryToPullCallWhileInEmergencyCall() throws Exception { - if (!mShouldTestTelecom) { + if (!mShouldTestTelecom || !TestUtils.hasTelephonyFeature(mContext)) { return; } setupForEmergencyCalling(TEST_EMERGENCY_NUMBER); @@ -166,7 +166,7 @@ public class ExternalCallTest extends BaseTelecomTestWithMockServices { * test to check external call and pull external call with call and connection states check */ public void testExternalCallAndPullCall() throws Exception { - if (!mShouldTestTelecom || !TestUtils.hasTelephonyFeature(mContext)) { + if (!mShouldTestTelecom || !TestUtils.hasTelephonyFeature(mContext)) { return; } diff --git a/tests/tests/telecom/src/android/telecom/cts/NonUiInCallServiceTest.java b/tests/tests/telecom/src/android/telecom/cts/NonUiInCallServiceTest.java index d4096aa5555..11780a13ff4 100644 --- a/tests/tests/telecom/src/android/telecom/cts/NonUiInCallServiceTest.java +++ b/tests/tests/telecom/src/android/telecom/cts/NonUiInCallServiceTest.java @@ -19,6 +19,7 @@ import android.util.Log; import android.util.Pair; import androidx.test.InstrumentationRegistry; +import androidx.test.filters.FlakyTest; import java.util.Arrays; @@ -99,6 +100,7 @@ public class NonUiInCallServiceTest extends BaseTelecomTestWithMockServices { * enablement. * @throws Exception */ + @FlakyTest public void testMidCallComponentEnablementWithNoneAvailableAtStart() throws Exception { if (!mShouldTestTelecom) { return; diff --git a/tests/tests/telecom/src/android/telecom/cts/TelecomManagerTest.java b/tests/tests/telecom/src/android/telecom/cts/TelecomManagerTest.java index cb2ecd591ae..7d20eae7356 100644 --- a/tests/tests/telecom/src/android/telecom/cts/TelecomManagerTest.java +++ b/tests/tests/telecom/src/android/telecom/cts/TelecomManagerTest.java @@ -151,7 +151,7 @@ public class TelecomManagerTest extends BaseTelecomTestWithMockServices { } public void testIsInEmergencyCall_ongoingEmergencyCall() throws Exception { - if (!mShouldTestTelecom) { + if (!mShouldTestTelecom || !TestUtils.hasTelephonyFeature(mContext)) { return; } diff --git a/tests/tests/telecom/src/android/telecom/cts/TransactionalApisTest.java b/tests/tests/telecom/src/android/telecom/cts/TransactionalApisTest.java index 356552230c8..92581c501ab 100644 --- a/tests/tests/telecom/src/android/telecom/cts/TransactionalApisTest.java +++ b/tests/tests/telecom/src/android/telecom/cts/TransactionalApisTest.java @@ -49,6 +49,7 @@ import android.util.Log; import android.util.Pair; import androidx.annotation.NonNull; +import androidx.test.filters.FlakyTest; import com.android.compatibility.common.util.ApiTest; import com.android.compatibility.common.util.CddTest; @@ -796,6 +797,7 @@ public class TransactionalApisTest extends BaseTelecomTestWithMockServices { @ApiTest(apis = {"android.telecom.TelecomManager#addCall", "android.telecom.CallControl#disconnect", "android.telecom.CallEventCallback#onCallEndpointChanged"}) + @FlakyTest public void testOnChangedCallEndpoint() { if (!mShouldTestTelecom) { return; diff --git a/tests/tests/telephony/current/src/android/telephony/cts/AsyncSmsMessageListener.java b/tests/tests/telephony/current/src/android/telephony/cts/AsyncSmsMessageListener.java index ca0cee2272a..b63e1de5378 100644 --- a/tests/tests/telephony/current/src/android/telephony/cts/AsyncSmsMessageListener.java +++ b/tests/tests/telephony/current/src/android/telephony/cts/AsyncSmsMessageListener.java @@ -34,6 +34,15 @@ public class AsyncSmsMessageListener { new LinkedBlockingQueue<>(1); /** + * Clear internal cache data. + */ + public void clear() { + mMessages.clear(); + mSentMessageResults.clear(); + mDeliveredMessageResults.clear(); + } + + /** * Offer a SMS message to the queue of SMS messages waiting to be processed. */ public void offerSmsMessage(String smsMessage) { 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 24805d6b6c9..16bb603c654 100644 --- a/tests/tests/telephony/current/src/android/telephony/cts/SmsUsageMonitorShortCodeTest.java +++ b/tests/tests/telephony/current/src/android/telephony/cts/SmsUsageMonitorShortCodeTest.java @@ -140,6 +140,10 @@ public class SmsUsageMonitorShortCodeTest { new ShortCodeTest("br", "2654", SMS_CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE), new ShortCodeTest("br", "2652", SMS_CATEGORY_FREE_SHORT_CODE), + new ShortCodeTest("bw", "166416", SMS_CATEGORY_NOT_SHORT_CODE), + new ShortCodeTest("bw", "16649", SMS_CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE), + new ShortCodeTest("bw", "16641", SMS_CATEGORY_FREE_SHORT_CODE), + new ShortCodeTest("by", "112", SMS_CATEGORY_NOT_SHORT_CODE), new ShortCodeTest("by", "1234", SMS_CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE), new ShortCodeTest("by", "3336", SMS_CATEGORY_PREMIUM_SHORT_CODE), @@ -222,6 +226,7 @@ public class SmsUsageMonitorShortCodeTest { new ShortCodeTest("do", "9128922", SMS_CATEGORY_NOT_SHORT_CODE), new ShortCodeTest("do", "912898", SMS_CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE), new ShortCodeTest("do", "912892", SMS_CATEGORY_FREE_SHORT_CODE), + new ShortCodeTest("do", "912", SMS_CATEGORY_FREE_SHORT_CODE), new ShortCodeTest("ec", "4664534", SMS_CATEGORY_NOT_SHORT_CODE), new ShortCodeTest("ec", "466499", SMS_CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE), @@ -288,9 +293,10 @@ public class SmsUsageMonitorShortCodeTest { new ShortCodeTest("ge", "95208", SMS_CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE), new ShortCodeTest("ge", "952014", SMS_CATEGORY_NOT_SHORT_CODE), - new ShortCodeTest("gh", "377789", SMS_CATEGORY_NOT_SHORT_CODE), + new ShortCodeTest("gh", "37778", SMS_CATEGORY_NOT_SHORT_CODE), new ShortCodeTest("gh", "3775", SMS_CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE), new ShortCodeTest("gh", "3777", SMS_CATEGORY_FREE_SHORT_CODE), + new ShortCodeTest("gh", "2333", SMS_CATEGORY_FREE_SHORT_CODE), new ShortCodeTest("gr", "112", SMS_CATEGORY_NOT_SHORT_CODE), new ShortCodeTest("gr", "116117", SMS_CATEGORY_FREE_SHORT_CODE), @@ -390,6 +396,7 @@ public class SmsUsageMonitorShortCodeTest { new ShortCodeTest("kw", "509761", SMS_CATEGORY_NOT_SHORT_CODE), new ShortCodeTest("kw", "50979", SMS_CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE), new ShortCodeTest("kw", "50976", SMS_CATEGORY_FREE_SHORT_CODE), + new ShortCodeTest("kw", "7112", SMS_CATEGORY_FREE_SHORT_CODE), new ShortCodeTest("id", "992626", SMS_CATEGORY_NOT_SHORT_CODE), new ShortCodeTest("id", "99268", SMS_CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE), @@ -440,6 +447,7 @@ public class SmsUsageMonitorShortCodeTest { new ShortCodeTest("mx", "30303025", SMS_CATEGORY_NOT_SHORT_CODE), new ShortCodeTest("mx", "3030302", SMS_CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE), new ShortCodeTest("mx", "3030303", SMS_CATEGORY_FREE_SHORT_CODE), + new ShortCodeTest("mx", "81811", SMS_CATEGORY_FREE_SHORT_CODE), new ShortCodeTest("mw", "427611", SMS_CATEGORY_NOT_SHORT_CODE), new ShortCodeTest("mw", "4279", SMS_CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE), @@ -495,7 +503,8 @@ public class SmsUsageMonitorShortCodeTest { new ShortCodeTest("pk", "90958", SMS_CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE), new ShortCodeTest("pk", "9092", SMS_CATEGORY_FREE_SHORT_CODE), new ShortCodeTest("pk", "909203", SMS_CATEGORY_FREE_SHORT_CODE), - new ShortCodeTest("pk", "909201", SMS_CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE), + new ShortCodeTest("pk", "909219", SMS_CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE), + new ShortCodeTest("pk", "909201", SMS_CATEGORY_FREE_SHORT_CODE), new ShortCodeTest("pl", "112", SMS_CATEGORY_NOT_SHORT_CODE), new ShortCodeTest("pl", "116117", SMS_CATEGORY_FREE_SHORT_CODE), @@ -634,9 +643,11 @@ public class SmsUsageMonitorShortCodeTest { new ShortCodeTest("uy", "191238", SMS_CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE), new ShortCodeTest("uy", "191289", SMS_CATEGORY_FREE_SHORT_CODE), - new ShortCodeTest("vn", "807982", SMS_CATEGORY_NOT_SHORT_CODE), - new ShortCodeTest("vn", "8078", SMS_CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE), new ShortCodeTest("vn", "8079", SMS_CATEGORY_FREE_SHORT_CODE), + new ShortCodeTest("vn", "90002", SMS_CATEGORY_FREE_SHORT_CODE), + new ShortCodeTest("vn", "1189892", SMS_CATEGORY_NOT_SHORT_CODE), + new ShortCodeTest("vn", "118998", SMS_CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE), + new ShortCodeTest("vn", "118989", SMS_CATEGORY_FREE_SHORT_CODE), new ShortCodeTest("ve", "5383526", SMS_CATEGORY_NOT_SHORT_CODE), new ShortCodeTest("ve", "538358", SMS_CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE), diff --git a/tests/tests/telephony/current/src/android/telephony/embms/cts/MbmsDownloadSessionTest.java b/tests/tests/telephony/current/src/android/telephony/embms/cts/MbmsDownloadSessionTest.java index eed8158a66b..0f359d90835 100644 --- a/tests/tests/telephony/current/src/android/telephony/embms/cts/MbmsDownloadSessionTest.java +++ b/tests/tests/telephony/current/src/android/telephony/embms/cts/MbmsDownloadSessionTest.java @@ -29,6 +29,7 @@ import android.telephony.mbms.DownloadRequest; import android.telephony.mbms.FileServiceInfo; import android.telephony.mbms.MbmsErrors; +import org.junit.Ignore; import org.junit.Test; import java.io.File; @@ -49,6 +50,7 @@ public class MbmsDownloadSessionTest extends MbmsDownloadTestBase { } } + @Ignore @Test public void testRequestUpdateDownloadServices() throws Exception { List<String> testClasses = Arrays.asList("class1", "class2"); diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/CallDomainSelectionTestOnMockModem.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/CallDomainSelectionTestOnMockModem.java index 19fb54c187d..33bf1f16619 100644 --- a/tests/tests/telephony/current/src/android/telephony/ims/cts/CallDomainSelectionTestOnMockModem.java +++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/CallDomainSelectionTestOnMockModem.java @@ -957,86 +957,57 @@ public class CallDomainSelectionTestOnMockModem extends ImsCallingBase { NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_SEARCHING, getRegState(Domain.CS, subId)); - // Register call state change callback - mCallState = TelephonyManager.CALL_STATE_IDLE; - sCallStateChangeCallbackHandler.post( - () -> { - sCallStateCallback = new CallStateListener(); - ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( - sTelephonyManager, - (tm) -> - tm.registerTelephonyCallback( - mCallStateChangeExecutor, sCallStateCallback)); - }); - // place first call placeOutgoingCall(TEST_DIAL_NUMBER); TimeUnit.SECONDS.sleep(1); - // Verify call state - synchronized (mCallStateChangeLock) { - if (mCallState == TelephonyManager.CALL_STATE_IDLE) { - Log.d(TAG, "Wait for call state change to offhook"); - mCallStateChangeLock.wait(WAIT_UPDATE_TIMEOUT_MS); - } - assertEquals(TelephonyManager.CALL_STATE_OFFHOOK, mCallState); - } Call call1 = getCall(mCurrentCallId); + // IMS call1 assertTrue(isPsDialing()); + TestImsCallSessionImpl callSession1 = + sServiceConnector.getCarrierService().getMmTelFeature().getImsCallsession(); + // place second call placeOutgoingCall(TEST_DIAL_NUMBER + 1); TimeUnit.SECONDS.sleep(1); - // call1 on hold - assertTrue(callingTestLatchCountdown(LATCH_IS_CALL_HOLDING, WAIT_FOR_CALL_STATE)); - assertEquals("Call is not on Hold", Call.STATE_HOLDING, call1.getDetails().getState()); Call call2 = getCall(mCurrentCallId); + + // IMS call2 assertTrue(isPsDialing()); + TestImsCallSessionImpl callSession2 = + sServiceConnector.getCarrierService().getMmTelFeature().getImsCallsession(); + + // call2 should be IMS and active + isCallActive(call2, callSession2); + // IMS call1 on hold + isCallHolding(call1, callSession1); + // Swap the call ImsUtils.waitInCurrentState(WAIT_IN_CURRENT_STATE); call1.unhold(); - assertTrue(callingTestLatchCountdown(LATCH_IS_CALL_HOLDING, WAIT_FOR_CALL_STATE)); - assertEquals("Call2 is not on Hold", Call.STATE_HOLDING, call2.getDetails().getState()); - assertTrue(callingTestLatchCountdown(LATCH_IS_CALL_ACTIVE, WAIT_FOR_CALL_STATE)); - assertEquals("Call1 is not Active", Call.STATE_ACTIVE, call1.getDetails().getState()); + // call1 should be IMS and active + isCallActive(call1, callSession1); + // IMS call2 on hold + isCallHolding(call2, callSession2); - // After successful call swap disconnect the call + // disconnect call1 call1.disconnect(); - assertTrue(callingTestLatchCountdown(LATCH_IS_CALL_DISCONNECTING, WAIT_FOR_CALL_STATE)); - assertTrue(callingTestLatchCountdown(LATCH_IS_CALL_DISCONNECTED, WAIT_FOR_CALL_STATE)); - assertTrue(callingTestLatchCountdown(LATCH_IS_ON_CALL_REMOVED, WAIT_FOR_CALL_STATE)); + // IMS call1 disconnected + isCallDisconnected(call1, callSession1); - // Wait till second call is in active state - waitUntilConditionIsTrueOrTimeout( - new Condition() { - @Override - public Object expected() { - return true; - } + // call2 should be IMS and active + isCallActive(call2, callSession2); - @Override - public Object actual() { - return call2.getDetails().getState() == Call.STATE_ACTIVE; - } - }, - WAIT_FOR_CALL_STATE_ACTIVE, - "Call in Active State"); - - // Call 2 is active - assertTrue(callingTestLatchCountdown(LATCH_IS_CALL_ACTIVE, WAIT_FOR_CALL_STATE)); - - // disconnect call 2 + // disconnect call2 call2.disconnect(); - assertTrue(callingTestLatchCountdown(LATCH_IS_CALL_DISCONNECTING, WAIT_FOR_CALL_STATE)); - assertTrue(callingTestLatchCountdown(LATCH_IS_CALL_DISCONNECTED, WAIT_FOR_CALL_STATE)); + // IMS call2 disconnected + isCallDisconnected(call2, callSession2); - // Unregister call state change callback - sTelephonyManager.unregisterTelephonyCallback(sCallStateCallback); - sCallStateCallback = null; waitForUnboundService(); } @@ -1061,18 +1032,6 @@ public class CallDomainSelectionTestOnMockModem extends ImsCallingBase { NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_SEARCHING, getRegState(Domain.CS, subId)); - // Register call state change callback - mCallState = TelephonyManager.CALL_STATE_IDLE; - sCallStateChangeCallbackHandler.post( - () -> { - sCallStateCallback = new CallStateListener(); - ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( - sTelephonyManager, - (tm) -> - tm.registerTelephonyCallback( - mCallStateChangeExecutor, sCallStateCallback)); - }); - Bundle extras = new Bundle(); extras.putBoolean("android.telephony.ims.feature.extra.IS_USSD", false); extras.putBoolean("android.telephony.ims.feature.extra.IS_UNKNOWN_CALL", false); @@ -1091,37 +1050,33 @@ public class CallDomainSelectionTestOnMockModem extends ImsCallingBase { } } + TestImsCallSessionImpl mtCallSession = + sServiceConnector.getCarrierService().getMmTelFeature().getImsCallsession(); + + // IMS MT call active + isCallActive(mtCall, mtCallSession); + // Place outgoing call placeOutgoingCall(TEST_DIAL_NUMBER); + + // IMS MO Call active assertTrue(isPsDialing()); Call moCall = getCall(mCurrentCallId); - // Register call state change callback - // Verify call state change - synchronized (mCallStateChangeLock) { - Log.d(TAG, "Wait for call state change"); - mCallStateChangeLock.wait(WAIT_UPDATE_TIMEOUT_MS); - moCall.answer(0); - } - - assertEquals("MO call is not Active ", Call.STATE_ACTIVE, moCall.getDetails().getState()); - assertTrue(callingTestLatchCountdown(LATCH_IS_CALL_HOLDING, WAIT_FOR_CALL_STATE)); - assertEquals("MT call is not on Hold ", Call.STATE_HOLDING, mtCall.getDetails().getState()); + // IMS MT call on hold + isCallHolding(mtCall, mtCallSession); + // disconnect MO call moCall.disconnect(); - assertTrue(callingTestLatchCountdown(LATCH_IS_CALL_DISCONNECTING, WAIT_FOR_CALL_STATE)); - assertTrue(callingTestLatchCountdown(LATCH_IS_ON_CALL_REMOVED, WAIT_FOR_CALL_STATE)); - // mtCall should activate - assertTrue(callingTestLatchCountdown(LATCH_IS_CALL_ACTIVE, WAIT_FOR_CALL_STATE)); - assertEquals("MT call is not Active ", Call.STATE_ACTIVE, mtCall.getDetails().getState()); + // IMS MT call active + isCallActive(mtCall, mtCallSession); + // disconenct MT call mtCall.disconnect(); - assertTrue(callingTestLatchCountdown(LATCH_IS_CALL_DISCONNECTING, WAIT_FOR_CALL_STATE)); + //IMS MT call disconnected + isCallDisconnected(mtCall, mtCallSession); - // Unregister call state change callback - sTelephonyManager.unregisterTelephonyCallback(sCallStateCallback); - sCallStateCallback = null; waitForUnboundService(); } @@ -1144,86 +1099,55 @@ public class CallDomainSelectionTestOnMockModem extends ImsCallingBase { NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_SEARCHING, getRegState(Domain.CS, subId)); - // Register call state change callback - mCallState = TelephonyManager.CALL_STATE_IDLE; - sCallStateChangeCallbackHandler.post( - () -> { - sCallStateCallback = new CallStateListener(); - ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( - sTelephonyManager, - (tm) -> - tm.registerTelephonyCallback( - mCallStateChangeExecutor, sCallStateCallback)); - }); - // place first call placeOutgoingCall(TEST_DIAL_NUMBER); TimeUnit.SECONDS.sleep(1); - // Verify call state - synchronized (mCallStateChangeLock) { - if (mCallState == TelephonyManager.CALL_STATE_IDLE) { - Log.d(TAG, "Wait for call state change to offhook"); - mCallStateChangeLock.wait(WAIT_UPDATE_TIMEOUT_MS); - } - assertEquals(TelephonyManager.CALL_STATE_OFFHOOK, mCallState); - } Call call1 = getCall(mCurrentCallId); + // IMS call1 active assertTrue(isPsDialing()); + TestImsCallSessionImpl callSession1 = + sServiceConnector.getCarrierService().getMmTelFeature().getImsCallsession(); + // place second call placeOutgoingCall(TEST_DIAL_NUMBER + 1); TimeUnit.SECONDS.sleep(1); - // call1 on hold - assertTrue(callingTestLatchCountdown(LATCH_IS_CALL_HOLDING, WAIT_FOR_CALL_STATE)); - assertEquals("Call is not on Hold", Call.STATE_HOLDING, call1.getDetails().getState()); Call call2 = getCall(mCurrentCallId); + + // IMS call2 active assertTrue(isPsDialing()); + TestImsCallSessionImpl callSession2 = + sServiceConnector.getCarrierService().getMmTelFeature().getImsCallsession(); + + // IMS call1 is on hold + isCallHolding(call1, callSession1); + // Swap the call ImsUtils.waitInCurrentState(WAIT_IN_CURRENT_STATE); call1.unhold(); - assertTrue(callingTestLatchCountdown(LATCH_IS_CALL_HOLDING, WAIT_FOR_CALL_STATE)); - assertEquals("Call2 is not on Hold", Call.STATE_HOLDING, call2.getDetails().getState()); - assertTrue(callingTestLatchCountdown(LATCH_IS_CALL_ACTIVE, WAIT_FOR_CALL_STATE)); - assertEquals("Call1 is not Active", Call.STATE_ACTIVE, call1.getDetails().getState()); + // IMS call1 is active + isCallActive(call1, callSession1); + // IMS call2 is on hold + isCallHolding(call2, callSession2); - // After successful call swap disconnect the call + // After successful call swap disconnect call1 call1.disconnect(); - assertTrue(callingTestLatchCountdown(LATCH_IS_CALL_DISCONNECTING, WAIT_FOR_CALL_STATE)); - assertTrue(callingTestLatchCountdown(LATCH_IS_CALL_DISCONNECTED, WAIT_FOR_CALL_STATE)); - assertTrue(callingTestLatchCountdown(LATCH_IS_ON_CALL_REMOVED, WAIT_FOR_CALL_STATE)); - - // Wait till second call is in active state - waitUntilConditionIsTrueOrTimeout( - new Condition() { - @Override - public Object expected() { - return true; - } + // IMS call1 disconnected + isCallDisconnected(call1, callSession1); - @Override - public Object actual() { - return call2.getDetails().getState() == Call.STATE_ACTIVE; - } - }, - WAIT_FOR_CALL_STATE_ACTIVE, - "Call in Active State"); - - // Call 2 is active - assertTrue(callingTestLatchCountdown(LATCH_IS_CALL_ACTIVE, WAIT_FOR_CALL_STATE)); + // IMS call2 is active + isCallActive(call2, callSession2); - // disconnect call 2 + // disconnect call2 call2.disconnect(); - assertTrue(callingTestLatchCountdown(LATCH_IS_CALL_DISCONNECTING, WAIT_FOR_CALL_STATE)); - assertTrue(callingTestLatchCountdown(LATCH_IS_CALL_DISCONNECTED, WAIT_FOR_CALL_STATE)); + // IMS call2 disconnected + isCallDisconnected(call2, callSession2); - // Unregister call state change callback - sTelephonyManager.unregisterTelephonyCallback(sCallStateCallback); - sCallStateCallback = null; waitForUnboundService(); } 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 a723a7fa8d4..0abdfd98c39 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 @@ -993,6 +993,9 @@ public class ImsServiceTest { SmsManager.getSmsManagerForSubscriptionId(sTestSub) .setStorageMonitorMemoryStatusOverride(false); + // Clear cached data before starting test. + AsyncSmsMessageListener.getInstance().clear(); + //Message received sServiceConnector.getCarrierService().getMmTelFeature().getSmsImplementation() .receiveSmsWaitForAcknowledgeMemoryFull(123456789, SmsMessage.FORMAT_3GPP, @@ -1021,6 +1024,9 @@ public class ImsServiceTest { } setupImsServiceForSms(); + // Clear cached data before starting test. + AsyncSmsMessageListener.getInstance().clear(); + // Message received sServiceConnector.getCarrierService().getMmTelFeature().getSmsImplementation() .receiveSmsWaitForAcknowledge(123456789, SmsMessage.FORMAT_3GPP, @@ -1047,6 +1053,9 @@ public class ImsServiceTest { setupImsServiceForSms(); + // Clear cached data before starting test. + AsyncSmsMessageListener.getInstance().clear(); + // Message received sServiceConnector.getCarrierService().getMmTelFeature().getSmsImplementation() .receiveSmsWaitForAcknowledge(123456789, SmsMessage.FORMAT_3GPP, diff --git a/tests/tests/tv/src/android/media/tv/tuner/cts/SharedFilterTestService.java b/tests/tests/tv/src/android/media/tv/tuner/cts/SharedFilterTestService.java index 6ed80ce9ae8..a2684388382 100644 --- a/tests/tests/tv/src/android/media/tv/tuner/cts/SharedFilterTestService.java +++ b/tests/tests/tv/src/android/media/tv/tuner/cts/SharedFilterTestService.java @@ -62,7 +62,7 @@ public class SharedFilterTestService extends Service { mTuner, getExecutor(), getFilterCallback()); // Open dvr playback as data source - mDvrPlayback = mTuner.openDvrPlayback(100, getExecutor(), getPlaybackListener()); + mDvrPlayback = mTuner.openDvrPlayback(188, getExecutor(), getPlaybackListener()); return mFilter.acquireSharedFilterToken(); } diff --git a/tests/tests/tv/src/android/media/tv/tuner/cts/TunerResourceTestService.java b/tests/tests/tv/src/android/media/tv/tuner/cts/TunerResourceTestService.java index e7145088bb3..93cdf000e15 100644 --- a/tests/tests/tv/src/android/media/tv/tuner/cts/TunerResourceTestService.java +++ b/tests/tests/tv/src/android/media/tv/tuner/cts/TunerResourceTestService.java @@ -69,6 +69,9 @@ public class TunerResourceTestService extends Service { mFeInfo = infos.get(frontendIndex); mFeSettings = TunerTest.createFrontendSettings(mFeInfo); + // apply target frontend only, for case when there are multiple instances in frontend type + mTuner.applyFrontend(mFeInfo); + // tune return mTuner.tune(mFeSettings); } diff --git a/tests/tests/tv/src/android/media/tv/tuner/cts/TunerTest.java b/tests/tests/tv/src/android/media/tv/tuner/cts/TunerTest.java index 16b3be390b8..0d07786977f 100644 --- a/tests/tests/tv/src/android/media/tv/tuner/cts/TunerTest.java +++ b/tests/tests/tv/src/android/media/tv/tuner/cts/TunerTest.java @@ -24,6 +24,7 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.junit.Assume.assumeNotNull; +import static org.junit.Assume.assumeTrue; import android.content.ComponentName; import android.content.Context; @@ -1111,7 +1112,8 @@ public class TunerTest { return; } - assertEquals(lnb.setVoltage(Lnb.VOLTAGE_5V), Tuner.RESULT_SUCCESS); + int targetLnbVoltage = getTargetLnbVoltage(); + assertEquals(lnb.setVoltage(targetLnbVoltage), Tuner.RESULT_SUCCESS); assertEquals(lnb.setTone(Lnb.TONE_NONE), Tuner.RESULT_SUCCESS); assertEquals( lnb.setSatellitePosition(Lnb.POSITION_A), Tuner.RESULT_SUCCESS); @@ -1597,14 +1599,14 @@ public class TunerTest { @Test public void testOpenDvrRecorder() throws Exception { - DvrRecorder d = mTuner.openDvrRecorder(100, getExecutor(), getRecordListener()); + DvrRecorder d = mTuner.openDvrRecorder(188, getExecutor(), getRecordListener()); assertNotNull(d); d.close(); } @Test public void testOpenDvPlayback() throws Exception { - DvrPlayback d = mTuner.openDvrPlayback(100, getExecutor(), getPlaybackListener()); + DvrPlayback d = mTuner.openDvrPlayback(188, getExecutor(), getPlaybackListener()); assertNotNull(d); d.close(); } @@ -1662,17 +1664,17 @@ public class TunerTest { assertFalse(ids.isEmpty()); int targetFrontendId = sTunerCtsConfiguration.getTargetFrontendId().intValueExact(); FrontendInfo info = mTuner.getFrontendInfoById(ids.get(targetFrontendId)); - FrontendSettings feSettings = createFrontendSettings(info); - // first tune with mTuner to acquire resource - int res = mTuner.tune(feSettings); + // first apply frontend with mTuner to acquire resource + int res = mTuner.applyFrontend(info); assertEquals(Tuner.RESULT_SUCCESS, res); assertNotNull(mTuner.getFrontendInfo()); - // now tune with a higher priority tuner to have mTuner's resource reclaimed + // now apply frontend with a higher priority tuner to have mTuner's resource reclaimed Tuner higherPrioTuner = new Tuner(mContext, null, 200); - res = higherPrioTuner.tune(feSettings); + res = higherPrioTuner.applyFrontend(info); assertEquals(Tuner.RESULT_SUCCESS, res); + assertNotNull(higherPrioTuner.getFrontendInfo()); higherPrioTuner.close(); @@ -1688,8 +1690,8 @@ public class TunerTest { FrontendInfo info = mTuner.getFrontendInfoById(ids.get(targetFrontendId)); FrontendSettings feSettings = createFrontendSettings(info); - // first tune with mTuner to acquire resource - int res = mTuner.tune(feSettings); + // first apply frontend with mTuner to acquire resource + int res = mTuner.applyFrontend(info); assertEquals(Tuner.RESULT_SUCCESS, res); assertNotNull(mTuner.getFrontendInfo()); @@ -1749,9 +1751,11 @@ public class TunerTest { tunerResourceTestServer = connection.getService(); // CASE1 - normal reclaim - // - // first tune with mTuner to acquire resource - int res = mTuner.tune(feSettings); + + // first apply frontend with mTuner to acquire resource + int res = mTuner.applyFrontend(info); + assertEquals(Tuner.RESULT_SUCCESS, res); + boolean tunerReclaimed = false; assertEquals(Tuner.RESULT_SUCCESS, res); assertNotNull(mTuner.getFrontendInfo()); @@ -1827,27 +1831,27 @@ public class TunerTest { @Test public void testShareFrontendFromTuner() throws Exception { - Tuner tuner100 = new Tuner(mContext, null, 100); - List<Integer> ids = tuner100.getFrontendIds(); + List<Integer> ids = mTuner.getFrontendIds(); assumeNotNull(ids); assertFalse(ids.isEmpty()); int targetFrontendId = sTunerCtsConfiguration.getTargetFrontendId().intValueExact(); - FrontendInfo info = tuner100.getFrontendInfoById(ids.get(targetFrontendId)); + FrontendInfo info = mTuner.getFrontendInfoById(ids.get(targetFrontendId)); FrontendSettings feSettings = createFrontendSettings(info); + int[] statusTypes = {1}; - boolean exceptionThrown = false; - int res; + boolean exceptionThrown; - // CASE1: check resource reclaim while sharee's priority < owner's priority - // let tuner100 share from tuner200 + Tuner tuner100 = new Tuner(mContext, null, 100); Tuner tuner200 = new Tuner(mContext, null, 200); - res = tuner200.tune(feSettings); - assertEquals(Tuner.RESULT_SUCCESS, res); + Tuner tuner300 = new Tuner(mContext, null, 300); - info = tuner200.getFrontendInfoById(ids.get(targetFrontendId)); - res = tuner200.tune(feSettings); + // CASE1: check resource reclaim while sharee's priority < owner's priority + + // apply target frontend only, for case when there are multiple instances in frontend type + int res = tuner200.applyFrontend(info); assertEquals(Tuner.RESULT_SUCCESS, res); + // let tuner100 share from tuner200 tuner100.shareFrontendFromTuner(tuner200); // call openFilter to trigger ITunerDemux.setFrontendDataSourceById() Filter f = tuner100.openFilter( @@ -1859,7 +1863,6 @@ public class TunerTest { TunerTestOnTuneEventListener cb200 = new TunerTestOnTuneEventListener(); // tune again on the owner - info = tuner200.getFrontendInfoById(ids.get(1)); tuner100.setOnTuneEventListener(getExecutor(), cb100); tuner200.setOnTuneEventListener(getExecutor(), cb200); res = tuner200.tune(feSettings); @@ -1870,8 +1873,7 @@ public class TunerTest { tuner200.clearOnTuneEventListener(); // now let the higher priority tuner steal the resource - Tuner tuner300 = new Tuner(mContext, null, 300); - res = tuner300.tune(feSettings); + res = tuner300.applyFrontend(info); assertEquals(Tuner.RESULT_SUCCESS, res); // confirm owner & sharee's resource gets reclaimed by confirming an exception is thrown @@ -1898,7 +1900,9 @@ public class TunerTest { // CASE2: check resource reclaim fail when sharee's priority > new requester tuner100 = new Tuner(mContext, null, 100); - res = tuner100.tune(feSettings); + + // apply target frontend only, for case when there are multiple instances in frontend type + res = tuner100.applyFrontend(info); assertEquals(Tuner.RESULT_SUCCESS, res); tuner300 = new Tuner(mContext, null, 300); @@ -1908,7 +1912,9 @@ public class TunerTest { assertNotNull(f); tuner200 = new Tuner(mContext, null, 200); - res = tuner200.tune(feSettings); + + // apply target frontend only, for case when there are multiple instances in frontend type + res = tuner200.applyFrontend(info); assertNotEquals(Tuner.RESULT_SUCCESS, res); // confirm the original tuner is still intact @@ -1926,19 +1932,21 @@ public class TunerTest { assertFalse(ids.isEmpty()); int targetFrontendId = sTunerCtsConfiguration.getTargetFrontendId().intValueExact(); FrontendInfo info = mTuner.getFrontendInfoById(ids.get(targetFrontendId)); - FrontendSettings feSettings = createFrontendSettings(info); + createFrontendSettings(info); // SCENARIO 1 - transfer and close the previous owner - // First create a tuner and tune() to acquire frontend resource + // First create a tuner and applyFrontend() to acquire frontend resource Tuner tunerA = new Tuner(mContext, null, 100); - int res = tunerA.tune(feSettings); + + // apply target frontend only, for case when there are multiple instances in frontend type + int res = tunerA.applyFrontend(info); assertEquals(Tuner.RESULT_SUCCESS, res); // Create another tuner and share frontend from tunerA Tuner tunerB = new Tuner(mContext, null, 500); tunerB.shareFrontendFromTuner(tunerA); - DvrRecorder d = tunerB.openDvrRecorder(100, getExecutor(), getRecordListener()); + DvrRecorder d = tunerB.openDvrRecorder(188, getExecutor(), getRecordListener()); assertNotNull(d); // Call transferOwner in the wrong configurations and confirm it fails @@ -1962,9 +1970,11 @@ public class TunerTest { // SCENARIO 2 - transfer and closeFrontend and tune on the previous owner - // First create a tuner and tune() to acquire frontend resource + // First create a tuner and applyFrontend() to acquire frontend resource tunerA = new Tuner(mContext, null, 200); - res = tunerA.tune(feSettings); + + // apply target frontend only, for case when there are multiple instances in frontend type + res = tunerA.applyFrontend(info); assertEquals(Tuner.RESULT_SUCCESS, res); // Create another tuner and share frontend from tunerA @@ -1981,7 +1991,9 @@ public class TunerTest { // Confirm tune works without going through Tuner.close() even after transferOwner() // The purpose isn't to get tunerB's frontend revoked, but doing so as singletuner // based test has wider coverage - res = tunerA.tune(feSettings); // this should reclaim tunerB + + // apply target frontend only, for case when there are multiple instances in frontend type + res = tunerA.applyFrontend(info); assertEquals(Tuner.RESULT_SUCCESS, res); // Confirm tuberB is revoked @@ -2071,8 +2083,9 @@ public class TunerTest { // Open Lnb and check the callback TunerTestLnbCallback lnbCB1 = new TunerTestLnbCallback(); Lnb lnbA = tunerA.openLnb(getExecutor(), lnbCB1); - assertNotNull(lnbA); - lnbA.setVoltage(Lnb.VOLTAGE_5V); + assumeTrue(lnbA != null); + int targetLnbVoltage = getTargetLnbVoltage(); + lnbA.setVoltage(targetLnbVoltage); lnbA.setTone(Lnb.TONE_CONTINUOUS); lnbA.sendDiseqcMessage(new byte[] {1, 2}); assertTrue(lnbCB1.getOnDiseqcMessageCalled()); @@ -2706,7 +2719,7 @@ public class TunerTest { assertTrue(token2 == null); // Use DvrPlayback as data source - DvrPlayback d = mTuner.openDvrPlayback(100, getExecutor(), getPlaybackListener()); + DvrPlayback d = mTuner.openDvrPlayback(188, getExecutor(), getPlaybackListener()); assertNotNull(d); Settings settings = SectionSettingsWithTableInfo @@ -2848,7 +2861,7 @@ public class TunerTest { assertTrue(token != null); // Use DvrPlayer as data source - DvrPlayback d = mTuner.openDvrPlayback(100, getExecutor(), getPlaybackListener()); + DvrPlayback d = mTuner.openDvrPlayback(188, getExecutor(), getPlaybackListener()); assertNotNull(d); assertTrue(mSharedFilterTestServer.verifySharedFilter(token)); @@ -2887,7 +2900,7 @@ public class TunerTest { f.configure(config); - DvrPlayback d = mTuner.openDvrPlayback(100, getExecutor(), getPlaybackListener()); + DvrPlayback d = mTuner.openDvrPlayback(188, getExecutor(), getPlaybackListener()); assertNotNull(d); d.configure(getDvrSettings()); @@ -2937,29 +2950,33 @@ public class TunerTest { int type = mTuner.getFrontendInfoById(ids.get(i)).getType(); if (TunerVersionChecker.isHigherOrEqualVersionTo( TunerVersionChecker.TUNER_VERSION_2_0)) { - int defaultMax = -1; + int defaultMax = mTuner.getMaxNumberOfFrontends(type); int status; - // Check default value - defaultMax = mTuner.getMaxNumberOfFrontends(type); - assertTrue(defaultMax > 0); - // Set to -1 - status = mTuner.setMaxNumberOfFrontends(type, -1); - assertEquals(Tuner.RESULT_INVALID_ARGUMENT, status); - // Set to defaultMax + 1 - status = mTuner.setMaxNumberOfFrontends(type, defaultMax + 1); - assertEquals(Tuner.RESULT_INVALID_ARGUMENT, status); - // Set to 0 - status = mTuner.setMaxNumberOfFrontends(type, 0); - assertEquals(Tuner.RESULT_SUCCESS, status); - // Check after set - int currentMax = -1; - currentMax = mTuner.getMaxNumberOfFrontends(type); - assertEquals(currentMax, 0); - // Reset to default - status = mTuner.setMaxNumberOfFrontends(type, defaultMax); - assertEquals(Tuner.RESULT_SUCCESS, status); - currentMax = mTuner.getMaxNumberOfFrontends(type); - assertEquals(defaultMax, currentMax); + // Use try block to ensure restoring the max Tuner + try { + // Check default value + assertTrue(defaultMax > 0); + // Set to -1 + status = mTuner.setMaxNumberOfFrontends(type, -1); + assertEquals(Tuner.RESULT_INVALID_ARGUMENT, status); + // Set to defaultMax + 1 + status = mTuner.setMaxNumberOfFrontends(type, defaultMax + 1); + assertEquals(Tuner.RESULT_INVALID_ARGUMENT, status); + // Set to 0 + status = mTuner.setMaxNumberOfFrontends(type, 0); + assertEquals(Tuner.RESULT_SUCCESS, status); + // Check after set + int currentMax = mTuner.getMaxNumberOfFrontends(type); + assertEquals(currentMax, 0); + } catch (Exception e) { + throw (e); + } finally { + // Reset to default + status = mTuner.setMaxNumberOfFrontends(type, defaultMax); + assertEquals(Tuner.RESULT_SUCCESS, status); + int currentMax = mTuner.getMaxNumberOfFrontends(type); + assertEquals(defaultMax, currentMax); + } } else { int defaultMax = mTuner.getMaxNumberOfFrontends(type); assertEquals(defaultMax, -1); @@ -2979,45 +2996,64 @@ public class TunerTest { assertEquals(Tuner.RESULT_SUCCESS, mTuner.tune(feSettings1)); assertNotNull(mTuner.getFrontendInfo()); - // validate that set max cannot be set to lower value than current usage - assertEquals(Tuner.RESULT_INVALID_ARGUMENT, + // Use try block to ensure restoring the max Tuner + try { + // validate that set max cannot be set to lower value than current usage + assertEquals(Tuner.RESULT_INVALID_ARGUMENT, mTuner.setMaxNumberOfFrontends(type1, 0)); - // validate max value is reflected in the tune behavior - mTuner.closeFrontend(); - assertEquals(Tuner.RESULT_SUCCESS, + // validate max value is reflected in the tune behavior + mTuner.closeFrontend(); + assertEquals(Tuner.RESULT_SUCCESS, mTuner.setMaxNumberOfFrontends(type1, 0)); - assertEquals(Tuner.RESULT_UNAVAILABLE, + assertEquals(Tuner.RESULT_UNAVAILABLE, mTuner.tune(feSettings1)); - assertEquals(Tuner.RESULT_SUCCESS, + assertEquals(Tuner.RESULT_SUCCESS, mTuner.setMaxNumberOfFrontends(type1, originalMax1)); - assertEquals(Tuner.RESULT_SUCCESS, mTuner.tune(feSettings1)); - assertNotNull(mTuner.getFrontendInfo()); - mTuner.closeFrontend(); + assertEquals(Tuner.RESULT_SUCCESS, mTuner.tune(feSettings1)); + assertNotNull(mTuner.getFrontendInfo()); + mTuner.closeFrontend(); + } catch (Exception e) { + throw(e); + } finally { + assertEquals(Tuner.RESULT_SUCCESS, + mTuner.setMaxNumberOfFrontends(type1, originalMax1)); + } } // validate max number on one frontend type has no impact on other if (ids.size() >= 2) { - FrontendInfo info2 = mTuner.getFrontendInfoById(ids.get(1)); - int type2 = info2.getType(); - int originalMax2 = mTuner.getMaxNumberOfFrontends(type2); - - assertEquals(Tuner.RESULT_SUCCESS, - mTuner.setMaxNumberOfFrontends(type2, 0)); - assertEquals(Tuner.RESULT_SUCCESS, - mTuner.tune(feSettings1)); - assertNotNull(mTuner.getFrontendInfo()); - - // set it back to the original max - assertEquals(Tuner.RESULT_SUCCESS, - mTuner.setMaxNumberOfFrontends(type2, originalMax2)); - mTuner.closeFrontend(); + int type2 = type1; + for (int i = 0; i < ids.size(); i++) { + FrontendInfo info2 = mTuner.getFrontendInfoById(ids.get(i)); + type2 = info2.getType(); + if (type1 != type2) break; + } + if (type1 != type2) { + int originalMax2 = mTuner.getMaxNumberOfFrontends(type2); + // Use try block to ensure restoring the max Tuner + try { + assertEquals(Tuner.RESULT_SUCCESS, + mTuner.setMaxNumberOfFrontends(type2, 0)); + assertEquals(Tuner.RESULT_SUCCESS, + mTuner.tune(feSettings1)); + assertNotNull(mTuner.getFrontendInfo()); + mTuner.closeFrontend(); + } catch (Exception e) { + throw (e); + } finally { + // set it back to the original max + assertEquals(Tuner.RESULT_SUCCESS, + mTuner.setMaxNumberOfFrontends(type2, originalMax2)); + } + } } } } + public static Filter createTsSectionFilter( Tuner tuner, Executor e, FilterCallback cb) { Filter f = tuner.openFilter(Filter.TYPE_TS, Filter.SUBTYPE_SECTION, 1000, e, cb); diff --git a/tests/tests/vcn/src/android/net/vcn/cts/VcnManagerTest.java b/tests/tests/vcn/src/android/net/vcn/cts/VcnManagerTest.java index 41b426c9f00..7d13fea0b2c 100644 --- a/tests/tests/vcn/src/android/net/vcn/cts/VcnManagerTest.java +++ b/tests/tests/vcn/src/android/net/vcn/cts/VcnManagerTest.java @@ -16,6 +16,8 @@ package android.net.vcn.cts; +import static android.content.pm.PackageManager.FEATURE_TELEPHONY; +import static android.content.pm.PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION; import static android.ipsec.ike.cts.IkeTunUtils.PortPair; import static android.net.ConnectivityDiagnosticsManager.DataStallReport.DETECTION_METHOD_DNS_EVENTS; import static android.net.ConnectivitySettingsManager.CAPTIVE_PORTAL_MODE_PROMPT; @@ -36,7 +38,6 @@ import static android.net.vcn.VcnManager.VCN_STATUS_CODE_SAFE_MODE; import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_ANY; import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_FORBIDDEN; import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED; -import static android.net.vcn.cts.VcnSystemRequirementsTest.assumeNotNullVcnDependencies; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; @@ -52,7 +53,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import static org.junit.Assume.assumeNotNull; +import static org.junit.Assume.assumeTrue; import android.annotation.NonNull; import android.annotation.Nullable; @@ -157,10 +158,23 @@ public class VcnManagerTest extends VcnTestBase { @Before public void setUp() throws Exception { - // This test assumes the device supports VCN and its dependent system services. Refer to - // VcnSystemRequirementsTest for comprehensive verification of VCN system requirements. - assumeNotNullVcnDependencies(mContext); - assumeNotNull(mVcnManager); + final boolean hasFeatureTelephony = + mContext.getPackageManager().hasSystemFeature(FEATURE_TELEPHONY); + final boolean hasFeatureTelSubscription = + mContext.getPackageManager().hasSystemFeature(FEATURE_TELEPHONY_SUBSCRIPTION); + final boolean hasTelephonyFlag = hasFeatureTelephony || hasFeatureTelSubscription; + + // Before V, only devices with FEATURE_TELEPHONY are required to run the tests. Starting + // from V, tests are also required on following cases: + // + // Device that has a non-null VcnManager even if it has neither of FEATURE_TELEPHONY or + // FEATURE_TELEPHONY_SUBSCRIPTION. + // + // Device that has FEATURE_TELEPHONY_SUBSCRIPTION. This should not be a new requirement + // since before V devices with FEATURE_TELEPHONY_SUBSCRIPTION are already enforced to have + // FEATURE_TELEPHONY. + assumeTrue(hasTelephonyFlag || mVcnManager != null); + getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(); // Ensure Internet probing check will be performed on VCN networks diff --git a/tests/tests/vcn/src/android/net/vcn/cts/VcnSystemRequirementsTest.java b/tests/tests/vcn/src/android/net/vcn/cts/VcnSystemRequirementsTest.java index 2672b3b82f8..b095d7f674b 100644 --- a/tests/tests/vcn/src/android/net/vcn/cts/VcnSystemRequirementsTest.java +++ b/tests/tests/vcn/src/android/net/vcn/cts/VcnSystemRequirementsTest.java @@ -57,15 +57,6 @@ public class VcnSystemRequirementsTest { context.getSystemService(TelephonyManager.class)); } - // Package private for use in VcnManagerTest - static void assumeNotNullVcnDependencies(Context context) { - try { - assertNotNullVcnDependencies(context); - } catch (AssertionError e) { - assumeTrue(e.toString(), false); - } - } - /** * This test verifies that devices with vendor API greater than or equal to V, and which support * FEATURE_TELEPHONY_SUBSCRIPTION, must also implement VCN and its dependent system services. @@ -80,17 +71,4 @@ public class VcnSystemRequirementsTest { assertNotNullVcnDependencies(mContext); assertNotNull("VcnManager must be present on this device", mVcnManager); } - - /** - * For devices with vendor API lower than V, it is not mandatory to support VCN or VCN's - * dependent system services. However, if all VCN dependencies are present, the device must - * support VCN. - */ - @Test - public void testVcndDependenciesOnDevicesBeforeV() throws Exception { - assumeTrue(getVsrApiLevel() < VENDOR_API_FOR_ANDROID_V); - assumeNotNullVcnDependencies(mContext); - - assertNotNull("VcnManager must be present on this device", mVcnManager); - } } diff --git a/tests/tests/view/src/android/view/cts/OWNERS b/tests/tests/view/src/android/view/cts/OWNERS index fae65c55352..b5449803b17 100644 --- a/tests/tests/view/src/android/view/cts/OWNERS +++ b/tests/tests/view/src/android/view/cts/OWNERS @@ -13,3 +13,5 @@ per-file *KeyEvent* = file:platform/frameworks/base:/INPUT_OWNERS per-file *PointerCapture* = file:platform/frameworks/base:/INPUT_OWNERS per-file *MotionEvent* = file:platform/frameworks/base:/INPUT_OWNERS per-file *VelocityTracker* = file:platform/frameworks/base:/INPUT_OWNERS + +per-file *NativeHeapLeakDetector* = file:platform/frameworks/base:/INPUT_OWNERS diff --git a/tests/tests/view/src/android/view/cts/ViewTest.java b/tests/tests/view/src/android/view/cts/ViewTest.java index 7607569e230..f32376d0036 100644 --- a/tests/tests/view/src/android/view/cts/ViewTest.java +++ b/tests/tests/view/src/android/view/cts/ViewTest.java @@ -4124,24 +4124,29 @@ public class ViewTest { CtsMouseUtil.obtainMouseEvent(MotionEvent.ACTION_SCROLL, mockView, mockView.getWidth() - 1, 0); mInstrumentation.sendPointerSync(event); + mInstrumentation.waitForIdleSync(); assertTrue(fitWindowsView.isInTouchMode()); mInstrumentation.sendKeySync(keyEvent); + mInstrumentation.waitForIdleSync(); mActivityRule.runOnUiThread(() -> assertFalse(fitWindowsView.isInTouchMode())); event.setAction(MotionEvent.ACTION_DOWN); mInstrumentation.sendPointerSync(event); event.setAction(MotionEvent.ACTION_UP); mInstrumentation.sendPointerSync(event); + mInstrumentation.waitForIdleSync(); assertTrue(fitWindowsView.isInTouchMode()); mInstrumentation.sendKeySync(keyEvent); + mInstrumentation.waitForIdleSync(); mActivityRule.runOnUiThread(() -> assertFalse(fitWindowsView.isInTouchMode())); // Stylus events should trigger touch mode. event.setAction(MotionEvent.ACTION_DOWN); event.setSource(InputDevice.SOURCE_STYLUS); mInstrumentation.sendPointerSync(event); + mInstrumentation.waitForIdleSync(); assertTrue(fitWindowsView.isInTouchMode()); } 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 83f70e0301c..21e156c8d1a 100644 --- a/tests/tests/wifi/src/android/net/wifi/cts/WifiManagerTest.java +++ b/tests/tests/wifi/src/android/net/wifi/cts/WifiManagerTest.java @@ -1173,26 +1173,30 @@ public class WifiManagerTest extends WifiJUnit4TestBase { } boolean wifiEnabled = sWifiManager.isWifiEnabled(); if (wifiEnabled) { - // Re-enabled Wi-Fi as shell for HalDeviceManager legacy LOHS behavior when there's no - // STA+AP concurrency. - ShellIdentityUtils.invokeWithShellPermissions(() -> sWifiManager.setWifiEnabled(false)); + // Re-enabled Wi-Fi as shell for HalDeviceManager legacy LOHS behavior when there's + // no STA+AP concurrency. + ShellIdentityUtils.invokeWithShellPermissions(() -> + sWifiManager.setWifiEnabled(false)); PollingCheck.check("Wifi turn off failed!", WIFI_OFF_ON_TIMEOUT_MILLIS, () -> !sWifiManager.isWifiEnabled()); SystemUtil.runShellCommand("cmd wifi set-wifi-enabled enabled"); PollingCheck.check("Wifi turn on failed!", WIFI_OFF_ON_TIMEOUT_MILLIS, () -> sWifiManager.isWifiEnabled()); } - TestLocalOnlyHotspotCallback callback = startLocalOnlyHotspot(); + runWithScanning(() -> { + TestLocalOnlyHotspotCallback callback = startLocalOnlyHotspot(); - // add sleep to avoid calling stopLocalOnlyHotspot before TetherController initialization. - // TODO: remove this sleep as soon as b/124330089 is fixed. - Log.d(TAG, "Sleeping for 2 seconds"); - Thread.sleep(2000); + // add sleep to avoid calling stopLocalOnlyHotspot before TetherController + // initialization. + // TODO: remove this sleep as soon as b/124330089 is fixed. + Log.d(TAG, "Sleeping for 2 seconds"); + Thread.sleep(2000); - stopLocalOnlyHotspot(callback, wifiEnabled); + stopLocalOnlyHotspot(callback, wifiEnabled); - // wifi should either stay on, or come back on - assertEquals(wifiEnabled, sWifiManager.isWifiEnabled()); + // wifi should either stay on, or come back on + assertEquals(wifiEnabled, sWifiManager.isWifiEnabled()); + }, false); } /** @@ -1952,13 +1956,12 @@ public class WifiManagerTest extends WifiJUnit4TestBase { // check that softap mode is supported by the device assumeTrue(sWifiManager.isPortableHotspotSupported()); - boolean caughtException = false; - boolean wifiEnabled = sWifiManager.isWifiEnabled(); if (wifiEnabled) { - // Re-enabled Wi-Fi as shell for HalDeviceManager legacy LOHS behavior when there's no - // STA+AP concurrency. - ShellIdentityUtils.invokeWithShellPermissions(() -> sWifiManager.setWifiEnabled(false)); + // Re-enabled Wi-Fi as shell for HalDeviceManager legacy LOHS behavior when there's + // no STA+AP concurrency. + ShellIdentityUtils.invokeWithShellPermissions(() -> + sWifiManager.setWifiEnabled(false)); PollingCheck.check("Wifi turn off failed!", WIFI_OFF_ON_TIMEOUT_MILLIS, () -> !sWifiManager.isWifiEnabled()); SystemUtil.runShellCommand("cmd wifi set-wifi-enabled enabled"); @@ -1966,36 +1969,42 @@ public class WifiManagerTest extends WifiJUnit4TestBase { () -> sWifiManager.isWifiEnabled()); } - TestLocalOnlyHotspotCallback callback = startLocalOnlyHotspot(); + runWithScanning(() -> { + boolean caughtException = false; + TestLocalOnlyHotspotCallback callback = startLocalOnlyHotspot(); - // now make a second request - this should fail. - TestLocalOnlyHotspotCallback callback2 = new TestLocalOnlyHotspotCallback(mLock); - try { - sWifiManager.startLocalOnlyHotspot(callback2, null); - } catch (IllegalStateException e) { - Log.d(TAG, "Caught the IllegalStateException we expected: called startLOHS twice"); - caughtException = true; - } - if (!caughtException) { - // second start did not fail, should clean up the hotspot. + // now make a second request - this should fail. + TestLocalOnlyHotspotCallback callback2 = new TestLocalOnlyHotspotCallback(mLock); + try { + sWifiManager.startLocalOnlyHotspot(callback2, null); + } catch (IllegalStateException e) { + Log.d(TAG, "Caught the IllegalStateException we expected: called startLOHS twice"); + caughtException = true; + } + if (!caughtException) { + // second start did not fail, should clean up the hotspot. + + // add sleep to avoid calling stopLocalOnlyHotspot before TetherController + // initialization. + // TODO: remove this sleep as soon as b/124330089 is fixed. + Log.d(TAG, "Sleeping for 2 seconds"); + Thread.sleep(2000); - // add sleep to avoid calling stopLocalOnlyHotspot before TetherController initialization. + stopLocalOnlyHotspot(callback2, wifiEnabled); + } + assertTrue(caughtException); + + // add sleep to avoid calling stopLocalOnlyHotspot before TetherController + // initialization. // TODO: remove this sleep as soon as b/124330089 is fixed. Log.d(TAG, "Sleeping for 2 seconds"); Thread.sleep(2000); - stopLocalOnlyHotspot(callback2, wifiEnabled); - } - assertTrue(caughtException); - - // add sleep to avoid calling stopLocalOnlyHotspot before TetherController initialization. - // TODO: remove this sleep as soon as b/124330089 is fixed. - Log.d(TAG, "Sleeping for 2 seconds"); - Thread.sleep(2000); - - stopLocalOnlyHotspot(callback, wifiEnabled); + stopLocalOnlyHotspot(callback, wifiEnabled); + }, false); } + private static class TestExecutor implements Executor { private ConcurrentLinkedQueue<Runnable> tasks = new ConcurrentLinkedQueue<>(); @@ -3701,28 +3710,29 @@ public class WifiManagerTest extends WifiJUnit4TestBase { // Re-enabled Wi-Fi as shell for HalDeviceManager legacy LOHS behavior when there's no // STA+AP concurrency. ShellIdentityUtils.invokeWithShellPermissions(() -> sWifiManager.setWifiEnabled(false)); - PollingCheck.check("Wifi turn off failed!", WIFI_OFF_ON_TIMEOUT_MILLIS, - () -> !sWifiManager.isWifiEnabled()); + PollingCheck.check("Wifi turn off failed!", 2_000, () -> !sWifiManager.isWifiEnabled()); SystemUtil.runShellCommand("cmd wifi set-wifi-enabled enabled"); PollingCheck.check("Wifi turn on failed!", WIFI_OFF_ON_TIMEOUT_MILLIS, () -> sWifiManager.isWifiEnabled()); - boolean isStaApConcurrencySupported = sWifiManager.isStaApConcurrencySupported(); - // start local only hotspot. - TestLocalOnlyHotspotCallback callback = startLocalOnlyHotspot(); - try { - if (isStaApConcurrencySupported) { - assertTrue(sWifiManager.isWifiEnabled()); - } else { - // no concurrency, wifi should be disabled. - assertFalse(sWifiManager.isWifiEnabled()); + runWithScanning(() -> { + boolean isStaApConcurrencySupported = sWifiManager.isStaApConcurrencySupported(); + // start local only hotspot. + TestLocalOnlyHotspotCallback callback = startLocalOnlyHotspot(); + try { + if (isStaApConcurrencySupported) { + assertTrue(sWifiManager.isWifiEnabled()); + } else { + // no concurrency, wifi should be disabled. + assertFalse(sWifiManager.isWifiEnabled()); + } + } finally { + // clean up local only hotspot no matter if assertion passed or failed + stopLocalOnlyHotspot(callback, true); } - } finally { - // clean up local only hotspot no matter if assertion passed or failed - stopLocalOnlyHotspot(callback, true); - } - assertTrue(sWifiManager.isWifiEnabled()); + assertTrue(sWifiManager.isWifiEnabled()); + }, false); } /** @@ -3811,7 +3821,7 @@ public class WifiManagerTest extends WifiJUnit4TestBase { boolean newState = !currState; sWifiManager.setScanAlwaysAvailable(newState); PollingCheck.check( - "Wifi settings toggle failed!", + "Wifi scanning toggle failed!", DURATION_SETTINGS_TOGGLE, () -> sWifiManager.isScanAlwaysAvailable() == newState); assertEquals(newState, sWifiManager.isScanAlwaysAvailable()); diff --git a/tools/cts-api-coverage/Android.bp b/tools/cts-api-coverage/Android.bp index e9aeb8ca070..51d57de70c3 100644 --- a/tools/cts-api-coverage/Android.bp +++ b/tools/cts-api-coverage/Android.bp @@ -20,7 +20,8 @@ java_library_host { name: "api-coverage", srcs: [ - "**/*.java", + "src/com/android/cts/apicommon/**/*.java", + "src/com/android/cts/apicoverage/**/*.java", "proto/**/*.proto", ], @@ -55,3 +56,20 @@ java_binary_host { static_libs: ["api-coverage"], manifest: "MANIFEST.mf", } + +java_library_host { + name: "api-map", + srcs: [ + "src/com/android/cts/apicommon/**/*.java", + "src/com/android/cts/apimap/**/*.java", + "src/com/android/cts/ctsprofiles/**/*.java", + ], + static_libs: [ + "apache-commons-math3", + "jsr305", + "ow2-asm", + "ow2-asm-analysis", + "ow2-asm-commons", + "ow2-asm-tree", + ], +} diff --git a/tools/cts-api-coverage/src/com/android/cts/apicommon/ApiClass.java b/tools/cts-api-coverage/src/com/android/cts/apicommon/ApiClass.java index 59c5d534cc7..b05d74e4920 100644 --- a/tools/cts-api-coverage/src/com/android/cts/apicommon/ApiClass.java +++ b/tools/cts-api-coverage/src/com/android/cts/apicommon/ApiClass.java @@ -114,6 +114,37 @@ public class ApiClass implements Comparable<ApiClass>, HasCoverage { mApiMethods.add(method); } + /** Look for a matching constructor and mark it as covered by the given test method */ + public void markConstructorCoveredTest( + List<String> parameterTypes, String testMethod) { + if (mSuperClass != null) { + // Mark matching constructors in the superclass + mSuperClass.markConstructorCoveredTest(parameterTypes, testMethod); + } + Optional<ApiConstructor> apiConstructor = getConstructor(parameterTypes); + apiConstructor.ifPresent(constructor -> constructor.setCoveredTest(testMethod)); + } + + + /** Look for a matching method and if found and mark it as covered by the given test method */ + public void markMethodCoveredTest( + String name, List<String> parameterTypes, String testMethod) { + if (mSuperClass != null) { + // Mark matching methods in the super class + mSuperClass.markMethodCoveredTest(name, parameterTypes, testMethod); + } + if (!mInterfaceMap.isEmpty()) { + // Mark matching methods in the interfaces + for (ApiClass mInterface : mInterfaceMap.values()) { + if (mInterface != null) { + mInterface.markMethodCoveredTest(name, parameterTypes, testMethod); + } + } + } + Optional<ApiMethod> apiMethod = getMethod(name, parameterTypes); + apiMethod.ifPresent(method -> method.setCoveredTest(testMethod)); + } + /** Look for a matching constructor and mark it as covered */ public void markConstructorCovered(List<String> parameterTypes, String coveredbyApk) { if (mSuperClass != null) { diff --git a/tools/cts-api-coverage/src/com/android/cts/apicommon/ApiConstructor.java b/tools/cts-api-coverage/src/com/android/cts/apicommon/ApiConstructor.java index af9c99ec88c..1f765e7cc3d 100644 --- a/tools/cts-api-coverage/src/com/android/cts/apicommon/ApiConstructor.java +++ b/tools/cts-api-coverage/src/com/android/cts/apicommon/ApiConstructor.java @@ -35,6 +35,9 @@ public class ApiConstructor implements Comparable<ApiConstructor> { // A list of test APKs (aka CTS modules) that use this method. private final Map<String, Boolean> mCoveredWith = new ConcurrentHashMap<>(); + // A list of CTS test methods that call this API constructor. + private final Map<String, Boolean> mCoveredTests = new ConcurrentHashMap<>(); + public ApiConstructor(String name, List<String> parameterTypes, boolean deprecated) { mName = name; mParameterTypes = new ArrayList<String>(parameterTypes); @@ -69,7 +72,16 @@ public class ApiConstructor implements Comparable<ApiConstructor> { mCoveredWith.put(coveredWithModule, true); } + /** Adds a test method that is calling this API. */ + public void setCoveredTest(String testMethod) { + mCoveredTests.put(testMethod, true); + } + public Set<String> getCoveredWith() { return mCoveredWith.keySet(); } + + public Set<String> getCoveredTests() { + return mCoveredTests.keySet(); + } } diff --git a/tools/cts-api-coverage/src/com/android/cts/apicommon/ApiMethod.java b/tools/cts-api-coverage/src/com/android/cts/apicommon/ApiMethod.java index 43746c51035..eb900cee862 100644 --- a/tools/cts-api-coverage/src/com/android/cts/apicommon/ApiMethod.java +++ b/tools/cts-api-coverage/src/com/android/cts/apicommon/ApiMethod.java @@ -46,6 +46,9 @@ public class ApiMethod implements Comparable<ApiMethod> { // A list of test APKs (aka CTS modules) that use this method. private final Map<String, Boolean> mCoveredWith = new ConcurrentHashMap<>(); + // A list of CTS test methods that call this API method. + private final Map<String, Boolean> mCoveredTests = new ConcurrentHashMap<>(); + public ApiMethod( String name, List<String> parameterTypes, @@ -110,6 +113,10 @@ public class ApiMethod implements Comparable<ApiMethod> { return mCoveredWith.keySet(); } + public Set<String> getCoveredTests() { + return mCoveredTests.keySet(); + } + public void setCovered(String coveredWithModule) { if (coveredWithModule.endsWith(".apk")) { coveredWithModule = coveredWithModule.substring(0, coveredWithModule.length() - 4); @@ -117,4 +124,9 @@ public class ApiMethod implements Comparable<ApiMethod> { mCoveredWith.put(coveredWithModule, true); } + + /** Adds a test method that is calling this API. */ + public void setCoveredTest(String coveredTest) { + mCoveredTests.put(coveredTest, true); + } } diff --git a/tools/cts-api-coverage/src/com/android/cts/apicommon/ApiPackage.java b/tools/cts-api-coverage/src/com/android/cts/apicommon/ApiPackage.java index 902f53872eb..9f588b43c17 100644 --- a/tools/cts-api-coverage/src/com/android/cts/apicommon/ApiPackage.java +++ b/tools/cts-api-coverage/src/com/android/cts/apicommon/ApiPackage.java @@ -117,4 +117,3 @@ public class ApiPackage implements HasCoverage { return null; } } - diff --git a/tools/cts-api-coverage/src/com/android/cts/apicoverage/CoverageComparator.java b/tools/cts-api-coverage/src/com/android/cts/apicommon/CoverageComparator.java index f33a839daf3..8169c254916 100644 --- a/tools/cts-api-coverage/src/com/android/cts/apicoverage/CoverageComparator.java +++ b/tools/cts-api-coverage/src/com/android/cts/apicommon/CoverageComparator.java @@ -14,13 +14,13 @@ * limitations under the License. */ -package com.android.cts.apicoverage; - -import com.android.cts.apicommon.HasCoverage; +package com.android.cts.apicommon; import java.util.Comparator; public class CoverageComparator implements Comparator<HasCoverage> { + + /** Compares whether two coverage entities are same. */ public int compare(HasCoverage entity, HasCoverage otherEntity) { int lhsPct = Math.round(entity.getCoveragePercentage()); int rhsPct = Math.round(otherEntity.getCoveragePercentage()); diff --git a/tools/cts-api-coverage/src/com/android/cts/apicoverage/TextReport.java b/tools/cts-api-coverage/src/com/android/cts/apicoverage/TextReport.java index 2ab69ddc865..3450a7bf2f0 100644 --- a/tools/cts-api-coverage/src/com/android/cts/apicoverage/TextReport.java +++ b/tools/cts-api-coverage/src/com/android/cts/apicoverage/TextReport.java @@ -21,6 +21,7 @@ import com.android.cts.apicommon.ApiConstructor; import com.android.cts.apicommon.ApiCoverage; import com.android.cts.apicommon.ApiMethod; import com.android.cts.apicommon.ApiPackage; +import com.android.cts.apicommon.CoverageComparator; import java.io.OutputStream; import java.io.PrintStream; diff --git a/tools/cts-api-coverage/src/com/android/cts/apicoverage/XmlReport.java b/tools/cts-api-coverage/src/com/android/cts/apicoverage/XmlReport.java index 8302882a4ce..ec656d53668 100644 --- a/tools/cts-api-coverage/src/com/android/cts/apicoverage/XmlReport.java +++ b/tools/cts-api-coverage/src/com/android/cts/apicoverage/XmlReport.java @@ -21,6 +21,7 @@ import com.android.cts.apicommon.ApiConstructor; import com.android.cts.apicommon.ApiCoverage; import com.android.cts.apicommon.ApiMethod; import com.android.cts.apicommon.ApiPackage; +import com.android.cts.apicommon.CoverageComparator; import java.io.File; import java.io.OutputStream; diff --git a/tools/cts-api-coverage/src/com/android/cts/apimap/AnnotationAnalyzer.java b/tools/cts-api-coverage/src/com/android/cts/apimap/AnnotationAnalyzer.java new file mode 100644 index 00000000000..9bd28b04a0b --- /dev/null +++ b/tools/cts-api-coverage/src/com/android/cts/apimap/AnnotationAnalyzer.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.cts.apimap; + +import com.android.cts.ctsprofiles.AnnotationManagement; + +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.Opcodes; + +/** A class for collecting annotation values. */ +class AnnotationAnalyzer extends AnnotationVisitor { + + private final AnnotationManagement mAnnotationManagement; + + private final String mFieldSignature; + + AnnotationAnalyzer( + AnnotationManagement annotationManagement, + String fieldSignature) { + super(Opcodes.ASM9); + mAnnotationManagement = annotationManagement; + mFieldSignature = fieldSignature; + } + + @Override + public AnnotationVisitor visitArray(String name) { + return new AnnotationAnalyzer(mAnnotationManagement, getFieldSignature(name)); + } + + @Override + public void visit(String name, Object value) { + mAnnotationManagement.addTestMetadata( + getFieldSignature(name), String.valueOf(value)); + } + + /** Appends an annotation field name to the field signature. */ + private String getFieldSignature(String fieldName) { + if (fieldName == null) { + return mFieldSignature; + } + return String.format("%s:%s", mFieldSignature, fieldName); + } +} diff --git a/tools/cts-api-coverage/src/com/android/cts/apimap/ApiMap.java b/tools/cts-api-coverage/src/com/android/cts/apimap/ApiMap.java new file mode 100644 index 00000000000..e7a929ed102 --- /dev/null +++ b/tools/cts-api-coverage/src/com/android/cts/apimap/ApiMap.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.cts.apimap; + +public class ApiMap { + + /** Entry of the CTS-M automation tool. */ + public static void main(String[] args) {} +} diff --git a/tools/cts-api-coverage/src/com/android/cts/apimap/CallGraphManager.java b/tools/cts-api-coverage/src/com/android/cts/apimap/CallGraphManager.java new file mode 100644 index 00000000000..f1eca857113 --- /dev/null +++ b/tools/cts-api-coverage/src/com/android/cts/apimap/CallGraphManager.java @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.cts.apimap; + +import com.android.cts.apicommon.ApiClass; +import com.android.cts.apicommon.ApiCoverage; +import com.android.cts.apicommon.ApiPackage; +import com.android.cts.ctsprofiles.ClassProfile; +import com.android.cts.ctsprofiles.MethodProfile; +import com.android.cts.ctsprofiles.ModuleProfile; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Stack; + + +/** A class for collecting APIs covered by a CTS module. */ +public class CallGraphManager { + + // Cache API calls for each CTS method. + private final Map<String, CoveredApiCache> mCoveredApiCaches = new HashMap<>(); + + private final ModuleProfile mModule; + + /** Cache the covered API list for a CTS method. */ + static class CoveredApiCache { + + private final Map<String, MethodProfile> mApiConstructors = new HashMap<>(); + + private final Map<String, MethodProfile> mApiMethods = new HashMap<>(); + + public void mergeApis(CoveredApiCache apis) { + addMethods(apis.getApiMethods()); + addConstructors(apis.getApiConstructors()); + } + + public void addMethods(Map<String, MethodProfile> methods) { + mApiMethods.putAll(methods); + } + + public void addConstructors(Map<String, MethodProfile> constructors) { + mApiConstructors.putAll(constructors); + } + + public Map<String, MethodProfile> getApiConstructors() { + return mApiConstructors; + } + + public Map<String, MethodProfile> getApiMethods() { + return mApiMethods; + } + } + + public CallGraphManager(ModuleProfile moduleProfile) { + mModule = moduleProfile; + } + + public ModuleProfile getModule() { + return mModule; + } + + /** + * Maps detected APIs to CTS test methods and marks them as covered by this CTS module. + */ + public void resolveCoveredApis(ApiCoverage apiCoverage) { + for (ClassProfile classProfile : mModule.getClasses()) { + if (!classProfile.isNonAbstractTestClass()) { + continue; + } + for (MethodProfile methodProfile : classProfile.getTestMethods().values()) { + TarJan tarjan = new TarJan(methodProfile, mCoveredApiCaches.keySet()); + Stack<Integer> stack = new Stack<>(); + Set<Integer> visitedComponents = new HashSet<>(); + String methodSignature = methodProfile.getMethodSignatureWithClass(); + stack.add(tarjan.getComponentID(methodSignature)); + visitedComponents.add(tarjan.getComponentID(methodSignature)); + // Do recursive search for API calls. + resolveMethodCoveredApis(stack, visitedComponents, tarjan); + markCoveredApisWithCaller(methodSignature, apiCoverage); + } + } + markCoveredApisWithoutCaller(apiCoverage); + } + + /** Collects covered APIs for a test method via memorized search. */ + private CoveredApiCache resolveMethodCoveredApis( + Stack<Integer> stack, + Set<Integer> visitedComponents, + TarJan tarjan) { + List<MethodProfile> methods = tarjan.getComponent(stack.peek()); + String methodSignature = methods.get(0).getMethodSignatureWithClass(); + CoveredApiCache coveredApis = mCoveredApiCaches.get(methodSignature); + if (coveredApis != null) { + return coveredApis; + } + coveredApis = new CoveredApiCache(); + for (MethodProfile method: methods) { + coveredApis.addMethods(method.getApiMethodCalls()); + coveredApis.addConstructors(method.getApiConstructorCalls()); + } + for (MethodProfile method: methods) { + for (MethodProfile methodCall : method.getCommonMethodCalls().values()) { + String methodCallSignature = methodCall.getMethodSignatureWithClass(); + int componentID = tarjan.getComponentID(methodCallSignature); + if (visitedComponents.contains(componentID)) { + continue; + } + visitedComponents.add(componentID); + stack.add(componentID); + CoveredApiCache apis = resolveMethodCoveredApis(stack, visitedComponents, tarjan); + coveredApis.mergeApis(apis); + stack.pop(); + } + } + for (MethodProfile method: methods) { + mCoveredApiCaches.put(method.getMethodSignatureWithClass(), coveredApis); + } + return coveredApis; + } + + /** Searches for the API class based on the given package name and class name. */ + private ApiClass getApiClass( + String packageName, String className, ApiCoverage apiCoverage) { + ApiPackage apiPackage = apiCoverage.getPackage(packageName); + if (apiPackage != null) { + return apiPackage.getClass(className); + } + return null; + } + + /** Marks that APIs are covered by this CTS module. */ + private void markCoveredApisWithoutCaller(ApiCoverage apiCoverage) { + for (ClassProfile classProfile: mModule.getClasses()) { + if (!classProfile.isApiClass()) { + continue; + } + ApiClass apiClass = getApiClass( + classProfile.getPackageName(), + classProfile.getClassName(), + apiCoverage + ); + if (apiClass == null) { + continue; + } + for (MethodProfile methodProfile: classProfile.getMethods().values()) { + if (methodProfile.getMethodName().equals("<init>")) { + apiClass.markConstructorCovered( + methodProfile.getMethodParams(), + mModule.getModuleName() + ); + } else { + apiClass.markMethodCovered( + methodProfile.getMethodName(), + methodProfile.getMethodParams(), + mModule.getModuleName() + ); + } + } + } + } + + /** Marks that APIs are called by the given CTS test method. */ + private void markCoveredApisWithCaller(String methodSignature, ApiCoverage apiCoverage) { + CoveredApiCache apiCache = mCoveredApiCaches.get(methodSignature); + if (apiCache == null) { + return; + } + for (MethodProfile apiConstructor : apiCache.getApiConstructors().values()) { + ApiClass apiClass = getApiClass( + apiConstructor.getPackageName(), + apiConstructor.getClassName(), + apiCoverage + ); + if (apiClass != null) { + apiClass.markConstructorCoveredTest( + apiConstructor.getMethodParams(), + String.format("[%s] %s", mModule.getModuleName(), methodSignature) + ); + } + } + for (MethodProfile apiMethod : apiCache.getApiMethods().values()) { + ApiClass apiClass = getApiClass( + apiMethod.getPackageName(), apiMethod.getClassName(), apiCoverage); + if (apiClass != null) { + apiClass.markMethodCoveredTest( + apiMethod.getMethodName(), + apiMethod.getMethodParams(), + String.format("[%s] %s", mModule.getModuleName(), methodSignature) + ); + } + } + } +} diff --git a/tools/cts-api-coverage/src/com/android/cts/apimap/ClassAnalyzer.java b/tools/cts-api-coverage/src/com/android/cts/apimap/ClassAnalyzer.java new file mode 100644 index 00000000000..87d932690da --- /dev/null +++ b/tools/cts-api-coverage/src/com/android/cts/apimap/ClassAnalyzer.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.cts.apimap; + +import com.android.cts.apicommon.ApiCoverage; +import com.android.cts.ctsprofiles.ClassProfile; +import com.android.cts.ctsprofiles.ClassProfile.ClassType; +import com.android.cts.ctsprofiles.MethodProfile; +import com.android.cts.ctsprofiles.MethodProfile.MethodType; +import com.android.cts.ctsprofiles.ModuleProfile; +import com.android.cts.ctsprofiles.Utils; + +import org.apache.commons.math3.util.Pair; +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + + +/** A class for collecting class methods and annotations. */ +public class ClassAnalyzer extends ClassVisitor { + + private final ClassProfile mClass; + + private final ModuleProfile mModule; + + private final ApiCoverage mApiCoverage; + + static final Set<String> XTS_ANNOTATIONS = new HashSet<>( + List.of("com.android.compatibility.common.util.CddTest", + "com.android.compatibility.common.util.ApiTest", + "com.android.compatibility.common.util.GmsTest", + "com.android.compatibility.common.util.VsrTest", + "android.platform.test.annotations.RequiresFlagsEnabled", + "android.platform.test.annotations.RequiresFlagsDisabled") + ); + + public ClassAnalyzer( + ClassProfile classProfile, ModuleProfile moduleProfile, ApiCoverage apiCoverage) { + super(Opcodes.ASM9); + mClass = classProfile; + mModule = moduleProfile; + mApiCoverage = apiCoverage; + } + + @Override + public void visit( + int version, + int access, + String name, + String signature, + String superName, + String[] interfaces) { + addClassType(access, Opcodes.ACC_ABSTRACT, ClassType.ABSTRACT); + addClassType(access, Opcodes.ACC_INTERFACE, ClassType.INTERFACE); + addClassType(access, Opcodes.ACC_ANNOTATION, ClassType.ANNOTATION); + if (superName != null) { + mClass.setSuperClass(getClassProfile(superName)); + } + if (interfaces != null) { + for (String interfaceName : interfaces) { + mClass.addInterface(getClassProfile(interfaceName)); + } + } + } + + @Override + public MethodVisitor visitMethod( + int access, + String name, + String desc, + String signature, + String[] exceptions) { + // Add a method in the class. + List<String> params = new ArrayList<>(); + for (Type type: Type.getArgumentTypes(desc)) { + params.add(type.getClassName().replace("\\$", ".")); + } + MethodProfile method = mClass.getOrCreateMethod(name, params); + method.addMethodType(MethodType.DIRECT_MEMBER); + Type returnType = Type.getReturnType(desc); + // Set the method type to common if it is not a test methods candidate. + if ((access & Opcodes.ACC_PUBLIC) == 0 + || !returnType.getClassName().equals("void") + || !params.isEmpty()) { + method.addMethodType(MethodType.COMMON); + } + return new MethodAnalyzer(method, mModule, mApiCoverage); + } + + @Override + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + // Add an annotation for the class. + String type = Type.getType(desc).getClassName(); + if (!shouldRecordAnnotation(type)) { + return super.visitAnnotation(desc, visible); + } + Pair<String, String> packageClass = Utils.getPackageClass(type); + ClassProfile annotationClass = mModule.getOrCreateClass( + packageClass.getFirst(), packageClass.getSecond(), mApiCoverage); + mClass.annotationManagement.addAnnotation(annotationClass); + if (XTS_ANNOTATIONS.contains(type)) { + return new AnnotationAnalyzer(mClass.annotationManagement, packageClass.getSecond()); + } + return super.visitAnnotation(desc, visible); + } + + private ClassProfile getClassProfile(String asmClassName) { + // Add a class in the module. + Pair<String, String> packageClass = Utils.getPackageClassFromASM(asmClassName); + return mModule.getOrCreateClass( + packageClass.getFirst(), packageClass.getSecond(), mApiCoverage); + } + + private void addClassType(int access, int asmType, ClassType classType) { + if ((access & asmType) != 0) { + mClass.addClassType(classType); + } + } + + private static boolean shouldRecordAnnotation(String name) { + // TODO(slotus): Filter out some annotations. + return true; + } +} diff --git a/tools/cts-api-coverage/src/com/android/cts/apimap/HtmlWriter.java b/tools/cts-api-coverage/src/com/android/cts/apimap/HtmlWriter.java new file mode 100644 index 00000000000..de2fb6fcfa2 --- /dev/null +++ b/tools/cts-api-coverage/src/com/android/cts/apimap/HtmlWriter.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.cts.apimap; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; + +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; + +/** + * Class that outputs an HTML report of the API mapping data. The format is as same as + * cts-api-coverage HTML reports. + */ +class HtmlWriter { + + public static void printHtmlReport(XmlWriter xmlWriter, OutputStream htmlOut) + throws TransformerException, IOException { + + final PipedOutputStream xmlOut = new PipedOutputStream(); + final PipedInputStream xmlIn = new PipedInputStream(xmlOut); + + Thread t = new Thread(new Runnable() { + @Override + public void run() { + try { + xmlWriter.dumpXml(xmlOut); + } catch (FileNotFoundException | TransformerException e) { + throw new RuntimeException(e); + } + // Close the output stream to avoid "Write dead end" errors. + try { + xmlOut.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }); + t.start(); + + InputStream xsl = ApiMap.class.getResourceAsStream("/api-coverage.xsl"); + StreamSource xslSource = new StreamSource(xsl); + TransformerFactory factory = TransformerFactory.newInstance(); + Transformer transformer = factory.newTransformer(xslSource); + + StreamSource xmlSource = new StreamSource(xmlIn); + StreamResult result = new StreamResult(htmlOut); + transformer.transform(xmlSource, result); + } +} + diff --git a/tools/cts-api-coverage/src/com/android/cts/apimap/MethodAnalyzer.java b/tools/cts-api-coverage/src/com/android/cts/apimap/MethodAnalyzer.java new file mode 100644 index 00000000000..fef283ac693 --- /dev/null +++ b/tools/cts-api-coverage/src/com/android/cts/apimap/MethodAnalyzer.java @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.cts.apimap; + +import static com.android.cts.apimap.ClassAnalyzer.XTS_ANNOTATIONS; + +import com.android.cts.apicommon.ApiCoverage; +import com.android.cts.ctsprofiles.ClassProfile; +import com.android.cts.ctsprofiles.MethodProfile; +import com.android.cts.ctsprofiles.ModuleProfile; +import com.android.cts.ctsprofiles.Utils; + +import org.apache.commons.math3.util.Pair; +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.Handle; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + + +/** A class for collecting method calls and method annotations. */ +public class MethodAnalyzer extends MethodVisitor { + + private final MethodProfile mMethod; + + private final ModuleProfile mModule; + + private final ApiCoverage mApiCoverage; + + // TODO: Add known common method packages that don't need to be recorded. + private static final Set<String> COMMON_PACKAGES = new HashSet<>(List.of()); + + public MethodAnalyzer( + MethodProfile methodProfile, + ModuleProfile moduleProfile, + ApiCoverage apiCoverage) { + super(Opcodes.ASM9); + mMethod = methodProfile; + mModule = moduleProfile; + mApiCoverage = apiCoverage; + } + + @Override + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + // Add an annotation for the method. + String type = Type.getType(desc).getClassName(); + if (!shouldRecordAnnotation(type)) { + return super.visitAnnotation(desc, visible); + } + Pair<String, String> packageClass = Utils.getPackageClass(type); + ClassProfile annotationClass = mModule.getOrCreateClass( + packageClass.getFirst(), packageClass.getSecond(), mApiCoverage); + mMethod.annotationManagement.addAnnotation(annotationClass); + if (XTS_ANNOTATIONS.contains(type)) { + return new AnnotationAnalyzer(mMethod.annotationManagement, packageClass.getSecond()); + } + return super.visitAnnotation(desc, visible); + } + + @Override + public void visitInvokeDynamicInsn( + String name, + String desc, + Handle bootstrapMethodHandle, Object... bootstrapMethodArgs) { + for (Object obj : bootstrapMethodArgs) { + if (!(obj instanceof Handle handle)) { + continue; + } + handleMethodCall(handle.getOwner(), handle.getName(), handle.getDesc()); + } + } + + @Override + public void visitMethodInsn(int opcode, String owners, String name, String desc, boolean itf) { + handleMethodCall(owners, name, desc); + } + + /** Records a method call. */ + private void handleMethodCall(String owners, String name, String desc) { + Pair<String, String> packageClass = Utils.getPackageClassFromASM(owners); + String packageName = packageClass.getFirst(); + String className = packageClass.getSecond(); + if (!shouldRecordMethodCall(packageName, className)) { + return; + } + List<String> params = new ArrayList<>(); + for (Type type : Type.getArgumentTypes(desc)) { + params.add(type.getClassName().replaceAll("\\$", ".")); + } + ClassProfile classProfile = mModule.getOrCreateClass( + packageName, className, mApiCoverage); + MethodProfile callMethod = classProfile.getOrCreateMethod(name, params); + if (classProfile.isApiClass()) { + if (name.equals("<init>")) { + mMethod.addApiConstructorCall(callMethod); + } else { + mMethod.addApiMethodCall(callMethod); + } + } else { + mMethod.addCommonMethodCall(callMethod); + } + } + + private static boolean shouldRecordMethodCall(String packageName, String className) { + if (className.startsWith("[")) { + return false; + } + for (String commonPackage: COMMON_PACKAGES) { + if (packageName.startsWith(commonPackage)) { + return false; + } + } + return true; + } + + private static boolean shouldRecordAnnotation(String name) { + // TODO(slotus): Filter out some annotations. + return true; + } +} diff --git a/tools/cts-api-coverage/src/com/android/cts/apimap/TarJan.java b/tools/cts-api-coverage/src/com/android/cts/apimap/TarJan.java new file mode 100644 index 00000000000..c4b80392215 --- /dev/null +++ b/tools/cts-api-coverage/src/com/android/cts/apimap/TarJan.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package com.android.cts.apimap; + +import com.android.cts.ctsprofiles.MethodProfile; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Stack; + +/** A class to implement Tarjan's strongly connected components algorithm. */ +public final class TarJan { + + private int mTime = 0; + private int mNewNodeIndex = 0; + private final Map<String, Node> mVertices = new HashMap<>(); + private final Stack<Node> mStack = new Stack<>(); + private final Set<String> mResolvedMethods; + private final Map<String, Integer> mComponentIDs = new HashMap<>(); + private final Map<Integer, List<MethodProfile>> mComponentNodes = new HashMap<>(); + + private static final class Node { + final int mDfn; + final MethodProfile mMethod; + int mLow; + boolean mInStack; + + Node(int index, MethodProfile method) { + mDfn = index; + mMethod = method; + } + } + + public TarJan(MethodProfile testMethod, Set<String> resolvedMethods) { + mResolvedMethods = resolvedMethods; + tarjan(testMethod); + } + + /** Gets the strongly connected component the given method belongs to. */ + public int getComponentID(String methodSignature) { + return mComponentIDs.get(methodSignature); + } + + /** Gets a list methods belong to the given strongly connected component. */ + public List<MethodProfile> getComponent(int id) { + return mComponentNodes.get(id); + } + + private Node tarjan(MethodProfile method) { + Node v = new Node(mTime++, method); + v.mLow = v.mDfn; + String methodSignature = method.getMethodSignatureWithClass(); + mVertices.put(methodSignature, v); + mStack.add(v); + v.mInStack = true; + + if (!mResolvedMethods.contains(methodSignature)) { + for (MethodProfile callee: method.getCommonMethodCalls().values()) { + String calleeSignature = callee.getMethodSignatureWithClass(); + Node w = mVertices.get(calleeSignature); + if (w == null) { + w = tarjan(callee); + v.mLow = Math.min(v.mLow, w.mLow); + } else if (w.mInStack) { + v.mLow = Math.min(v.mLow, w.mDfn); + } + } + } + + if (v.mLow == v.mDfn) { + Node w; + mNewNodeIndex++; + mComponentNodes.put(mNewNodeIndex, new ArrayList<>()); + do { + w = mStack.pop(); + mComponentIDs.put(methodSignature, mNewNodeIndex); + mComponentNodes.get(mNewNodeIndex).add(w.mMethod); + w.mInStack = false; + } while (!w.equals(v)); + } + return v; + } +} diff --git a/tools/cts-api-coverage/src/com/android/cts/apimap/XmlWriter.java b/tools/cts-api-coverage/src/com/android/cts/apimap/XmlWriter.java new file mode 100644 index 00000000000..93b8f1ee4d7 --- /dev/null +++ b/tools/cts-api-coverage/src/com/android/cts/apimap/XmlWriter.java @@ -0,0 +1,278 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.cts.apimap; + +import com.android.cts.apicommon.ApiClass; +import com.android.cts.apicommon.ApiConstructor; +import com.android.cts.apicommon.ApiCoverage; +import com.android.cts.apicommon.ApiMethod; +import com.android.cts.apicommon.ApiPackage; +import com.android.cts.apicommon.CoverageComparator; +import com.android.cts.ctsprofiles.ClassProfile; +import com.android.cts.ctsprofiles.MethodProfile; +import com.android.cts.ctsprofiles.ModuleProfile; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.ProcessingInstruction; + +import java.io.FileNotFoundException; +import java.io.OutputStream; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; + +/** Class that outputs an XML report of API and xTS-annotation mapping data. */ +public class XmlWriter { + + private final Document mDoc; + + private final Element mXTSAnnotationMapElement; + + private final Element mXTSApiMapElement; + + public XmlWriter() throws ParserConfigurationException { + DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder documentBuilder = docFactory.newDocumentBuilder(); + mDoc = documentBuilder.newDocument(); + ProcessingInstruction pi = mDoc.createProcessingInstruction( + "xml-stylesheet", "type=\"text/xsl\" href=\"api-map.xsl\"?>"); + SimpleDateFormat format = new SimpleDateFormat( + "EEE, MMM d, yyyy h:mm a z", Locale.CHINA); + String date = format.format(new Date(System.currentTimeMillis())); + Element rootElement = mDoc.createElement("api-coverage"); + rootElement.setAttribute("generatedTime", date); + rootElement.setAttribute("title", "cts-m-automation"); + mDoc.appendChild(rootElement); + mDoc.insertBefore(pi, rootElement); + mXTSAnnotationMapElement = mDoc.createElement("xts-annotation"); + rootElement.appendChild(mXTSAnnotationMapElement); + mXTSApiMapElement = mDoc.createElement("api"); + rootElement.appendChild(mXTSApiMapElement); + } + + /** Dumps the document to an xml file. */ + public void dumpXml(OutputStream outputStream) + throws FileNotFoundException, TransformerException { + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + Transformer transformer = transformerFactory.newTransformer(); + transformer.setOutputProperty("indent", "yes"); + DOMSource source = new DOMSource(mDoc); + StreamResult result = new StreamResult(outputStream); + transformer.transform(source, result); + } + + /** Generates the data for API coverage. */ + public void generateApiMapData(ApiCoverage apiCoverage) { + CoverageComparator comparator = new CoverageComparator(); + List<ApiPackage> packages = new ArrayList<>(apiCoverage.getPackages()); + packages.sort(comparator); + ApiStatistics statistics = new ApiStatistics(); + for (ApiPackage pkg : packages) { + if (pkg.getTotalMethods() > 0) { + mXTSApiMapElement.appendChild(createApiPackageElement(pkg, statistics)); + } + } + mXTSApiMapElement.appendChild(createApiTotalElement( + statistics.mTotalMethods, statistics.mTotalCoveredMethods)); + } + + /** Generates the data for xTS annotations. */ + public void generateXtsAnnotationMapData(ModuleProfile module) { + Element moduleElement = mDoc.createElement("test-module"); + moduleElement.setAttribute("module", module.getModuleName()); + for (ClassProfile classProfile : module.getClasses()) { + if (!classProfile.isNonAbstractTestClass()) { + continue; + } + Element classElement = createTestClassElement(classProfile); + if (classElement.hasChildNodes()) { + moduleElement.appendChild(classElement); + } + } + if (moduleElement.hasChildNodes()) { + mXTSAnnotationMapElement.appendChild(moduleElement); + } + } + + private Element createApiConstructorElement( + ApiConstructor constructor, ApiStatistics statistics) { + if (constructor.isDeprecated()) { + if (constructor.isCovered()) { + statistics.mTotalCoveredMethods -= 1; + } + statistics.mTotalMethods -= 1; + } + List<String> coveredWithList = new ArrayList<>(constructor.getCoveredWith()); + Collections.sort(coveredWithList); + String coveredWith = String.join(",", coveredWithList); + Element element = mDoc.createElement("constructor"); + element.setAttribute("name", constructor.getName()); + element.setAttribute("deprecated", String.valueOf(constructor.isDeprecated())); + element.setAttribute("covered", String.valueOf(constructor.isCovered())); + element.setAttribute("with", coveredWith); + for (String parameterType : constructor.getParameterTypes()) { + Element paramElement = mDoc.createElement("parameter"); + paramElement.setAttribute("type", parameterType); + element.appendChild(paramElement); + } + for (String test: constructor.getCoveredTests()) { + element.appendChild(createCoveredByElement(test)); + } + return element; + } + + private Element createApiMethodElement(ApiMethod method, ApiStatistics statistics) { + if (method.isDeprecated()) { + if (method.isCovered()) { + statistics.mTotalCoveredMethods -= 1; + } + statistics.mTotalMethods -= 1; + } + List<String> coveredWithList = new ArrayList<>(method.getCoveredWith()); + Collections.sort(coveredWithList); + String coveredWith = String.join(",", coveredWithList); + Element element = mDoc.createElement("method"); + element.setAttribute("name", method.getName()); + element.setAttribute("returnType", method.getReturnType()); + element.setAttribute("deprecated", String.valueOf(method.isDeprecated())); + element.setAttribute("static", String.valueOf(method.isStaticMethod())); + element.setAttribute("final", String.valueOf(method.isFinalMethod())); + element.setAttribute("visibility", method.getVisibility()); + element.setAttribute("abstract", String.valueOf(method.isAbstractMethod())); + element.setAttribute("covered", String.valueOf(method.isCovered())); + element.setAttribute("with", coveredWith); + for (String parameterType : method.getParameterTypes()) { + Element paramElement = mDoc.createElement("parameter"); + paramElement.setAttribute("type", parameterType); + element.appendChild(paramElement); + } + for (String test: method.getCoveredTests()) { + element.appendChild(createCoveredByElement(test)); + } + return element; + } + + private Element createCoveredByElement(String test) { + Element element = mDoc.createElement("covered-by"); + element.setAttribute("name", test); + return element; + } + + private Element createApiPackageElement(ApiPackage pkg, ApiStatistics statistics) { + Element element = mDoc.createElement("package"); + element.setAttribute("name", pkg.getName()); + element.setAttribute("numCovered", String.valueOf(pkg.getNumCoveredMethods())); + element.setAttribute("numTotal", String.valueOf(pkg.getTotalMethods())); + element.setAttribute("coveragePercentage", String.valueOf( + Math.round(pkg.getCoveragePercentage()))); + statistics.mTotalMethods += pkg.getTotalMethods(); + statistics.mTotalCoveredMethods += pkg.getNumCoveredMethods(); + List<ApiClass> classes = new ArrayList<>(pkg.getClasses()); + CoverageComparator comparator = new CoverageComparator(); + classes.sort(comparator); + for (ApiClass apiClass : classes) { + if (apiClass.getTotalMethods() > 0) { + element.appendChild(createApiClassElement(apiClass, statistics)); + } + } + return element; + } + + private Element createApiClassElement(ApiClass apiClass, ApiStatistics statistics) { + Element element = mDoc.createElement("class"); + element.setAttribute("name", apiClass.getName()); + element.setAttribute("numCovered", String.valueOf(apiClass.getNumCoveredMethods())); + element.setAttribute("numTotal", String.valueOf(apiClass.getTotalMethods())); + element.setAttribute("deprecated", String.valueOf(apiClass.isDeprecated())); + element.setAttribute("coveragePercentage", String.valueOf( + Math.round(apiClass.getCoveragePercentage()))); + for (ApiConstructor constructor : apiClass.getConstructors()) { + element.appendChild(createApiConstructorElement(constructor, statistics)); + } + for (ApiMethod method : apiClass.getMethods()) { + element.appendChild(createApiMethodElement(method, statistics)); + } + return element; + } + + private Element createApiTotalElement(int totalMethods, int totalCoveredMethods) { + Element total = mDoc.createElement("total"); + total.setAttribute("numCovered", String.valueOf(totalCoveredMethods)); + total.setAttribute("numTotal", String.valueOf(totalMethods)); + total.setAttribute("coveragePercentage", String.valueOf( + Math.round((float) totalCoveredMethods / totalMethods * 100.0f))); + return total; + } + + private Element createTestClassElement(ClassProfile classProfile) { + Element classElement = mDoc.createElement("test-class"); + classElement.setAttribute("package", classProfile.getPackageName()); + classElement.setAttribute("name", classProfile.getClassName()); + for (Map.Entry<String, Set<String>> xtsAnnotation : + classProfile.annotationManagement.getTestMetadata().entrySet()) { + classElement.appendChild(createXtsAnnotationElement( + xtsAnnotation.getKey(), + xtsAnnotation.getValue())); + } + for (MethodProfile testMethod : classProfile.getTestMethods().values()) { + Element methodElement = createTestMethodElement(testMethod); + if (methodElement.hasChildNodes()) { + classElement.appendChild(methodElement); + } + } + return classElement; + } + + private Element createTestMethodElement(MethodProfile methodProfile) { + Element methodElement = mDoc.createElement("test-method"); + methodElement.setAttribute("name", methodProfile.getMethodName()); + for (Map.Entry<String, Set<String>> xtsAnnotation : + methodProfile.annotationManagement.getTestMetadata().entrySet()) { + methodElement.appendChild(createXtsAnnotationElement( + xtsAnnotation.getKey(), + xtsAnnotation.getValue())); + } + return methodElement; + } + + private Element createXtsAnnotationElement(String name, Set<String> values) { + Element element = mDoc.createElement("cts-annotation"); + element.setAttribute("name", name); + element.setAttribute("values", String.join(";", values)); + return element; + } + + static final class ApiStatistics { + int mTotalMethods = 0; + int mTotalCoveredMethods = 0; + } +} diff --git a/tools/cts-api-coverage/src/com/android/cts/ctsprofiles/AnnotationManagement.java b/tools/cts-api-coverage/src/com/android/cts/ctsprofiles/AnnotationManagement.java new file mode 100644 index 00000000000..abe5b85b324 --- /dev/null +++ b/tools/cts-api-coverage/src/com/android/cts/ctsprofiles/AnnotationManagement.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package com.android.cts.ctsprofiles; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** A class to manage annotations marked on the CTS class/method. */ +public final class AnnotationManagement { + + private static final String TEST_METADATA_ERROR_MSG = "Not allowed to add new test metadata"; + + // A list of annotations marked on the CTS class/method. + private final List<ClassProfile> mAnnotations = new ArrayList<>(); + + // A map to store useful annotation values. For example, values collected from @ApiTest, + // @CddTest annotations. This values are indicating what the class/method is testing. + private final Map<String, Set<String>> mTestMetadata = new HashMap<>(); + + private boolean mTestMetadataMerged = false; + + /** Gets all xTS annotation values mapped to the class/method. */ + public List<ClassProfile> getAnnotations() { + return mAnnotations; + } + + /** Gets all test metadata mapped to the class/method. */ + public Map<String, Set<String>> getTestMetadata() { + if (!mTestMetadataMerged) { + resolveNestedTestMetadata(); + } + return mTestMetadata; + } + + /** Adds a test metadata. */ + public void addTestMetadata(String type, String value) { + if (mTestMetadataMerged) { + throw new RuntimeException(TEST_METADATA_ERROR_MSG); + } + mTestMetadata.putIfAbsent(type, new HashSet<>()); + mTestMetadata.get(type).add(value); + } + + /** Adds an annotation marked on the class/method. */ + public void addAnnotation(ClassProfile classProfile) { + mAnnotations.add(classProfile); + } + + /** + * Merges nested test metadata. For example, a customized annotation @MyAnnotation could be + * annotated by @ApiTest. + */ + private void resolveNestedTestMetadata() { + for (ClassProfile annotation : mAnnotations) { + annotation.annotationManagement.getTestMetadata().forEach((key, value) -> { + mTestMetadata.putIfAbsent(key, new HashSet<>()); + mTestMetadata.get(key).addAll(value); + }); + } + mTestMetadataMerged = true; + } +} diff --git a/tools/cts-api-coverage/src/com/android/cts/ctsprofiles/ClassProfile.java b/tools/cts-api-coverage/src/com/android/cts/ctsprofiles/ClassProfile.java new file mode 100644 index 00000000000..3b169fcb7f2 --- /dev/null +++ b/tools/cts-api-coverage/src/com/android/cts/ctsprofiles/ClassProfile.java @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.cts.ctsprofiles; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** Representation of a class included in the CTS package. */ +public class ClassProfile { + + public final AnnotationManagement annotationManagement = new AnnotationManagement(); + + private final String mModule; + + private final String mPackage; + + private final String mClass; + + private int mClassType = 0; + + private ClassProfile mSuperClass = null; + + // A list of interfaces implemented by this class. + private final List<ClassProfile> mInterfaces = new ArrayList<>(); + + // A map of methods defined in this class with the method signature as the key. + private final Map<String, MethodProfile> mMethods = new HashMap<>(); + + // A map of test methods defined in this class with the method signature as the key. + private Map<String, MethodProfile> mTestMethods = null; + + // TODO(slotus): Add known patterns. + private static final Set<String> JUNIT4_ANNOTATION_PATTERNS = new HashSet<>(List.of()); + + // TODO(slotus): Add known patterns. + private static final Set<String> JUNIT3_CLASS_PATTERNS = new HashSet<>(List.of()); + + public ClassProfile(String moduleName, String packageName, String className, boolean apiClass) { + mModule = moduleName; + mClass = className; + mPackage = packageName; + if (apiClass) { + mClassType |= ClassType.API.getValue(); + } + } + + /** Representation of the class type. */ + public enum ClassType { + INTERFACE(1), + ABSTRACT(2), + JUNIT3(4), + JUNIT4(8), + ANNOTATION(16), + /** A non-test and non-annotation class.*/ + COMMON(32), + API(64); + + private final int mValue; + + ClassType(int value) { + mValue = value; + } + + public int getValue() { + return mValue; + } + } + + public String getClassSignature() { + return Utils.getClassSignature(mPackage, mClass); + } + + public String getClassName() { + return mClass; + } + + public String getPackageName() { + return mPackage; + } + + public String getModuleName() { + return mModule; + } + + public Map<String, MethodProfile> getMethods() { + return mMethods; + } + + /** Creates a class method. */ + public MethodProfile getOrCreateMethod( + String methodName, List<String> params) { + String methodSignature = Utils.getMethodSignature(methodName, params); + if (!mMethods.containsKey(methodSignature)) { + mMethods.put(methodSignature, new MethodProfile(this, methodName, params)); + } + return mMethods.get(methodSignature); + } + + /** Adds an interface implemented by the class. */ + public void addInterface(ClassProfile interfaceProfile) { + mInterfaces.add(interfaceProfile); + } + + /** Adds a class type for the class. */ + public void addClassType(ClassType classType) { + mClassType |= classType.getValue(); + } + + public void setSuperClass(ClassProfile superClass) { + mSuperClass = superClass; + } + + /** Collects all test methods contained in the class. */ + public Map<String, MethodProfile> getTestMethods() { + if (mTestMethods != null) { + return mTestMethods; + } + mTestMethods = new HashMap<>(); + mMethods.forEach((methodKey, method) -> { + if (method.isTestMethod()) { + mTestMethods.put(methodKey, method); + } + }); + // Test methods defined in the super class will also be collected. + if (mSuperClass != null) { + mSuperClass.getTestMethods().forEach(mTestMethods::putIfAbsent); + } + return mTestMethods; + } + + /** Returns true if the class is a test class. */ + public boolean isTestClass() { + if (matchAnyTypes(ClassType.ANNOTATION.getValue() | ClassType.API.getValue())) { + return false; + } + if (!isJunit4Class() && !isJunit3Class()) { + addClassType(ClassType.COMMON); + return false; + } + return true; + } + + /** Returns true if the class is an API class. */ + public boolean isApiClass() { + return matchAllTypes(ClassType.API.getValue()); + } + + /** Returns true if the class is a test class but not an abstract class. */ + public boolean isNonAbstractTestClass() { + return (isTestClass() && !matchAnyTypes( + ClassType.ABSTRACT.getValue() | ClassType.INTERFACE.getValue())); + } + + + /** Returns true if it is decided that whether this is a test class or not. */ + private boolean testClassResolved() { + return matchAnyTypes( + ClassType.JUNIT3.getValue() + | ClassType.JUNIT4.getValue() + | ClassType.COMMON.getValue() + | ClassType.ANNOTATION.getValue() + | ClassType.API.getValue() + ); + } + + /** Returns true if the class is a Junit4 test class. */ + protected boolean isJunit4Class() { + if (testClassResolved()) { + return matchAllTypes(ClassType.JUNIT4.getValue()); + } + // Check if the class is marked by a Junit4 runner. + for (ClassProfile annotation : annotationManagement.getAnnotations()) { + for (String pattern : JUNIT4_ANNOTATION_PATTERNS) { + if (annotation.getClassSignature().matches(pattern)) { + addClassType(ClassType.JUNIT4); + return true; + } + } + } + // Check if any methods are marked by a Junit4 annotation. + for (MethodProfile method : mMethods.values()) { + if (method.isJunit4Method()) { + addClassType(ClassType.JUNIT4); + return true; + } + } + // Check if the class is extended from a Junit4 class. + if (mSuperClass != null && mSuperClass.isJunit4Class()) { + addClassType(ClassType.JUNIT4); + return true; + } + return false; + } + + /** Returns true if the class is a Junit3 test class. */ + protected boolean isJunit3Class() { + if (testClassResolved()) { + return matchAllTypes(ClassType.JUNIT3.getValue()); + } + if (mSuperClass != null) { + // Check if the class is extended from a Junit3 base class. + for (String pattern : JUNIT3_CLASS_PATTERNS) { + if (mSuperClass.getClassSignature().matches(pattern)) { + addClassType(ClassType.JUNIT3); + return true; + } + } + // Check if the class is extended from a Junit3 class. + if (mSuperClass.isJunit3Class()) { + addClassType(ClassType.JUNIT3); + return true; + } + } + return false; + } + + + private boolean matchAnyTypes(int typesValue) { + return (mClassType & typesValue) != 0; + } + + private boolean matchAllTypes(int typesValue) { + return (mClassType & typesValue) == typesValue; + } +} diff --git a/tools/cts-api-coverage/src/com/android/cts/ctsprofiles/MethodProfile.java b/tools/cts-api-coverage/src/com/android/cts/ctsprofiles/MethodProfile.java new file mode 100644 index 00000000000..e4a4315195c --- /dev/null +++ b/tools/cts-api-coverage/src/com/android/cts/ctsprofiles/MethodProfile.java @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.cts.ctsprofiles; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** Representation of a method included in the CTS package. */ +public class MethodProfile { + + public final AnnotationManagement annotationManagement = new AnnotationManagement(); + + private final String mMethod; + + private final ClassProfile mClass; + + private final List<String> mParams; + + private int mMethodType = 0; + + // A map of non-api methods called by this method with the method signature as the key. + private final HashMap<String, MethodProfile> mCommonMethodCalls = new HashMap<>(); + + // A map of api methods called by this method with the method signature as the key. + private final HashMap<String, MethodProfile> mApiMethodCalls = new HashMap<>(); + + // A map of api constructors called by this method with the method signature as the key. + private final HashMap<String, MethodProfile> mApiConstructorCalls = new HashMap<>(); + + // TODO(slotus): Add known patterns. + private static final Set<String> JUNIT4_ANNOTATION_PATTERNS = new HashSet<>(List.of()); + + public enum MethodType { + JUNIT3(1), + JUNIT4(2), + /** A non-test method.*/ + COMMON(4), + // TODO(slotus): Solve the override case. + /** A method that is not extended from the super class. */ + DIRECT_MEMBER(8); + + private final int mValue; + + MethodType(int value) { + mValue = value; + } + + public int getValue() { + return mValue; + } + } + + public MethodProfile( + ClassProfile classProfile, String methodName, List<String> params) { + mClass = classProfile; + mMethod = methodName; + mParams = params; + } + + public String getMethodName() { + return mMethod; + } + + public String getModuleName() { + return mClass.getModuleName(); + } + + public String getPackageName() { + return mClass.getPackageName(); + } + + public String getClassName() { + return mClass.getClassName(); + } + + public List<String> getMethodParams() { + return mParams; + } + + public Map<String, MethodProfile> getApiMethodCalls() { + return mApiMethodCalls; + } + + public Map<String, MethodProfile> getApiConstructorCalls() { + return mApiConstructorCalls; + } + + public Map<String, MethodProfile> getCommonMethodCalls() { + return mCommonMethodCalls; + } + + public String getMethodSignatureWithClass() { + return Utils.getMethodSignatureWithClass( + mClass.getPackageName(), mClass.getClassName(), mMethod, mParams); + } + + /** Adds an API method called by the method. **/ + public void addApiMethodCall(MethodProfile apiMethod) { + mApiMethodCalls.putIfAbsent(apiMethod.getMethodSignatureWithClass(), apiMethod); + } + + /** Adds an API constructor called by the method. **/ + public void addApiConstructorCall(MethodProfile apiConstructor) { + mApiConstructorCalls.putIfAbsent( + apiConstructor.getMethodSignatureWithClass(), apiConstructor); + } + + /** Adds a non-API method called by the method. **/ + public void addCommonMethodCall(MethodProfile method) { + mCommonMethodCalls.putIfAbsent(method.getMethodSignatureWithClass(), method); + } + + /** Adds a method type for the method. */ + public void addMethodType(MethodType methodType) { + mMethodType |= methodType.getValue(); + } + + /** Returns true if it is decided that whether this is a test method or not. */ + private boolean testMethodResolved() { + return matchAnyTypes( + MethodType.JUNIT3.getValue() + | MethodType.JUNIT4.getValue() + | MethodType.COMMON.getValue()); + } + + /** Returns true if the method is a test method. */ + public boolean isTestMethod() { + if (!isJunit4Method() && !isJunit3Method()) { + addMethodType(MethodType.COMMON); + return false; + } + return true; + } + + /** Returns true if the method is a JUnit3 test method. */ + protected boolean isJunit3Method() { + if (testMethodResolved()) { + return matchAllTypes(MethodType.JUNIT3.getValue()); + } + if (mClass.isJunit3Class() && mMethod.startsWith("test")) { + addMethodType(MethodType.JUNIT3); + return true; + } + return false; + } + + /** Returns true if the method is a JUnit4 test method. */ + protected boolean isJunit4Method() { + if (testMethodResolved()) { + return matchAllTypes(MethodType.JUNIT4.getValue()); + } + for (ClassProfile annotation : annotationManagement.getAnnotations()) { + for (String pattern : JUNIT4_ANNOTATION_PATTERNS) { + if (annotation.getClassSignature().matches(pattern)) { + addMethodType(MethodType.JUNIT4); + return true; + } + } + } + return false; + } + + private boolean matchAnyTypes(int typesValue) { + return (mMethodType & typesValue) != 0; + } + + private boolean matchAllTypes(int typesValue) { + return (mMethodType & typesValue) == typesValue; + } +} diff --git a/tools/cts-api-coverage/src/com/android/cts/ctsprofiles/ModuleProfile.java b/tools/cts-api-coverage/src/com/android/cts/ctsprofiles/ModuleProfile.java new file mode 100644 index 00000000000..1855c3d3a10 --- /dev/null +++ b/tools/cts-api-coverage/src/com/android/cts/ctsprofiles/ModuleProfile.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.cts.ctsprofiles; + +import com.android.cts.apicommon.ApiClass; +import com.android.cts.apicommon.ApiCoverage; +import com.android.cts.apicommon.ApiPackage; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** Representation of a CTS module. */ +public class ModuleProfile { + + private final Map<String, ClassProfile> mClasses = new HashMap<>(); + + private final String mName; + + public ModuleProfile(String name) { + mName = name; + } + + /** Creates a class packaged in the module. */ + public ClassProfile getOrCreateClass( + String packageName, String className, ApiCoverage apiCoverage) { + String classSignature = Utils.getClassSignature(packageName, className); + ApiPackage apiPackage = apiCoverage.getPackage(packageName); + ApiClass apiClass = apiPackage != null ? apiPackage.getClass(className) : null; + if (!mClasses.containsKey(classSignature)) { + mClasses.put( + classSignature, + new ClassProfile(mName, packageName, className, apiClass != null) + ); + } + return mClasses.get(classSignature); + } + + public String getModuleName() { + return mName; + } + + public Collection<ClassProfile> getClasses() { + return Collections.unmodifiableCollection(mClasses.values()); + } +} diff --git a/tools/cts-api-coverage/src/com/android/cts/ctsprofiles/Utils.java b/tools/cts-api-coverage/src/com/android/cts/ctsprofiles/Utils.java new file mode 100644 index 00000000000..c4e9a8df7e3 --- /dev/null +++ b/tools/cts-api-coverage/src/com/android/cts/ctsprofiles/Utils.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.cts.ctsprofiles; + +import org.apache.commons.math3.util.Pair; + +import java.util.List; + +/** Utility for generating package, class or method information. */ +public final class Utils { + + /** Splits a class signature and returns the (package, class) pair. */ + public static Pair<String, String> getPackageClass(String classSignature) { + int splitPos = classSignature.lastIndexOf('.'); + String className = classSignature.substring(splitPos + 1); + String packageName = ""; + if (splitPos > 0) { + packageName = classSignature.substring(0, splitPos); + } + return Pair.create(packageName, className.replaceAll("\\$", ".")); + } + + /** Splits a class name represented in ASM and returns the (package, class) pair. */ + public static Pair<String, String> getPackageClassFromASM(String packageClass) { + // The class name obtained from ASM includes package information and is separated by "/". + // For example, the class com.my.package.MyClass.NestClass is represented as + // com/my/package/MyClass$NestClass in ASM. + int splitPos = packageClass.lastIndexOf("/"); + String packageName = ""; + if (splitPos != -1) { + packageName = packageClass.substring(0, splitPos); + } + String className = packageClass.substring(splitPos + 1); + return Pair.create( + packageName.replaceAll("/", "."), + className.replaceAll("\\$", ".") + ); + } + + /** Returns a class signature in the format {package}.{class}. */ + public static String getClassSignature(String packageName, String className) { + if (packageName.isEmpty()) { + return className; + } + return String.format("%s.%s", packageName, className); + } + + /** Returns a method signature in the format {method}({param1}, {param2}, ...). */ + public static String getMethodSignature(String methodName, List<String> paramTypes) { + return String.format("%s(%s)", methodName, String.join(", ", paramTypes)); + } + + /** + * Returns a full method signature in the format + * {package}.{class}#{method}({param1}, {param2}, ...). + */ + public static String getMethodSignatureWithClass( + String packageName, String className, String methodName, List<String> paramTypes) { + String classSignature = getClassSignature(packageName, className); + String methodSignature = getMethodSignature(methodName, paramTypes); + return String.format("%s#%s", classSignature, methodSignature); + } +} diff --git a/tools/cts-tradefed/res/config/cts-exclude.xml b/tools/cts-tradefed/res/config/cts-exclude.xml index b754596cd74..75d417a3368 100644 --- a/tools/cts-tradefed/res/config/cts-exclude.xml +++ b/tools/cts-tradefed/res/config/cts-exclude.xml @@ -33,4 +33,11 @@ <!-- Exclude @SecurityTest tests from CTS. These are tested in STS. b/180417031--> <option name="compatibility:test-arg" value="com.android.tradefed.testtype.AndroidJUnitTest:exclude-annotation:android.platform.test.annotations.AsbSecurityTest" /> <option name="compatibility:test-arg" value="com.android.compatibility.common.tradefed.testtype.JarHostTest:exclude-annotation:android.platform.test.annotations.AsbSecurityTest" /> + + <!-- Exclude CtsJvmtiRunTest912HostTestCases as it depends on implementation details of the ART + module. b/287732826 --> + <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest912HostTestCases" /> + <!-- Exclude CtsJvmtiRunTest911HostTestCases as it depends on line numbers which were changed + due to openjdk 17 upgrade. b/338607812 --> + <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest911HostTestCases" /> </configuration> diff --git a/tools/cts-tradefed/res/config/cts-known-failures.xml b/tools/cts-tradefed/res/config/cts-known-failures.xml index 89605d7c201..0872306802c 100644 --- a/tools/cts-tradefed/res/config/cts-known-failures.xml +++ b/tools/cts-tradefed/res/config/cts-known-failures.xml @@ -246,6 +246,17 @@ <!-- b/300310474 --> <option name="compatibility:exclude-filter" value="CtsStatsdAtomHostTestCases[foldable:5:REAR_DUAL] android.cts.statsdatom.permissionstate.DangerousPermissionStateTests#testDangerousPermissionState" /> <option name="compatibility:exclude-filter" value="CtsStatsdAtomHostTestCases[foldable:5:REAR_DUAL] android.cts.statsdatom.permissionstate.DangerousPermissionStateTests#testDangerousPermissionStateSampled" /> + <!-- b/319385496 --> + <option name="compatibility:exclude-filter" value="CtsStatsdAtomHostTestCases[foldable:2:HALF_OPENED] android.cts.statsdatom.permissionstate.DangerousPermissionStateTests#testDangerousPermissionState" /> + <option name="compatibility:exclude-filter" value="CtsStatsdAtomHostTestCases[foldable:3:OPENED] android.cts.statsdatom.permissionstate.DangerousPermissionStateTests#testDangerousPermissionState" /> + <option name="compatibility:exclude-filter" value="CtsStatsdAtomHostTestCases[foldable:4:OPENED_REVERSE] android.cts.statsdatom.permissionstate.DangerousPermissionStateTests#testDangerousPermissionState" /> + <option name="compatibility:exclude-filter" value="CtsStatsdAtomHostTestCases[foldable:5:OPENED_PRESENTATION] android.cts.statsdatom.permissionstate.DangerousPermissionStateTests#testDangerousPermissionState" /> + <option name="compatibility:exclude-filter" value="CtsStatsdAtomHostTestCases[foldable:6:OPENED_REVERSE_PRESENTATION] android.cts.statsdatom.permissionstate.DangerousPermissionStateTests#testDangerousPermissionState" /> + <option name="compatibility:exclude-filter" value="CtsStatsdAtomHostTestCases[foldable:2:HALF_OPENED] android.cts.statsdatom.permissionstate.DangerousPermissionStateTests#testDangerousPermissionStateSampled" /> + <option name="compatibility:exclude-filter" value="CtsStatsdAtomHostTestCases[foldable:3:OPENED] android.cts.statsdatom.permissionstate.DangerousPermissionStateTests#testDangerousPermissionStateSampled" /> + <option name="compatibility:exclude-filter" value="CtsStatsdAtomHostTestCases[foldable:4:OPENED_REVERSE] android.cts.statsdatom.permissionstate.DangerousPermissionStateTests#testDangerousPermissionStateSampled" /> + <option name="compatibility:exclude-filter" value="CtsStatsdAtomHostTestCases[foldable:5:OPENED_PRESENTATION] android.cts.statsdatom.permissionstate.DangerousPermissionStateTests#testDangerousPermissionStateSampled" /> + <option name="compatibility:exclude-filter" value="CtsStatsdAtomHostTestCases[foldable:6:OPENED_REVERSE_PRESENTATION] android.cts.statsdatom.permissionstate.DangerousPermissionStateTests#testDangerousPermissionStateSampled" /> <!-- b/202357331 --> <option name="compatibility:exclude-filter" value="CtsDevicePolicyManagerTestCases com.android.cts.devicepolicy.MixedDeviceOwnerTest#testCreateAdminSupportIntent" /> 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 f194a52ec87..884df2c2907 100644 --- a/tools/cts-tradefed/res/config/cts-on-gsi-exclude.xml +++ b/tools/cts-tradefed/res/config/cts-on-gsi-exclude.xml @@ -137,6 +137,9 @@ <option name="compatibility:exclude-filter" value="CtsNetTestCases android.net.cts.IpSecManagerTest#testChaCha20Poly1305Udp4UdpEncap" /> <option name="compatibility:exclude-filter" value="CtsNetTestCases android.net.cts.IpSecManagerTest#testChaCha20Poly1305Tcp4UdpEncap" /> + <!-- b/335351293 Remove ApfIntegrationTest running against broken vendor images on GSI --> + <option name="compatibility:exclude-filter" value="CtsNetTestCases android.net.cts.ApfIntegrationTest" /> + <!-- b/285247534 Remove WM Extension tests on GSI --> <option name="compatibility:exclude-filter" value="CtsWindowManagerJetpackTestCases android.server.wm.jetpack.SdkAvailabilityTest" /> diff --git a/tools/cts-tradefed/tests/src/com/android/compatibility/common/tradefed/loading/CtsConfigLoadingTest.java b/tools/cts-tradefed/tests/src/com/android/compatibility/common/tradefed/loading/CtsConfigLoadingTest.java index 364123949a1..c89c6b23e63 100644 --- a/tools/cts-tradefed/tests/src/com/android/compatibility/common/tradefed/loading/CtsConfigLoadingTest.java +++ b/tools/cts-tradefed/tests/src/com/android/compatibility/common/tradefed/loading/CtsConfigLoadingTest.java @@ -95,6 +95,7 @@ public class CtsConfigLoadingTest { "systems", "sysui", "telecom", + "threadnetwork", "tv", "uitoolkit", "uwb", diff --git a/tools/vm-tests-tf/Android.bp b/tools/vm-tests-tf/Android.bp index 15428fb6b0c..da8c93baa8c 100644 --- a/tools/vm-tests-tf/Android.bp +++ b/tools/vm-tests-tf/Android.bp @@ -14,6 +14,7 @@ package { default_applicable_licenses: ["Android-Apache-2.0"], + default_team: "trendy_team_art_mainline", } java_library { |