Home | History | Annotate | Download | only in libmediandkjni
      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 
     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 
     60 static const size_t kPlayTimeSeconds = 30;
     61 static const size_t kUuidSize = 16;
     62 
     63 static const uint8_t kClearKeyUuid[kUuidSize] = {
     64     0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02,
     65     0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b
     66 };
     67 
     68 // The test content is not packaged with clearkey UUID,
     69 // we have to use a canned clearkey pssh for the test.
     70 static const uint8_t kClearkeyPssh[] = {
     71     // BMFF box header (4 bytes size + 'pssh')
     72     0x00, 0x00, 0x00, 0x34, 0x70, 0x73, 0x73, 0x68,
     73     // full box header (version = 1 flags = 0)
     74     0x01, 0x00, 0x00, 0x00,
     75     // system id
     76     0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02,
     77     0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b,
     78     // number of key ids
     79     0x00, 0x00, 0x00, 0x01,
     80     // key id
     81     0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
     82     0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
     83     // size of data, must be zero
     84     0x00, 0x00, 0x00, 0x00
     85 };
     86 
     87 static const uint8_t kKeyRequestData[] = {
     88     0x7b, 0x22, 0x6b, 0x69, 0x64,
     89     0x73, 0x22, 0x3a, 0x5b, 0x22,
     90     0x4d, 0x44, 0x41, 0x77, 0x4d,
     91     0x44, 0x41, 0x77, 0x4d, 0x44,
     92     0x41, 0x77, 0x4d, 0x44, 0x41,
     93     0x77, 0x4d, 0x44, 0x41, 0x77,
     94     0x4d, 0x41, 0x22, 0x5d, 0x2c,
     95     0x22, 0x74, 0x79, 0x70, 0x65,
     96     0x22, 0x3a, 0x22, 0x74, 0x65,
     97     0x6d, 0x70, 0x6f, 0x72, 0x61,
     98     0x72, 0x79, 0x22, 0x7d
     99 };
    100 
    101 static const size_t kKeyRequestSize = sizeof(kKeyRequestData);
    102 
    103 // base 64 encoded JSON response string, must not contain padding character '='
    104 static const char kResponse[] = "{\"keys\":[{\"kty\":\"oct\"," \
    105         "\"kid\":\"MDAwMDAwMDAwMDAwMDAwMA\",\"k\":" \
    106         "\"Pwoz80CYueIrwHjgobXoVA\"}]}";
    107 
    108 static bool isUuidSizeValid(Uuid uuid) {
    109     return (uuid.size() == kUuidSize);
    110 }
    111 
    112 static std::vector<uint8_t> jbyteArrayToVector(
    113     JNIEnv* env, jbyteArray const &byteArray) {
    114     uint8_t* buffer = reinterpret_cast<uint8_t*>(
    115         env->GetByteArrayElements(byteArray, /*is_copy*/NULL));
    116     std::vector<uint8_t> vector;
    117     for (jsize i = 0; i < env->GetArrayLength(byteArray); ++i) {
    118         vector.push_back(buffer[i]);
    119     }
    120     return vector;
    121 }
    122 
    123 static Uuid jbyteArrayToUuid(JNIEnv* env, jbyteArray const &uuid) {
    124     Uuid juuid;
    125     juuid.resize(0);
    126     if (uuid != NULL) {
    127         juuid = jbyteArrayToVector(env, uuid);
    128     }
    129     return juuid;
    130 }
    131 
    132 extern "C" jboolean Java_android_media_cts_NativeMediaDrmClearkeyTest_isCryptoSchemeSupportedNative(
    133     JNIEnv* env, jclass /*clazz*/, jbyteArray uuid) {
    134 
    135     if (NULL == uuid) {
    136         jniThrowException(env, "java/lang/NullPointerException", "null uuid");
    137         return JNI_FALSE;
    138     }
    139 
    140     Uuid juuid = jbyteArrayToUuid(env, uuid);
    141     if (isUuidSizeValid(juuid)) {
    142          return AMediaDrm_isCryptoSchemeSupported(&juuid[0], NULL);
    143     } else {
    144           jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
    145                   "invalid UUID size, expected %u bytes", kUuidSize);
    146     }
    147     return JNI_FALSE;
    148 }
    149 
    150 void initPlaybackParams(JNIEnv* env, const jobject &playbackParams, PlaybackParams &params) {
    151     params.surface = env->GetObjectField(
    152         playbackParams, gFieldIds.surface);
    153 
    154     params.mimeType = static_cast<jstring>(env->GetObjectField(
    155         playbackParams, gFieldIds.mimeType));
    156 
    157     params.audioUrl = static_cast<jstring>(env->GetObjectField(
    158         playbackParams, gFieldIds.audioUrl));
    159 
    160     params.videoUrl = static_cast<jstring>(env->GetObjectField(
    161         playbackParams, gFieldIds.videoUrl));
    162 }
    163 
    164 extern "C" jboolean Java_android_media_cts_NativeMediaDrmClearkeyTest_testGetPropertyStringNative(
    165     JNIEnv* env, jclass clazz, jbyteArray uuid,
    166     jstring name, jobject outValue) {
    167 
    168     if (NULL == uuid || NULL == name || NULL == outValue) {
    169         jniThrowException(env, "java/lang/NullPointerException",
    170                 "One or more null input parameters");
    171         return JNI_FALSE;
    172     }
    173 
    174     Uuid juuid = jbyteArrayToUuid(env, uuid);
    175     if (!isUuidSizeValid(juuid)) {
    176         jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
    177                 "invalid UUID size, expected %u bytes", kUuidSize);
    178         return JNI_FALSE;
    179     }
    180 
    181     AMediaObjects aMediaObjects;
    182     aMediaObjects.setDrm(AMediaDrm_createByUUID(&juuid[0]));
    183     if (NULL == aMediaObjects.getDrm()) {
    184         jniThrowException(env, "java/lang/RuntimeException", "null MediaDrm");
    185         return JNI_FALSE;
    186     }
    187 
    188     const char *utf8_name = env->GetStringUTFChars(name, NULL);
    189     const char *utf8_outValue = NULL;
    190     media_status_t status = AMediaDrm_getPropertyString(aMediaObjects.getDrm(),
    191             utf8_name, &utf8_outValue);
    192     env->ReleaseStringUTFChars(name, utf8_name);
    193 
    194     if (NULL != utf8_outValue) {
    195         clazz = env->GetObjectClass(outValue);
    196         jmethodID mId = env->GetMethodID (clazz, "append",
    197                 "(Ljava/lang/String;)Ljava/lang/StringBuffer;");
    198         jstring outString = env->NewStringUTF(
    199                 static_cast<const char *>(utf8_outValue));
    200         env->CallObjectMethod(outValue, mId, outString);
    201     } else {
    202         jniThrowExceptionFmt(env, "java/lang/RuntimeException",
    203                 "get property string returns %d", status);
    204         return JNI_FALSE;
    205     }
    206     return JNI_TRUE;
    207 }
    208 
    209 extern "C" jboolean Java_android_media_cts_NativeMediaDrmClearkeyTest__testPsshNative(
    210     JNIEnv* env, jclass /*clazz*/, jbyteArray uuid, jstring videoUrl) {
    211 
    212     if (NULL == uuid || NULL == videoUrl) {
    213         jniThrowException(env, "java/lang/NullPointerException",
    214                 "null uuid or null videoUrl");
    215         return JNI_FALSE;
    216     }
    217 
    218     Uuid juuid = jbyteArrayToUuid(env, uuid);
    219     if (!isUuidSizeValid(juuid)) {
    220         jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
    221                 "invalid UUID size, expected %u bytes", kUuidSize);
    222         return JNI_FALSE;
    223     }
    224 
    225     AMediaObjects aMediaObjects;
    226     aMediaObjects.setVideoExtractor(AMediaExtractor_new());
    227     const char* url = env->GetStringUTFChars(videoUrl, 0);
    228     if (url) {
    229         media_status_t status = AMediaExtractor_setDataSource(
    230             aMediaObjects.getVideoExtractor(), url);
    231         env->ReleaseStringUTFChars(videoUrl, url);
    232 
    233         if (status != AMEDIA_OK) {
    234             jniThrowExceptionFmt(env, "java/lang/RuntimeException",
    235                     "set video data source error=%d", status);
    236             return JNI_FALSE;
    237         }
    238     }
    239 
    240     PsshInfo* psshInfo = AMediaExtractor_getPsshInfo(aMediaObjects.getVideoExtractor());
    241     if (psshInfo == NULL) {
    242         jniThrowException(env, "java/lang/RuntimeException", "null psshInfo");
    243         return JNI_FALSE;
    244     }
    245 
    246     jboolean testResult = JNI_FALSE;
    247     for (size_t i = 0; i < psshInfo->numentries; i++) {
    248         PsshEntry *entry = &psshInfo->entries[i];
    249 
    250         if (0 == memcmp(entry->uuid, kClearKeyUuid, sizeof(entry->uuid))) {
    251             aMediaObjects.setDrm(AMediaDrm_createByUUID(&juuid[0]));
    252             if (aMediaObjects.getDrm()) {
    253                 testResult = JNI_TRUE;
    254             } else {
    255                 ALOGE("Failed to create media drm=%zd", i);
    256                 testResult = JNI_FALSE;
    257             }
    258             break;
    259         }
    260     }
    261     return testResult;
    262 }
    263 
    264 static bool isVideo(const char* mime) {
    265     return !strncmp(mime, "video/", 6) ? true : false;
    266 }
    267 
    268 static bool isAudio(const char* mime) {
    269     return !strncmp(mime, "audio/", 6) ? true : false;
    270 }
    271 
    272 static void addTrack(const AMediaFormat* format,
    273         const char* mime, const AMediaCrypto* crypto,
    274         const ANativeWindow* window, AMediaCodec** codec) {
    275 
    276     *codec = AMediaCodec_createDecoderByType(mime);
    277     if (codec == NULL) {
    278         ALOGE("cannot create codec for %s", mime);
    279         return;
    280     }
    281 
    282     AMediaCodec_configure(*codec, format,
    283             const_cast<ANativeWindow*>(window),
    284             const_cast<AMediaCrypto*>(crypto), 0);
    285 }
    286 
    287 static void addTracks(const AMediaExtractor* extractor,
    288         const AMediaCrypto* crypto, const ANativeWindow* window,
    289         AMediaCodec** codec) {
    290     size_t numTracks = AMediaExtractor_getTrackCount(
    291         const_cast<AMediaExtractor*>(extractor));
    292 
    293     AMediaFormat* trackFormat = NULL;
    294     for (size_t i = 0; i < numTracks; ++i) {
    295         trackFormat = AMediaExtractor_getTrackFormat(
    296             const_cast<AMediaExtractor*>(extractor), i);
    297         if (trackFormat) {
    298             ALOGV("track %zd format: %s", i,
    299                     AMediaFormat_toString(trackFormat));
    300 
    301             const char* mime = "";
    302             if (!AMediaFormat_getString(
    303                 trackFormat, AMEDIAFORMAT_KEY_MIME, &mime)) {
    304                 ALOGE("no mime type");
    305 
    306                 AMediaFormat_delete(trackFormat);
    307                 return;
    308             } else if (isAudio(mime) || isVideo(mime)) {
    309                 AMediaExtractor_selectTrack(
    310                     const_cast<AMediaExtractor*>(extractor), i);
    311                 ALOGV("track %zd codec format: %s", i,
    312                         AMediaFormat_toString(trackFormat));
    313 
    314                 addTrack(trackFormat, mime, crypto, window, codec);
    315                 AMediaCodec_start(*codec);
    316                 AMediaCodec_flush(*codec);
    317                 AMediaExtractor_seekTo(
    318                     const_cast<AMediaExtractor*>(extractor), 0,
    319                             AMEDIAEXTRACTOR_SEEK_PREVIOUS_SYNC);
    320             }
    321             AMediaFormat_delete(trackFormat);
    322         }
    323     }
    324 }
    325 
    326 static int64_t getSystemNanoTime() {
    327     timespec now;
    328     clock_gettime(CLOCK_MONOTONIC, &now);
    329     return now.tv_sec * 1000000000LL + now.tv_nsec;
    330 }
    331 
    332 static void fillDecoder(AMediaCodec* codec, AMediaExtractor* extractor,
    333         int64_t* presentationTimeUs, bool* eosReached) {
    334     media_status_t status = AMEDIA_OK;
    335 
    336     ssize_t bufferIndex = AMediaCodec_dequeueInputBuffer(codec, 2000);
    337     if (bufferIndex >= 0) {
    338         size_t bufsize;
    339         uint8_t* buf = AMediaCodec_getInputBuffer(codec, bufferIndex, &bufsize);
    340 
    341         int sampleSize = AMediaExtractor_readSampleData(extractor, buf, bufsize);
    342         if (sampleSize < 0) {
    343             sampleSize = 0;
    344             *eosReached = true;
    345         }
    346 
    347         *presentationTimeUs = AMediaExtractor_getSampleTime(extractor);
    348 
    349         AMediaCodecCryptoInfo *cryptoInfo =
    350             AMediaExtractor_getSampleCryptoInfo(extractor);
    351 
    352         if (cryptoInfo) {
    353             status = AMediaCodec_queueSecureInputBuffer(
    354                 codec, bufferIndex, 0, cryptoInfo,
    355                 *presentationTimeUs,
    356                 *eosReached ? AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM : 0);
    357             AMediaCodecCryptoInfo_delete(cryptoInfo);
    358         } else {
    359             status = AMediaCodec_queueInputBuffer(
    360                 codec, bufferIndex, 0, sampleSize,
    361                 *presentationTimeUs,
    362                 *eosReached ? AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM : 0);
    363         }
    364         AMediaExtractor_advance(extractor);
    365     }
    366 }
    367 
    368 static bool drainDecoder(AMediaCodec* codec, int64_t presentationTimeUs,
    369     int64_t* startTimeNano) {
    370 
    371     AMediaCodecBufferInfo info;
    372     ssize_t bufferIndex  = AMediaCodec_dequeueOutputBuffer(codec, &info, 0);
    373     if (bufferIndex >= 0) {
    374         if (info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
    375             return true;  // eos reached
    376         }
    377 
    378         if (*startTimeNano < 0) {
    379             *startTimeNano = getSystemNanoTime() - (presentationTimeUs * 1000);
    380         }
    381         int64_t delay = (*startTimeNano + presentationTimeUs * 1000) -
    382                 getSystemNanoTime();
    383         if (delay > 0) {
    384             usleep(delay / 1000);
    385         }
    386 
    387         AMediaCodec_releaseOutputBuffer(codec, bufferIndex, info.size != 0);
    388     } else if (bufferIndex == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
    389         ALOGV("output buffers changed");
    390     } else if (bufferIndex == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
    391         AMediaFormat* format = AMediaCodec_getOutputFormat(codec);
    392         ALOGV("format changed to: %s", AMediaFormat_toString(format));
    393         AMediaFormat_delete(format);
    394     } else if (bufferIndex == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
    395          ALOGV("no output buffer right now");
    396          usleep(20000);
    397     } else {
    398          ALOGV("unexpected info code: %zd", bufferIndex);
    399     }
    400     return false;
    401 }
    402 
    403 static jboolean playContent(JNIEnv* env, const AMediaObjects& aMediaObjects,
    404         PlaybackParams& params, const AMediaDrmSessionId& sessionId, Uuid uuid) {
    405 
    406     ANativeWindow *window = ANativeWindow_fromSurface(env, params.surface);
    407     AMediaExtractor* audioExtractor = aMediaObjects.getAudioExtractor();
    408     AMediaExtractor* videoExtractor = aMediaObjects.getVideoExtractor();
    409 
    410     AMediaCodec* audioCodec = NULL;
    411     AMediaCodec* videoCodec = NULL;
    412     AMediaCrypto* crypto = NULL;
    413 
    414     crypto = AMediaCrypto_new(&uuid[0], sessionId.ptr, sessionId.length);
    415     if (crypto == NULL) {
    416         jniThrowException(env, "java/lang/RuntimeException",
    417                 "failed to create crypto object");
    418         return JNI_FALSE;
    419     }
    420 
    421     addTracks(audioExtractor, NULL, NULL, &audioCodec);
    422 
    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 
    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 
    499 static void acquireLicense(
    500     JNIEnv* env, const AMediaObjects& aMediaObjects, const AMediaDrmSessionId& sessionId,
    501     AMediaDrmKeyType keyType) {
    502     // Pointer to keyRequest memory, which remains until the next
    503     // AMediaDrm_getKeyRequest call or until the drm object is released.
    504     const uint8_t* keyRequest;
    505     size_t keyRequestSize = 0;
    506     std::string errorMessage;
    507 
    508     // The server recognizes "video/mp4" but not "video/avc".
    509     media_status_t status = AMediaDrm_getKeyRequest(aMediaObjects.getDrm(),
    510             &sessionId, kClearkeyPssh, sizeof(kClearkeyPssh),
    511             "video/mp4" /*mimeType*/, keyType,
    512             NULL, 0, &keyRequest, &keyRequestSize);
    513     if (status != AMEDIA_OK) {
    514         errorMessage.assign("getKeyRequest failed, error = %d");
    515         goto errorOut;
    516     }
    517 
    518     if (kKeyRequestSize != keyRequestSize) {
    519         ALOGE("Invalid keyRequestSize %zd", kKeyRequestSize);
    520         errorMessage.assign("Invalid key request size, error = %d");
    521         status = AMEDIA_DRM_NEED_KEY;
    522         goto errorOut;
    523     }
    524 
    525     if (memcmp(kKeyRequestData, keyRequest, kKeyRequestSize) != 0) {
    526         errorMessage.assign("Invalid key request data is returned, error = %d");
    527         status = AMEDIA_DRM_NEED_KEY;
    528         goto errorOut;
    529     }
    530 
    531     AMediaDrmKeySetId keySetId;
    532     gGotVendorDefinedEvent = false;
    533     status = AMediaDrm_provideKeyResponse(aMediaObjects.getDrm(), &sessionId,
    534             reinterpret_cast<const uint8_t*>(kResponse),
    535             sizeof(kResponse), &keySetId);
    536     if (status == AMEDIA_OK) {
    537         return;  // success
    538     }
    539 
    540     errorMessage.assign("provideKeyResponse failed, error = %d");
    541 
    542 errorOut:
    543     AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
    544     jniThrowExceptionFmt(env, "java/lang/RuntimeException", errorMessage.c_str(), status);
    545 }
    546 
    547 extern "C" jboolean Java_android_media_cts_NativeMediaDrmClearkeyTest_testClearKeyPlaybackNative(
    548     JNIEnv* env, jclass /*clazz*/, jbyteArray uuid, jobject playbackParams) {
    549     if (NULL == uuid || NULL == playbackParams) {
    550         jniThrowException(env, "java/lang/NullPointerException",
    551                 "null uuid or null playback parameters");
    552         return JNI_FALSE;
    553     }
    554 
    555     Uuid juuid = jbyteArrayToUuid(env, uuid);
    556     if (!isUuidSizeValid(juuid)) {
    557         jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
    558                 "invalid UUID size, expected %u bytes", kUuidSize);
    559         return JNI_FALSE;
    560     }
    561 
    562     PlaybackParams params;
    563     initPlaybackParams(env, playbackParams, params);
    564 
    565     AMediaObjects aMediaObjects;
    566     media_status_t status = AMEDIA_OK;
    567     aMediaObjects.setDrm(AMediaDrm_createByUUID(&juuid[0]));
    568     if (NULL == aMediaObjects.getDrm()) {
    569         jniThrowException(env, "java/lang/RuntimeException", "null MediaDrm");
    570         return JNI_FALSE;
    571     }
    572 
    573     status = AMediaDrm_setOnEventListener(aMediaObjects.getDrm(), listener);
    574     if (status != AMEDIA_OK) {
    575         jniThrowException(env, "java/lang/RuntimeException",
    576                 "setOnEventListener failed");
    577         return JNI_FALSE;
    578     }
    579 
    580     aMediaObjects.setAudioExtractor(AMediaExtractor_new());
    581     const char* url = env->GetStringUTFChars(params.audioUrl, 0);
    582     if (url) {
    583         status = AMediaExtractor_setDataSource(
    584             aMediaObjects.getAudioExtractor(), url);
    585         env->ReleaseStringUTFChars(params.audioUrl, url);
    586 
    587         if (status != AMEDIA_OK) {
    588             jniThrowExceptionFmt(env, "java/lang/RuntimeException",
    589                     "set audio data source error=%d", status);
    590             return JNI_FALSE;
    591         }
    592     }
    593 
    594     aMediaObjects.setVideoExtractor(AMediaExtractor_new());
    595     url = env->GetStringUTFChars(params.videoUrl, 0);
    596     if (url) {
    597         status = AMediaExtractor_setDataSource(
    598             aMediaObjects.getVideoExtractor(), url);
    599         env->ReleaseStringUTFChars(params.videoUrl, url);
    600 
    601         if (status != AMEDIA_OK) {
    602             jniThrowExceptionFmt(env, "java/lang/RuntimeException",
    603                     "set video data source error=%d", status);
    604             return JNI_FALSE;
    605         }
    606     }
    607 
    608     AMediaDrmSessionId sessionId;
    609     status = AMediaDrm_openSession(aMediaObjects.getDrm(), &sessionId);
    610     if (status != AMEDIA_OK) {
    611         jniThrowException(env, "java/lang/RuntimeException",
    612                 "openSession failed");
    613         return JNI_FALSE;
    614     }
    615 
    616     acquireLicense(env, aMediaObjects, sessionId, KEY_TYPE_STREAMING);
    617 
    618     // Check if the event listener has received the expected event sent by
    619     // provideKeyResponse. This is for testing AMediaDrm_setOnEventListener().
    620     const char *utf8_outValue = NULL;
    621     status = AMediaDrm_getPropertyString(aMediaObjects.getDrm(),
    622             "listenerTestSupport", &utf8_outValue);
    623     if (status == AMEDIA_OK && NULL != utf8_outValue) {
    624         std::string eventType(utf8_outValue);
    625         if (eventType.compare("true") == 0) {
    626             int count = 0;
    627             while (!gGotVendorDefinedEvent && count++ < 5) {
    628                // Prevents race condition when the event arrives late
    629                usleep(2000);
    630             }
    631             if (!gGotVendorDefinedEvent) {
    632                 ALOGE("Event listener did not receive the expected event.");
    633                 jniThrowExceptionFmt(env, "java/lang/RuntimeException",
    634                         "Event listener did not receive the expected event.");
    635                 AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
    636                 return JNI_FALSE;
    637            }
    638         }
    639     }
    640 
    641     playContent(env, aMediaObjects, params, sessionId, juuid);
    642 
    643     status = AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
    644     if (status != AMEDIA_OK) {
    645         jniThrowException(env, "java/lang/RuntimeException",
    646                 "closeSession failed");
    647         return JNI_FALSE;
    648     }
    649     return JNI_TRUE;
    650 }
    651 
    652 extern "C" jboolean Java_android_media_cts_NativeMediaDrmClearkeyTest_testQueryKeyStatusNative(
    653     JNIEnv* env, jclass /*clazz*/, jbyteArray uuid) {
    654 
    655     if (NULL == uuid) {
    656         jniThrowException(env, "java/lang/NullPointerException", "null uuid");
    657         return JNI_FALSE;
    658     }
    659 
    660     Uuid juuid = jbyteArrayToUuid(env, uuid);
    661     if (!isUuidSizeValid(juuid)) {
    662         jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
    663                 "invalid UUID size, expected %u bytes", kUuidSize);
    664         return JNI_FALSE;
    665     }
    666 
    667     AMediaObjects aMediaObjects;
    668     media_status_t status = AMEDIA_OK;
    669     aMediaObjects.setDrm(AMediaDrm_createByUUID(&juuid[0]));
    670     if (NULL == aMediaObjects.getDrm()) {
    671         jniThrowException(env, "java/lang/RuntimeException", "failed to create drm");
    672         return JNI_FALSE;
    673     }
    674 
    675     AMediaDrmSessionId sessionId;
    676     status = AMediaDrm_openSession(aMediaObjects.getDrm(), &sessionId);
    677     if (status != AMEDIA_OK) {
    678         jniThrowException(env, "java/lang/RuntimeException",
    679                 "openSession failed");
    680         return JNI_FALSE;
    681     }
    682 
    683     size_t numPairs = 3;
    684     AMediaDrmKeyValue keyStatus[numPairs];
    685 
    686     // Test default key status, should return zero key status
    687     status = AMediaDrm_queryKeyStatus(aMediaObjects.getDrm(), &sessionId, keyStatus, &numPairs);
    688     if (status != AMEDIA_OK) {
    689         jniThrowExceptionFmt(env, "java/lang/RuntimeException",
    690                 "AMediaDrm_queryKeyStatus failed, error = %d", status);
    691         AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
    692         return JNI_FALSE;
    693     }
    694 
    695     if (numPairs != 0) {
    696         jniThrowExceptionFmt(env, "java/lang/RuntimeException",
    697                 "AMediaDrm_queryKeyStatus failed, no policy should be defined");
    698         AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
    699         return JNI_FALSE;
    700     }
    701 
    702     acquireLicense(env, aMediaObjects, sessionId, KEY_TYPE_STREAMING);
    703 
    704     // Test short buffer
    705     numPairs = 2;
    706     status = AMediaDrm_queryKeyStatus(aMediaObjects.getDrm(), &sessionId, keyStatus, &numPairs);
    707     if (status != AMEDIA_DRM_SHORT_BUFFER) {
    708         jniThrowExceptionFmt(env, "java/lang/RuntimeException",
    709                 "AMediaDrm_queryKeyStatus should return AMEDIA_DRM_SHORT_BUFFER, error = %d",
    710                         status);
    711         AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
    712         return JNI_FALSE;
    713     }
    714 
    715     // Test valid key status
    716     numPairs = 3;
    717     status = AMediaDrm_queryKeyStatus(aMediaObjects.getDrm(), &sessionId, keyStatus, &numPairs);
    718     if (status != AMEDIA_OK) {
    719         jniThrowExceptionFmt(env, "java/lang/RuntimeException",
    720                 "AMediaDrm_queryKeyStatus failed, error = %d", status);
    721         AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
    722         return JNI_FALSE;
    723     }
    724 
    725     for (size_t i = 0; i < numPairs; ++i) {
    726         ALOGI("AMediaDrm_queryKeyStatus: key=%s, value=%s", keyStatus[i].mKey, keyStatus[i].mValue);
    727     }
    728 
    729     if (numPairs !=  3) {
    730         jniThrowExceptionFmt(env, "java/lang/RuntimeException",
    731                 "AMediaDrm_queryKeyStatus returns %zd key status, expecting 3", numPairs);
    732         AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
    733         return JNI_FALSE;
    734     }
    735 
    736     status = AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
    737     if (status != AMEDIA_OK) {
    738         jniThrowException(env, "java/lang/RuntimeException",
    739                 "closeSession failed");
    740         return JNI_FALSE;
    741     }
    742     return JNI_TRUE;
    743 }
    744 
    745 extern "C" jboolean Java_android_media_cts_NativeMediaDrmClearkeyTest_testFindSessionIdNative(
    746     JNIEnv* env, jclass /*clazz*/, jbyteArray uuid) {
    747 
    748     if (NULL == uuid) {
    749         jniThrowException(env, "java/lang/NullPointerException", "null uuid");
    750         return JNI_FALSE;
    751     }
    752 
    753     Uuid juuid = jbyteArrayToUuid(env, uuid);
    754     if (!isUuidSizeValid(juuid)) {
    755         jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
    756                 "invalid UUID size, expected %u bytes", kUuidSize);
    757         return JNI_FALSE;
    758     }
    759 
    760     AMediaObjects aMediaObjects;
    761     media_status_t status = AMEDIA_OK;
    762     aMediaObjects.setDrm(AMediaDrm_createByUUID(&juuid[0]));
    763     if (NULL == aMediaObjects.getDrm()) {
    764         jniThrowException(env, "java/lang/RuntimeException", "failed to create drm");
    765         return JNI_FALSE;
    766     }
    767 
    768     // Stores duplicates of session id.
    769     std::vector<std::vector<uint8_t> > sids;
    770 
    771     std::list<AMediaDrmSessionId> sessionIds;
    772     AMediaDrmSessionId sessionId;
    773     for (int i = 0; i < 5; ++i) {
    774         status = AMediaDrm_openSession(aMediaObjects.getDrm(), &sessionId);
    775         if (status != AMEDIA_OK) {
    776             jniThrowException(env, "java/lang/RuntimeException", "openSession failed");
    777             return JNI_FALSE;
    778         }
    779 
    780         // Allocates a new pointer to duplicate the session id returned by
    781         // AMediaDrm_openSession. These new pointers will be passed to
    782         // AMediaDrm_closeSession, which verifies that the ndk
    783         // can find the session id even if the pointer has changed.
    784         sids.push_back(std::vector<uint8_t>(sessionId.length));
    785         memcpy(sids.at(i).data(), sessionId.ptr, sessionId.length);
    786         sessionId.ptr = static_cast<uint8_t *>(sids.at(i).data());
    787         sessionIds.push_back(sessionId);
    788     }
    789 
    790     for (auto sessionId : sessionIds) {
    791         status = AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
    792         if (status != AMEDIA_OK) {
    793             jniThrowException(env, "java/lang/RuntimeException", "closeSession failed");
    794             return JNI_FALSE;
    795         }
    796     }
    797 
    798     return JNI_TRUE;
    799 }
    800 
    801 static JNINativeMethod gMethods[] = {
    802     { "isCryptoSchemeSupportedNative", "([B)Z",
    803             (void *)Java_android_media_cts_NativeMediaDrmClearkeyTest_isCryptoSchemeSupportedNative },
    804 
    805     { "testClearKeyPlaybackNative",
    806             "([BLandroid/media/cts/NativeMediaDrmClearkeyTest$PlaybackParams;)Z",
    807             (void *)Java_android_media_cts_NativeMediaDrmClearkeyTest_testClearKeyPlaybackNative },
    808 
    809     { "testGetPropertyStringNative",
    810             "([BLjava/lang/String;Ljava/lang/StringBuffer;)Z",
    811             (void *)Java_android_media_cts_NativeMediaDrmClearkeyTest_testGetPropertyStringNative },
    812 
    813     { "testPsshNative", "([BLjava/lang/String;)Z",
    814             (void *)Java_android_media_cts_NativeMediaDrmClearkeyTest__testPsshNative },
    815 
    816     { "testQueryKeyStatusNative", "([B)Z",
    817             (void *)Java_android_media_cts_NativeMediaDrmClearkeyTest_testQueryKeyStatusNative },
    818 
    819     { "testFindSessionIdNative", "([B)Z",
    820             (void *)Java_android_media_cts_NativeMediaDrmClearkeyTest_testFindSessionIdNative },
    821 };
    822 
    823 int register_android_media_cts_NativeMediaDrmClearkeyTest(JNIEnv* env) {
    824     jint result = JNI_ERR;
    825     jclass testClass =
    826         env->FindClass("android/media/cts/NativeMediaDrmClearkeyTest");
    827     if (testClass) {
    828         jclass playbackParamsClass = env->FindClass(
    829             "android/media/cts/NativeMediaDrmClearkeyTest$PlaybackParams");
    830         if (playbackParamsClass) {
    831             jclass surfaceClass =
    832                 env->FindClass("android/view/Surface");
    833             if (surfaceClass) {
    834                 gFieldIds.surface = env->GetFieldID(playbackParamsClass,
    835                         "surface", "Landroid/view/Surface;");
    836             } else {
    837                 gFieldIds.surface = NULL;
    838             }
    839             gFieldIds.mimeType = env->GetFieldID(playbackParamsClass,
    840                     "mimeType", "Ljava/lang/String;");
    841             gFieldIds.audioUrl = env->GetFieldID(playbackParamsClass,
    842                     "audioUrl", "Ljava/lang/String;");
    843             gFieldIds.videoUrl = env->GetFieldID(playbackParamsClass,
    844                     "videoUrl", "Ljava/lang/String;");
    845         } else {
    846             ALOGE("PlaybackParams class not found");
    847         }
    848 
    849     } else {
    850         ALOGE("NativeMediaDrmClearkeyTest class not found");
    851     }
    852 
    853     result = env->RegisterNatives(testClass, gMethods,
    854             sizeof(gMethods) / sizeof(JNINativeMethod));
    855     return result;
    856 }
    857