• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <list>
23 #include <string>
24 #include <vector>
25 
26 #include <assert.h>
27 #include <jni.h>
28 #include <nativehelper/JNIHelp.h>
29 #include <sys/stat.h>
30 #include <android/native_window_jni.h>
31 
32 #include "AMediaObjects.h"
33 
34 #include "media/NdkMediaCodec.h"
35 #include "media/NdkMediaCrypto.h"
36 #include "media/NdkMediaDrm.h"
37 #include "media/NdkMediaExtractor.h"
38 #include "media/NdkMediaFormat.h"
39 #include "media/NdkMediaMuxer.h"
40 
41 typedef std::vector<uint8_t> Uuid;
42 
43 struct fields_t {
44     jfieldID surface;
45     jfieldID mimeType;
46     jfieldID audioUrl;
47     jfieldID videoUrl;
48 };
49 
50 struct PlaybackParams {
51     jobject surface;
52     jstring mimeType;
53     jstring audioUrl;
54     jstring videoUrl;
55 };
56 
57 static fields_t gFieldIds;
58 static bool gGotVendorDefinedEvent = false;
59 static bool gListenerGotValidExpiryTime = false;
60 static bool gOnKeyChangeListenerOK = false;
61 
62 static const char kFileScheme[] = "file://";
63 static constexpr size_t kFileSchemeStrLen = sizeof(kFileScheme) - 1;
64 // Test time must be under 30 seconds to meet CTS quality bar.
65 // The first ten seconds in kPlayTimeSeconds plays the clear lead,
66 // the next ten seconds verifies encrypted playback.
67 static constexpr size_t kPlayTimeSeconds = 20;
68 static constexpr size_t kUuidSize = 16;
69 
70 static const uint8_t kClearKeyUuid[kUuidSize] = {
71     0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02,
72     0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b
73 };
74 
75 // The test content is not packaged with clearkey UUID,
76 // we have to use a canned clearkey pssh for the test.
77 static const uint8_t kClearkeyPssh[] = {
78     // BMFF box header (4 bytes size + 'pssh')
79     0x00, 0x00, 0x00, 0x34, 0x70, 0x73, 0x73, 0x68,
80     // full box header (version = 1 flags = 0)
81     0x01, 0x00, 0x00, 0x00,
82     // system id
83     0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02,
84     0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b,
85     // number of key ids
86     0x00, 0x00, 0x00, 0x01,
87     // key id
88     0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
89     0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
90     // size of data, must be zero
91     0x00, 0x00, 0x00, 0x00
92 };
93 
94 static const uint8_t kKeyRequestData[] = {
95     0x7b, 0x22, 0x6b, 0x69, 0x64,
96     0x73, 0x22, 0x3a, 0x5b, 0x22,
97     0x4d, 0x44, 0x41, 0x77, 0x4d,
98     0x44, 0x41, 0x77, 0x4d, 0x44,
99     0x41, 0x77, 0x4d, 0x44, 0x41,
100     0x77, 0x4d, 0x44, 0x41, 0x77,
101     0x4d, 0x41, 0x22, 0x5d, 0x2c,
102     0x22, 0x74, 0x79, 0x70, 0x65,
103     0x22, 0x3a, 0x22, 0x74, 0x65,
104     0x6d, 0x70, 0x6f, 0x72, 0x61,
105     0x72, 0x79, 0x22, 0x7d
106 };
107 
108 static const char kDefaultUrl[] = "https://default.url";
109 
110 static const size_t kKeyRequestSize = sizeof(kKeyRequestData);
111 
112 // base 64 encoded JSON response string, must not contain padding character '='
113 static const char kResponse[] = "{\"keys\":[{\"kty\":\"oct\"," \
114         "\"kid\":\"MDAwMDAwMDAwMDAwMDAwMA\",\"k\":" \
115         "\"Pwoz80CYueIrwHjgobXoVA\"}]}";
116 
isUuidSizeValid(Uuid uuid)117 static bool isUuidSizeValid(Uuid uuid) {
118     return (uuid.size() == kUuidSize);
119 }
120 
jbyteArrayToVector(JNIEnv * env,jbyteArray const & byteArray)121 static std::vector<uint8_t> jbyteArrayToVector(
122     JNIEnv* env, jbyteArray const &byteArray) {
123     uint8_t* buffer = reinterpret_cast<uint8_t*>(
124         env->GetByteArrayElements(byteArray, /*is_copy*/NULL));
125     std::vector<uint8_t> vector;
126     for (jsize i = 0; i < env->GetArrayLength(byteArray); ++i) {
127         vector.push_back(buffer[i]);
128     }
129     return vector;
130 }
131 
jbyteArrayToUuid(JNIEnv * env,jbyteArray const & uuid)132 static Uuid jbyteArrayToUuid(JNIEnv* env, jbyteArray const &uuid) {
133     Uuid juuid;
134     juuid.resize(0);
135     if (uuid != NULL) {
136         juuid = jbyteArrayToVector(env, uuid);
137     }
138     return juuid;
139 }
140 
setDataSourceFdFromUrl(AMediaExtractor * extractor,const char * url)141 static media_status_t setDataSourceFdFromUrl(
142     AMediaExtractor* extractor, const char* url) {
143 
144     const char *path = url + kFileSchemeStrLen;
145     FILE* fp = fopen(path, "r");
146     struct stat buf {};
147     media_status_t status = AMEDIA_ERROR_BASE;
148     if (fp && !fstat(fileno(fp), &buf)) {
149       status = AMediaExtractor_setDataSourceFd(
150           extractor,
151           fileno(fp), 0, buf.st_size);
152     } else {
153         status = AMEDIA_ERROR_IO;
154         ALOGE("Failed to convert URL to Fd");
155     }
156     if (fp && fclose(fp) == EOF) {
157         // 0 indicate success, EOF for error
158         ALOGE("Failed to close file pointer");
159     }
160     return status;
161 }
162 
setDataSourceFdOrUrl(AMediaExtractor * extractor,const char * url)163 static media_status_t setDataSourceFdOrUrl(
164     AMediaExtractor* extractor, const char* url) {
165 
166     media_status_t status = AMEDIA_ERROR_BASE;
167     if (strlen(url) > kFileSchemeStrLen && strncmp(url, kFileScheme, kFileSchemeStrLen) == 0) {
168         status = setDataSourceFdFromUrl(extractor, url);
169     } else {
170        status = AMediaExtractor_setDataSource(extractor, url);
171     }
172     return status;
173 }
174 
isCryptoSchemeSupportedNative(JNIEnv * env,jclass,jbyteArray uuid)175 extern "C" jboolean isCryptoSchemeSupportedNative(
176     JNIEnv* env, jclass /*clazz*/, jbyteArray uuid) {
177 
178     if (NULL == uuid) {
179         jniThrowException(env, "java/lang/NullPointerException", "null uuid");
180         return JNI_FALSE;
181     }
182 
183     Uuid juuid = jbyteArrayToUuid(env, uuid);
184     if (isUuidSizeValid(juuid)) {
185          return AMediaDrm_isCryptoSchemeSupported(&juuid[0], NULL);
186     } else {
187           jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
188                   "invalid UUID size, expected %u bytes", kUuidSize);
189     }
190     return JNI_FALSE;
191 }
192 
initPlaybackParams(JNIEnv * env,const jobject & playbackParams,PlaybackParams & params)193 void initPlaybackParams(JNIEnv* env, const jobject &playbackParams, PlaybackParams &params) {
194     params.surface = env->GetObjectField(
195         playbackParams, gFieldIds.surface);
196 
197     params.mimeType = static_cast<jstring>(env->GetObjectField(
198         playbackParams, gFieldIds.mimeType));
199 
200     params.audioUrl = static_cast<jstring>(env->GetObjectField(
201         playbackParams, gFieldIds.audioUrl));
202 
203     params.videoUrl = static_cast<jstring>(env->GetObjectField(
204         playbackParams, gFieldIds.videoUrl));
205 }
206 
testGetPropertyStringNative(JNIEnv * env,jclass clazz,jbyteArray uuid,jstring name,jobject outValue)207 extern "C" jboolean testGetPropertyStringNative(
208     JNIEnv* env, jclass clazz, jbyteArray uuid,
209     jstring name, jobject outValue) {
210 
211     if (NULL == uuid || NULL == name || NULL == outValue) {
212         jniThrowException(env, "java/lang/NullPointerException",
213                 "One or more null input parameters");
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.setDrm(AMediaDrm_createByUUID(&juuid[0]));
226     if (NULL == aMediaObjects.getDrm()) {
227         jniThrowException(env, "java/lang/RuntimeException", "null MediaDrm");
228         return JNI_FALSE;
229     }
230 
231     const char *utf8_name = env->GetStringUTFChars(name, NULL);
232     const char *utf8_outValue = NULL;
233     media_status_t status = AMediaDrm_getPropertyString(aMediaObjects.getDrm(),
234             utf8_name, &utf8_outValue);
235     env->ReleaseStringUTFChars(name, utf8_name);
236 
237     if (NULL != utf8_outValue) {
238         clazz = env->GetObjectClass(outValue);
239         jmethodID mId = env->GetMethodID (clazz, "append",
240                 "(Ljava/lang/String;)Ljava/lang/StringBuffer;");
241         jstring outString = env->NewStringUTF(
242                 static_cast<const char *>(utf8_outValue));
243         env->CallObjectMethod(outValue, mId, outString);
244     } else {
245         jniThrowExceptionFmt(env, "java/lang/RuntimeException",
246                 "get property string returns %d", status);
247         return JNI_FALSE;
248     }
249     return JNI_TRUE;
250 }
251 
testPropertyByteArrayNative(JNIEnv * env,jclass,jbyteArray uuid)252 extern "C" jboolean testPropertyByteArrayNative(
253         JNIEnv* env, jclass /* clazz */, jbyteArray uuid) {
254 
255     if (NULL == uuid) {
256         jniThrowException(env, "java/lang/NullPointerException",
257                 "uuid is NULL");
258         return JNI_FALSE;
259     }
260 
261     Uuid juuid = jbyteArrayToUuid(env, uuid);
262     if (!isUuidSizeValid(juuid)) {
263         jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
264                 "invalid UUID size, expected %u bytes", kUuidSize);
265         return JNI_FALSE;
266     }
267 
268     AMediaDrm* drm = AMediaDrm_createByUUID(&juuid[0]);
269     const char *propertyName = "clientId";
270     const uint8_t value[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
271     media_status_t status = AMediaDrm_setPropertyByteArray(drm, propertyName, value, sizeof(value));
272     if (status != AMEDIA_OK) {
273         jniThrowException(env, "java/lang/RuntimeException", "setPropertyByteArray failed");
274         AMediaDrm_release(drm);
275         return JNI_FALSE;
276     }
277     AMediaDrmByteArray array;
278     status = AMediaDrm_getPropertyByteArray(drm, propertyName, &array);
279     if (status != AMEDIA_OK) {
280         jniThrowException(env, "java/lang/RuntimeException", "getPropertyByteArray failed");
281         AMediaDrm_release(drm);
282         return JNI_FALSE;
283     }
284     if (array.length != sizeof(value)) {
285         jniThrowException(env, "java/lang/RuntimeException", "byte array size differs");
286         AMediaDrm_release(drm);
287         return JNI_FALSE;
288     }
289     if (!array.ptr) {
290         jniThrowException(env, "java/lang/RuntimeException", "byte array pointer is null");
291         AMediaDrm_release(drm);
292         return JNI_FALSE;
293     }
294     if (memcmp(array.ptr, value, sizeof(value)) != 0) {
295         jniThrowException(env, "java/lang/RuntimeException", "byte array content differs");
296         AMediaDrm_release(drm);
297         return JNI_FALSE;
298     }
299     AMediaDrm_release(drm);
300     return JNI_TRUE;
301 }
302 
testPsshNative(JNIEnv * env,jclass,jbyteArray uuid,jstring videoUrl)303 extern "C" jboolean testPsshNative(
304     JNIEnv* env, jclass /*clazz*/, jbyteArray uuid, jstring videoUrl) {
305 
306     if (NULL == uuid || NULL == videoUrl) {
307         jniThrowException(env, "java/lang/NullPointerException",
308                 "null uuid or null videoUrl");
309         return JNI_FALSE;
310     }
311 
312     Uuid juuid = jbyteArrayToUuid(env, uuid);
313     if (!isUuidSizeValid(juuid)) {
314         jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
315                 "invalid UUID size, expected %u bytes", kUuidSize);
316         return JNI_FALSE;
317     }
318 
319     AMediaObjects aMediaObjects;
320     aMediaObjects.setVideoExtractor(AMediaExtractor_new());
321     const char* url = env->GetStringUTFChars(videoUrl, 0);
322     if (url) {
323         media_status_t status = setDataSourceFdOrUrl(
324             aMediaObjects.getVideoExtractor(), url);
325         env->ReleaseStringUTFChars(videoUrl, url);
326 
327         if (status != AMEDIA_OK) {
328             jniThrowExceptionFmt(env, "java/lang/RuntimeException",
329                     "set video data source error=%d", status);
330             return JNI_FALSE;
331         }
332     }
333 
334     PsshInfo* psshInfo = AMediaExtractor_getPsshInfo(aMediaObjects.getVideoExtractor());
335     if (psshInfo == NULL) {
336         jniThrowException(env, "java/lang/RuntimeException", "null psshInfo");
337         return JNI_FALSE;
338     }
339 
340     jboolean testResult = JNI_FALSE;
341     for (size_t i = 0; i < psshInfo->numentries; i++) {
342         PsshEntry *entry = &psshInfo->entries[i];
343 
344         if (0 == memcmp(entry->uuid, kClearKeyUuid, sizeof(entry->uuid))) {
345             aMediaObjects.setDrm(AMediaDrm_createByUUID(&juuid[0]));
346             if (aMediaObjects.getDrm()) {
347                 testResult = JNI_TRUE;
348             } else {
349                 ALOGE("Failed to create media drm=%zd", i);
350                 testResult = JNI_FALSE;
351             }
352             break;
353         }
354     }
355     return testResult;
356 }
357 
isVideo(const char * mime)358 static bool isVideo(const char* mime) {
359     return !strncmp(mime, "video/", 6) ? true : false;
360 }
361 
isAudio(const char * mime)362 static bool isAudio(const char* mime) {
363     return !strncmp(mime, "audio/", 6) ? true : false;
364 }
365 
addTrack(const AMediaFormat * format,const char * mime,const AMediaCrypto * crypto,const ANativeWindow * window,AMediaCodec ** codec)366 static void addTrack(const AMediaFormat* format,
367         const char* mime, const AMediaCrypto* crypto,
368         const ANativeWindow* window, AMediaCodec** codec) {
369 
370     *codec = AMediaCodec_createDecoderByType(mime);
371     if (codec == NULL) {
372         ALOGE("cannot create codec for %s", mime);
373         return;
374     }
375 
376     AMediaCodec_configure(*codec, format,
377             const_cast<ANativeWindow*>(window),
378             const_cast<AMediaCrypto*>(crypto), 0);
379 }
380 
addTracks(const AMediaExtractor * extractor,const AMediaCrypto * crypto,const ANativeWindow * window,AMediaCodec ** codec)381 static void addTracks(const AMediaExtractor* extractor,
382         const AMediaCrypto* crypto, const ANativeWindow* window,
383         AMediaCodec** codec) {
384     size_t numTracks = AMediaExtractor_getTrackCount(
385         const_cast<AMediaExtractor*>(extractor));
386 
387     AMediaFormat* trackFormat = NULL;
388     for (size_t i = 0; i < numTracks; ++i) {
389         trackFormat = AMediaExtractor_getTrackFormat(
390             const_cast<AMediaExtractor*>(extractor), i);
391         if (trackFormat) {
392             ALOGV("track %zd format: %s", i,
393                     AMediaFormat_toString(trackFormat));
394 
395             const char* mime = "";
396             if (!AMediaFormat_getString(
397                 trackFormat, AMEDIAFORMAT_KEY_MIME, &mime)) {
398                 ALOGE("no mime type");
399 
400                 AMediaFormat_delete(trackFormat);
401                 return;
402             } else if (isAudio(mime) || isVideo(mime)) {
403                 AMediaExtractor_selectTrack(
404                     const_cast<AMediaExtractor*>(extractor), i);
405                 ALOGV("track %zd codec format: %s", i,
406                         AMediaFormat_toString(trackFormat));
407 
408                 addTrack(trackFormat, mime, crypto, window, codec);
409                 AMediaCodec_start(*codec);
410                 AMediaCodec_flush(*codec);
411                 AMediaExtractor_seekTo(
412                     const_cast<AMediaExtractor*>(extractor), 0,
413                             AMEDIAEXTRACTOR_SEEK_PREVIOUS_SYNC);
414             }
415             AMediaFormat_delete(trackFormat);
416         }
417     }
418 }
419 
getSystemNanoTime()420 static int64_t getSystemNanoTime() {
421     timespec now;
422     clock_gettime(CLOCK_MONOTONIC, &now);
423     return now.tv_sec * 1000000000LL + now.tv_nsec;
424 }
425 
fillDecoder(AMediaCodec * codec,AMediaExtractor * extractor,int64_t * presentationTimeUs,bool * eosReached)426 static void fillDecoder(AMediaCodec* codec, AMediaExtractor* extractor,
427         int64_t* presentationTimeUs, bool* eosReached) {
428     media_status_t status = AMEDIA_OK;
429 
430     ssize_t bufferIndex = AMediaCodec_dequeueInputBuffer(codec, 2000);
431     if (bufferIndex >= 0) {
432         size_t bufsize;
433         uint8_t* buf = AMediaCodec_getInputBuffer(codec, bufferIndex, &bufsize);
434 
435         int sampleSize = AMediaExtractor_readSampleData(extractor, buf, bufsize);
436         if (sampleSize < 0) {
437             sampleSize = 0;
438             *eosReached = true;
439         }
440 
441         *presentationTimeUs = AMediaExtractor_getSampleTime(extractor);
442 
443         AMediaCodecCryptoInfo *cryptoInfo =
444             AMediaExtractor_getSampleCryptoInfo(extractor);
445 
446         if (cryptoInfo) {
447             status = AMediaCodec_queueSecureInputBuffer(
448                 codec, bufferIndex, 0, cryptoInfo,
449                 *presentationTimeUs,
450                 *eosReached ? AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM : 0);
451             AMediaCodecCryptoInfo_delete(cryptoInfo);
452         } else {
453             status = AMediaCodec_queueInputBuffer(
454                 codec, bufferIndex, 0, sampleSize,
455                 *presentationTimeUs,
456                 *eosReached ? AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM : 0);
457         }
458         AMediaExtractor_advance(extractor);
459     }
460 }
461 
drainDecoder(AMediaCodec * codec,int64_t presentationTimeUs,int64_t * startTimeNano)462 static bool drainDecoder(AMediaCodec* codec, int64_t presentationTimeUs,
463     int64_t* startTimeNano) {
464 
465     AMediaCodecBufferInfo info;
466     ssize_t bufferIndex  = AMediaCodec_dequeueOutputBuffer(codec, &info, 0);
467     if (bufferIndex >= 0) {
468         if (info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
469             return true;  // eos reached
470         }
471 
472         if (*startTimeNano < 0) {
473             *startTimeNano = getSystemNanoTime() - (presentationTimeUs * 1000);
474         }
475         int64_t delay = (*startTimeNano + presentationTimeUs * 1000) -
476                 getSystemNanoTime();
477         if (delay > 0) {
478             usleep(delay / 1000);
479         }
480 
481         AMediaCodec_releaseOutputBuffer(codec, bufferIndex, info.size != 0);
482     } else if (bufferIndex == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
483         ALOGV("output buffers changed");
484     } else if (bufferIndex == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
485         AMediaFormat* format = AMediaCodec_getOutputFormat(codec);
486         ALOGV("format changed to: %s", AMediaFormat_toString(format));
487         AMediaFormat_delete(format);
488     } else if (bufferIndex == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
489          ALOGV("no output buffer right now");
490          usleep(20000);
491     } else {
492          ALOGV("unexpected info code: %zd", bufferIndex);
493     }
494     return false;
495 }
496 
playContent(JNIEnv * env,const AMediaObjects & aMediaObjects,PlaybackParams & params,const AMediaDrmSessionId & sessionId,Uuid uuid)497 static jboolean playContent(JNIEnv* env, const AMediaObjects& aMediaObjects,
498         PlaybackParams& params, const AMediaDrmSessionId& sessionId, Uuid uuid) {
499 
500     ANativeWindow *window = ANativeWindow_fromSurface(env, params.surface);
501     AMediaExtractor* audioExtractor = aMediaObjects.getAudioExtractor();
502     AMediaExtractor* videoExtractor = aMediaObjects.getVideoExtractor();
503 
504     AMediaCodec* audioCodec = NULL;
505     AMediaCodec* videoCodec = NULL;
506     AMediaCrypto* crypto = NULL;
507 
508     crypto = AMediaCrypto_new(&uuid[0], sessionId.ptr, sessionId.length);
509     if (crypto == NULL) {
510         jniThrowException(env, "java/lang/RuntimeException",
511                 "failed to create crypto object");
512         return JNI_FALSE;
513     }
514 
515     addTracks(audioExtractor, NULL, NULL, &audioCodec);
516 
517     addTracks(videoExtractor, crypto, window, &videoCodec);
518 
519     bool sawAudioInputEos = false;
520     bool sawAudioOutputEos = false;
521     bool sawVideoInputEos = false;
522     bool sawVideoOutputEos = false;
523     int64_t videoPresentationTimeUs = 0;
524     int64_t videoStartTimeNano = -1;
525     struct timespec timeSpec;
526     clock_gettime(CLOCK_MONOTONIC, &timeSpec);
527     time_t startTimeSec = timeSpec.tv_sec;
528 
529     while (!sawAudioOutputEos && !sawVideoOutputEos) {
530         if (!sawVideoInputEos) {
531             fillDecoder(videoCodec, videoExtractor,
532                     &videoPresentationTimeUs, &sawVideoInputEos);
533         }
534 
535         if (!sawAudioInputEos) {
536             // skip audio, still need to advance the audio extractor
537             AMediaExtractor_advance(audioExtractor);
538         }
539 
540         if (!sawVideoOutputEos) {
541             sawVideoOutputEos = drainDecoder(videoCodec, videoPresentationTimeUs,
542                     &videoStartTimeNano);
543         }
544 
545         clock_gettime(CLOCK_MONOTONIC, &timeSpec);
546         if (timeSpec.tv_sec >= static_cast<time_t>(
547             (startTimeSec + kPlayTimeSeconds))) {
548             // stop reading samples and drain the output buffers
549             sawAudioInputEos = sawVideoInputEos = true;
550             sawAudioOutputEos = true; // ignore audio
551         }
552     }
553 
554     if (audioCodec) {
555         AMediaCodec_stop(audioCodec);
556         AMediaCodec_delete(audioCodec);
557     }
558     if (videoCodec) {
559         AMediaCodec_stop(videoCodec);
560         AMediaCodec_delete(videoCodec);
561     }
562 
563     AMediaCrypto_delete(crypto);
564     ANativeWindow_release(window);
565     return JNI_TRUE;
566 }
567 
listener(AMediaDrm *,const AMediaDrmSessionId *,AMediaDrmEventType eventType,int,const uint8_t *,size_t)568 static void listener(
569     AMediaDrm* /*drm*/, const AMediaDrmSessionId* /*sessionId*/,
570     AMediaDrmEventType eventType,
571     int /*extra*/, const uint8_t* /*data*/, size_t /*dataSize*/) {
572 
573     switch (eventType) {
574         case EVENT_PROVISION_REQUIRED:
575             ALOGD("EVENT_PROVISION_REQUIRED received");
576             break;
577         case EVENT_KEY_REQUIRED:
578             ALOGD("EVENT_KEY_REQUIRED received");
579             break;
580         case EVENT_KEY_EXPIRED:
581             ALOGD("EVENT_KEY_EXPIRED received");
582             break;
583         case EVENT_VENDOR_DEFINED:
584             gGotVendorDefinedEvent = true;
585             ALOGD("EVENT_VENDOR_DEFINED received");
586             break;
587         case EVENT_SESSION_RECLAIMED:
588             ALOGD("EVENT_SESSION_RECLAIMED received");
589             break;
590         default:
591             ALOGD("Unknown event received");
592             break;
593     }
594 }
595 
onExpirationUpdateListener(AMediaDrm *,const AMediaDrmSessionId *,int64_t expiryTimeInMS)596 static void onExpirationUpdateListener(
597     AMediaDrm* /*drm*/, const AMediaDrmSessionId* /*sessionId*/,
598     int64_t expiryTimeInMS) {
599 
600     if (expiryTimeInMS == 100) {
601         ALOGD("Updates new expiration time to %" PRId64 " ms", expiryTimeInMS);
602         gListenerGotValidExpiryTime = true;
603     } else {
604         ALOGE("Expects 100 ms for expiry time, received: %" PRId64 " ms", expiryTimeInMS);
605         gListenerGotValidExpiryTime = false;
606     }
607 }
608 
onKeysChangeListener(AMediaDrm *,const AMediaDrmSessionId *,const AMediaDrmKeyStatus * keysStatus,size_t numKeys,bool hasNewUsableKey)609 static void onKeysChangeListener(
610     AMediaDrm* /*drm*/, const AMediaDrmSessionId* /*sessionId*/,
611     const AMediaDrmKeyStatus* keysStatus, size_t numKeys, bool hasNewUsableKey) {
612 
613     gOnKeyChangeListenerOK = false;
614     if (numKeys != 3) {
615         ALOGE("Expects 3 keys, received %zd keys", numKeys);
616         return;
617     }
618 
619     if (!hasNewUsableKey) {
620         ALOGE("Expects hasNewUsableKey to be true");
621         return;
622     }
623 
624     ALOGD("Number of keys changed=%zd", numKeys);
625     AMediaDrmKeyStatus keyStatus;
626     for (size_t i = 0; i < numKeys; ++i) {
627         keyStatus.keyId.ptr = keysStatus[i].keyId.ptr;
628         keyStatus.keyId.length = keysStatus[i].keyId.length;
629         keyStatus.keyType = keysStatus[i].keyType;
630 
631         ALOGD("key[%zd]: key: %0x, %0x, %0x", i, keyStatus.keyId.ptr[0], keyStatus.keyId.ptr[1],
632                 keyStatus.keyId.ptr[2]);
633         ALOGD("key[%zd]: key type=%d", i, keyStatus.keyType);
634     }
635     gOnKeyChangeListenerOK = true;
636 }
637 
acquireLicense(JNIEnv * env,const AMediaObjects & aMediaObjects,const AMediaDrmSessionId & sessionId,AMediaDrmKeyType keyType)638 static void acquireLicense(
639     JNIEnv* env, const AMediaObjects& aMediaObjects, const AMediaDrmSessionId& sessionId,
640     AMediaDrmKeyType keyType) {
641     // Pointer to keyRequest memory, which remains until the next
642     // AMediaDrm_getKeyRequest call or until the drm object is released.
643     const uint8_t* keyRequest;
644     size_t keyRequestSize = 0;
645     std::string errorMessage;
646 
647     // The server recognizes "video/mp4" but not "video/avc".
648     media_status_t status = AMediaDrm_getKeyRequest(aMediaObjects.getDrm(),
649             &sessionId, kClearkeyPssh, sizeof(kClearkeyPssh),
650             "video/mp4" /*mimeType*/, keyType,
651             NULL, 0, &keyRequest, &keyRequestSize);
652     if (status != AMEDIA_OK) {
653         errorMessage.assign("getKeyRequest failed, error = %d");
654         goto errorOut;
655     }
656 
657     if (kKeyRequestSize != keyRequestSize) {
658         ALOGE("Invalid keyRequestSize %zd", kKeyRequestSize);
659         errorMessage.assign("Invalid key request size, error = %d");
660         status = AMEDIA_DRM_NEED_KEY;
661         goto errorOut;
662     }
663 
664     if (memcmp(kKeyRequestData, keyRequest, kKeyRequestSize) != 0) {
665         errorMessage.assign("Invalid key request data is returned, error = %d");
666         status = AMEDIA_DRM_NEED_KEY;
667         goto errorOut;
668     }
669 
670     AMediaDrmKeySetId keySetId;
671     gGotVendorDefinedEvent = false;
672     gListenerGotValidExpiryTime = false;
673     gOnKeyChangeListenerOK = false;
674     status = AMediaDrm_provideKeyResponse(aMediaObjects.getDrm(), &sessionId,
675             reinterpret_cast<const uint8_t*>(kResponse),
676             sizeof(kResponse), &keySetId);
677     if (status == AMEDIA_OK) {
678         return;  // success
679     }
680 
681     errorMessage.assign("provideKeyResponse failed, error = %d");
682 
683 errorOut:
684     AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
685     jniThrowExceptionFmt(env, "java/lang/RuntimeException", errorMessage.c_str(), status);
686 }
687 
testClearKeyPlaybackNative(JNIEnv * env,jclass,jbyteArray uuid,jobject playbackParams)688 extern "C" jboolean testClearKeyPlaybackNative(
689     JNIEnv* env, jclass /*clazz*/, jbyteArray uuid, jobject playbackParams) {
690     if (NULL == uuid || NULL == playbackParams) {
691         jniThrowException(env, "java/lang/NullPointerException",
692                 "null uuid or null playback parameters");
693         return JNI_FALSE;
694     }
695 
696     Uuid juuid = jbyteArrayToUuid(env, uuid);
697     if (!isUuidSizeValid(juuid)) {
698         jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
699                 "invalid UUID size, expected %u bytes", kUuidSize);
700         return JNI_FALSE;
701     }
702 
703     PlaybackParams params;
704     initPlaybackParams(env, playbackParams, params);
705 
706     AMediaObjects aMediaObjects;
707     media_status_t status = AMEDIA_OK;
708     aMediaObjects.setDrm(AMediaDrm_createByUUID(&juuid[0]));
709     if (NULL == aMediaObjects.getDrm()) {
710         jniThrowException(env, "java/lang/RuntimeException", "null MediaDrm");
711         return JNI_FALSE;
712     }
713 
714     status = AMediaDrm_setOnEventListener(aMediaObjects.getDrm(), listener);
715     if (status != AMEDIA_OK) {
716         jniThrowException(env, "java/lang/RuntimeException",
717                 "setOnEventListener failed");
718         return JNI_FALSE;
719     }
720 
721     status = AMediaDrm_setOnExpirationUpdateListener(aMediaObjects.getDrm(),
722             onExpirationUpdateListener);
723     if (status != AMEDIA_OK) {
724         jniThrowException(env, "java/lang/RuntimeException",
725                 "setOnExpirationUpdateListener failed");
726         return JNI_FALSE;
727     }
728 
729     status = AMediaDrm_setOnKeysChangeListener(aMediaObjects.getDrm(),
730             onKeysChangeListener);
731     if (status != AMEDIA_OK) {
732         jniThrowException(env, "java/lang/RuntimeException",
733                 "setOnKeysChangeListener failed");
734         return JNI_FALSE;
735     }
736 
737     aMediaObjects.setAudioExtractor(AMediaExtractor_new());
738     const char* url = env->GetStringUTFChars(params.audioUrl, 0);
739     if (url) {
740         status = setDataSourceFdOrUrl(
741             aMediaObjects.getAudioExtractor(), url);
742         env->ReleaseStringUTFChars(params.audioUrl, url);
743 
744         if (status != AMEDIA_OK) {
745             jniThrowExceptionFmt(env, "java/lang/RuntimeException",
746                     "set audio data source error=%d", status);
747             return JNI_FALSE;
748         }
749     }
750 
751     aMediaObjects.setVideoExtractor(AMediaExtractor_new());
752     url = env->GetStringUTFChars(params.videoUrl, 0);
753     if (url) {
754         status = setDataSourceFdOrUrl(
755             aMediaObjects.getVideoExtractor(), url);
756         env->ReleaseStringUTFChars(params.videoUrl, url);
757 
758         if (status != AMEDIA_OK) {
759             jniThrowExceptionFmt(env, "java/lang/RuntimeException",
760                     "set video data source error=%d", status);
761             return JNI_FALSE;
762         }
763     }
764 
765     AMediaDrmSessionId sessionId;
766     status = AMediaDrm_openSession(aMediaObjects.getDrm(), &sessionId);
767     if (status != AMEDIA_OK) {
768         jniThrowException(env, "java/lang/RuntimeException",
769                 "openSession failed");
770         return JNI_FALSE;
771     }
772 
773     acquireLicense(env, aMediaObjects, sessionId, KEY_TYPE_STREAMING);
774 
775     // Checks if the event listener has received the expected event sent by
776     // provideKeyResponse. This is for testing AMediaDrm_setOnEventListener().
777     const char *utf8_outValue = NULL;
778     status = AMediaDrm_getPropertyString(aMediaObjects.getDrm(),
779             "listenerTestSupport", &utf8_outValue);
780     if (status == AMEDIA_OK && NULL != utf8_outValue) {
781         std::string eventType(utf8_outValue);
782         if (eventType.compare("true") == 0) {
783             int count = 0;
784             while ((!gGotVendorDefinedEvent ||
785                     !gListenerGotValidExpiryTime ||
786                     !gOnKeyChangeListenerOK) && count++ < 5) {
787                // Prevents race condition when the event arrives late
788                usleep(10000);
789             }
790 
791             if (!gGotVendorDefinedEvent) {
792                 ALOGE("Event listener did not receive the expected event.");
793                 jniThrowExceptionFmt(env, "java/lang/RuntimeException",
794                         "Event listener did not receive the expected event.");
795                 AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
796                 return JNI_FALSE;
797            }
798 
799           // Checks if onExpirationUpdateListener received the correct expiry time.
800            if (!gListenerGotValidExpiryTime) {
801                jniThrowExceptionFmt(env, "java/lang/RuntimeException",
802                        "onExpirationUpdateListener received incorrect expiry time.");
803                AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
804                return JNI_FALSE;
805            }
806 
807           // Checks if onKeysChangeListener succeeded.
808           if (!gOnKeyChangeListenerOK) {
809               jniThrowExceptionFmt(env, "java/lang/RuntimeException",
810                       "onKeysChangeListener failed");
811               AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
812               return JNI_FALSE;
813           }
814         }
815     }
816 
817     playContent(env, aMediaObjects, params, sessionId, juuid);
818 
819     status = AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
820     if (status != AMEDIA_OK) {
821         jniThrowException(env, "java/lang/RuntimeException",
822                 "closeSession failed");
823         return JNI_FALSE;
824     }
825     return JNI_TRUE;
826 }
827 
testQueryKeyStatusNative(JNIEnv * env,jclass,jbyteArray uuid)828 extern "C" jboolean testQueryKeyStatusNative(
829     JNIEnv* env, jclass /*clazz*/, jbyteArray uuid) {
830 
831     if (NULL == uuid) {
832         jniThrowException(env, "java/lang/NullPointerException", "null uuid");
833         return JNI_FALSE;
834     }
835 
836     Uuid juuid = jbyteArrayToUuid(env, uuid);
837     if (!isUuidSizeValid(juuid)) {
838         jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
839                 "invalid UUID size, expected %u bytes", kUuidSize);
840         return JNI_FALSE;
841     }
842 
843     AMediaObjects aMediaObjects;
844     media_status_t status = AMEDIA_OK;
845     aMediaObjects.setDrm(AMediaDrm_createByUUID(&juuid[0]));
846     if (NULL == aMediaObjects.getDrm()) {
847         jniThrowException(env, "java/lang/RuntimeException", "failed to create drm");
848         return JNI_FALSE;
849     }
850 
851     AMediaDrmSessionId sessionId;
852     status = AMediaDrm_openSession(aMediaObjects.getDrm(), &sessionId);
853     if (status != AMEDIA_OK) {
854         jniThrowException(env, "java/lang/RuntimeException",
855                 "openSession failed");
856         return JNI_FALSE;
857     }
858 
859     size_t numPairs = 3;
860     AMediaDrmKeyValue keyStatus[numPairs];
861 
862     // Test default key status, should return zero key status
863     status = AMediaDrm_queryKeyStatus(aMediaObjects.getDrm(), &sessionId, keyStatus, &numPairs);
864     if (status != AMEDIA_OK) {
865         jniThrowExceptionFmt(env, "java/lang/RuntimeException",
866                 "AMediaDrm_queryKeyStatus failed, error = %d", status);
867         AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
868         return JNI_FALSE;
869     }
870 
871     if (numPairs != 0) {
872         jniThrowExceptionFmt(env, "java/lang/RuntimeException",
873                 "AMediaDrm_queryKeyStatus failed, no policy should be defined");
874         AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
875         return JNI_FALSE;
876     }
877 
878     acquireLicense(env, aMediaObjects, sessionId, KEY_TYPE_STREAMING);
879 
880     // Test short buffer
881     numPairs = 2;
882     status = AMediaDrm_queryKeyStatus(aMediaObjects.getDrm(), &sessionId, keyStatus, &numPairs);
883     if (status != AMEDIA_DRM_SHORT_BUFFER) {
884         jniThrowExceptionFmt(env, "java/lang/RuntimeException",
885                 "AMediaDrm_queryKeyStatus should return AMEDIA_DRM_SHORT_BUFFER, error = %d",
886                         status);
887         AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
888         return JNI_FALSE;
889     }
890 
891     // Test valid key status
892     numPairs = 3;
893     status = AMediaDrm_queryKeyStatus(aMediaObjects.getDrm(), &sessionId, keyStatus, &numPairs);
894     if (status != AMEDIA_OK) {
895         jniThrowExceptionFmt(env, "java/lang/RuntimeException",
896                 "AMediaDrm_queryKeyStatus failed, error = %d", status);
897         AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
898         return JNI_FALSE;
899     }
900 
901     for (size_t i = 0; i < numPairs; ++i) {
902         ALOGI("AMediaDrm_queryKeyStatus: key=%s, value=%s", keyStatus[i].mKey, keyStatus[i].mValue);
903     }
904 
905     if (numPairs != 3) {
906         jniThrowExceptionFmt(env, "java/lang/RuntimeException",
907                 "AMediaDrm_queryKeyStatus returns %zd key status, expecting 3", numPairs);
908         AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
909         return JNI_FALSE;
910     }
911 
912     status = AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
913     if (status != AMEDIA_OK) {
914         jniThrowException(env, "java/lang/RuntimeException",
915                 "closeSession failed");
916         return JNI_FALSE;
917     }
918     return JNI_TRUE;
919 }
920 
testFindSessionIdNative(JNIEnv * env,jclass,jbyteArray uuid)921 extern "C" jboolean testFindSessionIdNative(
922     JNIEnv* env, jclass /*clazz*/, jbyteArray uuid) {
923 
924     if (NULL == uuid) {
925         jniThrowException(env, "java/lang/NullPointerException", "null uuid");
926         return JNI_FALSE;
927     }
928 
929     Uuid juuid = jbyteArrayToUuid(env, uuid);
930     if (!isUuidSizeValid(juuid)) {
931         jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
932                 "invalid UUID size, expected %u bytes", kUuidSize);
933         return JNI_FALSE;
934     }
935 
936     AMediaObjects aMediaObjects;
937     media_status_t status = AMEDIA_OK;
938     aMediaObjects.setDrm(AMediaDrm_createByUUID(&juuid[0]));
939     if (NULL == aMediaObjects.getDrm()) {
940         jniThrowException(env, "java/lang/RuntimeException", "failed to create drm");
941         return JNI_FALSE;
942     }
943 
944     // Stores duplicates of session id.
945     std::vector<std::vector<uint8_t> > sids;
946 
947     std::list<AMediaDrmSessionId> sessionIds;
948     AMediaDrmSessionId sessionId;
949     for (int i = 0; i < 5; ++i) {
950         status = AMediaDrm_openSession(aMediaObjects.getDrm(), &sessionId);
951         if (status != AMEDIA_OK) {
952             jniThrowException(env, "java/lang/RuntimeException", "openSession failed");
953             return JNI_FALSE;
954         }
955 
956         // Allocates a new pointer to duplicate the session id returned by
957         // AMediaDrm_openSession. These new pointers will be passed to
958         // AMediaDrm_closeSession, which verifies that the ndk
959         // can find the session id even if the pointer has changed.
960         sids.push_back(std::vector<uint8_t>(sessionId.length));
961         memcpy(sids.at(i).data(), sessionId.ptr, sessionId.length);
962         sessionId.ptr = static_cast<uint8_t *>(sids.at(i).data());
963         sessionIds.push_back(sessionId);
964     }
965 
966     for (auto sessionId : sessionIds) {
967         status = AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
968         if (status != AMEDIA_OK) {
969             jniThrowException(env, "java/lang/RuntimeException", "closeSession failed");
970             return JNI_FALSE;
971         }
972     }
973 
974     return JNI_TRUE;
975 }
976 
testGetKeyRequestNative(JNIEnv * env,jclass,jbyteArray uuid,jobject playbackParams)977 extern "C" jboolean testGetKeyRequestNative(
978     JNIEnv* env, jclass /*clazz*/, jbyteArray uuid, jobject playbackParams) {
979 
980     if (NULL == uuid || NULL == playbackParams) {
981         jniThrowException(env, "java/lang/NullPointerException",
982                 "null uuid or null playback parameters");
983         return JNI_FALSE;
984     }
985 
986     Uuid juuid = jbyteArrayToUuid(env, uuid);
987     if (!isUuidSizeValid(juuid)) {
988         jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
989                 "invalid UUID size, expected %u bytes", kUuidSize);
990         return JNI_FALSE;
991     }
992 
993     PlaybackParams params;
994     initPlaybackParams(env, playbackParams, params);
995 
996     AMediaObjects aMediaObjects;
997     media_status_t status = AMEDIA_OK;
998     aMediaObjects.setDrm(AMediaDrm_createByUUID(&juuid[0]));
999     if (NULL == aMediaObjects.getDrm()) {
1000         jniThrowException(env, "java/lang/RuntimeException", "null MediaDrm");
1001         return JNI_FALSE;
1002     }
1003 
1004     AMediaDrmSessionId sessionId;
1005     status = AMediaDrm_openSession(aMediaObjects.getDrm(), &sessionId);
1006     if (status != AMEDIA_OK) {
1007         jniThrowException(env, "java/lang/RuntimeException",
1008                 "openSession failed");
1009         return JNI_FALSE;
1010     }
1011 
1012     // Pointer to keyRequest memory, which remains until the next
1013     // AMediaDrm_getKeyRequest call or until the drm object is released.
1014     const uint8_t* keyRequest;
1015     size_t keyRequestSize = 0;
1016     std::string errorMessage;
1017 
1018     const char *defaultUrl;
1019     AMediaDrmKeyRequestType keyRequestType;
1020 
1021     // The server recognizes "video/mp4" but not "video/avc".
1022     status = AMediaDrm_getKeyRequestWithDefaultUrlAndType(aMediaObjects.getDrm(),
1023             &sessionId, kClearkeyPssh, sizeof(kClearkeyPssh),
1024             "video/mp4" /*mimeType*/, KEY_TYPE_STREAMING,
1025             NULL, 0, &keyRequest, &keyRequestSize, &defaultUrl, &keyRequestType);
1026 
1027     if(status != AMEDIA_OK) return JNI_FALSE;
1028 
1029     switch(keyRequestType) {
1030         case KEY_REQUEST_TYPE_INITIAL:
1031         case KEY_REQUEST_TYPE_RENEWAL:
1032         case KEY_REQUEST_TYPE_RELEASE:
1033         case KEY_REQUEST_TYPE_NONE:
1034         case KEY_REQUEST_TYPE_UPDATE:
1035             break;
1036         default:
1037             errorMessage.assign("keyRequestType returned is [%d], error = %d");
1038             AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
1039             jniThrowExceptionFmt(env, "java/lang/RuntimeException", errorMessage.c_str(), keyRequestType, status);
1040             return JNI_FALSE;
1041     }
1042 
1043     // Check service availability
1044     const char *outValue = NULL;
1045     status = AMediaDrm_getPropertyString(aMediaObjects.getDrm(),
1046             "aidlVersion", &outValue);
1047     if (status != AMEDIA_OK) {
1048         // Drm service not using aidl interface, skip checking default url value
1049         return JNI_TRUE;
1050     }
1051 
1052     ALOGD("aidlVersion is [%s]", outValue);
1053 
1054     ALOGD("kDefaultUrl [%s], length %d, defaultUrl [%s], length %d",
1055         kDefaultUrl,
1056         (int)strlen(kDefaultUrl),
1057         defaultUrl,
1058         (int)strlen(defaultUrl));
1059 
1060     if (strlen(kDefaultUrl) != strlen(defaultUrl) || strcmp(kDefaultUrl, defaultUrl) != 0) {
1061         AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
1062         jniThrowExceptionFmt(env, "java/lang/RuntimeException", "Default Url is not correct [%s], error = %d", defaultUrl, status);
1063         return JNI_FALSE;
1064     }
1065 
1066     return JNI_TRUE;
1067 }
1068 
1069 static JNINativeMethod gMethods[] = {
1070     { "isCryptoSchemeSupportedNative", "([B)Z",
1071             (void *)isCryptoSchemeSupportedNative },
1072 
1073     { "testClearKeyPlaybackNative",
1074             "([BLandroid/media/drmframework/cts/NativeMediaDrmClearkeyTest$PlaybackParams;)Z",
1075             (void *)testClearKeyPlaybackNative },
1076 
1077     { "testGetPropertyStringNative",
1078             "([BLjava/lang/String;Ljava/lang/StringBuffer;)Z",
1079             (void *)testGetPropertyStringNative },
1080 
1081     { "testPropertyByteArrayNative",
1082             "([B)Z",
1083             (void *)testPropertyByteArrayNative },
1084 
1085     { "testPsshNative", "([BLjava/lang/String;)Z",
1086             (void *)testPsshNative },
1087 
1088     { "testQueryKeyStatusNative", "([B)Z",
1089             (void *)testQueryKeyStatusNative },
1090 
1091     { "testFindSessionIdNative", "([B)Z",
1092             (void *)testFindSessionIdNative },
1093 
1094     { "testGetKeyRequestNative",
1095             "([BLandroid/media/drmframework/cts/NativeMediaDrmClearkeyTest$PlaybackParams;)Z",
1096             (void *)testGetKeyRequestNative},
1097 };
1098 
registerNativeMediaDrmClearkeyTest(JNIEnv * env)1099 int registerNativeMediaDrmClearkeyTest(JNIEnv* env) {
1100     jint result = JNI_ERR;
1101     jclass testClass =
1102         env->FindClass("android/media/drmframework/cts/NativeMediaDrmClearkeyTest");
1103     if (testClass) {
1104         jclass playbackParamsClass = env->FindClass(
1105             "android/media/drmframework/cts/NativeMediaDrmClearkeyTest$PlaybackParams");
1106         if (playbackParamsClass) {
1107             jclass surfaceClass =
1108                 env->FindClass("android/view/Surface");
1109             if (surfaceClass) {
1110                 gFieldIds.surface = env->GetFieldID(playbackParamsClass,
1111                         "surface", "Landroid/view/Surface;");
1112             } else {
1113                 gFieldIds.surface = NULL;
1114             }
1115             gFieldIds.mimeType = env->GetFieldID(playbackParamsClass,
1116                     "mimeType", "Ljava/lang/String;");
1117             gFieldIds.audioUrl = env->GetFieldID(playbackParamsClass,
1118                     "audioUrl", "Ljava/lang/String;");
1119             gFieldIds.videoUrl = env->GetFieldID(playbackParamsClass,
1120                     "videoUrl", "Ljava/lang/String;");
1121         } else {
1122             ALOGE("PlaybackParams class not found");
1123         }
1124 
1125     } else {
1126         ALOGE("NativeMediaDrmClearkeyTest class not found");
1127     }
1128 
1129     result = env->RegisterNatives(testClass, gMethods,
1130             sizeof(gMethods) / sizeof(JNINativeMethod));
1131     return result;
1132 }
1133