1 /* 2 * Copyright 2013, 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 LOG_NDEBUG 0 18 #define LOG_TAG "MediaDrm-JNI" 19 #include <utils/Log.h> 20 21 #include "android_media_MediaDrm.h" 22 23 #include "android_runtime/AndroidRuntime.h" 24 #include "android_os_Parcel.h" 25 #include "jni.h" 26 #include "JNIHelp.h" 27 28 #include <binder/IServiceManager.h> 29 #include <binder/Parcel.h> 30 #include <media/IDrm.h> 31 #include <media/IMediaPlayerService.h> 32 #include <media/stagefright/foundation/ADebug.h> 33 #include <media/stagefright/MediaErrors.h> 34 35 namespace android { 36 37 #define FIND_CLASS(var, className) \ 38 var = env->FindClass(className); \ 39 LOG_FATAL_IF(! var, "Unable to find class " className); 40 41 #define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \ 42 var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \ 43 LOG_FATAL_IF(! var, "Unable to find field " fieldName); 44 45 #define GET_METHOD_ID(var, clazz, fieldName, fieldDescriptor) \ 46 var = env->GetMethodID(clazz, fieldName, fieldDescriptor); \ 47 LOG_FATAL_IF(! var, "Unable to find method " fieldName); 48 49 #define GET_STATIC_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \ 50 var = env->GetStaticFieldID(clazz, fieldName, fieldDescriptor); \ 51 LOG_FATAL_IF(! var, "Unable to find field " fieldName); 52 53 #define GET_STATIC_METHOD_ID(var, clazz, fieldName, fieldDescriptor) \ 54 var = env->GetStaticMethodID(clazz, fieldName, fieldDescriptor); \ 55 LOG_FATAL_IF(! var, "Unable to find static method " fieldName); 56 57 58 struct RequestFields { 59 jfieldID data; 60 jfieldID defaultUrl; 61 }; 62 63 struct ArrayListFields { 64 jmethodID init; 65 jmethodID add; 66 }; 67 68 struct HashmapFields { 69 jmethodID init; 70 jmethodID get; 71 jmethodID put; 72 jmethodID entrySet; 73 }; 74 75 struct SetFields { 76 jmethodID iterator; 77 }; 78 79 struct IteratorFields { 80 jmethodID next; 81 jmethodID hasNext; 82 }; 83 84 struct EntryFields { 85 jmethodID getKey; 86 jmethodID getValue; 87 }; 88 89 struct EventTypes { 90 jint kEventProvisionRequired; 91 jint kEventKeyRequired; 92 jint kEventKeyExpired; 93 jint kEventVendorDefined; 94 } gEventTypes; 95 96 struct KeyTypes { 97 jint kKeyTypeStreaming; 98 jint kKeyTypeOffline; 99 jint kKeyTypeRelease; 100 } gKeyTypes; 101 102 struct fields_t { 103 jfieldID context; 104 jmethodID post_event; 105 RequestFields keyRequest; 106 RequestFields provisionRequest; 107 ArrayListFields arraylist; 108 HashmapFields hashmap; 109 SetFields set; 110 IteratorFields iterator; 111 EntryFields entry; 112 }; 113 114 static fields_t gFields; 115 116 // ---------------------------------------------------------------------------- 117 // ref-counted object for callbacks 118 class JNIDrmListener: public DrmListener 119 { 120 public: 121 JNIDrmListener(JNIEnv* env, jobject thiz, jobject weak_thiz); 122 ~JNIDrmListener(); 123 virtual void notify(DrmPlugin::EventType eventType, int extra, const Parcel *obj = NULL); 124 private: 125 JNIDrmListener(); 126 jclass mClass; // Reference to MediaDrm class 127 jobject mObject; // Weak ref to MediaDrm Java object to call on 128 }; 129 130 JNIDrmListener::JNIDrmListener(JNIEnv* env, jobject thiz, jobject weak_thiz) 131 { 132 // Hold onto the MediaDrm class for use in calling the static method 133 // that posts events to the application thread. 134 jclass clazz = env->GetObjectClass(thiz); 135 if (clazz == NULL) { 136 ALOGE("Can't find android/media/MediaDrm"); 137 jniThrowException(env, "java/lang/Exception", 138 "Can't find android/media/MediaDrm"); 139 return; 140 } 141 mClass = (jclass)env->NewGlobalRef(clazz); 142 143 // We use a weak reference so the MediaDrm object can be garbage collected. 144 // The reference is only used as a proxy for callbacks. 145 mObject = env->NewGlobalRef(weak_thiz); 146 } 147 148 JNIDrmListener::~JNIDrmListener() 149 { 150 // remove global references 151 JNIEnv *env = AndroidRuntime::getJNIEnv(); 152 env->DeleteGlobalRef(mObject); 153 env->DeleteGlobalRef(mClass); 154 } 155 156 void JNIDrmListener::notify(DrmPlugin::EventType eventType, int extra, 157 const Parcel *obj) 158 { 159 jint jeventType; 160 161 // translate DrmPlugin event types into their java equivalents 162 switch(eventType) { 163 case DrmPlugin::kDrmPluginEventProvisionRequired: 164 jeventType = gEventTypes.kEventProvisionRequired; 165 break; 166 case DrmPlugin::kDrmPluginEventKeyNeeded: 167 jeventType = gEventTypes.kEventKeyRequired; 168 break; 169 case DrmPlugin::kDrmPluginEventKeyExpired: 170 jeventType = gEventTypes.kEventKeyExpired; 171 break; 172 case DrmPlugin::kDrmPluginEventVendorDefined: 173 jeventType = gEventTypes.kEventVendorDefined; 174 break; 175 default: 176 ALOGE("Invalid event DrmPlugin::EventType %d, ignored", (int)eventType); 177 return; 178 } 179 180 JNIEnv *env = AndroidRuntime::getJNIEnv(); 181 if (obj && obj->dataSize() > 0) { 182 jobject jParcel = createJavaParcelObject(env); 183 if (jParcel != NULL) { 184 Parcel* nativeParcel = parcelForJavaObject(env, jParcel); 185 nativeParcel->setData(obj->data(), obj->dataSize()); 186 env->CallStaticVoidMethod(mClass, gFields.post_event, mObject, 187 jeventType, extra, jParcel); 188 } 189 } 190 191 if (env->ExceptionCheck()) { 192 ALOGW("An exception occurred while notifying an event."); 193 LOGW_EX(env); 194 env->ExceptionClear(); 195 } 196 } 197 198 199 static bool throwExceptionAsNecessary( 200 JNIEnv *env, status_t err, const char *msg = NULL) { 201 202 const char *drmMessage = NULL; 203 204 switch(err) { 205 case ERROR_DRM_UNKNOWN: 206 drmMessage = "General DRM error"; 207 break; 208 case ERROR_DRM_NO_LICENSE: 209 drmMessage = "No license"; 210 break; 211 case ERROR_DRM_LICENSE_EXPIRED: 212 drmMessage = "License expired"; 213 break; 214 case ERROR_DRM_SESSION_NOT_OPENED: 215 drmMessage = "Session not opened"; 216 break; 217 case ERROR_DRM_DECRYPT_UNIT_NOT_INITIALIZED: 218 drmMessage = "Not initialized"; 219 break; 220 case ERROR_DRM_DECRYPT: 221 drmMessage = "Decrypt error"; 222 break; 223 case ERROR_DRM_CANNOT_HANDLE: 224 drmMessage = "Unsupported scheme or data format"; 225 break; 226 case ERROR_DRM_TAMPER_DETECTED: 227 drmMessage = "Invalid state"; 228 break; 229 default: 230 break; 231 } 232 233 String8 vendorMessage; 234 if (err >= ERROR_DRM_VENDOR_MIN && err <= ERROR_DRM_VENDOR_MAX) { 235 vendorMessage.format("DRM vendor-defined error: %d", err); 236 drmMessage = vendorMessage.string(); 237 } 238 239 if (err == BAD_VALUE) { 240 jniThrowException(env, "java/lang/IllegalArgumentException", msg); 241 return true; 242 } else if (err == ERROR_DRM_NOT_PROVISIONED) { 243 jniThrowException(env, "android/media/NotProvisionedException", msg); 244 return true; 245 } else if (err == ERROR_DRM_DEVICE_REVOKED) { 246 jniThrowException(env, "android/media/DeniedByServerException", msg); 247 return true; 248 } else if (err != OK) { 249 String8 errbuf; 250 if (drmMessage != NULL) { 251 if (msg == NULL) { 252 msg = drmMessage; 253 } else { 254 errbuf.format("%s: %s", msg, drmMessage); 255 msg = errbuf.string(); 256 } 257 } 258 ALOGE("Illegal state exception: %s", msg); 259 jniThrowException(env, "java/lang/IllegalStateException", msg); 260 return true; 261 } 262 return false; 263 } 264 265 static sp<IDrm> GetDrm(JNIEnv *env, jobject thiz) { 266 JDrm *jdrm = (JDrm *)env->GetIntField(thiz, gFields.context); 267 return jdrm ? jdrm->getDrm() : NULL; 268 } 269 270 JDrm::JDrm( 271 JNIEnv *env, jobject thiz, const uint8_t uuid[16]) { 272 mObject = env->NewWeakGlobalRef(thiz); 273 mDrm = MakeDrm(uuid); 274 if (mDrm != NULL) { 275 mDrm->setListener(this); 276 } 277 } 278 279 JDrm::~JDrm() { 280 mDrm.clear(); 281 282 JNIEnv *env = AndroidRuntime::getJNIEnv(); 283 284 env->DeleteWeakGlobalRef(mObject); 285 mObject = NULL; 286 } 287 288 // static 289 sp<IDrm> JDrm::MakeDrm() { 290 sp<IServiceManager> sm = defaultServiceManager(); 291 292 sp<IBinder> binder = 293 sm->getService(String16("media.player")); 294 295 sp<IMediaPlayerService> service = 296 interface_cast<IMediaPlayerService>(binder); 297 298 if (service == NULL) { 299 return NULL; 300 } 301 302 sp<IDrm> drm = service->makeDrm(); 303 304 if (drm == NULL || (drm->initCheck() != OK && drm->initCheck() != NO_INIT)) { 305 return NULL; 306 } 307 308 return drm; 309 } 310 311 // static 312 sp<IDrm> JDrm::MakeDrm(const uint8_t uuid[16]) { 313 sp<IDrm> drm = MakeDrm(); 314 315 if (drm == NULL) { 316 return NULL; 317 } 318 319 status_t err = drm->createPlugin(uuid); 320 321 if (err != OK) { 322 return NULL; 323 } 324 325 return drm; 326 } 327 328 status_t JDrm::setListener(const sp<DrmListener>& listener) { 329 Mutex::Autolock lock(mLock); 330 mListener = listener; 331 return OK; 332 } 333 334 void JDrm::notify(DrmPlugin::EventType eventType, int extra, const Parcel *obj) { 335 sp<DrmListener> listener; 336 mLock.lock(); 337 listener = mListener; 338 mLock.unlock(); 339 340 if (listener != NULL) { 341 Mutex::Autolock lock(mNotifyLock); 342 listener->notify(eventType, extra, obj); 343 } 344 } 345 346 347 // static 348 bool JDrm::IsCryptoSchemeSupported(const uint8_t uuid[16]) { 349 sp<IDrm> drm = MakeDrm(); 350 351 if (drm == NULL) { 352 return false; 353 } 354 355 return drm->isCryptoSchemeSupported(uuid); 356 } 357 358 status_t JDrm::initCheck() const { 359 return mDrm == NULL ? NO_INIT : OK; 360 } 361 362 // JNI conversion utilities 363 static Vector<uint8_t> JByteArrayToVector(JNIEnv *env, jbyteArray const &byteArray) { 364 Vector<uint8_t> vector; 365 size_t length = env->GetArrayLength(byteArray); 366 vector.insertAt((size_t)0, length); 367 env->GetByteArrayRegion(byteArray, 0, length, (jbyte *)vector.editArray()); 368 return vector; 369 } 370 371 static jbyteArray VectorToJByteArray(JNIEnv *env, Vector<uint8_t> const &vector) { 372 size_t length = vector.size(); 373 jbyteArray result = env->NewByteArray(length); 374 if (result != NULL) { 375 env->SetByteArrayRegion(result, 0, length, (jbyte *)vector.array()); 376 } 377 return result; 378 } 379 380 static String8 JStringToString8(JNIEnv *env, jstring const &jstr) { 381 String8 result; 382 383 const char *s = env->GetStringUTFChars(jstr, NULL); 384 if (s) { 385 result = s; 386 env->ReleaseStringUTFChars(jstr, s); 387 } 388 return result; 389 } 390 391 /* 392 import java.util.HashMap; 393 import java.util.Set; 394 import java.Map.Entry; 395 import jav.util.Iterator; 396 397 HashMap<k, v> hm; 398 Set<Entry<k, v> > s = hm.entrySet(); 399 Iterator i = s.iterator(); 400 Entry e = s.next(); 401 */ 402 403 static KeyedVector<String8, String8> HashMapToKeyedVector(JNIEnv *env, jobject &hashMap) { 404 jclass clazz; 405 FIND_CLASS(clazz, "java/lang/String"); 406 KeyedVector<String8, String8> keyedVector; 407 408 jobject entrySet = env->CallObjectMethod(hashMap, gFields.hashmap.entrySet); 409 if (entrySet) { 410 jobject iterator = env->CallObjectMethod(entrySet, gFields.set.iterator); 411 if (iterator) { 412 jboolean hasNext = env->CallBooleanMethod(iterator, gFields.iterator.hasNext); 413 while (hasNext) { 414 jobject entry = env->CallObjectMethod(iterator, gFields.iterator.next); 415 if (entry) { 416 jobject obj = env->CallObjectMethod(entry, gFields.entry.getKey); 417 if (!env->IsInstanceOf(obj, clazz)) { 418 jniThrowException(env, "java/lang/IllegalArgumentException", 419 "HashMap key is not a String"); 420 } 421 jstring jkey = static_cast<jstring>(obj); 422 423 obj = env->CallObjectMethod(entry, gFields.entry.getValue); 424 if (!env->IsInstanceOf(obj, clazz)) { 425 jniThrowException(env, "java/lang/IllegalArgumentException", 426 "HashMap value is not a String"); 427 } 428 jstring jvalue = static_cast<jstring>(obj); 429 430 String8 key = JStringToString8(env, jkey); 431 String8 value = JStringToString8(env, jvalue); 432 keyedVector.add(key, value); 433 434 env->DeleteLocalRef(jkey); 435 env->DeleteLocalRef(jvalue); 436 hasNext = env->CallBooleanMethod(iterator, gFields.iterator.hasNext); 437 } 438 env->DeleteLocalRef(entry); 439 } 440 env->DeleteLocalRef(iterator); 441 } 442 env->DeleteLocalRef(entrySet); 443 } 444 return keyedVector; 445 } 446 447 static jobject KeyedVectorToHashMap (JNIEnv *env, KeyedVector<String8, String8> const &map) { 448 jclass clazz; 449 FIND_CLASS(clazz, "java/util/HashMap"); 450 jobject hashMap = env->NewObject(clazz, gFields.hashmap.init); 451 for (size_t i = 0; i < map.size(); ++i) { 452 jstring jkey = env->NewStringUTF(map.keyAt(i).string()); 453 jstring jvalue = env->NewStringUTF(map.valueAt(i).string()); 454 env->CallObjectMethod(hashMap, gFields.hashmap.put, jkey, jvalue); 455 env->DeleteLocalRef(jkey); 456 env->DeleteLocalRef(jvalue); 457 } 458 return hashMap; 459 } 460 461 static jobject ListOfVectorsToArrayListOfByteArray(JNIEnv *env, 462 List<Vector<uint8_t> > list) { 463 jclass clazz; 464 FIND_CLASS(clazz, "java/util/ArrayList"); 465 jobject arrayList = env->NewObject(clazz, gFields.arraylist.init); 466 List<Vector<uint8_t> >::iterator iter = list.begin(); 467 while (iter != list.end()) { 468 jbyteArray byteArray = VectorToJByteArray(env, *iter); 469 env->CallBooleanMethod(arrayList, gFields.arraylist.add, byteArray); 470 env->DeleteLocalRef(byteArray); 471 iter++; 472 } 473 474 return arrayList; 475 } 476 477 } // namespace android 478 479 using namespace android; 480 481 static sp<JDrm> setDrm( 482 JNIEnv *env, jobject thiz, const sp<JDrm> &drm) { 483 sp<JDrm> old = (JDrm *)env->GetIntField(thiz, gFields.context); 484 if (drm != NULL) { 485 drm->incStrong(thiz); 486 } 487 if (old != NULL) { 488 old->decStrong(thiz); 489 } 490 env->SetIntField(thiz, gFields.context, (int)drm.get()); 491 492 return old; 493 } 494 495 static bool CheckSession(JNIEnv *env, const sp<IDrm> &drm, jbyteArray const &jsessionId) 496 { 497 if (drm == NULL) { 498 jniThrowException(env, "java/lang/IllegalStateException", "MediaDrm obj is null"); 499 return false; 500 } 501 502 if (jsessionId == NULL) { 503 jniThrowException(env, "java/lang/IllegalArgumentException", "sessionId is null"); 504 return false; 505 } 506 return true; 507 } 508 509 static void android_media_MediaDrm_release(JNIEnv *env, jobject thiz) { 510 sp<JDrm> drm = setDrm(env, thiz, NULL); 511 if (drm != NULL) { 512 drm->setListener(NULL); 513 } 514 } 515 516 static void android_media_MediaDrm_native_init(JNIEnv *env) { 517 jclass clazz; 518 FIND_CLASS(clazz, "android/media/MediaDrm"); 519 GET_FIELD_ID(gFields.context, clazz, "mNativeContext", "I"); 520 GET_STATIC_METHOD_ID(gFields.post_event, clazz, "postEventFromNative", 521 "(Ljava/lang/Object;IILjava/lang/Object;)V"); 522 523 jfieldID field; 524 GET_STATIC_FIELD_ID(field, clazz, "EVENT_PROVISION_REQUIRED", "I"); 525 gEventTypes.kEventProvisionRequired = env->GetStaticIntField(clazz, field); 526 GET_STATIC_FIELD_ID(field, clazz, "EVENT_KEY_REQUIRED", "I"); 527 gEventTypes.kEventKeyRequired = env->GetStaticIntField(clazz, field); 528 GET_STATIC_FIELD_ID(field, clazz, "EVENT_KEY_EXPIRED", "I"); 529 gEventTypes.kEventKeyExpired = env->GetStaticIntField(clazz, field); 530 GET_STATIC_FIELD_ID(field, clazz, "EVENT_VENDOR_DEFINED", "I"); 531 gEventTypes.kEventVendorDefined = env->GetStaticIntField(clazz, field); 532 533 GET_STATIC_FIELD_ID(field, clazz, "KEY_TYPE_STREAMING", "I"); 534 gKeyTypes.kKeyTypeStreaming = env->GetStaticIntField(clazz, field); 535 GET_STATIC_FIELD_ID(field, clazz, "KEY_TYPE_OFFLINE", "I"); 536 gKeyTypes.kKeyTypeOffline = env->GetStaticIntField(clazz, field); 537 GET_STATIC_FIELD_ID(field, clazz, "KEY_TYPE_RELEASE", "I"); 538 gKeyTypes.kKeyTypeRelease = env->GetStaticIntField(clazz, field); 539 540 FIND_CLASS(clazz, "android/media/MediaDrm$KeyRequest"); 541 GET_FIELD_ID(gFields.keyRequest.data, clazz, "mData", "[B"); 542 GET_FIELD_ID(gFields.keyRequest.defaultUrl, clazz, "mDefaultUrl", "Ljava/lang/String;"); 543 544 FIND_CLASS(clazz, "android/media/MediaDrm$ProvisionRequest"); 545 GET_FIELD_ID(gFields.provisionRequest.data, clazz, "mData", "[B"); 546 GET_FIELD_ID(gFields.provisionRequest.defaultUrl, clazz, "mDefaultUrl", "Ljava/lang/String;"); 547 548 FIND_CLASS(clazz, "java/util/ArrayList"); 549 GET_METHOD_ID(gFields.arraylist.init, clazz, "<init>", "()V"); 550 GET_METHOD_ID(gFields.arraylist.add, clazz, "add", "(Ljava/lang/Object;)Z"); 551 552 FIND_CLASS(clazz, "java/util/HashMap"); 553 GET_METHOD_ID(gFields.hashmap.init, clazz, "<init>", "()V"); 554 GET_METHOD_ID(gFields.hashmap.get, clazz, "get", "(Ljava/lang/Object;)Ljava/lang/Object;"); 555 GET_METHOD_ID(gFields.hashmap.put, clazz, "put", 556 "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); 557 GET_METHOD_ID(gFields.hashmap.entrySet, clazz, "entrySet", "()Ljava/util/Set;"); 558 559 FIND_CLASS(clazz, "java/util/Set"); 560 GET_METHOD_ID(gFields.set.iterator, clazz, "iterator", "()Ljava/util/Iterator;"); 561 562 FIND_CLASS(clazz, "java/util/Iterator"); 563 GET_METHOD_ID(gFields.iterator.next, clazz, "next", "()Ljava/lang/Object;"); 564 GET_METHOD_ID(gFields.iterator.hasNext, clazz, "hasNext", "()Z"); 565 566 FIND_CLASS(clazz, "java/util/Map$Entry"); 567 GET_METHOD_ID(gFields.entry.getKey, clazz, "getKey", "()Ljava/lang/Object;"); 568 GET_METHOD_ID(gFields.entry.getValue, clazz, "getValue", "()Ljava/lang/Object;"); 569 } 570 571 static void android_media_MediaDrm_native_setup( 572 JNIEnv *env, jobject thiz, 573 jobject weak_this, jbyteArray uuidObj) { 574 575 if (uuidObj == NULL) { 576 jniThrowException(env, "java/lang/IllegalArgumentException", "uuid is null"); 577 return; 578 } 579 580 Vector<uint8_t> uuid = JByteArrayToVector(env, uuidObj); 581 582 if (uuid.size() != 16) { 583 jniThrowException(env, "java/lang/IllegalArgumentException", 584 "invalid UUID size, expected 16 bytes"); 585 return; 586 } 587 588 sp<JDrm> drm = new JDrm(env, thiz, uuid.array()); 589 590 status_t err = drm->initCheck(); 591 592 if (err != OK) { 593 jniThrowException( 594 env, 595 "android/media/UnsupportedSchemeException", 596 "Failed to instantiate drm object."); 597 return; 598 } 599 600 sp<JNIDrmListener> listener = new JNIDrmListener(env, thiz, weak_this); 601 drm->setListener(listener); 602 setDrm(env, thiz, drm); 603 } 604 605 static void android_media_MediaDrm_native_finalize( 606 JNIEnv *env, jobject thiz) { 607 android_media_MediaDrm_release(env, thiz); 608 } 609 610 static jboolean android_media_MediaDrm_isCryptoSchemeSupportedNative( 611 JNIEnv *env, jobject thiz, jbyteArray uuidObj) { 612 613 if (uuidObj == NULL) { 614 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 615 return false; 616 } 617 618 Vector<uint8_t> uuid = JByteArrayToVector(env, uuidObj); 619 620 if (uuid.size() != 16) { 621 jniThrowException( 622 env, 623 "java/lang/IllegalArgumentException", 624 "invalid UUID size, expected 16 bytes"); 625 return false; 626 } 627 628 return JDrm::IsCryptoSchemeSupported(uuid.array()); 629 } 630 631 static jbyteArray android_media_MediaDrm_openSession( 632 JNIEnv *env, jobject thiz) { 633 sp<IDrm> drm = GetDrm(env, thiz); 634 635 if (drm == NULL) { 636 jniThrowException(env, "java/lang/IllegalStateException", 637 "MediaDrm obj is null"); 638 return NULL; 639 } 640 641 Vector<uint8_t> sessionId; 642 status_t err = drm->openSession(sessionId); 643 644 if (throwExceptionAsNecessary(env, err, "Failed to open session")) { 645 return NULL; 646 } 647 648 return VectorToJByteArray(env, sessionId); 649 } 650 651 static void android_media_MediaDrm_closeSession( 652 JNIEnv *env, jobject thiz, jbyteArray jsessionId) { 653 sp<IDrm> drm = GetDrm(env, thiz); 654 655 if (!CheckSession(env, drm, jsessionId)) { 656 return; 657 } 658 659 Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId)); 660 661 status_t err = drm->closeSession(sessionId); 662 663 throwExceptionAsNecessary(env, err, "Failed to close session"); 664 } 665 666 static jobject android_media_MediaDrm_getKeyRequest( 667 JNIEnv *env, jobject thiz, jbyteArray jsessionId, jbyteArray jinitData, 668 jstring jmimeType, jint jkeyType, jobject joptParams) { 669 sp<IDrm> drm = GetDrm(env, thiz); 670 671 if (!CheckSession(env, drm, jsessionId)) { 672 return NULL; 673 } 674 675 Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId)); 676 677 Vector<uint8_t> initData; 678 if (jinitData != NULL) { 679 initData = JByteArrayToVector(env, jinitData); 680 } 681 682 String8 mimeType; 683 if (jmimeType != NULL) { 684 mimeType = JStringToString8(env, jmimeType); 685 } 686 687 DrmPlugin::KeyType keyType; 688 if (jkeyType == gKeyTypes.kKeyTypeStreaming) { 689 keyType = DrmPlugin::kKeyType_Streaming; 690 } else if (jkeyType == gKeyTypes.kKeyTypeOffline) { 691 keyType = DrmPlugin::kKeyType_Offline; 692 } else if (jkeyType == gKeyTypes.kKeyTypeRelease) { 693 keyType = DrmPlugin::kKeyType_Release; 694 } else { 695 jniThrowException(env, "java/lang/IllegalArgumentException", 696 "invalid keyType"); 697 return NULL; 698 } 699 700 KeyedVector<String8, String8> optParams; 701 if (joptParams != NULL) { 702 optParams = HashMapToKeyedVector(env, joptParams); 703 } 704 705 Vector<uint8_t> request; 706 String8 defaultUrl; 707 708 status_t err = drm->getKeyRequest(sessionId, initData, mimeType, 709 keyType, optParams, request, defaultUrl); 710 711 if (throwExceptionAsNecessary(env, err, "Failed to get key request")) { 712 return NULL; 713 } 714 715 // Fill out return obj 716 jclass clazz; 717 FIND_CLASS(clazz, "android/media/MediaDrm$KeyRequest"); 718 719 jobject keyObj = NULL; 720 721 if (clazz) { 722 keyObj = env->AllocObject(clazz); 723 jbyteArray jrequest = VectorToJByteArray(env, request); 724 env->SetObjectField(keyObj, gFields.keyRequest.data, jrequest); 725 726 jstring jdefaultUrl = env->NewStringUTF(defaultUrl.string()); 727 env->SetObjectField(keyObj, gFields.keyRequest.defaultUrl, jdefaultUrl); 728 } 729 730 return keyObj; 731 } 732 733 static jbyteArray android_media_MediaDrm_provideKeyResponse( 734 JNIEnv *env, jobject thiz, jbyteArray jsessionId, jbyteArray jresponse) { 735 sp<IDrm> drm = GetDrm(env, thiz); 736 737 if (!CheckSession(env, drm, jsessionId)) { 738 return NULL; 739 } 740 741 Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId)); 742 743 if (jresponse == NULL) { 744 jniThrowException(env, "java/lang/IllegalArgumentException", 745 "key response is null"); 746 return NULL; 747 } 748 Vector<uint8_t> response(JByteArrayToVector(env, jresponse)); 749 Vector<uint8_t> keySetId; 750 751 status_t err = drm->provideKeyResponse(sessionId, response, keySetId); 752 753 throwExceptionAsNecessary(env, err, "Failed to handle key response"); 754 return VectorToJByteArray(env, keySetId); 755 } 756 757 static void android_media_MediaDrm_removeKeys( 758 JNIEnv *env, jobject thiz, jbyteArray jkeysetId) { 759 sp<IDrm> drm = GetDrm(env, thiz); 760 761 if (jkeysetId == NULL) { 762 jniThrowException(env, "java/lang/IllegalArgumentException", 763 "keySetId is null"); 764 return; 765 } 766 767 Vector<uint8_t> keySetId(JByteArrayToVector(env, jkeysetId)); 768 769 status_t err = drm->removeKeys(keySetId); 770 771 throwExceptionAsNecessary(env, err, "Failed to remove keys"); 772 } 773 774 static void android_media_MediaDrm_restoreKeys( 775 JNIEnv *env, jobject thiz, jbyteArray jsessionId, 776 jbyteArray jkeysetId) { 777 778 sp<IDrm> drm = GetDrm(env, thiz); 779 780 if (!CheckSession(env, drm, jsessionId)) { 781 return; 782 } 783 784 if (jkeysetId == NULL) { 785 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 786 return; 787 } 788 789 Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId)); 790 Vector<uint8_t> keySetId(JByteArrayToVector(env, jkeysetId)); 791 792 status_t err = drm->restoreKeys(sessionId, keySetId); 793 794 throwExceptionAsNecessary(env, err, "Failed to restore keys"); 795 } 796 797 static jobject android_media_MediaDrm_queryKeyStatus( 798 JNIEnv *env, jobject thiz, jbyteArray jsessionId) { 799 sp<IDrm> drm = GetDrm(env, thiz); 800 801 if (!CheckSession(env, drm, jsessionId)) { 802 return NULL; 803 } 804 Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId)); 805 806 KeyedVector<String8, String8> infoMap; 807 808 status_t err = drm->queryKeyStatus(sessionId, infoMap); 809 810 if (throwExceptionAsNecessary(env, err, "Failed to query key status")) { 811 return NULL; 812 } 813 814 return KeyedVectorToHashMap(env, infoMap); 815 } 816 817 static jobject android_media_MediaDrm_getProvisionRequest( 818 JNIEnv *env, jobject thiz) { 819 sp<IDrm> drm = GetDrm(env, thiz); 820 821 if (drm == NULL) { 822 jniThrowException(env, "java/lang/IllegalStateException", 823 "MediaDrm obj is null"); 824 return NULL; 825 } 826 827 Vector<uint8_t> request; 828 String8 defaultUrl; 829 830 status_t err = drm->getProvisionRequest(request, defaultUrl); 831 832 if (throwExceptionAsNecessary(env, err, "Failed to get provision request")) { 833 return NULL; 834 } 835 836 // Fill out return obj 837 jclass clazz; 838 FIND_CLASS(clazz, "android/media/MediaDrm$ProvisionRequest"); 839 840 jobject provisionObj = NULL; 841 842 if (clazz) { 843 provisionObj = env->AllocObject(clazz); 844 jbyteArray jrequest = VectorToJByteArray(env, request); 845 env->SetObjectField(provisionObj, gFields.provisionRequest.data, jrequest); 846 847 jstring jdefaultUrl = env->NewStringUTF(defaultUrl.string()); 848 env->SetObjectField(provisionObj, gFields.provisionRequest.defaultUrl, jdefaultUrl); 849 } 850 851 return provisionObj; 852 } 853 854 static void android_media_MediaDrm_provideProvisionResponse( 855 JNIEnv *env, jobject thiz, jbyteArray jresponse) { 856 sp<IDrm> drm = GetDrm(env, thiz); 857 858 if (drm == NULL) { 859 jniThrowException(env, "java/lang/IllegalStateException", 860 "MediaDrm obj is null"); 861 return; 862 } 863 864 if (jresponse == NULL) { 865 jniThrowException(env, "java/lang/IllegalArgumentException", 866 "provision response is null"); 867 return; 868 } 869 870 Vector<uint8_t> response(JByteArrayToVector(env, jresponse)); 871 872 status_t err = drm->provideProvisionResponse(response); 873 874 throwExceptionAsNecessary(env, err, "Failed to handle provision response"); 875 } 876 877 static jobject android_media_MediaDrm_getSecureStops( 878 JNIEnv *env, jobject thiz) { 879 sp<IDrm> drm = GetDrm(env, thiz); 880 881 if (drm == NULL) { 882 jniThrowException(env, "java/lang/IllegalStateException", 883 "MediaDrm obj is null"); 884 return NULL; 885 } 886 887 List<Vector<uint8_t> > secureStops; 888 889 status_t err = drm->getSecureStops(secureStops); 890 891 if (throwExceptionAsNecessary(env, err, "Failed to get secure stops")) { 892 return NULL; 893 } 894 895 return ListOfVectorsToArrayListOfByteArray(env, secureStops); 896 } 897 898 static void android_media_MediaDrm_releaseSecureStops( 899 JNIEnv *env, jobject thiz, jbyteArray jssRelease) { 900 sp<IDrm> drm = GetDrm(env, thiz); 901 902 if (drm == NULL) { 903 jniThrowException(env, "java/lang/IllegalStateException", 904 "MediaDrm obj is null"); 905 return; 906 } 907 908 Vector<uint8_t> ssRelease(JByteArrayToVector(env, jssRelease)); 909 910 status_t err = drm->releaseSecureStops(ssRelease); 911 912 throwExceptionAsNecessary(env, err, "Failed to release secure stops"); 913 } 914 915 static jstring android_media_MediaDrm_getPropertyString( 916 JNIEnv *env, jobject thiz, jstring jname) { 917 sp<IDrm> drm = GetDrm(env, thiz); 918 919 if (drm == NULL) { 920 jniThrowException(env, "java/lang/IllegalStateException", 921 "MediaDrm obj is null"); 922 return NULL; 923 } 924 925 if (jname == NULL) { 926 jniThrowException(env, "java/lang/IllegalArgumentException", 927 "property name String is null"); 928 return NULL; 929 } 930 931 String8 name = JStringToString8(env, jname); 932 String8 value; 933 934 status_t err = drm->getPropertyString(name, value); 935 936 if (throwExceptionAsNecessary(env, err, "Failed to get property")) { 937 return NULL; 938 } 939 940 return env->NewStringUTF(value.string()); 941 } 942 943 static jbyteArray android_media_MediaDrm_getPropertyByteArray( 944 JNIEnv *env, jobject thiz, jstring jname) { 945 sp<IDrm> drm = GetDrm(env, thiz); 946 947 if (drm == NULL) { 948 jniThrowException(env, "java/lang/IllegalStateException", 949 "MediaDrm obj is null"); 950 return NULL; 951 } 952 953 if (jname == NULL) { 954 jniThrowException(env, "java/lang/IllegalArgumentException", 955 "property name String is null"); 956 return NULL; 957 } 958 959 String8 name = JStringToString8(env, jname); 960 Vector<uint8_t> value; 961 962 status_t err = drm->getPropertyByteArray(name, value); 963 964 if (throwExceptionAsNecessary(env, err, "Failed to get property")) { 965 return NULL; 966 } 967 968 return VectorToJByteArray(env, value); 969 } 970 971 static void android_media_MediaDrm_setPropertyString( 972 JNIEnv *env, jobject thiz, jstring jname, jstring jvalue) { 973 sp<IDrm> drm = GetDrm(env, thiz); 974 975 if (drm == NULL) { 976 jniThrowException(env, "java/lang/IllegalStateException", 977 "MediaDrm obj is null"); 978 return; 979 } 980 981 if (jname == NULL) { 982 jniThrowException(env, "java/lang/IllegalArgumentException", 983 "property name String is null"); 984 return; 985 } 986 987 if (jvalue == NULL) { 988 jniThrowException(env, "java/lang/IllegalArgumentException", 989 "property value String is null"); 990 return; 991 } 992 993 String8 name = JStringToString8(env, jname); 994 String8 value = JStringToString8(env, jvalue); 995 996 status_t err = drm->setPropertyString(name, value); 997 998 throwExceptionAsNecessary(env, err, "Failed to set property"); 999 } 1000 1001 static void android_media_MediaDrm_setPropertyByteArray( 1002 JNIEnv *env, jobject thiz, jstring jname, jbyteArray jvalue) { 1003 sp<IDrm> drm = GetDrm(env, thiz); 1004 1005 if (drm == NULL) { 1006 jniThrowException(env, "java/lang/IllegalStateException", 1007 "MediaDrm obj is null"); 1008 return; 1009 } 1010 1011 if (jname == NULL) { 1012 jniThrowException(env, "java/lang/IllegalArgumentException", 1013 "property name String is null"); 1014 return; 1015 } 1016 1017 if (jvalue == NULL) { 1018 jniThrowException(env, "java/lang/IllegalArgumentException", 1019 "property value byte array is null"); 1020 return; 1021 } 1022 1023 String8 name = JStringToString8(env, jname); 1024 Vector<uint8_t> value = JByteArrayToVector(env, jvalue); 1025 1026 status_t err = drm->setPropertyByteArray(name, value); 1027 1028 throwExceptionAsNecessary(env, err, "Failed to set property"); 1029 } 1030 1031 static void android_media_MediaDrm_setCipherAlgorithmNative( 1032 JNIEnv *env, jobject thiz, jobject jdrm, jbyteArray jsessionId, 1033 jstring jalgorithm) { 1034 1035 sp<IDrm> drm = GetDrm(env, jdrm); 1036 1037 if (!CheckSession(env, drm, jsessionId)) { 1038 return; 1039 } 1040 1041 if (jalgorithm == NULL) { 1042 jniThrowException(env, "java/lang/IllegalArgumentException", 1043 "algorithm String is null"); 1044 return; 1045 } 1046 1047 Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId)); 1048 String8 algorithm = JStringToString8(env, jalgorithm); 1049 1050 status_t err = drm->setCipherAlgorithm(sessionId, algorithm); 1051 1052 throwExceptionAsNecessary(env, err, "Failed to set cipher algorithm"); 1053 } 1054 1055 static void android_media_MediaDrm_setMacAlgorithmNative( 1056 JNIEnv *env, jobject thiz, jobject jdrm, jbyteArray jsessionId, 1057 jstring jalgorithm) { 1058 1059 sp<IDrm> drm = GetDrm(env, jdrm); 1060 1061 if (!CheckSession(env, drm, jsessionId)) { 1062 return; 1063 } 1064 1065 if (jalgorithm == NULL) { 1066 jniThrowException(env, "java/lang/IllegalArgumentException", 1067 "algorithm String is null"); 1068 return; 1069 } 1070 1071 Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId)); 1072 String8 algorithm = JStringToString8(env, jalgorithm); 1073 1074 status_t err = drm->setMacAlgorithm(sessionId, algorithm); 1075 1076 throwExceptionAsNecessary(env, err, "Failed to set mac algorithm"); 1077 } 1078 1079 1080 static jbyteArray android_media_MediaDrm_encryptNative( 1081 JNIEnv *env, jobject thiz, jobject jdrm, jbyteArray jsessionId, 1082 jbyteArray jkeyId, jbyteArray jinput, jbyteArray jiv) { 1083 1084 sp<IDrm> drm = GetDrm(env, jdrm); 1085 1086 if (!CheckSession(env, drm, jsessionId)) { 1087 return NULL; 1088 } 1089 1090 if (jkeyId == NULL || jinput == NULL || jiv == NULL) { 1091 jniThrowException(env, "java/lang/IllegalArgumentException", 1092 "required argument is null"); 1093 return NULL; 1094 } 1095 1096 Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId)); 1097 Vector<uint8_t> keyId(JByteArrayToVector(env, jkeyId)); 1098 Vector<uint8_t> input(JByteArrayToVector(env, jinput)); 1099 Vector<uint8_t> iv(JByteArrayToVector(env, jiv)); 1100 Vector<uint8_t> output; 1101 1102 status_t err = drm->encrypt(sessionId, keyId, input, iv, output); 1103 1104 throwExceptionAsNecessary(env, err, "Failed to encrypt"); 1105 1106 return VectorToJByteArray(env, output); 1107 } 1108 1109 static jbyteArray android_media_MediaDrm_decryptNative( 1110 JNIEnv *env, jobject thiz, jobject jdrm, jbyteArray jsessionId, 1111 jbyteArray jkeyId, jbyteArray jinput, jbyteArray jiv) { 1112 1113 sp<IDrm> drm = GetDrm(env, jdrm); 1114 1115 if (!CheckSession(env, drm, jsessionId)) { 1116 return NULL; 1117 } 1118 1119 if (jkeyId == NULL || jinput == NULL || jiv == NULL) { 1120 jniThrowException(env, "java/lang/IllegalArgumentException", 1121 "required argument is null"); 1122 return NULL; 1123 } 1124 1125 Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId)); 1126 Vector<uint8_t> keyId(JByteArrayToVector(env, jkeyId)); 1127 Vector<uint8_t> input(JByteArrayToVector(env, jinput)); 1128 Vector<uint8_t> iv(JByteArrayToVector(env, jiv)); 1129 Vector<uint8_t> output; 1130 1131 status_t err = drm->decrypt(sessionId, keyId, input, iv, output); 1132 throwExceptionAsNecessary(env, err, "Failed to decrypt"); 1133 1134 return VectorToJByteArray(env, output); 1135 } 1136 1137 static jbyteArray android_media_MediaDrm_signNative( 1138 JNIEnv *env, jobject thiz, jobject jdrm, jbyteArray jsessionId, 1139 jbyteArray jkeyId, jbyteArray jmessage) { 1140 1141 sp<IDrm> drm = GetDrm(env, jdrm); 1142 1143 if (!CheckSession(env, drm, jsessionId)) { 1144 return NULL; 1145 } 1146 1147 if (jkeyId == NULL || jmessage == NULL) { 1148 jniThrowException(env, "java/lang/IllegalArgumentException", 1149 "required argument is null"); 1150 return NULL; 1151 } 1152 1153 Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId)); 1154 Vector<uint8_t> keyId(JByteArrayToVector(env, jkeyId)); 1155 Vector<uint8_t> message(JByteArrayToVector(env, jmessage)); 1156 Vector<uint8_t> signature; 1157 1158 status_t err = drm->sign(sessionId, keyId, message, signature); 1159 1160 throwExceptionAsNecessary(env, err, "Failed to sign"); 1161 1162 return VectorToJByteArray(env, signature); 1163 } 1164 1165 static jboolean android_media_MediaDrm_verifyNative( 1166 JNIEnv *env, jobject thiz, jobject jdrm, jbyteArray jsessionId, 1167 jbyteArray jkeyId, jbyteArray jmessage, jbyteArray jsignature) { 1168 1169 sp<IDrm> drm = GetDrm(env, jdrm); 1170 1171 if (!CheckSession(env, drm, jsessionId)) { 1172 return false; 1173 } 1174 1175 if (jkeyId == NULL || jmessage == NULL || jsignature == NULL) { 1176 jniThrowException(env, "java/lang/IllegalArgumentException", 1177 "required argument is null"); 1178 return false; 1179 } 1180 1181 Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId)); 1182 Vector<uint8_t> keyId(JByteArrayToVector(env, jkeyId)); 1183 Vector<uint8_t> message(JByteArrayToVector(env, jmessage)); 1184 Vector<uint8_t> signature(JByteArrayToVector(env, jsignature)); 1185 bool match; 1186 1187 status_t err = drm->verify(sessionId, keyId, message, signature, match); 1188 1189 throwExceptionAsNecessary(env, err, "Failed to verify"); 1190 return match; 1191 } 1192 1193 1194 static JNINativeMethod gMethods[] = { 1195 { "release", "()V", (void *)android_media_MediaDrm_release }, 1196 { "native_init", "()V", (void *)android_media_MediaDrm_native_init }, 1197 1198 { "native_setup", "(Ljava/lang/Object;[B)V", 1199 (void *)android_media_MediaDrm_native_setup }, 1200 1201 { "native_finalize", "()V", 1202 (void *)android_media_MediaDrm_native_finalize }, 1203 1204 { "isCryptoSchemeSupportedNative", "([B)Z", 1205 (void *)android_media_MediaDrm_isCryptoSchemeSupportedNative }, 1206 1207 { "openSession", "()[B", 1208 (void *)android_media_MediaDrm_openSession }, 1209 1210 { "closeSession", "([B)V", 1211 (void *)android_media_MediaDrm_closeSession }, 1212 1213 { "getKeyRequest", "([B[BLjava/lang/String;ILjava/util/HashMap;)" 1214 "Landroid/media/MediaDrm$KeyRequest;", 1215 (void *)android_media_MediaDrm_getKeyRequest }, 1216 1217 { "provideKeyResponse", "([B[B)[B", 1218 (void *)android_media_MediaDrm_provideKeyResponse }, 1219 1220 { "removeKeys", "([B)V", 1221 (void *)android_media_MediaDrm_removeKeys }, 1222 1223 { "restoreKeys", "([B[B)V", 1224 (void *)android_media_MediaDrm_restoreKeys }, 1225 1226 { "queryKeyStatus", "([B)Ljava/util/HashMap;", 1227 (void *)android_media_MediaDrm_queryKeyStatus }, 1228 1229 { "getProvisionRequest", "()Landroid/media/MediaDrm$ProvisionRequest;", 1230 (void *)android_media_MediaDrm_getProvisionRequest }, 1231 1232 { "provideProvisionResponse", "([B)V", 1233 (void *)android_media_MediaDrm_provideProvisionResponse }, 1234 1235 { "getSecureStops", "()Ljava/util/List;", 1236 (void *)android_media_MediaDrm_getSecureStops }, 1237 1238 { "releaseSecureStops", "([B)V", 1239 (void *)android_media_MediaDrm_releaseSecureStops }, 1240 1241 { "getPropertyString", "(Ljava/lang/String;)Ljava/lang/String;", 1242 (void *)android_media_MediaDrm_getPropertyString }, 1243 1244 { "getPropertyByteArray", "(Ljava/lang/String;)[B", 1245 (void *)android_media_MediaDrm_getPropertyByteArray }, 1246 1247 { "setPropertyString", "(Ljava/lang/String;Ljava/lang/String;)V", 1248 (void *)android_media_MediaDrm_setPropertyString }, 1249 1250 { "setPropertyByteArray", "(Ljava/lang/String;[B)V", 1251 (void *)android_media_MediaDrm_setPropertyByteArray }, 1252 1253 { "setCipherAlgorithmNative", 1254 "(Landroid/media/MediaDrm;[BLjava/lang/String;)V", 1255 (void *)android_media_MediaDrm_setCipherAlgorithmNative }, 1256 1257 { "setMacAlgorithmNative", 1258 "(Landroid/media/MediaDrm;[BLjava/lang/String;)V", 1259 (void *)android_media_MediaDrm_setMacAlgorithmNative }, 1260 1261 { "encryptNative", "(Landroid/media/MediaDrm;[B[B[B[B)[B", 1262 (void *)android_media_MediaDrm_encryptNative }, 1263 1264 { "decryptNative", "(Landroid/media/MediaDrm;[B[B[B[B)[B", 1265 (void *)android_media_MediaDrm_decryptNative }, 1266 1267 { "signNative", "(Landroid/media/MediaDrm;[B[B[B)[B", 1268 (void *)android_media_MediaDrm_signNative }, 1269 1270 { "verifyNative", "(Landroid/media/MediaDrm;[B[B[B[B)Z", 1271 (void *)android_media_MediaDrm_verifyNative }, 1272 }; 1273 1274 int register_android_media_Drm(JNIEnv *env) { 1275 return AndroidRuntime::registerNativeMethods(env, 1276 "android/media/MediaDrm", gMethods, NELEM(gMethods)); 1277 } 1278 1279