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 &params) {
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