Merge "Merge "Merge "Fix cts crash in testStagefright_cve_2015_3836" into marshmallow-cts-dev am: d4fe797f08" into nougat-cts-dev am: a1490fd005" into nougat-mr1-cts-dev am: f6687dce5b -s ours am: db79894072
am: a0032be82d
Change-Id: Ie9390cd7b44c0d43ee2cd5d24f1a4f26444436a2
diff --git a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerPinnedStackTests.java b/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerPinnedStackTests.java
index 7a691ec..b99a748 100644
--- a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerPinnedStackTests.java
+++ b/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerPinnedStackTests.java
@@ -34,6 +34,8 @@
pinnedStackTester(PIP_ACTIVITY, PIP_ACTIVITY, true, false);
}
+ /**
+ * Disabled for b/35314835
public void testAlwaysFocusablePipActivity() throws Exception {
pinnedStackTester(ALWAYS_FOCUSABLE_PIP_ACTIVITY, ALWAYS_FOCUSABLE_PIP_ACTIVITY, true, true);
}
@@ -42,6 +44,7 @@
pinnedStackTester(
LAUNCH_INTO_PINNED_STACK_PIP_ACTIVITY, ALWAYS_FOCUSABLE_PIP_ACTIVITY, false, true);
}
+ */
private void pinnedStackTester(String startActivity, String topActivityName,
boolean moveTopToPinnedStack, boolean isFocusable) throws Exception {
diff --git a/hostsidetests/services/windowmanager/src/android/wm/cts/CrossAppDragAndDropTests.java b/hostsidetests/services/windowmanager/src/android/wm/cts/CrossAppDragAndDropTests.java
index fa1ae69..77119c0 100644
--- a/hostsidetests/services/windowmanager/src/android/wm/cts/CrossAppDragAndDropTests.java
+++ b/hostsidetests/services/windowmanager/src/android/wm/cts/CrossAppDragAndDropTests.java
@@ -19,10 +19,12 @@
import com.android.tradefed.device.CollectingOutputReceiver;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.testtype.DeviceTestCase;
import java.util.HashMap;
import java.util.Map;
+import java.util.regex.Pattern;
public class CrossAppDragAndDropTests extends DeviceTestCase {
// Constants copied from ActivityManager.StackId. If they are changed there, these must be
@@ -47,13 +49,17 @@
private static final String INPUT_MOUSE_SWIPE = "input mouse swipe ";
private static final String TASK_ID_PREFIX = "taskId";
+ // Regex pattern to match adb shell am stack list output of the form:
+ // taskId=<TASK_ID>: <componentName> bounds=[LEFT,TOP][RIGHT,BOTTOM]
+ private static final String TASK_REGEX_PATTERN_STRING =
+ "taskId=[0-9]+: %s bounds=\\[[0-9]+,[0-9]+\\]\\[[0-9]+,[0-9]+\\]";
+
private static final int SWIPE_DURATION_MS = 500;
private static final String SOURCE_PACKAGE_NAME = "android.wm.cts.dndsourceapp";
private static final String TARGET_PACKAGE_NAME = "android.wm.cts.dndtargetapp";
private static final String TARGET_23_PACKAGE_NAME = "android.wm.cts.dndtargetappsdk23";
-
private static final String SOURCE_ACTIVITY_NAME = "DragSource";
private static final String TARGET_ACTIVITY_NAME = "DropTarget";
@@ -218,8 +224,15 @@
CollectingOutputReceiver outputReceiver = new CollectingOutputReceiver();
mDevice.executeShellCommand(AM_STACK_LIST, outputReceiver);
final String output = outputReceiver.getOutput();
+ final StringBuilder builder = new StringBuilder();
+ builder.append("Finding task info for task: ");
+ builder.append(name);
+ builder.append("\nParsing adb shell am output: " );
+ builder.append(output);
+ CLog.i(builder.toString());
+ final Pattern pattern = Pattern.compile(String.format(TASK_REGEX_PATTERN_STRING, name));
for (String line : output.split("\\n")) {
- if (line.contains(name)) {
+ if (pattern.matcher(line).find()) {
return line;
}
}
diff --git a/tests/tests/media/Android.mk b/tests/tests/media/Android.mk
index eda78e8..47e8ae1 100644
--- a/tests/tests/media/Android.mk
+++ b/tests/tests/media/Android.mk
@@ -44,9 +44,11 @@
# include both the 32 and 64 bit versions
LOCAL_MULTILIB := both
-LOCAL_STATIC_JAVA_LIBRARIES := ctsmediautil ctsdeviceutil compatibility-device-util ctstestserver ctstestrunner ndkaudio
+LOCAL_STATIC_JAVA_LIBRARIES := ctsmediautil ctsdeviceutil compatibility-device-util
+LOCAL_STATIC_JAVA_LIBRARIES += ctstestserver ctstestrunner ndkaudio
-LOCAL_JNI_SHARED_LIBRARIES := libctsmediacodec_jni libaudio_jni libnativehelper_compat_libc++ libndkaudioLib
+LOCAL_JNI_SHARED_LIBRARIES := libctsmediacodec_jni libaudio_jni libnativehelper_compat_libc++
+LOCAL_JNI_SHARED_LIBRARIES += libndkaudioLib libctsmediadrm_jni
# do not compress VP9 video files
LOCAL_AAPT_FLAGS := -0 .vp9
diff --git a/tests/tests/media/libmediandkjni/AMediaObjects.h b/tests/tests/media/libmediandkjni/AMediaObjects.h
new file mode 100644
index 0000000..c4d5397
--- /dev/null
+++ b/tests/tests/media/libmediandkjni/AMediaObjects.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2016 The Android Open 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.
+ */
+
+#ifndef AMEDIAOBJECTS_H_
+#define AMEDIAOBJECTS_H_
+
+#include <utils/Log.h>
+
+#include "media/NdkMediaCrypto.h"
+#include "media/NdkMediaDrm.h"
+#include "media/NdkMediaExtractor.h"
+
+namespace {
+
+// Simple class to manage deletion of AMedia objects
+class AMediaObjects {
+ public:
+ AMediaObjects();
+ virtual ~AMediaObjects();
+
+ void setCrypto(AMediaCrypto* const theCrypto) {
+ mCrypto = theCrypto;
+ }
+ void setDrm(AMediaDrm* const theDrm) {
+ mDrm = theDrm;
+ }
+ void setVideoExtractor(AMediaExtractor* const theExtractor) {
+ mVideoExtractor = theExtractor;
+ }
+ void setAudioExtractor(AMediaExtractor* const theExtractor) {
+ mAudioExtractor = theExtractor;
+ }
+
+ AMediaCrypto* getCrypto() const { return mCrypto; }
+ AMediaDrm* getDrm() const { return mDrm; }
+ AMediaExtractor* getAudioExtractor() const { return mAudioExtractor; }
+ AMediaExtractor* getVideoExtractor() const { return mVideoExtractor; }
+
+ private:
+ AMediaCrypto *mCrypto;
+ AMediaDrm* mDrm;
+ AMediaExtractor* mAudioExtractor;
+ AMediaExtractor* mVideoExtractor;
+
+ // Disallow copy and assignment
+ AMediaObjects(const AMediaObjects&);
+ void operator=(const AMediaObjects&);
+};
+
+AMediaObjects::AMediaObjects(void) : mCrypto(NULL), mDrm(NULL),
+ mAudioExtractor(NULL), mVideoExtractor(NULL) {
+}
+
+AMediaObjects::~AMediaObjects() {
+ if (mCrypto) {
+ AMediaCrypto_delete(mCrypto);
+ }
+ if (mAudioExtractor) {
+ AMediaExtractor_delete(mAudioExtractor);
+ }
+ if (mVideoExtractor) {
+ AMediaExtractor_delete(mVideoExtractor);
+ }
+ if (mDrm) {
+ AMediaDrm_release(mDrm);
+ }
+}
+
+} // anonymous namespace
+#endif // AMEDIAOBJECTS_H_
+
diff --git a/tests/tests/media/libmediandkjni/Android.mk b/tests/tests/media/libmediandkjni/Android.mk
index 259c95e..1ccdede 100644
--- a/tests/tests/media/libmediandkjni/Android.mk
+++ b/tests/tests/media/libmediandkjni/Android.mk
@@ -14,6 +14,9 @@
#
LOCAL_PATH := $(call my-dir)
+#------------------------------------------------------------------------------
+# Builds libctsmediacodec_jni.so
+#
include $(CLEAR_VARS)
LOCAL_MODULE := libctsmediacodec_jni
@@ -21,20 +24,57 @@
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := \
- native-media-jni.cpp \
- codec-utils-jni.cpp \
- md5_utils.cpp
+ native-media-jni.cpp \
+ codec-utils-jni.cpp \
+ md5_utils.cpp
LOCAL_C_INCLUDES := \
- $(JNI_H_INCLUDE) \
- system/core/include
+ $(JNI_H_INCLUDE) \
+ system/core/include
LOCAL_C_INCLUDES += $(call include-path-for, mediandk)
-LOCAL_SHARED_LIBRARIES := libandroid libnativehelper_compat_libc++ liblog libmediandk
+LOCAL_SHARED_LIBRARIES := \
+ libandroid libnativehelper_compat_libc++ \
+ liblog libmediandk
LOCAL_SDK_VERSION := 23
LOCAL_CFLAGS := -Werror -Wall
include $(BUILD_SHARED_LIBRARY)
+
+#------------------------------------------------------------------------------
+# Builds libctsmediadrm_jni.so
+#
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libctsmediadrm_jni
+
+# Don't include this package in any configuration by default.
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := \
+ CtsMediaDrmJniOnLoad.cpp \
+ codec-utils-jni.cpp \
+ md5_utils.cpp \
+ native-mediadrm-jni.cpp \
+
+LOCAL_C_INCLUDES := \
+ $(JNI_H_INCLUDE) \
+ system/core/include
+
+
+LOCAL_C_INCLUDES += $(call include-path-for, mediandk)
+
+LOCAL_SHARED_LIBRARIES := \
+ libandroid libnativehelper_compat_libc++ \
+ liblog libmediandk libdl
+
+LOCAL_SDK_VERSION := 23
+
+LOCAL_NDK_STL_VARIANT := c++_static
+
+LOCAL_CFLAGS := -Werror -Wall
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/tests/tests/media/libmediandkjni/CtsMediaDrmJniOnLoad.cpp b/tests/tests/media/libmediandkjni/CtsMediaDrmJniOnLoad.cpp
new file mode 100644
index 0000000..24714a3
--- /dev/null
+++ b/tests/tests/media/libmediandkjni/CtsMediaDrmJniOnLoad.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016 The Android Open 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 <jni.h>
+#include <stdio.h>
+
+extern int register_android_media_cts_NativeClearKeySystemTest(JNIEnv*);
+
+jint JNI_OnLoad(JavaVM *vm, void */*reserved*/) {
+ JNIEnv *env = NULL;
+
+ if (vm->GetEnv((void **) &env, JNI_VERSION_1_4) != JNI_OK) {
+ return JNI_ERR;
+ }
+
+ if (register_android_media_cts_NativeClearKeySystemTest(env)) {
+ return JNI_ERR;
+ }
+
+ return JNI_VERSION_1_4;
+}
diff --git a/tests/tests/media/libmediandkjni/md5_utils.cpp b/tests/tests/media/libmediandkjni/md5_utils.cpp
index f4f893a..8e520e1 100644
--- a/tests/tests/media/libmediandkjni/md5_utils.cpp
+++ b/tests/tests/media/libmediandkjni/md5_utils.cpp
@@ -157,7 +157,7 @@
*/
void
MD5Transform(UWORD32 buf[4], UWORD32 const in[16]) {
- register UWORD32 a, b, c, d;
+ UWORD32 a, b, c, d;
a = buf[0];
b = buf[1];
diff --git a/tests/tests/media/libmediandkjni/native-mediadrm-jni.cpp b/tests/tests/media/libmediandkjni/native-mediadrm-jni.cpp
new file mode 100644
index 0000000..b98a6af
--- /dev/null
+++ b/tests/tests/media/libmediandkjni/native-mediadrm-jni.cpp
@@ -0,0 +1,690 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define TAG "NativeMediaDrm"
+
+#include <utils/Log.h>
+#include <sys/types.h>
+
+#include <string>
+#include <vector>
+
+#include <assert.h>
+#include <jni.h>
+#include <JNIHelp.h>
+
+#include <android/native_window_jni.h>
+
+#include "AMediaObjects.h"
+
+#include "media/NdkMediaCodec.h"
+#include "media/NdkMediaCrypto.h"
+#include "media/NdkMediaDrm.h"
+#include "media/NdkMediaExtractor.h"
+#include "media/NdkMediaFormat.h"
+#include "media/NdkMediaMuxer.h"
+
+typedef std::vector<uint8_t> Uuid;
+
+struct fields_t {
+ jfieldID surface;
+ jfieldID mimeType;
+ jfieldID audioUrl;
+ jfieldID videoUrl;
+};
+
+struct PlaybackParams {
+ jobject surface;
+ jstring mimeType;
+ jstring audioUrl;
+ jstring videoUrl;
+};
+
+static fields_t gFieldIds;
+static bool gGotVendorDefinedEvent = false;
+
+static const size_t kPlayTimeSeconds = 30;
+static const size_t kUuidSize = 16;
+
+static const uint8_t kWidevineUuid[kUuidSize] = {
+ 0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6, 0x4a, 0xce,
+ 0xa3, 0xc8, 0x27, 0xdc, 0xd5, 0x1d, 0x21, 0xed
+};
+
+// The test content is not packaged with clearkey UUID,
+// we have to use a canned clearkey pssh for the test.
+static const uint8_t kClearkeyPssh[] = {
+ // BMFF box header (4 bytes size + 'pssh')
+ 0x00, 0x00, 0x00, 0x34, 0x70, 0x73, 0x73, 0x68,
+ // full box header (version = 1 flags = 0)
+ 0x01, 0x00, 0x00, 0x00,
+ // system id
+ 0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02,
+ 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b,
+ // number of key ids
+ 0x00, 0x00, 0x00, 0x01,
+ // key id
+ 0x60, 0x06, 0x1e, 0x01, 0x7e, 0x47, 0x7e, 0x87,
+ 0x7e, 0x57, 0xd0, 0x0d, 0x1e, 0xd0, 0x0d, 0x1e,
+ // size of data, must be zero
+ 0x00, 0x00, 0x00, 0x00
+};
+
+static const uint8_t kKeyRequestData[] = {
+ 0x7b, 0x22, 0x6b, 0x69, 0x64,
+ 0x73, 0x22, 0x3a, 0x5b, 0x22,
+ 0x59, 0x41, 0x59, 0x65, 0x41,
+ 0x58, 0x35, 0x48, 0x66, 0x6f,
+ 0x64, 0x2b, 0x56, 0x39, 0x41,
+ 0x4e, 0x48, 0x74, 0x41, 0x4e,
+ 0x48, 0x67, 0x22, 0x5d, 0x2c,
+ 0x22, 0x74, 0x79, 0x70, 0x65,
+ 0x22, 0x3a, 0x22, 0x74, 0x65,
+ 0x6d, 0x70, 0x6f, 0x72, 0x61,
+ 0x72, 0x79, 0x22, 0x7d,
+};
+
+static const size_t kKeyRequestSize = sizeof(kKeyRequestData);
+
+// base 64 encoded JSON response string, must not contain padding character '='
+static const char kResponse[] = "{\"keys\":[{\"kty\":\"oct\"," \
+ "\"kid\":\"YAYeAX5Hfod+V9ANHtANHg\",\"k\":" \
+ "\"GoogleTestKeyBase64ggg\"}]}";
+
+static bool isUuidSizeValid(Uuid uuid) {
+ return (uuid.size() == kUuidSize);
+}
+
+static std::vector<uint8_t> jbyteArrayToVector(
+ JNIEnv* env, jbyteArray const &byteArray) {
+ uint8_t* buffer = reinterpret_cast<uint8_t*>(
+ env->GetByteArrayElements(byteArray, /*is_copy*/NULL));
+ std::vector<uint8_t> vector;
+ for (jsize i = 0; i < env->GetArrayLength(byteArray); ++i) {
+ vector.push_back(buffer[i]);
+ }
+ return vector;
+}
+
+static Uuid jbyteArrayToUuid(JNIEnv* env, jbyteArray const &uuid) {
+ Uuid juuid;
+ juuid.resize(0);
+ if (uuid != NULL) {
+ juuid = jbyteArrayToVector(env, uuid);
+ }
+ return juuid;
+}
+
+extern "C" jboolean Java_android_media_cts_NativeClearKeySystemTest_isCryptoSchemeSupportedNative(
+ JNIEnv* env, jclass /*clazz*/, jbyteArray uuid) {
+
+ if (NULL == uuid) {
+ jniThrowException(env, "java/lang/NullPointerException", "null uuid");
+ return JNI_FALSE;
+ }
+
+ Uuid juuid = jbyteArrayToUuid(env, uuid);
+ if (isUuidSizeValid(juuid)) {
+ return AMediaDrm_isCryptoSchemeSupported(&juuid[0], NULL);
+ } else {
+ jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+ "invalid UUID size, expected %u bytes", kUuidSize);
+ }
+ return JNI_FALSE;
+}
+
+void initPlaybackParams(JNIEnv* env, const jobject &playbackParams, PlaybackParams ¶ms) {
+ params.surface = env->GetObjectField(
+ playbackParams, gFieldIds.surface);
+
+ params.mimeType = static_cast<jstring>(env->GetObjectField(
+ playbackParams, gFieldIds.mimeType));
+
+ params.audioUrl = static_cast<jstring>(env->GetObjectField(
+ playbackParams, gFieldIds.audioUrl));
+
+ params.videoUrl = static_cast<jstring>(env->GetObjectField(
+ playbackParams, gFieldIds.videoUrl));
+}
+
+extern "C" jboolean Java_android_media_cts_NativeClearKeySystemTest_testGetPropertyStringNative(
+ JNIEnv* env, jclass clazz, jbyteArray uuid,
+ jstring name, jobject outValue) {
+
+ if (NULL == uuid || NULL == name || NULL == outValue) {
+ jniThrowException(env, "java/lang/NullPointerException",
+ "One or more null input parameters");
+ return JNI_FALSE;
+ }
+
+ Uuid juuid = jbyteArrayToUuid(env, uuid);
+ if (!isUuidSizeValid(juuid)) {
+ jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+ "invalid UUID size, expected %u bytes", kUuidSize);
+ return JNI_FALSE;
+ }
+
+ AMediaObjects aMediaObjects;
+ aMediaObjects.setDrm(AMediaDrm_createByUUID(&juuid[0]));
+ if (NULL == aMediaObjects.getDrm()) {
+ jniThrowException(env, "java/lang/RuntimeException", "null MediaDrm");
+ return JNI_FALSE;
+ }
+
+ const char *utf8_name = env->GetStringUTFChars(name, NULL);
+ const char *utf8_outValue = NULL;
+ media_status_t status = AMediaDrm_getPropertyString(aMediaObjects.getDrm(),
+ utf8_name, &utf8_outValue);
+ env->ReleaseStringUTFChars(name, utf8_name);
+
+ if (NULL != utf8_outValue) {
+ clazz = env->GetObjectClass(outValue);
+ jmethodID mId = env->GetMethodID (clazz, "append",
+ "(Ljava/lang/String;)Ljava/lang/StringBuffer;");
+ jstring outString = env->NewStringUTF(
+ static_cast<const char *>(utf8_outValue));
+ env->CallObjectMethod(outValue, mId, outString);
+ } else {
+ jniThrowExceptionFmt(env, "java/lang/RuntimeException",
+ "get property string returns %d", status);
+ return JNI_FALSE;
+ }
+ return JNI_TRUE;
+}
+
+extern "C" jboolean Java_android_media_cts_NativeClearKeySystemTest__testPsshNative(
+ JNIEnv* env, jclass /*clazz*/, jbyteArray uuid, jstring videoUrl) {
+
+ if (NULL == uuid || NULL == videoUrl) {
+ jniThrowException(env, "java/lang/NullPointerException",
+ "null uuid or null videoUrl");
+ return JNI_FALSE;
+ }
+
+ Uuid juuid = jbyteArrayToUuid(env, uuid);
+ if (!isUuidSizeValid(juuid)) {
+ jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+ "invalid UUID size, expected %u bytes", kUuidSize);
+ return JNI_FALSE;
+ }
+
+ AMediaObjects aMediaObjects;
+ aMediaObjects.setVideoExtractor(AMediaExtractor_new());
+ const char* url = env->GetStringUTFChars(videoUrl, 0);
+ if (url) {
+ media_status_t status = AMediaExtractor_setDataSource(
+ aMediaObjects.getVideoExtractor(), url);
+ env->ReleaseStringUTFChars(videoUrl, url);
+
+ if (status != AMEDIA_OK) {
+ jniThrowExceptionFmt(env, "java/lang/RuntimeException",
+ "set video data source error=%d", status);
+ return JNI_FALSE;
+ }
+ }
+
+ PsshInfo* psshInfo = AMediaExtractor_getPsshInfo(aMediaObjects.getVideoExtractor());
+ if (psshInfo == NULL) {
+ jniThrowException(env, "java/lang/RuntimeException", "null psshInfo");
+ return JNI_FALSE;
+ }
+
+ jboolean testResult = JNI_FALSE;
+ for (size_t i = 0; i < psshInfo->numentries; i++) {
+ PsshEntry *entry = &psshInfo->entries[i];
+
+ // We do not have clearkey content that contains ClearKey UUID in the
+ // pssh box. So we have to test if it has Widevine UUID instead.
+ // TODO: Replace kWidevineUuid with uuid when test content contains
+ // ClearKey UUID.
+ if (0 == memcmp(entry->uuid, kWidevineUuid, sizeof(entry->uuid))) {
+ aMediaObjects.setCrypto(
+ AMediaCrypto_new(entry->uuid, entry->data, entry->datalen));
+ if (aMediaObjects.getCrypto()) {
+ testResult = JNI_TRUE;
+ } else {
+ ALOGE("Failed to create media crypto=%zd", i);
+ testResult = JNI_FALSE;
+ }
+ break;
+ }
+ }
+ return testResult;
+}
+
+static bool isVideo(const char* mime) {
+ return !strncmp(mime, "video/", 6) ? true : false;
+}
+
+static bool isAudio(const char* mime) {
+ return !strncmp(mime, "audio/", 6) ? true : false;
+}
+
+static void addTrack(const AMediaFormat* format,
+ const char* mime, const AMediaCrypto* crypto,
+ const ANativeWindow* window, AMediaCodec** codec) {
+
+ *codec = AMediaCodec_createDecoderByType(mime);
+ if (codec == NULL) {
+ ALOGE("cannot create codec for %s", mime);
+ return;
+ }
+
+ AMediaCodec_configure(*codec, format,
+ const_cast<ANativeWindow*>(window),
+ const_cast<AMediaCrypto*>(crypto), 0);
+}
+
+static void addTracks(const AMediaExtractor* extractor,
+ const AMediaCrypto* crypto, const ANativeWindow* window,
+ AMediaCodec** codec) {
+ size_t numTracks = AMediaExtractor_getTrackCount(
+ const_cast<AMediaExtractor*>(extractor));
+ AMediaFormat* trackFormat = NULL;
+ for (size_t i = 0; i < numTracks; ++i) {
+ trackFormat = AMediaExtractor_getTrackFormat(
+ const_cast<AMediaExtractor*>(extractor), i);
+ if (trackFormat) {
+ ALOGV("track %zd format: %s", i,
+ AMediaFormat_toString(trackFormat));
+
+ const char* mime = "";
+ if (!AMediaFormat_getString(
+ trackFormat, AMEDIAFORMAT_KEY_MIME, &mime)) {
+ ALOGE("no mime type");
+ AMediaFormat_delete(trackFormat);
+ return;
+ } else if (isAudio(mime) || isVideo(mime)) {
+ AMediaExtractor_selectTrack(
+ const_cast<AMediaExtractor*>(extractor), i);
+ ALOGV("track %zd codec format: %s", i,
+ AMediaFormat_toString(trackFormat));
+
+ addTrack(trackFormat, mime, crypto, window, codec);
+ AMediaCodec_start(*codec);
+ AMediaCodec_flush(*codec);
+ AMediaExtractor_seekTo(
+ const_cast<AMediaExtractor*>(extractor), 0,
+ AMEDIAEXTRACTOR_SEEK_PREVIOUS_SYNC);
+ }
+ AMediaFormat_delete(trackFormat);
+ }
+ }
+}
+
+static int64_t getSystemNanoTime() {
+ timespec now;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ return now.tv_sec * 1000000000LL + now.tv_nsec;
+}
+
+static void fillDecoder(AMediaCodec* codec, AMediaExtractor* extractor,
+ int64_t* presentationTimeUs, bool* eosReached) {
+ media_status_t status = AMEDIA_OK;
+
+ ssize_t bufferIndex = AMediaCodec_dequeueInputBuffer(codec, 2000);
+ if (bufferIndex >= 0) {
+ size_t bufsize;
+ uint8_t* buf = AMediaCodec_getInputBuffer(codec, bufferIndex, &bufsize);
+
+ int sampleSize = AMediaExtractor_readSampleData(extractor, buf, bufsize);
+ if (sampleSize < 0) {
+ sampleSize = 0;
+ *eosReached = true;
+ }
+
+ *presentationTimeUs = AMediaExtractor_getSampleTime(extractor);
+
+ AMediaCodecCryptoInfo *cryptoInfo =
+ AMediaExtractor_getSampleCryptoInfo(extractor);
+ if (cryptoInfo) {
+ status = AMediaCodec_queueSecureInputBuffer(
+ codec, bufferIndex, 0, cryptoInfo,
+ *presentationTimeUs,
+ *eosReached ? AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM : 0);
+ AMediaCodecCryptoInfo_delete(cryptoInfo);
+ } else {
+ status = AMediaCodec_queueInputBuffer(
+ codec, bufferIndex, 0, sampleSize,
+ *presentationTimeUs,
+ *eosReached ? AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM : 0);
+ }
+ AMediaExtractor_advance(extractor);
+ }
+}
+
+static bool drainDecoder(AMediaCodec* codec, int64_t presentationTimeUs,
+ int64_t* startTimeNano) {
+
+ AMediaCodecBufferInfo info;
+ ssize_t bufferIndex = AMediaCodec_dequeueOutputBuffer(codec, &info, 0);
+ if (bufferIndex >= 0) {
+ if (info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
+ return true; // eos reached
+ }
+
+ if (*startTimeNano < 0) {
+ *startTimeNano = getSystemNanoTime() - (presentationTimeUs * 1000);
+ }
+ int64_t delay = (*startTimeNano + presentationTimeUs * 1000) -
+ getSystemNanoTime();
+ if (delay > 0) {
+ usleep(delay / 1000);
+ }
+
+ AMediaCodec_releaseOutputBuffer(codec, bufferIndex, info.size != 0);
+ } else if (bufferIndex == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
+ ALOGV("output buffers changed");
+ } else if (bufferIndex == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
+ AMediaFormat* format = AMediaCodec_getOutputFormat(codec);
+ ALOGV("format changed to: %s", AMediaFormat_toString(format));
+ AMediaFormat_delete(format);
+ } else if (bufferIndex == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
+ ALOGV("no output buffer right now");
+ usleep(20000);
+ } else {
+ ALOGV("unexpected info code: %zd", bufferIndex);
+ }
+ return false;
+}
+
+static jboolean playContent(JNIEnv* env, const AMediaObjects& aMediaObjects,
+ PlaybackParams& params, const AMediaDrmSessionId& sessionId, Uuid uuid) {
+
+ ANativeWindow *window = ANativeWindow_fromSurface(env, params.surface);
+ AMediaExtractor* audioExtractor = aMediaObjects.getAudioExtractor();
+ AMediaExtractor* videoExtractor = aMediaObjects.getVideoExtractor();
+
+ AMediaCodec* audioCodec = NULL;
+ AMediaCodec* videoCodec = NULL;
+ AMediaCrypto* crypto = NULL;
+
+ crypto = AMediaCrypto_new(&uuid[0], sessionId.ptr, sessionId.length);
+ if (crypto == NULL) {
+ jniThrowException(env, "java/lang/RuntimeException",
+ "failed to create crypto object");
+ return JNI_FALSE;
+ }
+
+ addTracks(audioExtractor, NULL, NULL, &audioCodec);
+ addTracks(videoExtractor, crypto, window, &videoCodec);
+
+ bool sawAudioInputEos = false;
+ bool sawAudioOutputEos = false;
+ bool sawVideoInputEos = false;
+ bool sawVideoOutputEos = false;
+ int64_t videoPresentationTimeUs = 0;
+ int64_t videoStartTimeNano = -1;
+ struct timespec timeSpec;
+ clock_gettime(CLOCK_MONOTONIC, &timeSpec);
+ time_t startTimeSec = timeSpec.tv_sec;
+
+ while (!sawAudioOutputEos && !sawVideoOutputEos) {
+ if (!sawVideoInputEos) {
+ fillDecoder(videoCodec, videoExtractor,
+ &videoPresentationTimeUs, &sawVideoInputEos);
+ }
+
+ if (!sawAudioInputEos) {
+ // skip audio, still need to advance the audio extractor
+ AMediaExtractor_advance(audioExtractor);
+ }
+
+ if (!sawVideoOutputEos) {
+ sawVideoOutputEos = drainDecoder(videoCodec, videoPresentationTimeUs,
+ &videoStartTimeNano);
+ }
+
+ clock_gettime(CLOCK_MONOTONIC, &timeSpec);
+ if (timeSpec.tv_sec >= static_cast<time_t>(
+ (startTimeSec + kPlayTimeSeconds))) {
+ // stop reading samples and drain the output buffers
+ sawAudioInputEos = sawVideoInputEos = true;
+ sawAudioOutputEos = true; // ignore audio
+ }
+ }
+
+ if (audioCodec) {
+ AMediaCodec_stop(audioCodec);
+ AMediaCodec_delete(audioCodec);
+ }
+ if (videoCodec) {
+ AMediaCodec_stop(videoCodec);
+ AMediaCodec_delete(videoCodec);
+ }
+
+ AMediaCrypto_delete(crypto);
+ ANativeWindow_release(window);
+ return JNI_TRUE;
+}
+
+static void listener(
+ AMediaDrm* /*drm*/, const AMediaDrmSessionId* /*sessionId*/,
+ AMediaDrmEventType eventType,
+ int /*extra*/, const uint8_t* /*data*/, size_t /*dataSize*/) {
+
+ switch (eventType) {
+ case EVENT_PROVISION_REQUIRED:
+ ALOGD("EVENT_PROVISION_REQUIRED received");
+ break;
+ case EVENT_KEY_REQUIRED:
+ ALOGD("EVENT_KEY_REQUIRED received");
+ break;
+ case EVENT_KEY_EXPIRED:
+ ALOGD("EVENT_KEY_EXPIRED received");
+ break;
+ case EVENT_VENDOR_DEFINED:
+ gGotVendorDefinedEvent = true;
+ ALOGD("EVENT_VENDOR_DEFINED received");
+ break;
+ default:
+ ALOGD("Unknown event received");
+ break;
+ }
+}
+
+extern "C" jboolean Java_android_media_cts_NativeClearKeySystemTest_testClearKeyPlaybackNative(
+ JNIEnv* env, jclass /*clazz*/, jbyteArray uuid, jobject playbackParams) {
+ if (NULL == uuid || NULL == playbackParams) {
+ jniThrowException(env, "java/lang/NullPointerException",
+ "null uuid or null playback parameters");
+ return JNI_FALSE;
+ }
+
+ Uuid juuid = jbyteArrayToUuid(env, uuid);
+ if (!isUuidSizeValid(juuid)) {
+ jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+ "invalid UUID size, expected %u bytes", kUuidSize);
+ return JNI_FALSE;
+ }
+
+ PlaybackParams params;
+ initPlaybackParams(env, playbackParams, params);
+
+ AMediaObjects aMediaObjects;
+ media_status_t status = AMEDIA_OK;
+ aMediaObjects.setDrm(AMediaDrm_createByUUID(&juuid[0]));
+ if (NULL == aMediaObjects.getDrm()) {
+ jniThrowException(env, "java/lang/RuntimeException", "null MediaDrm");
+ return JNI_FALSE;
+ }
+
+ status = AMediaDrm_setOnEventListener(aMediaObjects.getDrm(), listener);
+ if (status != AMEDIA_OK) {
+ jniThrowException(env, "java/lang/RuntimeException",
+ "setOnEventListener failed");
+ return JNI_FALSE;
+ }
+
+ aMediaObjects.setAudioExtractor(AMediaExtractor_new());
+ const char* url = env->GetStringUTFChars(params.audioUrl, 0);
+ if (url) {
+ status = AMediaExtractor_setDataSource(
+ aMediaObjects.getAudioExtractor(), url);
+ env->ReleaseStringUTFChars(params.audioUrl, url);
+
+ if (status != AMEDIA_OK) {
+ jniThrowExceptionFmt(env, "java/lang/RuntimeException",
+ "set audio data source error=%d", status);
+ return JNI_FALSE;
+ }
+ }
+
+ aMediaObjects.setVideoExtractor(AMediaExtractor_new());
+ url = env->GetStringUTFChars(params.videoUrl, 0);
+ if (url) {
+ status = AMediaExtractor_setDataSource(
+ aMediaObjects.getVideoExtractor(), url);
+ env->ReleaseStringUTFChars(params.videoUrl, url);
+
+ if (status != AMEDIA_OK) {
+ jniThrowExceptionFmt(env, "java/lang/RuntimeException",
+ "set video data source error=%d", status);
+ return JNI_FALSE;
+ }
+ }
+
+ AMediaDrmSessionId sessionId;
+ status = AMediaDrm_openSession(aMediaObjects.getDrm(), &sessionId);
+ if (status != AMEDIA_OK) {
+ jniThrowException(env, "java/lang/RuntimeException",
+ "openSession failed");
+ return JNI_FALSE;
+ }
+
+ // Pointer to keyRequest memory, which remains until the next
+ // AMediaDrm_getKeyRequest call or until the drm object is released.
+ const uint8_t* keyRequest;
+ size_t keyRequestSize = 0;
+
+ // The server recognizes "video/mp4" but not "video/avc".
+ status = AMediaDrm_getKeyRequest(aMediaObjects.getDrm(), &sessionId,
+ kClearkeyPssh, sizeof(kClearkeyPssh),
+ "video/mp4" /*mimeType*/, KEY_TYPE_STREAMING,
+ NULL, 0, &keyRequest, &keyRequestSize);
+ if (status != AMEDIA_OK) {
+ jniThrowExceptionFmt(env, "java/lang/RuntimeException",
+ "getKeyRequest failed, error = %d", status);
+ AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
+ return JNI_FALSE;
+ }
+
+ if (kKeyRequestSize != keyRequestSize) {
+ ALOGE("Invalid keyRequestSize %zd", keyRequestSize);
+ return JNI_FALSE;
+ }
+
+ if (memcmp(kKeyRequestData, keyRequest, kKeyRequestSize) != 0) {
+ ALOGE("Invalid keyRequest data is returned");
+ return JNI_FALSE;
+ }
+
+ AMediaDrmKeySetId keySetId;
+ gGotVendorDefinedEvent = false;
+ status = AMediaDrm_provideKeyResponse(aMediaObjects.getDrm(), &sessionId,
+ reinterpret_cast<const uint8_t*>(kResponse),
+ sizeof(kResponse), &keySetId);
+ if (status != AMEDIA_OK) {
+ jniThrowExceptionFmt(env, "java/lang/RuntimeException",
+ "provideKeyResponse failed, error = %d", status);
+ AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
+ return JNI_FALSE;
+ }
+
+ // Check if the event listener has received the expected event sent by
+ // provideKeyResponse. This is for testing AMediaDrm_setOnEventListener().
+ const char *utf8_outValue = NULL;
+ status = AMediaDrm_getPropertyString(aMediaObjects.getDrm(),
+ "listenerTestSupport", &utf8_outValue);
+ if (status == AMEDIA_OK && NULL != utf8_outValue) {
+ std::string eventType(utf8_outValue);
+ if (eventType.compare("true") == 0) {
+ int count = 0;
+ while (!gGotVendorDefinedEvent && count++ < 5) {
+ // Prevents race condition when the event arrives late
+ usleep(1000);
+ }
+ if (!gGotVendorDefinedEvent) {
+ ALOGE("Event listener did not receive the expected event.");
+ jniThrowExceptionFmt(env, "java/lang/RuntimeException",
+ "Event listener did not receive the expected event.");
+ AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
+ return JNI_FALSE;
+ }
+ }
+ }
+
+ playContent(env, aMediaObjects, params, sessionId, juuid);
+
+ status = AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
+ if (status != AMEDIA_OK) {
+ jniThrowException(env, "java/lang/RuntimeException",
+ "closeSession failed");
+ return JNI_FALSE;
+ }
+ return JNI_TRUE;
+}
+
+static JNINativeMethod gMethods[] = {
+ { "isCryptoSchemeSupportedNative", "([B)Z",
+ (void *)Java_android_media_cts_NativeClearKeySystemTest_isCryptoSchemeSupportedNative },
+
+ { "testClearKeyPlaybackNative",
+ "([BLandroid/media/cts/NativeClearKeySystemTest$PlaybackParams;)Z",
+ (void *)Java_android_media_cts_NativeClearKeySystemTest_testClearKeyPlaybackNative },
+
+ { "testGetPropertyStringNative",
+ "([BLjava/lang/String;Ljava/lang/StringBuffer;)Z",
+ (void *)Java_android_media_cts_NativeClearKeySystemTest_testGetPropertyStringNative },
+
+ { "testPsshNative", "([BLjava/lang/String;)Z",
+ (void *)Java_android_media_cts_NativeClearKeySystemTest__testPsshNative },
+};
+
+int register_android_media_cts_NativeClearKeySystemTest(JNIEnv* env) {
+ jint result = JNI_ERR;
+ jclass testClass =
+ env->FindClass("android/media/cts/NativeClearKeySystemTest");
+ if (testClass) {
+ jclass playbackParamsClass = env->FindClass(
+ "android/media/cts/NativeClearKeySystemTest$PlaybackParams");
+ if (playbackParamsClass) {
+ jclass surfaceClass =
+ env->FindClass("android/view/Surface");
+ if (surfaceClass) {
+ gFieldIds.surface = env->GetFieldID(playbackParamsClass,
+ "surface", "Landroid/view/Surface;");
+ } else {
+ gFieldIds.surface = NULL;
+ }
+ gFieldIds.mimeType = env->GetFieldID(playbackParamsClass,
+ "mimeType", "Ljava/lang/String;");
+ gFieldIds.audioUrl = env->GetFieldID(playbackParamsClass,
+ "audioUrl", "Ljava/lang/String;");
+ gFieldIds.videoUrl = env->GetFieldID(playbackParamsClass,
+ "videoUrl", "Ljava/lang/String;");
+ } else {
+ ALOGE("PlaybackParams class not found");
+ }
+
+ } else {
+ ALOGE("NativeClearKeySystemTest class not found");
+ }
+
+ result = env->RegisterNatives(testClass, gMethods,
+ sizeof(gMethods) / sizeof(JNINativeMethod));
+ return result;
+}
diff --git a/tests/tests/media/src/android/media/cts/ConnectionStatus.java b/tests/tests/media/src/android/media/cts/ConnectionStatus.java
index 407e553..37fc75e 100644
--- a/tests/tests/media/src/android/media/cts/ConnectionStatus.java
+++ b/tests/tests/media/src/android/media/cts/ConnectionStatus.java
@@ -132,14 +132,6 @@
}
public void testConnection(Uri uri) {
- final String GOOG = "www.google.com";
-
- if (pingTest(GOOG)) {
- Log.d(TAG, "Successfully pinged " + GOOG);
- } else {
- Log.e(TAG, "Failed to ping " + GOOG);
- }
-
if (pingTest(uri.getHost())) {
Log.d(TAG, "Successfully pinged " + uri.getHost());
} else {
diff --git a/tests/tests/media/src/android/media/cts/NativeClearKeySystemTest.java b/tests/tests/media/src/android/media/cts/NativeClearKeySystemTest.java
new file mode 100644
index 0000000..1d4500f
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/NativeClearKeySystemTest.java
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2016 The Android Open 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 org.junit.Assert.assertThat;
+import static org.junit.matchers.JUnitMatchers.containsString;
+
+import android.cts.util.MediaUtils;
+import android.net.Uri;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+
+import com.google.android.collect.Lists;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.UUID;
+
+/**
+ * Tests MediaDrm NDK APIs. ClearKey system uses a subset of NDK APIs,
+ * this test only tests the APIs that are supported by ClearKey system.
+ */
+public class NativeClearKeySystemTest extends MediaPlayerTestBase {
+ private static final String TAG = NativeClearKeySystemTest.class.getSimpleName();
+
+ private static final int CONNECTION_RETRIES = 10;
+ private static final int VIDEO_WIDTH_CENC = 1280;
+ private static final int VIDEO_HEIGHT_CENC = 720;
+ private static final String ISO_BMFF_VIDEO_MIME_TYPE = "video/avc";
+ private static final String ISO_BMFF_AUDIO_MIME_TYPE = "audio/avc";
+ private static final Uri CENC_AUDIO_URL = Uri.parse(
+ "http://yt-dash-mse-test.commondatastorage.googleapis.com/media/" +
+ "car_cenc-20120827-8c.mp4");
+ private static final Uri CENC_CLEARKEY_VIDEO_URL = Uri.parse(
+ "http://yt-dash-mse-test.commondatastorage.googleapis.com/media/" +
+ "car_cenc-20120827-88.mp4");
+
+ private static final int UUID_BYTE_SIZE = 16;
+ private static final UUID CLEARKEY_SCHEME_UUID =
+ new UUID(0x1077efecc0b24d02L, 0xace33c1e52e2fb4bL);
+ private static final UUID BAD_SCHEME_UUID =
+ new UUID(0xffffffffffffffffL, 0xffffffffffffffffL);
+ private MediaCodecClearKeyPlayer mMediaCodecPlayer;
+
+ static {
+ try {
+ System.loadLibrary("ctsmediadrm_jni");
+ } catch (UnsatisfiedLinkError e) {
+ Log.e(TAG, "NativeClearKeySystemTest: Error loading JNI library");
+ e.printStackTrace();
+ }
+ try {
+ System.loadLibrary("mediandk");
+ } catch (UnsatisfiedLinkError e) {
+ Log.e(TAG, "NativeClearKeySystemTest: Error loading JNI library");
+ e.printStackTrace();
+ }
+ }
+
+ public static class PlaybackParams {
+ public Surface surface;
+ public String mimeType;
+ public String audioUrl;
+ public String videoUrl;
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ if (false == deviceHasMediaDrm()) {
+ tearDown();
+ }
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ private boolean deviceHasMediaDrm() {
+ // ClearKey is introduced after KitKat.
+ if (android.os.Build.VERSION.SDK_INT <= android.os.Build.VERSION_CODES.KITKAT) {
+ Log.i(TAG, "This test is designed to work after Android KitKat.");
+ return false;
+ }
+ return true;
+ }
+
+ private static final byte[] uuidByteArray(UUID uuid) {
+ ByteBuffer buffer = ByteBuffer.wrap(new byte[UUID_BYTE_SIZE]);
+ buffer.putLong(uuid.getMostSignificantBits());
+ buffer.putLong(uuid.getLeastSignificantBits());
+ return buffer.array();
+ }
+
+ public void testIsCryptoSchemeSupported() throws Exception {
+ assertTrue(isCryptoSchemeSupportedNative(uuidByteArray(CLEARKEY_SCHEME_UUID)));
+ }
+
+ public void testIsCryptoSchemeNotSupported() throws Exception {
+ assertFalse(isCryptoSchemeSupportedNative(uuidByteArray(BAD_SCHEME_UUID)));
+ }
+
+ public void testPssh() throws Exception {
+ assertTrue(testPsshNative(uuidByteArray(CLEARKEY_SCHEME_UUID),
+ CENC_CLEARKEY_VIDEO_URL.toString()));
+ }
+
+ public void testGetPropertyString() throws Exception {
+ StringBuffer value = new StringBuffer();
+ testGetPropertyStringNative(uuidByteArray(CLEARKEY_SCHEME_UUID), "description", value);
+ assertEquals("ClearKey CDM", value.toString());
+ }
+
+ public void testUnknownPropertyString() throws Exception {
+ try {
+ StringBuffer value = new StringBuffer();
+ testGetPropertyStringNative(uuidByteArray(CLEARKEY_SCHEME_UUID),
+ "unknown-property", value);
+ } catch (RuntimeException e) {
+ Log.e(TAG, "testUnknownPropertyString error = '" + e.getMessage() + "'");
+ assertThat(e.getMessage(), containsString("get property string returns"));
+ }
+ }
+
+ /**
+ * Tests native clear key system playback.
+ */
+ private void testClearKeyPlayback(
+ String mimeType, /*String initDataType,*/ Uri audioUrl, Uri videoUrl,
+ int videoWidth, int videoHeight) throws Exception {
+
+ if (!isCryptoSchemeSupportedNative(uuidByteArray(CLEARKEY_SCHEME_UUID))) {
+ throw new Error("Crypto scheme is not supported.");
+ }
+
+ IConnectionStatus connectionStatus = new ConnectionStatus(mContext);
+ if (!connectionStatus.isAvailable()) {
+ throw new Error("Network is not available, reason: " +
+ connectionStatus.getNotConnectedReason());
+ }
+
+ // If device is not online, recheck the status a few times.
+ int retries = 0;
+ while (!connectionStatus.isConnected()) {
+ if (retries++ >= CONNECTION_RETRIES) {
+ throw new Error("Device is not online, reason: " +
+ connectionStatus.getNotConnectedReason());
+ }
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ // do nothing
+ }
+ }
+ connectionStatus.testConnection(videoUrl);
+
+ if (!MediaUtils.checkCodecsForPath(mContext, videoUrl.getPath())) {
+ Log.i(TAG, "Device does not support " +
+ videoWidth + "x" + videoHeight + " resolution for " + mimeType);
+ return; // skip
+ }
+
+ // set to true if modify isVersionSmaller()
+ if (false)
+ unitTestIsVersionSmaller();
+
+ // This test requires two changes in frameworks/av (go/av/1628977 and
+ // go/ag/1598174) that are in 7.1.2 and above.
+ // Version 8 and above does not need this check.
+ if (isVersionSmaller(android.os.Build.VERSION.RELEASE, "7.1.2")) {
+ Log.i(TAG, "This test requires android \"7.1.2\" or higher.");
+ Log.i(TAG, "This device is running \"" +
+ android.os.Build.VERSION.RELEASE + "\".");
+ return; // skip
+ }
+ PlaybackParams params = new PlaybackParams();
+ params.surface = mActivity.getSurfaceHolder().getSurface();
+ params.mimeType = mimeType;
+ params.audioUrl = audioUrl.toString();
+ params.videoUrl = videoUrl.toString();
+
+ if (!testClearKeyPlaybackNative(
+ uuidByteArray(CLEARKEY_SCHEME_UUID), params)) {
+ Log.e(TAG, "Fails play back using native media drm APIs.");
+ }
+ params.surface.release();
+ }
+
+ private void unitTestIsVersionSmaller() {
+ assertTrue(isVersionSmaller("6.9", "7.1.2"));
+ assertTrue(isVersionSmaller("7.1", "7.1.2"));
+ assertTrue(isVersionSmaller("7.1.1", "7.1.2"));
+ assertTrue(isVersionSmaller("7.1.1.4", "7.1.2"));
+ assertFalse(isVersionSmaller("7.1.2", "7.1.2"));
+ assertFalse(isVersionSmaller("8.0", "7.1.2"));
+ assertFalse(isVersionSmaller("8.1.2", "7.1.2"));
+ }
+
+ private ArrayList<Integer> intVersion(String version) {
+ String versions[] = version.split("\\.");
+
+ ArrayList<Integer> versionNumbers = Lists.newArrayList();
+ for (String subVersion : versions) {
+ versionNumbers.add(Integer.parseInt(subVersion));
+ }
+ return versionNumbers;
+ }
+
+ /**
+ * Return true if smaller, return false if great than or equal to the
+ * target version.
+ */
+ private boolean isVersionSmaller(String testVersion, String targetVersion) {
+ ArrayList<Integer> intTestVersion = intVersion(testVersion);
+ ArrayList<Integer> intTargetVersion = intVersion(targetVersion);
+
+ Iterator itr = intTestVersion.iterator();
+ for (int targetNumber : intTargetVersion) {
+ if (itr.hasNext()) {
+ int testNumber = (int) itr.next();
+ if (testNumber == targetNumber) {
+ continue;
+ } else {
+ return testNumber < targetNumber;
+ }
+ } else {
+ // treat test version as 0
+ return 0 != targetNumber;
+ }
+ }
+ return false;
+ }
+
+ private static native boolean isCryptoSchemeSupportedNative(final byte[] uuid);
+
+ private static native boolean testClearKeyPlaybackNative(final byte[] uuid,
+ PlaybackParams params);
+
+ private static native boolean testGetPropertyStringNative(final byte[] uuid,
+ final String name, StringBuffer value);
+
+ private static native boolean testPsshNative(final byte[] uuid, final String videoUrl);
+
+ public void testClearKeyPlaybackCenc() throws Exception {
+ testClearKeyPlayback(
+ ISO_BMFF_VIDEO_MIME_TYPE,
+ CENC_AUDIO_URL,
+ CENC_CLEARKEY_VIDEO_URL,
+ VIDEO_WIDTH_CENC, VIDEO_HEIGHT_CENC);
+ }
+}
diff --git a/tests/tests/os/Android.mk b/tests/tests/os/Android.mk
index 5397fc6a..4dc87b1 100644
--- a/tests/tests/os/Android.mk
+++ b/tests/tests/os/Android.mk
@@ -48,3 +48,34 @@
include $(BUILD_CTS_PACKAGE)
include $(call all-makefiles-under,$(LOCAL_PATH))
+
+# platform version check (b/32056228)
+# ============================================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := cts-platform-version-check
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts
+
+cts_platform_version_path := cts/tests/tests/os/assets/platform_versions.txt
+cts_platform_version_string := $(shell cat $(cts_platform_version_path))
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE) : $(cts_platform_version_path) build/core/version_defaults.mk
+ $(hide) if [ -z "$(findstring $(PLATFORM_VERSION),$(cts_platform_version_string))" ]; then \
+ echo "============================================================" 1>&2; \
+ echo "Could not find version \"$(PLATFORM_VERSION)\" in CTS platform version file:" 1>&2; \
+ echo "" 1>&2; \
+ echo " $(cts_platform_version_path)" 1>&2; \
+ echo "" 1>&2; \
+ echo "Most likely PLATFORM_VERSION in build/core/version_defaults.mk" 1>&2; \
+ echo "has changed and a new version must be added to this CTS file." 1>&2; \
+ echo "============================================================" 1>&2; \
+ exit 1; \
+ fi
+ @mkdir -p $(dir $@)
+ echo $(cts_platform_version_string) > $@
diff --git a/tests/tests/os/assets/platform_versions.txt b/tests/tests/os/assets/platform_versions.txt
new file mode 100644
index 0000000..6f9c237
--- /dev/null
+++ b/tests/tests/os/assets/platform_versions.txt
@@ -0,0 +1,3 @@
+7.1
+7.1.1
+7.1.2
diff --git a/tests/tests/os/src/android/os/cts/BuildVersionTest.java b/tests/tests/os/src/android/os/cts/BuildVersionTest.java
index e39249b..d06171b 100644
--- a/tests/tests/os/src/android/os/cts/BuildVersionTest.java
+++ b/tests/tests/os/src/android/os/cts/BuildVersionTest.java
@@ -16,10 +16,16 @@
package android.os.cts;
+import android.content.res.AssetManager;
import android.os.Build;
import android.platform.test.annotations.RestrictedBuildTest;
+import android.support.test.InstrumentationRegistry;
import android.util.Log;
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
@@ -29,17 +35,16 @@
public class BuildVersionTest extends TestCase {
private static final String LOG_TAG = "BuildVersionTest";
- private static final Set<String> EXPECTED_RELEASES =
- new HashSet<String>(Arrays.asList("7.1","7.1.1","7.1.2"));
private static final int EXPECTED_SDK = 25;
private static final String EXPECTED_BUILD_VARIANT = "user";
private static final String EXPECTED_TAG = "release-keys";
+ private static final String PLATFORM_VERSIONS_FILE = "platform_versions.txt";
@SuppressWarnings("deprecation")
@RestrictedBuildTest
public void testReleaseVersion() {
// Applications may rely on the exact release version
- assertAnyOf("BUILD.VERSION.RELEASE", Build.VERSION.RELEASE, EXPECTED_RELEASES);
+ assertAnyOf("BUILD.VERSION.RELEASE", Build.VERSION.RELEASE, getExpectedReleases());
assertEquals("Build.VERSION.SDK", "" + EXPECTED_SDK, Build.VERSION.SDK);
assertEquals("Build.VERSION.SDK_INT", EXPECTED_SDK, Build.VERSION.SDK_INT);
}
@@ -94,4 +99,20 @@
", should be one of: " + permittedValues);
}
}
+
+ private Set<String> getExpectedReleases() {
+ Set<String> expectedReleases = new HashSet<String>();
+ final AssetManager assets =
+ InstrumentationRegistry.getInstrumentation().getTargetContext().getAssets();
+ String line;
+ try (BufferedReader br =
+ new BufferedReader(new InputStreamReader(assets.open(PLATFORM_VERSIONS_FILE)))) {
+ while ((line = br.readLine()) != null) {
+ expectedReleases.add(line);
+ }
+ } catch (IOException e) {
+ fail("Could not open file " + PLATFORM_VERSIONS_FILE + " to run test");
+ }
+ return expectedReleases;
+ }
}
diff --git a/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java b/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
index 277198e..575b35e 100644
--- a/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
@@ -538,6 +538,7 @@
"/data/mdl",
"/data/misc",
"/data/misc/bluetooth",
+ "/data/misc/bluetooth/logs",
"/data/misc/dhcp",
"/data/misc/lockscreen",
"/data/misc/sensor",
diff --git a/tests/tests/security/Android.mk b/tests/tests/security/Android.mk
index f2b4470..96e095a 100644
--- a/tests/tests/security/Android.mk
+++ b/tests/tests/security/Android.mk
@@ -26,33 +26,13 @@
LOCAL_JAVA_LIBRARIES := android.test.runner org.apache.http.legacy
LOCAL_JNI_SHARED_LIBRARIES := libctssecurity_jni libcts_jni libnativehelper_compat_libc++ \
- libnativehelper \
- libbinder \
- libutils \
- libmedia \
- libselinux \
- libcutils \
- libcrypto \
- libc++ \
- libbacktrace \
- libui \
- libsonivox \
- libexpat \
- libcamera_client \
- libgui \
- libaudioutils \
- libnbaio \
- libpcre \
- libpackagelistparser \
- libpowermanager \
- libbase \
- libunwind \
- libhardware \
- libsync \
- libcamera_metadata \
- libspeexresampler \
- liblzma \
- libstagefright_foundation
+ libnativehelper \
+ libcutils \
+ libcrypto \
+ libselinux \
+ libc++ \
+ libpcre \
+ libpackagelistparser
LOCAL_SRC_FILES := $(call all-java-files-under, src)\
src/android/security/cts/activity/ISecureRandomService.aidl\
diff --git a/tests/tests/security/jni/Android.mk b/tests/tests/security/jni/Android.mk
index d39ac7e..cbfbe3a 100644
--- a/tests/tests/security/jni/Android.mk
+++ b/tests/tests/security/jni/Android.mk
@@ -31,37 +31,16 @@
android_security_cts_MMapExecutableTest.cpp \
android_security_cts_EncryptionTest.cpp \
-LOCAL_C_INCLUDES := $(JNI_H_INCLUDE) \
- $(TOP)/frameworks/native/include/media/openmax
-
-LOCAL_SHARED_LIBRARIES := libnativehelper \
+LOCAL_SHARED_LIBRARIES := \
+ libnativehelper \
liblog \
- libutils \
- libmedia \
- libselinux \
- libdl \
libcutils \
libcrypto \
+ libselinux \
libc++ \
- libbacktrace \
- libui \
- libsonivox \
- libexpat \
- libcamera_client \
- libgui \
- libaudioutils \
- libnbaio \
libpcre \
libpackagelistparser \
- libpowermanager \
- libbase \
- libunwind \
- libhardware \
- libsync \
- libcamera_metadata \
- libspeexresampler \
- liblzma \
- libstagefright_foundation
+
LOCAL_C_INCLUDES += ndk/sources/cpufeatures
LOCAL_STATIC_LIBRARIES := cpufeatures
diff --git a/tests/tests/toastlegacy/src/android/widget/toast/cts/legacy/ToastTest.java b/tests/tests/toastlegacy/src/android/widget/toast/cts/legacy/ToastTest.java
index 207e6ea..c2ecb3a 100644
--- a/tests/tests/toastlegacy/src/android/widget/toast/cts/legacy/ToastTest.java
+++ b/tests/tests/toastlegacy/src/android/widget/toast/cts/legacy/ToastTest.java
@@ -108,7 +108,10 @@
}
@Test
- public void testAddTwoToastsViaAddingWindowApisWhenUidFocusedQuickly() throws Exception {
+ public void testAddTwoToastsViaAddingWindowApisWhenUidNotFocusedQuickly() throws Exception {
+ // Finish the activity so the UID loses focus
+ finishActivity(false);
+
try {
showToastsViaAddingWindow(2, false);
Assert.fail("Only one custom toast window at a time should be allowed");
@@ -120,6 +123,14 @@
}
@Test
+ public void testAddTwoToastsViaAddingWindowApisWhenUidFocusedQuickly() throws Exception {
+ showToastsViaAddingWindow(2, false);
+
+ // Wait for the toast to timeout
+ waitForToastTimeout();
+ }
+
+ @Test
public void testAddTwoToastsViaAddingWindowApisWhenUidFocusedSlowly() throws Exception {
// Add one window
showToastsViaAddingWindow(1, true);
diff --git a/tests/tests/widget/res/layout/popup_window_scrollable.xml b/tests/tests/widget/res/layout/popup_window_scrollable.xml
new file mode 100644
index 0000000..aa1edd6
--- /dev/null
+++ b/tests/tests/widget/res/layout/popup_window_scrollable.xml
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open 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.
+-->
+
+<ScrollView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <RelativeLayout
+ android:id="@+id/main_container"
+ android:layout_width="10000dp"
+ android:layout_height="10000dp">
+
+ <View
+ android:id="@+id/anchor_upper_left"
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:background="#f00" />
+
+ <View
+ android:id="@+id/anchor_upper"
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:layout_centerHorizontal="true"
+ android:layout_alignParentTop="true"
+ android:background="#f00" />
+
+ <View
+ android:id="@+id/anchor_upper_right"
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentTop="true"
+ android:background="#f00" />
+
+ <View
+ android:id="@+id/anchor_middle_left"
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:layout_alignParentLeft="true"
+ android:layout_centerVertical="true"
+ android:background="#0f0" />
+
+ <View
+ android:id="@+id/anchor_middle"
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:layout_centerHorizontal="true"
+ android:layout_centerVertical="true"
+ android:background="#0f0" />
+
+ <View
+ android:id="@+id/anchor_middle_right"
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:layout_alignParentRight="true"
+ android:layout_centerVertical="true"
+ android:background="#0f0" />
+
+ <View
+ android:id="@+id/anchor_lower_left"
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentBottom="true"
+ android:background="#00f" />
+
+ <View
+ android:id="@+id/anchor_lower"
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:layout_centerHorizontal="true"
+ android:layout_alignParentBottom="true"
+ android:background="#00f" />
+
+ <View
+ android:id="@+id/anchor_lower_right"
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentBottom="true"
+ android:background="#00f" />
+
+ </RelativeLayout>
+
+</ScrollView>
\ No newline at end of file
diff --git a/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java b/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java
index 47efffc..918a161 100644
--- a/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java
+++ b/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java
@@ -16,6 +16,14 @@
package android.widget.cts;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
import android.app.Activity;
import android.app.Instrumentation;
import android.content.Context;
@@ -44,13 +52,6 @@
import android.widget.TextView;
import android.widget.cts.R;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
public class PopupWindowTest extends
ActivityInstrumentationTestCase2<PopupWindowCtsActivity> {
private Instrumentation mInstrumentation;
@@ -772,7 +773,19 @@
WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM & p.flags);
}
- public void testEnterExitTransition() {
+ public void testEnterExitTransitionAsDropDown() throws Throwable {
+ final View anchorView = mActivity.findViewById(R.id.anchor_upper);
+ verifyEnterExitTransition(
+ () -> mPopupWindow.showAsDropDown(anchorView, 0, 0));
+ }
+
+ public void testEnterExitTransitionAtLocation() throws Throwable {
+ final View anchorView = mActivity.findViewById(R.id.anchor_upper);
+ verifyEnterExitTransition(
+ () -> mPopupWindow.showAtLocation(anchorView, Gravity.BOTTOM, 0, 0));
+ }
+
+ private void verifyEnterExitTransition(Runnable showRunnable) throws Throwable {
TransitionListener enterListener = mock(TransitionListener.class);
Transition enterTransition = new BaseTransition();
enterTransition.addListener(enterListener);
@@ -791,8 +804,7 @@
verify(exitListener, never()).onTransitionStart(any(Transition.class));
verify(dismissListener, never()).onDismiss();
- final View anchorView = mActivity.findViewById(R.id.anchor_upper);
- mInstrumentation.runOnMainSync(() -> mPopupWindow.showAsDropDown(anchorView, 0, 0));
+ mInstrumentation.runOnMainSync(showRunnable);
mInstrumentation.waitForIdleSync();
verify(enterListener, times(1)).onTransitionStart(any(Transition.class));
verify(exitListener, never()).onTransitionStart(any(Transition.class));
@@ -1069,6 +1081,30 @@
assertEquals(LayoutParams.MATCH_PARENT, p.height);
}
+ public void testPositionAfterParentScroll() {
+ View.OnScrollChangeListener scrollChangeListener = mock(
+ View.OnScrollChangeListener.class);
+
+ getInstrumentation().runOnMainSync(() -> {
+ mActivity.setContentView(R.layout.popup_window_scrollable);
+
+ View anchor = mActivity.findViewById(R.id.anchor_upper);
+ PopupWindow window = createPopupWindow();
+ window.showAsDropDown(anchor);
+ });
+
+ getInstrumentation().runOnMainSync(() -> {
+ View parent = mActivity.findViewById(R.id.main_container);
+ parent.scrollBy(0, 500);
+ parent.setOnScrollChangeListener(scrollChangeListener);
+ });
+
+ getInstrumentation().waitForIdleSync();
+
+ verify(scrollChangeListener, never()).onScrollChange(
+ any(View.class), anyInt(), anyInt(), anyInt(), anyInt());
+ }
+
private static class BaseTransition extends Transition {
@Override
public void captureStartValues(TransitionValues transitionValues) {}
diff --git a/tests/vr/src/android/vr/cts/VrDisplayTest.java b/tests/vr/src/android/vr/cts/VrDisplayTest.java
index 9dfdf96..b26f4ed 100644
--- a/tests/vr/src/android/vr/cts/VrDisplayTest.java
+++ b/tests/vr/src/android/vr/cts/VrDisplayTest.java
@@ -52,18 +52,20 @@
*/
public void testRefreshRateIsAtLeast60Hz() throws Throwable {
final int NUM_FRAMES = 200;
- mActivity = getGlEsActivity(NUM_FRAMES, 3);
+ // Add an extra frame to allow the activity to start up.
+ mActivity = getGlEsActivity(NUM_FRAMES + 1, 3);
if (!mActivity.supportsVrHighPerformance())
return;
- long startNanos = System.nanoTime();
+ // Skip the first frame to allow for startup time.
+ mActivity.waitForFrameDrawn();
// Render a few hundred frames.
- int error;
+ long startNanos = System.nanoTime();
while (!mActivity.waitForFrameDrawn());
- error = mActivity.glGetError();
- assertEquals(GLES32.GL_NO_ERROR, error);
long endNanos = System.nanoTime();
+ int error = mActivity.glGetError();
+ assertEquals(GLES32.GL_NO_ERROR, error);
double fps = NUM_FRAMES / (double)(endNanos - startNanos) * 1e9;
assertTrue(fps >= 59.);