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