Add test for MediaExtractor entry point collection
Note: This patch also disables a broken
MediaSessionManagerHostTest.
The test is a hostside test, and checks against statsd output.
- Makes an on-device app extract a sample media file using
Java (SDK MediaExtractor), C++ (NDK, on the JNI thread) and
C++ (NDK, on a std::thread).
- Then checks that the generated statsd atom reports the correct
entry point.
Bug: 171012388
Bug: 170386720
Test: atest CtsMediaHostTestCases
BYPASS_INCLUSIVE_LANGUAGE_REASON=Pre-existing language issues.
Change-Id: I92f95c208874b367bd1effcd1381b4d1a08a1457
Merged-In: I92f95c208874b367bd1effcd1381b4d1a08a1457
diff --git a/hostsidetests/media/TEST_MAPPING b/hostsidetests/media/TEST_MAPPING
new file mode 100644
index 0000000..e7796eb
--- /dev/null
+++ b/hostsidetests/media/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsMediaHostTestCases"
+ }
+ ]
+}
diff --git a/hostsidetests/media/app/MediaExtractorTest/Android.bp b/hostsidetests/media/app/MediaExtractorTest/Android.bp
new file mode 100644
index 0000000..f5b32ab
--- /dev/null
+++ b/hostsidetests/media/app/MediaExtractorTest/Android.bp
@@ -0,0 +1,54 @@
+// Copyright 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test_helper_app {
+ name: "CtsMediaExtractorHostTestApp",
+ defaults: ["cts_defaults"],
+ test_suites: [
+ "cts",
+ "general-tests",
+ ],
+ srcs: [
+ "src/**/*.java",
+ ],
+ jni_libs: ["libCtsMediaExtractorHostTestAppJni"],
+ static_libs: [
+ "androidx.test.ext.junit",
+ "compatibility-device-util-axt",
+ "ctstestrunner-axt",
+ ],
+ compile_multilib: "both",
+ sdk_version: "test_current",
+}
+
+cc_test_library {
+ name: "libCtsMediaExtractorHostTestAppJni",
+ srcs: ["jni/MediaExtractorDeviceSideTestNative.cpp"],
+ shared_libs: [
+ "liblog",
+ "libmediandk",
+ "libandroid",
+ "libnativehelper_compat_libc++",
+ ],
+ include_dirs: [
+ "frameworks/av/media/ndk/include/media",
+ ],
+ stl: "libc++_static",
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+ gtest: false,
+ sdk_version: "current",
+}
diff --git a/hostsidetests/media/app/MediaExtractorTest/AndroidManifest.xml b/hostsidetests/media/app/MediaExtractorTest/AndroidManifest.xml
new file mode 100644
index 0000000..75f05b1
--- /dev/null
+++ b/hostsidetests/media/app/MediaExtractorTest/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.media.cts">
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.media.cts"
+ android:label="Device test app for MediaExtractor host side tests.">
+ <meta-data android:name="listener"
+ android:value="com.android.cts.runner.CtsTestRunListener" />
+ </instrumentation>
+</manifest>
diff --git a/hostsidetests/media/app/MediaExtractorTest/assets/raw/small_sample.mp4 b/hostsidetests/media/app/MediaExtractorTest/assets/raw/small_sample.mp4
new file mode 100644
index 0000000..a49c1cd
--- /dev/null
+++ b/hostsidetests/media/app/MediaExtractorTest/assets/raw/small_sample.mp4
Binary files differ
diff --git a/hostsidetests/media/app/MediaExtractorTest/jni/MediaExtractorDeviceSideTestNative.cpp b/hostsidetests/media/app/MediaExtractorTest/jni/MediaExtractorDeviceSideTestNative.cpp
new file mode 100644
index 0000000..b39d99b
--- /dev/null
+++ b/hostsidetests/media/app/MediaExtractorTest/jni/MediaExtractorDeviceSideTestNative.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <NdkMediaExtractor.h>
+#include <android/asset_manager.h>
+#include <android/asset_manager_jni.h>
+#include <jni.h>
+#include <nativehelper/ScopedUtfChars.h>
+#include <thread>
+
+extern "C" JNIEXPORT void JNICALL
+Java_android_media_cts_MediaExtractorDeviceSideTest_extractUsingNdkMediaExtractor(
+ JNIEnv* env, jobject, jobject assetManager, jstring assetPath, jboolean withAttachedJvm) {
+ ScopedUtfChars scopedPath(env, assetPath);
+
+ AAssetManager* nativeAssetManager = AAssetManager_fromJava(env, assetManager);
+ AAsset* asset = AAssetManager_open(nativeAssetManager, scopedPath.c_str(), AASSET_MODE_RANDOM);
+ off_t start;
+ off_t length;
+ int fd = AAsset_openFileDescriptor(asset, &start, &length);
+
+ auto mediaExtractorTask = [=]() {
+ AMediaExtractor* mediaExtractor = AMediaExtractor_new();
+ AMediaExtractor_setDataSourceFd(mediaExtractor, fd, start, length);
+ AMediaExtractor_delete(mediaExtractor);
+ };
+
+ if (withAttachedJvm) {
+ // The currently running thread is a Java thread so it has an attached JVM.
+ mediaExtractorTask();
+ } else {
+ // We want to run the MediaExtractor calls on a thread with no JVM, so we spawn a new native
+ // thread which will not have an associated JVM. We execute the MediaExtractor calls on the
+ // new thread, and immediately join its execution so as to wait for its completion.
+ std::thread(mediaExtractorTask).join();
+ }
+ // TODO: Make resource management automatic through scoped handles.
+ close(fd);
+ AAsset_close(asset);
+}
diff --git a/hostsidetests/media/app/MediaExtractorTest/src/android/media/cts/MediaExtractorDeviceSideTest.java b/hostsidetests/media/app/MediaExtractorTest/src/android/media/cts/MediaExtractorDeviceSideTest.java
new file mode 100644
index 0000000..51b2faf
--- /dev/null
+++ b/hostsidetests/media/app/MediaExtractorTest/src/android/media/cts/MediaExtractorDeviceSideTest.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.cts;
+
+import android.content.res.AssetFileDescriptor;
+import android.content.res.AssetManager;
+import android.media.MediaExtractor;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Test class used by host-side tests to trigger {@link MediaExtractor} media metric events. */
+@RunWith(AndroidJUnit4.class)
+public class MediaExtractorDeviceSideTest {
+
+ static {
+ System.loadLibrary("CtsMediaExtractorHostTestAppJni");
+ }
+
+ private static final String SAMPLE_PATH = "raw/small_sample.mp4";
+ private AssetManager mAssetManager;
+
+ @Before
+ public void setUp() {
+ mAssetManager = InstrumentationRegistry.getInstrumentation().getContext().getAssets();
+ }
+
+ @Test
+ public void testEntryPointSdk() throws Exception {
+ MediaExtractor mediaExtractor = new MediaExtractor();
+ AssetManager assetManager =
+ InstrumentationRegistry.getInstrumentation().getContext().getAssets();
+ try (AssetFileDescriptor fileDescriptor = assetManager.openFd(SAMPLE_PATH)) {
+ mediaExtractor.setDataSource(fileDescriptor);
+ }
+ mediaExtractor.release();
+ }
+
+ @Test
+ public void testEntryPointNdkNoJvm() {
+ extractUsingNdkMediaExtractor(mAssetManager, SAMPLE_PATH, /* withAttachedJvm= */ false);
+ }
+
+ @Test
+ public void testEntryPointNdkWithJvm() {
+ extractUsingNdkMediaExtractor(mAssetManager, SAMPLE_PATH, /* withAttachedJvm= */ true);
+ }
+
+ private native void extractUsingNdkMediaExtractor(
+ AssetManager assetManager, String assetPath, boolean withAttachedJvm);
+}
diff --git a/hostsidetests/media/src/android/media/cts/BaseMediaHostSideTest.java b/hostsidetests/media/src/android/media/cts/BaseMediaHostSideTest.java
new file mode 100644
index 0000000..ddce632
--- /dev/null
+++ b/hostsidetests/media/src/android/media/cts/BaseMediaHostSideTest.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.cts;
+
+import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.log.LogUtil;
+import com.android.tradefed.result.CollectingTestListener;
+import com.android.tradefed.result.TestDescription;
+import com.android.tradefed.result.TestResult;
+import com.android.tradefed.result.TestRunResult;
+import com.android.tradefed.testtype.DeviceTestCase;
+import com.android.tradefed.testtype.IBuildReceiver;
+
+import java.io.FileNotFoundException;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+/** Base class for host-side tests for media APIs. */
+public class BaseMediaHostSideTest extends DeviceTestCase implements IBuildReceiver {
+ private static final String RUNNER = "androidx.test.runner.AndroidJUnitRunner";
+
+ /**
+ * The defined timeout (in milliseconds) is used as a maximum waiting time when expecting the
+ * command output from the device. At any time, if the shell command does not output anything
+ * for a period longer than the defined timeout the Tradefed run terminates.
+ */
+ private static final long DEFAULT_SHELL_TIMEOUT_MILLIS = TimeUnit.MINUTES.toMillis(5);
+
+ /** Instrumentation test runner argument key used for individual test timeout. */
+ protected static final String TEST_TIMEOUT_INST_ARGS_KEY = "timeout_msec";
+
+ /**
+ * Sets timeout (in milliseconds) that will be applied to each test. In the event of a test
+ * timeout it will log the results and proceed with executing the next test.
+ */
+ private static final long DEFAULT_TEST_TIMEOUT_MILLIS = TimeUnit.MINUTES.toMillis(5);
+
+ protected IBuildInfo mCtsBuild;
+
+ @Override
+ public void setBuild(IBuildInfo buildInfo) {
+ mCtsBuild = buildInfo;
+ }
+
+ /**
+ * Runs tests on the device.
+ *
+ * @param pkgName The test package file name that contains the test.
+ * @param testClassName The class name to test within the test package. If {@code null}, runs
+ * all test classes in the package.
+ * @param testMethodName Method name to test within the test class. Ignored if {@code
+ * testClassName} is {@code null}. If {@code null}, runs all test classes in the class.
+ */
+ protected void runDeviceTests(
+ String pkgName, @Nullable String testClassName, @Nullable String testMethodName)
+ throws DeviceNotAvailableException {
+ RemoteAndroidTestRunner testRunner = getTestRunner(pkgName, testClassName, testMethodName);
+ CollectingTestListener listener = new CollectingTestListener();
+ assertTrue(getDevice().runInstrumentationTests(testRunner, listener));
+ assertTestsPassed(listener.getCurrentRunResults());
+ }
+
+ /**
+ * Excutes shell command and returns the result.
+ *
+ * @param command The command to run.
+ * @return The result from the command. If the result was {@code null}, empty string ("") will
+ * be returned instead. Otherwise, trimmed result will be returned.
+ */
+ protected @Nonnull String executeShellCommand(String command) throws Exception {
+ LogUtil.CLog.d("Starting command " + command);
+ String commandOutput = getDevice().executeShellCommand(command);
+ LogUtil.CLog.d("Output for command " + command + ": " + commandOutput);
+ return commandOutput != null ? commandOutput.trim() : "";
+ }
+
+ /** Installs the app with the given {@code appFileName}. */
+ protected void installApp(String appFileName)
+ throws FileNotFoundException, DeviceNotAvailableException {
+ LogUtil.CLog.d("Installing app " + appFileName);
+ CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mCtsBuild);
+ String result =
+ getDevice()
+ .installPackage(
+ buildHelper.getTestFile(appFileName),
+ /* reinstall= */ true,
+ /* grantPermissions= */ true,
+ "-t"); // Signals that this is a test APK.
+ assertNull("Failed to install " + appFileName + ": " + result, result);
+ }
+
+ /** Returns a {@link RemoteAndroidTestRunner} for the given test parameters. */
+ protected RemoteAndroidTestRunner getTestRunner(
+ String pkgName, String testClassName, String testMethodName) {
+ if (testClassName != null && testClassName.startsWith(".")) {
+ testClassName = pkgName + testClassName;
+ }
+
+ RemoteAndroidTestRunner testRunner =
+ new RemoteAndroidTestRunner(pkgName, RUNNER, getDevice().getIDevice());
+ testRunner.setMaxTimeToOutputResponse(DEFAULT_SHELL_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+ testRunner.addInstrumentationArg(
+ TEST_TIMEOUT_INST_ARGS_KEY, Long.toString(DEFAULT_TEST_TIMEOUT_MILLIS));
+ if (testClassName != null && testMethodName != null) {
+ testRunner.setMethodName(testClassName, testMethodName);
+ } else if (testClassName != null) {
+ testRunner.setClassName(testClassName);
+ }
+ return testRunner;
+ }
+
+ /**
+ * Asserts that {@code testRunResult} contains at least one test, and that all tests passed.
+ *
+ * <p>If the assertion fails, an {@link AssertionError} with a descriptive message is thrown.
+ */
+ protected void assertTestsPassed(TestRunResult testRunResult) {
+ if (testRunResult.isRunFailure()) {
+ throw new AssertionError(
+ "Failed to successfully run device tests for "
+ + testRunResult.getName()
+ + ": "
+ + testRunResult.getRunFailureMessage());
+ }
+ if (testRunResult.getNumTests() == 0) {
+ throw new AssertionError("No tests were run on the device");
+ }
+
+ if (testRunResult.hasFailedTests()) {
+ // Build a meaningful error message
+ StringBuilder errorBuilder = new StringBuilder("On-device tests failed:\n");
+ for (Map.Entry<TestDescription, TestResult> resultEntry :
+ testRunResult.getTestResults().entrySet()) {
+ if (!resultEntry
+ .getValue()
+ .getStatus()
+ .equals(com.android.ddmlib.testrunner.TestResult.TestStatus.PASSED)) {
+ errorBuilder.append(resultEntry.getKey().toString());
+ errorBuilder.append(":\n");
+ errorBuilder.append(resultEntry.getValue().getStackTrace());
+ }
+ }
+ throw new AssertionError(errorBuilder.toString());
+ }
+ }
+}
diff --git a/hostsidetests/media/src/android/media/cts/BaseMultiUserTest.java b/hostsidetests/media/src/android/media/cts/BaseMultiUserTest.java
index 08461fb..1a03581 100644
--- a/hostsidetests/media/src/android/media/cts/BaseMultiUserTest.java
+++ b/hostsidetests/media/src/android/media/cts/BaseMultiUserTest.java
@@ -43,26 +43,7 @@
/**
* Base class for host-side tests for multi-user aware media APIs.
*/
-public class BaseMultiUserTest extends DeviceTestCase implements IBuildReceiver {
- private static final String RUNNER = "androidx.test.runner.AndroidJUnitRunner";
-
- /**
- * The defined timeout (in milliseconds) is used as a maximum waiting time when expecting the
- * command output from the device. At any time, if the shell command does not output anything
- * for a period longer than the defined timeout the Tradefed run terminates.
- */
- private static final long DEFAULT_SHELL_TIMEOUT_MILLIS = TimeUnit.MINUTES.toMillis(5);
-
- /**
- * Instrumentation test runner argument key used for individual test timeout
- **/
- protected static final String TEST_TIMEOUT_INST_ARGS_KEY = "timeout_msec";
-
- /**
- * Sets timeout (in milliseconds) that will be applied to each test. In the
- * event of a test timeout it will log the results and proceed with executing the next test.
- */
- private static final long DEFAULT_TEST_TIMEOUT_MILLIS = TimeUnit.MINUTES.toMillis(5);
+public class BaseMultiUserTest extends BaseMediaHostSideTest {
private static final String SETTINGS_PACKAGE_VERIFIER_NAMESPACE = "global";
private static final String SETTINGS_PACKAGE_VERIFIER_NAME = "package_verifier_enable";
@@ -78,7 +59,6 @@
*/
protected static final int USER_SYSTEM = 0;
- private IBuildInfo mCtsBuild;
private String mPackageVerifier;
private Set<String> mExistingPackages;
@@ -104,7 +84,7 @@
"0",
USER_ALL);
- mExistingUsers = new ArrayList();
+ mExistingUsers = new ArrayList<>();
int primaryUserId = getDevice().getPrimaryUserId();
mExistingUsers.add(primaryUserId);
mExistingUsers.add(USER_SYSTEM);
@@ -139,11 +119,6 @@
super.tearDown();
}
- @Override
- public void setBuild(IBuildInfo buildInfo) {
- mCtsBuild = buildInfo;
- }
-
/**
* Installs the app as if the user of the ID {@param userId} has installed the app.
*
@@ -165,20 +140,6 @@
result);
}
- /**
- * Excutes shell command and returns the result.
- *
- * @param command command to run.
- * @return result from the command. If the result was {@code null}, empty string ("") will be
- * returned instead. Otherwise, trimmed result will be returned.
- */
- protected @Nonnull String executeShellCommand(final String command) throws Exception {
- CLog.d("Starting command " + command);
- String commandOutput = getDevice().executeShellCommand(command);
- CLog.d("Output for command " + command + ": " + commandOutput);
- return commandOutput != null ? commandOutput.trim() : "";
- }
-
private int createAndStartUser(String extraParam) throws Exception {
String command = "pm create-user" + extraParam + " TestUser_" + System.currentTimeMillis();
String commandOutput = executeShellCommand(command);
@@ -248,49 +209,15 @@
* {@code null}.
* @param userId user ID to run the tests as.
*/
- protected void runDeviceTestsAsUser(
+ protected void runDeviceTests(
String pkgName, @Nullable String testClassName,
@Nullable String testMethodName, int userId) throws DeviceNotAvailableException {
- if (testClassName != null && testClassName.startsWith(".")) {
- testClassName = pkgName + testClassName;
- }
-
- RemoteAndroidTestRunner testRunner = new RemoteAndroidTestRunner(
- pkgName, RUNNER, getDevice().getIDevice());
- testRunner.setMaxTimeToOutputResponse(DEFAULT_SHELL_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
- testRunner.addInstrumentationArg(
- TEST_TIMEOUT_INST_ARGS_KEY, Long.toString(DEFAULT_TEST_TIMEOUT_MILLIS));
- if (testClassName != null && testMethodName != null) {
- testRunner.setMethodName(testClassName, testMethodName);
- } else if (testClassName != null) {
- testRunner.setClassName(testClassName);
- }
-
+ RemoteAndroidTestRunner testRunner = getTestRunner(pkgName, testClassName, testMethodName);
CollectingTestListener listener = new CollectingTestListener();
assertTrue(getDevice().runInstrumentationTestsAsUser(testRunner, userId, listener));
final TestRunResult result = listener.getCurrentRunResults();
- if (result.isRunFailure()) {
- throw new AssertionError("Failed to successfully run device tests for "
- + result.getName() + ": " + result.getRunFailureMessage());
- }
- if (result.getNumTests() == 0) {
- throw new AssertionError("No tests were run on the device");
- }
-
- if (result.hasFailedTests()) {
- // Build a meaningful error message
- StringBuilder errorBuilder = new StringBuilder("On-device tests failed:\n");
- for (Map.Entry<TestDescription, TestResult> resultEntry :
- result.getTestResults().entrySet()) {
- if (!resultEntry.getValue().getStatus().equals(TestStatus.PASSED)) {
- errorBuilder.append(resultEntry.getKey().toString());
- errorBuilder.append(":\n");
- errorBuilder.append(resultEntry.getValue().getStackTrace());
- }
- }
- throw new AssertionError(errorBuilder.toString());
- }
+ assertTestsPassed(result);
}
/**
diff --git a/hostsidetests/media/src/android/media/cts/MediaExtractorHostSideTest.java b/hostsidetests/media/src/android/media/cts/MediaExtractorHostSideTest.java
new file mode 100644
index 0000000..c06603c
--- /dev/null
+++ b/hostsidetests/media/src/android/media/cts/MediaExtractorHostSideTest.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.cts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.stats.mediametrics.Mediametrics;
+
+import com.android.internal.os.StatsdConfigProto;
+import com.android.internal.os.StatsdConfigProto.SimpleAtomMatcher;
+import com.android.internal.os.StatsdConfigProto.StatsdConfig;
+import com.android.os.AtomsProto;
+import com.android.os.StatsLog;
+import com.android.os.StatsLog.ConfigMetricsReportList;
+import com.android.tradefed.device.CollectingByteOutputReceiver;
+
+import com.google.common.io.Files;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/** Host-side tests for MediaExtractor. */
+public class MediaExtractorHostSideTest extends BaseMediaHostSideTest {
+ /** Package name of the device-side tests. */
+ private static final String DEVICE_SIDE_TEST_PACKAGE = "android.media.cts";
+ /** Name of the APK that contains the device-side tests. */
+ private static final String DEVICE_SIDE_TEST_APK = "CtsMediaExtractorHostTestApp.apk";
+ /** Fully qualified class name for the device-side tests. */
+ private static final String DEVICE_SIDE_TEST_CLASS =
+ "android.media.cts.MediaExtractorDeviceSideTest";
+
+ private static final long CONFIG_ID = "cts_config".hashCode();
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ getDevice().uninstallPackage(DEVICE_SIDE_TEST_PACKAGE);
+ installApp(DEVICE_SIDE_TEST_APK);
+ removeConfig(); // Clear existing configs.
+ createAndUploadConfig();
+ getAndClearReportList(); // Clear existing reports.
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ removeConfig();
+ getDevice().uninstallPackage(DEVICE_SIDE_TEST_PACKAGE);
+ }
+
+ // Tests.
+
+ public void testMediaMetricsEntryPointSdk() throws Exception {
+ runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, DEVICE_SIDE_TEST_CLASS, "testEntryPointSdk");
+ assertThat(getMediaExtractorReportedEntryPoint())
+ .isEqualTo(Mediametrics.ExtractorData.EntryPoint.SDK);
+ }
+
+ public void testMediaMetricsEntryPointNdkNoJvm() throws Exception {
+ runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, DEVICE_SIDE_TEST_CLASS, "testEntryPointNdkNoJvm");
+ assertThat(getMediaExtractorReportedEntryPoint())
+ .isEqualTo(Mediametrics.ExtractorData.EntryPoint.NDK_NO_JVM);
+ }
+
+ public void testMediaMetricsEntryPointNdkWithJvm() throws Exception {
+ runDeviceTests(
+ DEVICE_SIDE_TEST_PACKAGE, DEVICE_SIDE_TEST_CLASS, "testEntryPointNdkWithJvm");
+ assertThat(getMediaExtractorReportedEntryPoint())
+ .isEqualTo(Mediametrics.ExtractorData.EntryPoint.NDK_WITH_JVM);
+ }
+
+ // Internal methods.
+
+ /** Removes any existing config with id {@link #CONFIG_ID}. */
+ private void removeConfig() throws Exception {
+ getDevice().executeShellCommand("cmd stats config remove " + CONFIG_ID);
+ }
+
+ /** Creates the statsd config and passes it to statsd. */
+ private void createAndUploadConfig() throws Exception {
+ StatsdConfig.Builder configBuilder =
+ StatsdConfigProto.StatsdConfig.newBuilder()
+ .setId(CONFIG_ID)
+ .addAllowedLogSource(DEVICE_SIDE_TEST_PACKAGE)
+ .addWhitelistedAtomIds(
+ AtomsProto.Atom.MEDIAMETRICS_EXTRACTOR_REPORTED_FIELD_NUMBER);
+ addAtomEvent(configBuilder);
+ uploadConfig(configBuilder.build());
+ }
+
+ /** Writes the given config into a file and passes is to statsd via standard input. */
+ private void uploadConfig(StatsdConfig config) throws Exception {
+ File configFile = File.createTempFile("statsdconfig", ".config");
+ configFile.deleteOnExit();
+ Files.write(config.toByteArray(), configFile);
+ String remotePath = "/data/local/tmp/" + configFile.getName();
+ // Make sure a config file with the same name doesn't exist already.
+ getDevice().deleteFile(remotePath);
+ assertThat(getDevice().pushFile(configFile, remotePath)).isTrue();
+ getDevice()
+ .executeShellCommand(
+ "cat " + remotePath + " | cmd stats config update " + CONFIG_ID);
+ getDevice().deleteFile(remotePath);
+ }
+
+ /** Adds an event to the config in order to match MediaParser reported atoms. */
+ private static void addAtomEvent(StatsdConfig.Builder config) {
+ String atomName = "Atom" + System.nanoTime();
+ String eventName = "Event" + System.nanoTime();
+ SimpleAtomMatcher.Builder sam =
+ SimpleAtomMatcher.newBuilder()
+ .setAtomId(AtomsProto.Atom.MEDIAMETRICS_EXTRACTOR_REPORTED_FIELD_NUMBER);
+ config.addAtomMatcher(
+ StatsdConfigProto.AtomMatcher.newBuilder()
+ .setId(atomName.hashCode())
+ .setSimpleAtomMatcher(sam));
+ config.addEventMetric(
+ StatsdConfigProto.EventMetric.newBuilder()
+ .setId(eventName.hashCode())
+ .setWhat(atomName.hashCode()));
+ }
+
+ /**
+ * Returns all MediaParser reported metric events sorted by timestamp.
+ *
+ * <p>Note: Calls {@link #getAndClearReportList()} to obtain the statsd report.
+ */
+ private Mediametrics.ExtractorData.EntryPoint getMediaExtractorReportedEntryPoint()
+ throws Exception {
+ ConfigMetricsReportList reportList = getAndClearReportList();
+ assertThat(reportList.getReportsCount()).isEqualTo(1);
+ StatsLog.ConfigMetricsReport report = reportList.getReports(0);
+ ArrayList<StatsLog.EventMetricData> data = new ArrayList<>();
+ report.getMetricsList()
+ .forEach(
+ statsLogReport ->
+ data.addAll(statsLogReport.getEventMetrics().getDataList()));
+ List<AtomsProto.MediametricsExtractorReported> mediametricsExtractorReported =
+ data.stream()
+ .map(element -> element.getAtom().getMediametricsExtractorReported())
+ .collect(Collectors.toList());
+ // During device boot, services may extract media files. We ensure we only pick up metric
+ // events from our device-side test.
+ mediametricsExtractorReported.removeIf(
+ entry -> !DEVICE_SIDE_TEST_PACKAGE.equals(entry.getPackageName()));
+ assertThat(mediametricsExtractorReported).hasSize(1);
+ return mediametricsExtractorReported.get(0).getExtractorData().getEntryPoint();
+ }
+
+ /** Gets a statsd report and removes it from the device. */
+ private ConfigMetricsReportList getAndClearReportList() throws Exception {
+ CollectingByteOutputReceiver receiver = new CollectingByteOutputReceiver();
+ getDevice()
+ .executeShellCommand(
+ "cmd stats dump-report " + CONFIG_ID + " --include_current_bucket --proto",
+ receiver);
+ return ConfigMetricsReportList.parser().parseFrom(receiver.getOutput());
+ }
+}
diff --git a/hostsidetests/media/src/android/media/session/cts/MediaSessionManagerHostTest.java b/hostsidetests/media/src/android/media/session/cts/MediaSessionManagerHostTest.java
index e4956b0..870893c 100644
--- a/hostsidetests/media/src/android/media/session/cts/MediaSessionManagerHostTest.java
+++ b/hostsidetests/media/src/android/media/session/cts/MediaSessionManagerHostTest.java
@@ -253,7 +253,9 @@
@AppModeFull
@RequiresDevice
- public void testIsTrusted_withEnabledNotificationListener_returnsTrue() throws Exception {
+ // Ignored due to b/171012388.
+ public void ignored_testIsTrusted_withEnabledNotificationListener_returnsTrue()
+ throws Exception {
if (!canCreateAdditionalUsers(1)) {
CLog.logAndDisplay(LogLevel.INFO,
"Cannot create a new user. Skipping multi-user test cases.");
@@ -288,8 +290,7 @@
private void runTestAsUser(String testMethodName, int userId)
throws DeviceNotAvailableException {
- runDeviceTestsAsUser(DEVICE_SIDE_TEST_PKG, DEVICE_SIDE_TEST_CLASS,
- testMethodName, userId);
+ runDeviceTests(DEVICE_SIDE_TEST_PKG, DEVICE_SIDE_TEST_CLASS, testMethodName, userId);
}
/**