1 /*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define TAG "NativeMediaDrm"
18
19 #include <utils/Log.h>
20 #include <sys/types.h>
21
22 #include <string>
23 #include <vector>
24
25 #include <assert.h>
26 #include <jni.h>
27 #include <JNIHelp.h>
28
29 #include <android/native_window_jni.h>
30
31 #include "AMediaObjects.h"
32
33 #include "media/NdkMediaCodec.h"
34 #include "media/NdkMediaCrypto.h"
35 #include "media/NdkMediaDrm.h"
36 #include "media/NdkMediaExtractor.h"
37 #include "media/NdkMediaFormat.h"
38 #include "media/NdkMediaMuxer.h"
39
40 typedef std::vector<uint8_t> Uuid;
41
42 struct fields_t {
43 jfieldID surface;
44 jfieldID mimeType;
45 jfieldID audioUrl;
46 jfieldID videoUrl;
47 };
48
49 struct PlaybackParams {
50 jobject surface;
51 jstring mimeType;
52 jstring audioUrl;
53 jstring videoUrl;
54 };
55
56 static fields_t gFieldIds;
57 static bool gGotVendorDefinedEvent = false;
58
59 static const size_t kPlayTimeSeconds = 30;
60 static const size_t kUuidSize = 16;
61
62 static const uint8_t kWidevineUuid[kUuidSize] = {
63 0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6, 0x4a, 0xce,
64 0xa3, 0xc8, 0x27, 0xdc, 0xd5, 0x1d, 0x21, 0xed
65 };
66
67 // The test content is not packaged with clearkey UUID,
68 // we have to use a canned clearkey pssh for the test.
69 static const uint8_t kClearkeyPssh[] = {
70 // BMFF box header (4 bytes size + 'pssh')
71 0x00, 0x00, 0x00, 0x34, 0x70, 0x73, 0x73, 0x68,
72 // full box header (version = 1 flags = 0)
73 0x01, 0x00, 0x00, 0x00,
74 // system id
75 0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02,
76 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b,
77 // number of key ids
78 0x00, 0x00, 0x00, 0x01,
79 // key id
80 0x60, 0x06, 0x1e, 0x01, 0x7e, 0x47, 0x7e, 0x87,
81 0x7e, 0x57, 0xd0, 0x0d, 0x1e, 0xd0, 0x0d, 0x1e,
82 // size of data, must be zero
83 0x00, 0x00, 0x00, 0x00
84 };
85
86 static const uint8_t kKeyRequestData[] = {
87 0x7b, 0x22, 0x6b, 0x69, 0x64,
88 0x73, 0x22, 0x3a, 0x5b, 0x22,
89 0x59, 0x41, 0x59, 0x65, 0x41,
90 0x58, 0x35, 0x48, 0x66, 0x6f,
91 0x64, 0x2b, 0x56, 0x39, 0x41,
92 0x4e, 0x48, 0x74, 0x41, 0x4e,
93 0x48, 0x67, 0x22, 0x5d, 0x2c,
94 0x22, 0x74, 0x79, 0x70, 0x65,
95 0x22, 0x3a, 0x22, 0x74, 0x65,
96 0x6d, 0x70, 0x6f, 0x72, 0x61,
97 0x72, 0x79, 0x22, 0x7d,
98 };
99
100 static const size_t kKeyRequestSize = sizeof(kKeyRequestData);
101
102 // base 64 encoded JSON response string, must not contain padding character '='
103 static const char kResponse[] = "{\"keys\":[{\"kty\":\"oct\"," \
104 "\"kid\":\"YAYeAX5Hfod+V9ANHtANHg\",\"k\":" \
105 "\"GoogleTestKeyBase64ggg\"}]}";
106
isUuidSizeValid(Uuid uuid)107 static bool isUuidSizeValid(Uuid uuid) {
108 return (uuid.size() == kUuidSize);
109 }
110
jbyteArrayToVector(JNIEnv * env,jbyteArray const & byteArray)111 static std::vector<uint8_t> jbyteArrayToVector(
112 JNIEnv* env, jbyteArray const &byteArray) {
113 uint8_t* buffer = reinterpret_cast<uint8_t*>(
114 env->GetByteArrayElements(byteArray, /*is_copy*/NULL));
115 std::vector<uint8_t> vector;
116 for (jsize i = 0; i < env->GetArrayLength(byteArray); ++i) {
117 vector.push_back(buffer[i]);
118 }
119 return vector;
120 }
121
jbyteArrayToUuid(JNIEnv * env,jbyteArray const & uuid)122 static Uuid jbyteArrayToUuid(JNIEnv* env, jbyteArray const &uuid) {
123 Uuid juuid;
124 juuid.resize(0);
125 if (uuid != NULL) {
126 juuid = jbyteArrayToVector(env, uuid);
127 }
128 return juuid;
129 }
130
Java_android_media_cts_NativeClearKeySystemTest_isCryptoSchemeSupportedNative(JNIEnv * env,jclass,jbyteArray uuid)131 extern "C" jboolean Java_android_media_cts_NativeClearKeySystemTest_isCryptoSchemeSupportedNative(
132 JNIEnv* env, jclass /*clazz*/, jbyteArray uuid) {
133
134 if (NULL == uuid) {
135 jniThrowException(env, "java/lang/NullPointerException", "null uuid");
136 return JNI_FALSE;
137 }
138
139 Uuid juuid = jbyteArrayToUuid(env, uuid);
140 if (isUuidSizeValid(juuid)) {
141 return AMediaDrm_isCryptoSchemeSupported(&juuid[0], NULL);
142 } else {
143 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
144 "invalid UUID size, expected %u bytes", kUuidSize);
145 }
146 return JNI_FALSE;
147 }
148
initPlaybackParams(JNIEnv * env,const jobject & playbackParams,PlaybackParams & params)149 void initPlaybackParams(JNIEnv* env, const jobject &playbackParams, PlaybackParams ¶ms) {
150 params.surface = env->GetObjectField(
151 playbackParams, gFieldIds.surface);
152
153 params.mimeType = static_cast<jstring>(env->GetObjectField(
154 playbackParams, gFieldIds.mimeType));
155
156 params.audioUrl = static_cast<jstring>(env->GetObjectField(
157 playbackParams, gFieldIds.audioUrl));
158
159 params.videoUrl = static_cast<jstring>(env->GetObjectField(
160 playbackParams, gFieldIds.videoUrl));
161 }
162
Java_android_media_cts_NativeClearKeySystemTest_testGetPropertyStringNative(JNIEnv * env,jclass clazz,jbyteArray uuid,jstring name,jobject outValue)163 extern "C" jboolean Java_android_media_cts_NativeClearKeySystemTest_testGetPropertyStringNative(
164 JNIEnv* env, jclass clazz, jbyteArray uuid,
165 jstring name, jobject outValue) {
166
167 if (NULL == uuid || NULL == name || NULL == outValue) {
168 jniThrowException(env, "java/lang/NullPointerException",
169 "One or more null input parameters");
170 return JNI_FALSE;
171 }
172
173 Uuid juuid = jbyteArrayToUuid(env, uuid);
174 if (!isUuidSizeValid(juuid)) {
175 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
176 "invalid UUID size, expected %u bytes", kUuidSize);
177 return JNI_FALSE;
178 }
179
180 AMediaObjects aMediaObjects;
181 aMediaObjects.setDrm(AMediaDrm_createByUUID(&juuid[0]));
182 if (NULL == aMediaObjects.getDrm()) {
183 jniThrowException(env, "java/lang/RuntimeException", "null MediaDrm");
184 return JNI_FALSE;
185 }
186
187 const char *utf8_name = env->GetStringUTFChars(name, NULL);
188 const char *utf8_outValue = NULL;
189 media_status_t status = AMediaDrm_getPropertyString(aMediaObjects.getDrm(),
190 utf8_name, &utf8_outValue);
191 env->ReleaseStringUTFChars(name, utf8_name);
192
193 if (NULL != utf8_outValue) {
194 clazz = env->GetObjectClass(outValue);
195 jmethodID mId = env->GetMethodID (clazz, "append",
196 "(Ljava/lang/String;)Ljava/lang/StringBuffer;");
197 jstring outString = env->NewStringUTF(
198 static_cast<const char *>(utf8_outValue));
199 env->CallObjectMethod(outValue, mId, outString);
200 } else {
201 jniThrowExceptionFmt(env, "java/lang/RuntimeException",
202 "get property string returns %d", status);
203 return JNI_FALSE;
204 }
205 return JNI_TRUE;
206 }
207
Java_android_media_cts_NativeClearKeySystemTest__testPsshNative(JNIEnv * env,jclass,jbyteArray uuid,jstring videoUrl)208 extern "C" jboolean Java_android_media_cts_NativeClearKeySystemTest__testPsshNative(
209 JNIEnv* env, jclass /*clazz*/, jbyteArray uuid, jstring videoUrl) {
210
211 if (NULL == uuid || NULL == videoUrl) {
212 jniThrowException(env, "java/lang/NullPointerException",
213 "null uuid or null videoUrl");
214 return JNI_FALSE;
215 }
216
217 Uuid juuid = jbyteArrayToUuid(env, uuid);
218 if (!isUuidSizeValid(juuid)) {
219 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
220 "invalid UUID size, expected %u bytes", kUuidSize);
221 return JNI_FALSE;
222 }
223
224 AMediaObjects aMediaObjects;
225 aMediaObjects.setVideoExtractor(AMediaExtractor_new());
226 const char* url = env->GetStringUTFChars(videoUrl, 0);
227 if (url) {
228 media_status_t status = AMediaExtractor_setDataSource(
229 aMediaObjects.getVideoExtractor(), url);
230 env->ReleaseStringUTFChars(videoUrl, url);
231
232 if (status != AMEDIA_OK) {
233 jniThrowExceptionFmt(env, "java/lang/RuntimeException",
234 "set video data source error=%d", status);
235 return JNI_FALSE;
236 }
237 }
238
239 PsshInfo* psshInfo = AMediaExtractor_getPsshInfo(aMediaObjects.getVideoExtractor());
240 if (psshInfo == NULL) {
241 jniThrowException(env, "java/lang/RuntimeException", "null psshInfo");
242 return JNI_FALSE;
243 }
244
245 jboolean testResult = JNI_FALSE;
246 for (size_t i = 0; i < psshInfo->numentries; i++) {
247 PsshEntry *entry = &psshInfo->entries[i];
248
249 // We do not have clearkey content that contains ClearKey UUID in the
250 // pssh box. So we have to test if it has Widevine UUID instead.
251 // TODO: Replace kWidevineUuid with uuid when test content contains
252 // ClearKey UUID.
253 if (0 == memcmp(entry->uuid, kWidevineUuid, sizeof(entry->uuid))) {
254 aMediaObjects.setCrypto(
255 AMediaCrypto_new(entry->uuid, entry->data, entry->datalen));
256 if (aMediaObjects.getCrypto()) {
257 testResult = JNI_TRUE;
258 } else {
259 ALOGE("Failed to create media crypto=%zd", i);
260 testResult = JNI_FALSE;
261 }
262 break;
263 }
264 }
265 return testResult;
266 }
267
isVideo(const char * mime)268 static bool isVideo(const char* mime) {
269 return !strncmp(mime, "video/", 6) ? true : false;
270 }
271
isAudio(const char * mime)272 static bool isAudio(const char* mime) {
273 return !strncmp(mime, "audio/", 6) ? true : false;
274 }
275
addTrack(const AMediaFormat * format,const char * mime,const AMediaCrypto * crypto,const ANativeWindow * window,AMediaCodec ** codec)276 static void addTrack(const AMediaFormat* format,
277 const char* mime, const AMediaCrypto* crypto,
278 const ANativeWindow* window, AMediaCodec** codec) {
279
280 *codec = AMediaCodec_createDecoderByType(mime);
281 if (codec == NULL) {
282 ALOGE("cannot create codec for %s", mime);
283 return;
284 }
285
286 AMediaCodec_configure(*codec, format,
287 const_cast<ANativeWindow*>(window),
288 const_cast<AMediaCrypto*>(crypto), 0);
289 }
290
addTracks(const AMediaExtractor * extractor,const AMediaCrypto * crypto,const ANativeWindow * window,AMediaCodec ** codec)291 static void addTracks(const AMediaExtractor* extractor,
292 const AMediaCrypto* crypto, const ANativeWindow* window,
293 AMediaCodec** codec) {
294 size_t numTracks = AMediaExtractor_getTrackCount(
295 const_cast<AMediaExtractor*>(extractor));
296 AMediaFormat* trackFormat = NULL;
297 for (size_t i = 0; i < numTracks; ++i) {
298 trackFormat = AMediaExtractor_getTrackFormat(
299 const_cast<AMediaExtractor*>(extractor), i);
300 if (trackFormat) {
301 ALOGV("track %zd format: %s", i,
302 AMediaFormat_toString(trackFormat));
303
304 const char* mime = "";
305 if (!AMediaFormat_getString(
306 trackFormat, AMEDIAFORMAT_KEY_MIME, &mime)) {
307 ALOGE("no mime type");
308 AMediaFormat_delete(trackFormat);
309 return;
310 } else if (isAudio(mime) || isVideo(mime)) {
311 AMediaExtractor_selectTrack(
312 const_cast<AMediaExtractor*>(extractor), i);
313 ALOGV("track %zd codec format: %s", i,
314 AMediaFormat_toString(trackFormat));
315
316 addTrack(trackFormat, mime, crypto, window, codec);
317 AMediaCodec_start(*codec);
318 AMediaCodec_flush(*codec);
319 AMediaExtractor_seekTo(
320 const_cast<AMediaExtractor*>(extractor), 0,
321 AMEDIAEXTRACTOR_SEEK_PREVIOUS_SYNC);
322 }
323 AMediaFormat_delete(trackFormat);
324 }
325 }
326 }
327
getSystemNanoTime()328 static int64_t getSystemNanoTime() {
329 timespec now;
330 clock_gettime(CLOCK_MONOTONIC, &now);
331 return now.tv_sec * 1000000000LL + now.tv_nsec;
332 }
333
fillDecoder(AMediaCodec * codec,AMediaExtractor * extractor,int64_t * presentationTimeUs,bool * eosReached)334 static void fillDecoder(AMediaCodec* codec, AMediaExtractor* extractor,
335 int64_t* presentationTimeUs, bool* eosReached) {
336 media_status_t status = AMEDIA_OK;
337
338 ssize_t bufferIndex = AMediaCodec_dequeueInputBuffer(codec, 2000);
339 if (bufferIndex >= 0) {
340 size_t bufsize;
341 uint8_t* buf = AMediaCodec_getInputBuffer(codec, bufferIndex, &bufsize);
342
343 int sampleSize = AMediaExtractor_readSampleData(extractor, buf, bufsize);
344 if (sampleSize < 0) {
345 sampleSize = 0;
346 *eosReached = true;
347 }
348
349 *presentationTimeUs = AMediaExtractor_getSampleTime(extractor);
350
351 AMediaCodecCryptoInfo *cryptoInfo =
352 AMediaExtractor_getSampleCryptoInfo(extractor);
353 if (cryptoInfo) {
354 status = AMediaCodec_queueSecureInputBuffer(
355 codec, bufferIndex, 0, cryptoInfo,
356 *presentationTimeUs,
357 *eosReached ? AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM : 0);
358 AMediaCodecCryptoInfo_delete(cryptoInfo);
359 } else {
360 status = AMediaCodec_queueInputBuffer(
361 codec, bufferIndex, 0, sampleSize,
362 *presentationTimeUs,
363 *eosReached ? AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM : 0);
364 }
365 AMediaExtractor_advance(extractor);
366 }
367 }
368
drainDecoder(AMediaCodec * codec,int64_t presentationTimeUs,int64_t * startTimeNano)369 static bool drainDecoder(AMediaCodec* codec, int64_t presentationTimeUs,
370 int64_t* startTimeNano) {
371
372 AMediaCodecBufferInfo info;
373 ssize_t bufferIndex = AMediaCodec_dequeueOutputBuffer(codec, &info, 0);
374 if (bufferIndex >= 0) {
375 if (info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
376 return true; // eos reached
377 }
378
379 if (*startTimeNano < 0) {
380 *startTimeNano = getSystemNanoTime() - (presentationTimeUs * 1000);
381 }
382 int64_t delay = (*startTimeNano + presentationTimeUs * 1000) -
383 getSystemNanoTime();
384 if (delay > 0) {
385 usleep(delay / 1000);
386 }
387
388 AMediaCodec_releaseOutputBuffer(codec, bufferIndex, info.size != 0);
389 } else if (bufferIndex == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
390 ALOGV("output buffers changed");
391 } else if (bufferIndex == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
392 AMediaFormat* format = AMediaCodec_getOutputFormat(codec);
393 ALOGV("format changed to: %s", AMediaFormat_toString(format));
394 AMediaFormat_delete(format);
395 } else if (bufferIndex == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
396 ALOGV("no output buffer right now");
397 usleep(20000);
398 } else {
399 ALOGV("unexpected info code: %zd", bufferIndex);
400 }
401 return false;
402 }
403
playContent(JNIEnv * env,const AMediaObjects & aMediaObjects,PlaybackParams & params,const AMediaDrmSessionId & sessionId,Uuid uuid)404 static jboolean playContent(JNIEnv* env, const AMediaObjects& aMediaObjects,
405 PlaybackParams& params, const AMediaDrmSessionId& sessionId, Uuid uuid) {
406
407 ANativeWindow *window = ANativeWindow_fromSurface(env, params.surface);
408 AMediaExtractor* audioExtractor = aMediaObjects.getAudioExtractor();
409 AMediaExtractor* videoExtractor = aMediaObjects.getVideoExtractor();
410
411 AMediaCodec* audioCodec = NULL;
412 AMediaCodec* videoCodec = NULL;
413 AMediaCrypto* crypto = NULL;
414
415 crypto = AMediaCrypto_new(&uuid[0], sessionId.ptr, sessionId.length);
416 if (crypto == NULL) {
417 jniThrowException(env, "java/lang/RuntimeException",
418 "failed to create crypto object");
419 return JNI_FALSE;
420 }
421
422 addTracks(audioExtractor, NULL, NULL, &audioCodec);
423 addTracks(videoExtractor, crypto, window, &videoCodec);
424
425 bool sawAudioInputEos = false;
426 bool sawAudioOutputEos = false;
427 bool sawVideoInputEos = false;
428 bool sawVideoOutputEos = false;
429 int64_t videoPresentationTimeUs = 0;
430 int64_t videoStartTimeNano = -1;
431 struct timespec timeSpec;
432 clock_gettime(CLOCK_MONOTONIC, &timeSpec);
433 time_t startTimeSec = timeSpec.tv_sec;
434
435 while (!sawAudioOutputEos && !sawVideoOutputEos) {
436 if (!sawVideoInputEos) {
437 fillDecoder(videoCodec, videoExtractor,
438 &videoPresentationTimeUs, &sawVideoInputEos);
439 }
440
441 if (!sawAudioInputEos) {
442 // skip audio, still need to advance the audio extractor
443 AMediaExtractor_advance(audioExtractor);
444 }
445
446 if (!sawVideoOutputEos) {
447 sawVideoOutputEos = drainDecoder(videoCodec, videoPresentationTimeUs,
448 &videoStartTimeNano);
449 }
450
451 clock_gettime(CLOCK_MONOTONIC, &timeSpec);
452 if (timeSpec.tv_sec >= static_cast<time_t>(
453 (startTimeSec + kPlayTimeSeconds))) {
454 // stop reading samples and drain the output buffers
455 sawAudioInputEos = sawVideoInputEos = true;
456 sawAudioOutputEos = true; // ignore audio
457 }
458 }
459
460 if (audioCodec) {
461 AMediaCodec_stop(audioCodec);
462 AMediaCodec_delete(audioCodec);
463 }
464 if (videoCodec) {
465 AMediaCodec_stop(videoCodec);
466 AMediaCodec_delete(videoCodec);
467 }
468
469 AMediaCrypto_delete(crypto);
470 ANativeWindow_release(window);
471 return JNI_TRUE;
472 }
473
listener(AMediaDrm *,const AMediaDrmSessionId *,AMediaDrmEventType eventType,int,const uint8_t *,size_t)474 static void listener(
475 AMediaDrm* /*drm*/, const AMediaDrmSessionId* /*sessionId*/,
476 AMediaDrmEventType eventType,
477 int /*extra*/, const uint8_t* /*data*/, size_t /*dataSize*/) {
478
479 switch (eventType) {
480 case EVENT_PROVISION_REQUIRED:
481 ALOGD("EVENT_PROVISION_REQUIRED received");
482 break;
483 case EVENT_KEY_REQUIRED:
484 ALOGD("EVENT_KEY_REQUIRED received");
485 break;
486 case EVENT_KEY_EXPIRED:
487 ALOGD("EVENT_KEY_EXPIRED received");
488 break;
489 case EVENT_VENDOR_DEFINED:
490 gGotVendorDefinedEvent = true;
491 ALOGD("EVENT_VENDOR_DEFINED received");
492 break;
493 default:
494 ALOGD("Unknown event received");
495 break;
496 }
497 }
498
Java_android_media_cts_NativeClearKeySystemTest_testClearKeyPlaybackNative(JNIEnv * env,jclass,jbyteArray uuid,jobject playbackParams)499 extern "C" jboolean Java_android_media_cts_NativeClearKeySystemTest_testClearKeyPlaybackNative(
500 JNIEnv* env, jclass /*clazz*/, jbyteArray uuid, jobject playbackParams) {
501 if (NULL == uuid || NULL == playbackParams) {
502 jniThrowException(env, "java/lang/NullPointerException",
503 "null uuid or null playback parameters");
504 return JNI_FALSE;
505 }
506
507 Uuid juuid = jbyteArrayToUuid(env, uuid);
508 if (!isUuidSizeValid(juuid)) {
509 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
510 "invalid UUID size, expected %u bytes", kUuidSize);
511 return JNI_FALSE;
512 }
513
514 PlaybackParams params;
515 initPlaybackParams(env, playbackParams, params);
516
517 AMediaObjects aMediaObjects;
518 media_status_t status = AMEDIA_OK;
519 aMediaObjects.setDrm(AMediaDrm_createByUUID(&juuid[0]));
520 if (NULL == aMediaObjects.getDrm()) {
521 jniThrowException(env, "java/lang/RuntimeException", "null MediaDrm");
522 return JNI_FALSE;
523 }
524
525 status = AMediaDrm_setOnEventListener(aMediaObjects.getDrm(), listener);
526 if (status != AMEDIA_OK) {
527 jniThrowException(env, "java/lang/RuntimeException",
528 "setOnEventListener failed");
529 return JNI_FALSE;
530 }
531
532 aMediaObjects.setAudioExtractor(AMediaExtractor_new());
533 const char* url = env->GetStringUTFChars(params.audioUrl, 0);
534 if (url) {
535 status = AMediaExtractor_setDataSource(
536 aMediaObjects.getAudioExtractor(), url);
537 env->ReleaseStringUTFChars(params.audioUrl, url);
538
539 if (status != AMEDIA_OK) {
540 jniThrowExceptionFmt(env, "java/lang/RuntimeException",
541 "set audio data source error=%d", status);
542 return JNI_FALSE;
543 }
544 }
545
546 aMediaObjects.setVideoExtractor(AMediaExtractor_new());
547 url = env->GetStringUTFChars(params.videoUrl, 0);
548 if (url) {
549 status = AMediaExtractor_setDataSource(
550 aMediaObjects.getVideoExtractor(), url);
551 env->ReleaseStringUTFChars(params.videoUrl, url);
552
553 if (status != AMEDIA_OK) {
554 jniThrowExceptionFmt(env, "java/lang/RuntimeException",
555 "set video data source error=%d", status);
556 return JNI_FALSE;
557 }
558 }
559
560 AMediaDrmSessionId sessionId;
561 status = AMediaDrm_openSession(aMediaObjects.getDrm(), &sessionId);
562 if (status != AMEDIA_OK) {
563 jniThrowException(env, "java/lang/RuntimeException",
564 "openSession failed");
565 return JNI_FALSE;
566 }
567
568 // Pointer to keyRequest memory, which remains until the next
569 // AMediaDrm_getKeyRequest call or until the drm object is released.
570 const uint8_t* keyRequest;
571 size_t keyRequestSize = 0;
572
573 // The server recognizes "video/mp4" but not "video/avc".
574 status = AMediaDrm_getKeyRequest(aMediaObjects.getDrm(), &sessionId,
575 kClearkeyPssh, sizeof(kClearkeyPssh),
576 "video/mp4" /*mimeType*/, KEY_TYPE_STREAMING,
577 NULL, 0, &keyRequest, &keyRequestSize);
578 if (status != AMEDIA_OK) {
579 jniThrowExceptionFmt(env, "java/lang/RuntimeException",
580 "getKeyRequest failed, error = %d", status);
581 AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
582 return JNI_FALSE;
583 }
584
585 if (kKeyRequestSize != keyRequestSize) {
586 ALOGE("Invalid keyRequestSize %zd", keyRequestSize);
587 return JNI_FALSE;
588 }
589
590 if (memcmp(kKeyRequestData, keyRequest, kKeyRequestSize) != 0) {
591 ALOGE("Invalid keyRequest data is returned");
592 return JNI_FALSE;
593 }
594
595 AMediaDrmKeySetId keySetId;
596 gGotVendorDefinedEvent = false;
597 status = AMediaDrm_provideKeyResponse(aMediaObjects.getDrm(), &sessionId,
598 reinterpret_cast<const uint8_t*>(kResponse),
599 sizeof(kResponse), &keySetId);
600 if (status != AMEDIA_OK) {
601 jniThrowExceptionFmt(env, "java/lang/RuntimeException",
602 "provideKeyResponse failed, error = %d", status);
603 AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
604 return JNI_FALSE;
605 }
606
607 // Check if the event listener has received the expected event sent by
608 // provideKeyResponse. This is for testing AMediaDrm_setOnEventListener().
609 const char *utf8_outValue = NULL;
610 status = AMediaDrm_getPropertyString(aMediaObjects.getDrm(),
611 "listenerTestSupport", &utf8_outValue);
612 if (status == AMEDIA_OK && NULL != utf8_outValue) {
613 std::string eventType(utf8_outValue);
614 if (eventType.compare("true") == 0) {
615 int count = 0;
616 while (!gGotVendorDefinedEvent && count++ < 5) {
617 // Prevents race condition when the event arrives late
618 usleep(1000);
619 }
620 if (!gGotVendorDefinedEvent) {
621 ALOGE("Event listener did not receive the expected event.");
622 jniThrowExceptionFmt(env, "java/lang/RuntimeException",
623 "Event listener did not receive the expected event.");
624 AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
625 return JNI_FALSE;
626 }
627 }
628 }
629
630 playContent(env, aMediaObjects, params, sessionId, juuid);
631
632 status = AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
633 if (status != AMEDIA_OK) {
634 jniThrowException(env, "java/lang/RuntimeException",
635 "closeSession failed");
636 return JNI_FALSE;
637 }
638 return JNI_TRUE;
639 }
640
641 static JNINativeMethod gMethods[] = {
642 { "isCryptoSchemeSupportedNative", "([B)Z",
643 (void *)Java_android_media_cts_NativeClearKeySystemTest_isCryptoSchemeSupportedNative },
644
645 { "testClearKeyPlaybackNative",
646 "([BLandroid/media/cts/NativeClearKeySystemTest$PlaybackParams;)Z",
647 (void *)Java_android_media_cts_NativeClearKeySystemTest_testClearKeyPlaybackNative },
648
649 { "testGetPropertyStringNative",
650 "([BLjava/lang/String;Ljava/lang/StringBuffer;)Z",
651 (void *)Java_android_media_cts_NativeClearKeySystemTest_testGetPropertyStringNative },
652
653 { "testPsshNative", "([BLjava/lang/String;)Z",
654 (void *)Java_android_media_cts_NativeClearKeySystemTest__testPsshNative },
655 };
656
register_android_media_cts_NativeClearKeySystemTest(JNIEnv * env)657 int register_android_media_cts_NativeClearKeySystemTest(JNIEnv* env) {
658 jint result = JNI_ERR;
659 jclass testClass =
660 env->FindClass("android/media/cts/NativeClearKeySystemTest");
661 if (testClass) {
662 jclass playbackParamsClass = env->FindClass(
663 "android/media/cts/NativeClearKeySystemTest$PlaybackParams");
664 if (playbackParamsClass) {
665 jclass surfaceClass =
666 env->FindClass("android/view/Surface");
667 if (surfaceClass) {
668 gFieldIds.surface = env->GetFieldID(playbackParamsClass,
669 "surface", "Landroid/view/Surface;");
670 } else {
671 gFieldIds.surface = NULL;
672 }
673 gFieldIds.mimeType = env->GetFieldID(playbackParamsClass,
674 "mimeType", "Ljava/lang/String;");
675 gFieldIds.audioUrl = env->GetFieldID(playbackParamsClass,
676 "audioUrl", "Ljava/lang/String;");
677 gFieldIds.videoUrl = env->GetFieldID(playbackParamsClass,
678 "videoUrl", "Ljava/lang/String;");
679 } else {
680 ALOGE("PlaybackParams class not found");
681 }
682
683 } else {
684 ALOGE("NativeClearKeySystemTest class not found");
685 }
686
687 result = env->RegisterNatives(testClass, gMethods,
688 sizeof(gMethods) / sizeof(JNINativeMethod));
689 return result;
690 }
691