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 ¶ms) { 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