android_media_MediaDrm.cpp revision 8117d8f7023f8981bc4b2651efed5b28104d83d3
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]) { 352 sp<IDrm> drm = MakeDrm(); 353 354 if (drm == NULL) { 355 return false; 356 } 357 358 return drm->isCryptoSchemeSupported(uuid); 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) { 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 return JDrm::IsCryptoSchemeSupported(uuid.array()); 632} 633 634static jbyteArray android_media_MediaDrm_openSession( 635 JNIEnv *env, jobject thiz) { 636 sp<IDrm> drm = GetDrm(env, thiz); 637 638 if (drm == NULL) { 639 jniThrowException(env, "java/lang/IllegalStateException", 640 "MediaDrm obj is null"); 641 return NULL; 642 } 643 644 Vector<uint8_t> sessionId; 645 status_t err = drm->openSession(sessionId); 646 647 if (throwExceptionAsNecessary(env, err, "Failed to open session")) { 648 return NULL; 649 } 650 651 return VectorToJByteArray(env, sessionId); 652} 653 654static void android_media_MediaDrm_closeSession( 655 JNIEnv *env, jobject thiz, jbyteArray jsessionId) { 656 sp<IDrm> drm = GetDrm(env, thiz); 657 658 if (!CheckSession(env, drm, jsessionId)) { 659 return; 660 } 661 662 Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId)); 663 664 status_t err = drm->closeSession(sessionId); 665 666 throwExceptionAsNecessary(env, err, "Failed to close session"); 667} 668 669static jobject android_media_MediaDrm_getKeyRequest( 670 JNIEnv *env, jobject thiz, jbyteArray jsessionId, jbyteArray jinitData, 671 jstring jmimeType, jint jkeyType, jobject joptParams) { 672 sp<IDrm> drm = GetDrm(env, thiz); 673 674 if (!CheckSession(env, drm, jsessionId)) { 675 return NULL; 676 } 677 678 Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId)); 679 680 Vector<uint8_t> initData; 681 if (jinitData != NULL) { 682 initData = JByteArrayToVector(env, jinitData); 683 } 684 685 String8 mimeType; 686 if (jmimeType != NULL) { 687 mimeType = JStringToString8(env, jmimeType); 688 } 689 690 DrmPlugin::KeyType keyType; 691 if (jkeyType == gKeyTypes.kKeyTypeStreaming) { 692 keyType = DrmPlugin::kKeyType_Streaming; 693 } else if (jkeyType == gKeyTypes.kKeyTypeOffline) { 694 keyType = DrmPlugin::kKeyType_Offline; 695 } else if (jkeyType == gKeyTypes.kKeyTypeRelease) { 696 keyType = DrmPlugin::kKeyType_Release; 697 } else { 698 jniThrowException(env, "java/lang/IllegalArgumentException", 699 "invalid keyType"); 700 return NULL; 701 } 702 703 KeyedVector<String8, String8> optParams; 704 if (joptParams != NULL) { 705 optParams = HashMapToKeyedVector(env, joptParams); 706 } 707 708 Vector<uint8_t> request; 709 String8 defaultUrl; 710 711 status_t err = drm->getKeyRequest(sessionId, initData, mimeType, 712 keyType, optParams, request, defaultUrl); 713 714 if (throwExceptionAsNecessary(env, err, "Failed to get key request")) { 715 return NULL; 716 } 717 718 // Fill out return obj 719 jclass clazz; 720 FIND_CLASS(clazz, "android/media/MediaDrm$KeyRequest"); 721 722 jobject keyObj = NULL; 723 724 if (clazz) { 725 keyObj = env->AllocObject(clazz); 726 jbyteArray jrequest = VectorToJByteArray(env, request); 727 env->SetObjectField(keyObj, gFields.keyRequest.data, jrequest); 728 729 jstring jdefaultUrl = env->NewStringUTF(defaultUrl.string()); 730 env->SetObjectField(keyObj, gFields.keyRequest.defaultUrl, jdefaultUrl); 731 } 732 733 return keyObj; 734} 735 736static jbyteArray android_media_MediaDrm_provideKeyResponse( 737 JNIEnv *env, jobject thiz, jbyteArray jsessionId, jbyteArray jresponse) { 738 sp<IDrm> drm = GetDrm(env, thiz); 739 740 if (!CheckSession(env, drm, jsessionId)) { 741 return NULL; 742 } 743 744 Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId)); 745 746 if (jresponse == NULL) { 747 jniThrowException(env, "java/lang/IllegalArgumentException", 748 "key response is null"); 749 return NULL; 750 } 751 Vector<uint8_t> response(JByteArrayToVector(env, jresponse)); 752 Vector<uint8_t> keySetId; 753 754 status_t err = drm->provideKeyResponse(sessionId, response, keySetId); 755 756 if (throwExceptionAsNecessary(env, err, "Failed to handle key response")) { 757 return NULL; 758 } 759 return VectorToJByteArray(env, keySetId); 760} 761 762static void android_media_MediaDrm_removeKeys( 763 JNIEnv *env, jobject thiz, jbyteArray jkeysetId) { 764 sp<IDrm> drm = GetDrm(env, thiz); 765 766 if (jkeysetId == NULL) { 767 jniThrowException(env, "java/lang/IllegalArgumentException", 768 "keySetId is null"); 769 return; 770 } 771 772 Vector<uint8_t> keySetId(JByteArrayToVector(env, jkeysetId)); 773 774 status_t err = drm->removeKeys(keySetId); 775 776 throwExceptionAsNecessary(env, err, "Failed to remove keys"); 777} 778 779static void android_media_MediaDrm_restoreKeys( 780 JNIEnv *env, jobject thiz, jbyteArray jsessionId, 781 jbyteArray jkeysetId) { 782 783 sp<IDrm> drm = GetDrm(env, thiz); 784 785 if (!CheckSession(env, drm, jsessionId)) { 786 return; 787 } 788 789 if (jkeysetId == NULL) { 790 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 791 return; 792 } 793 794 Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId)); 795 Vector<uint8_t> keySetId(JByteArrayToVector(env, jkeysetId)); 796 797 status_t err = drm->restoreKeys(sessionId, keySetId); 798 799 throwExceptionAsNecessary(env, err, "Failed to restore keys"); 800} 801 802static jobject android_media_MediaDrm_queryKeyStatus( 803 JNIEnv *env, jobject thiz, jbyteArray jsessionId) { 804 sp<IDrm> drm = GetDrm(env, thiz); 805 806 if (!CheckSession(env, drm, jsessionId)) { 807 return NULL; 808 } 809 Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId)); 810 811 KeyedVector<String8, String8> infoMap; 812 813 status_t err = drm->queryKeyStatus(sessionId, infoMap); 814 815 if (throwExceptionAsNecessary(env, err, "Failed to query key status")) { 816 return NULL; 817 } 818 819 return KeyedVectorToHashMap(env, infoMap); 820} 821 822static jobject android_media_MediaDrm_getProvisionRequest( 823 JNIEnv *env, jobject thiz) { 824 sp<IDrm> drm = GetDrm(env, thiz); 825 826 if (drm == NULL) { 827 jniThrowException(env, "java/lang/IllegalStateException", 828 "MediaDrm obj is null"); 829 return NULL; 830 } 831 832 Vector<uint8_t> request; 833 String8 defaultUrl; 834 835 status_t err = drm->getProvisionRequest(request, defaultUrl); 836 837 if (throwExceptionAsNecessary(env, err, "Failed to get provision request")) { 838 return NULL; 839 } 840 841 // Fill out return obj 842 jclass clazz; 843 FIND_CLASS(clazz, "android/media/MediaDrm$ProvisionRequest"); 844 845 jobject provisionObj = NULL; 846 847 if (clazz) { 848 provisionObj = env->AllocObject(clazz); 849 jbyteArray jrequest = VectorToJByteArray(env, request); 850 env->SetObjectField(provisionObj, gFields.provisionRequest.data, jrequest); 851 852 jstring jdefaultUrl = env->NewStringUTF(defaultUrl.string()); 853 env->SetObjectField(provisionObj, gFields.provisionRequest.defaultUrl, jdefaultUrl); 854 } 855 856 return provisionObj; 857} 858 859static void android_media_MediaDrm_provideProvisionResponse( 860 JNIEnv *env, jobject thiz, jbyteArray jresponse) { 861 sp<IDrm> drm = GetDrm(env, thiz); 862 863 if (drm == NULL) { 864 jniThrowException(env, "java/lang/IllegalStateException", 865 "MediaDrm obj is null"); 866 return; 867 } 868 869 if (jresponse == NULL) { 870 jniThrowException(env, "java/lang/IllegalArgumentException", 871 "provision response is null"); 872 return; 873 } 874 875 Vector<uint8_t> response(JByteArrayToVector(env, jresponse)); 876 877 status_t err = drm->provideProvisionResponse(response); 878 879 throwExceptionAsNecessary(env, err, "Failed to handle provision response"); 880} 881 882static jobject android_media_MediaDrm_getSecureStops( 883 JNIEnv *env, jobject thiz) { 884 sp<IDrm> drm = GetDrm(env, thiz); 885 886 if (drm == NULL) { 887 jniThrowException(env, "java/lang/IllegalStateException", 888 "MediaDrm obj is null"); 889 return NULL; 890 } 891 892 List<Vector<uint8_t> > secureStops; 893 894 status_t err = drm->getSecureStops(secureStops); 895 896 if (throwExceptionAsNecessary(env, err, "Failed to get secure stops")) { 897 return NULL; 898 } 899 900 return ListOfVectorsToArrayListOfByteArray(env, secureStops); 901} 902 903static void android_media_MediaDrm_releaseSecureStops( 904 JNIEnv *env, jobject thiz, jbyteArray jssRelease) { 905 sp<IDrm> drm = GetDrm(env, thiz); 906 907 if (drm == NULL) { 908 jniThrowException(env, "java/lang/IllegalStateException", 909 "MediaDrm obj is null"); 910 return; 911 } 912 913 Vector<uint8_t> ssRelease(JByteArrayToVector(env, jssRelease)); 914 915 status_t err = drm->releaseSecureStops(ssRelease); 916 917 throwExceptionAsNecessary(env, err, "Failed to release secure stops"); 918} 919 920static jstring android_media_MediaDrm_getPropertyString( 921 JNIEnv *env, jobject thiz, jstring jname) { 922 sp<IDrm> drm = GetDrm(env, thiz); 923 924 if (drm == NULL) { 925 jniThrowException(env, "java/lang/IllegalStateException", 926 "MediaDrm obj is null"); 927 return NULL; 928 } 929 930 if (jname == NULL) { 931 jniThrowException(env, "java/lang/IllegalArgumentException", 932 "property name String is null"); 933 return NULL; 934 } 935 936 String8 name = JStringToString8(env, jname); 937 String8 value; 938 939 status_t err = drm->getPropertyString(name, value); 940 941 if (throwExceptionAsNecessary(env, err, "Failed to get property")) { 942 return NULL; 943 } 944 945 return env->NewStringUTF(value.string()); 946} 947 948static jbyteArray android_media_MediaDrm_getPropertyByteArray( 949 JNIEnv *env, jobject thiz, jstring jname) { 950 sp<IDrm> drm = GetDrm(env, thiz); 951 952 if (drm == NULL) { 953 jniThrowException(env, "java/lang/IllegalStateException", 954 "MediaDrm obj is null"); 955 return NULL; 956 } 957 958 if (jname == NULL) { 959 jniThrowException(env, "java/lang/IllegalArgumentException", 960 "property name String is null"); 961 return NULL; 962 } 963 964 String8 name = JStringToString8(env, jname); 965 Vector<uint8_t> value; 966 967 status_t err = drm->getPropertyByteArray(name, value); 968 969 if (throwExceptionAsNecessary(env, err, "Failed to get property")) { 970 return NULL; 971 } 972 973 return VectorToJByteArray(env, value); 974} 975 976static void android_media_MediaDrm_setPropertyString( 977 JNIEnv *env, jobject thiz, jstring jname, jstring jvalue) { 978 sp<IDrm> drm = GetDrm(env, thiz); 979 980 if (drm == NULL) { 981 jniThrowException(env, "java/lang/IllegalStateException", 982 "MediaDrm obj is null"); 983 return; 984 } 985 986 if (jname == NULL) { 987 jniThrowException(env, "java/lang/IllegalArgumentException", 988 "property name String is null"); 989 return; 990 } 991 992 if (jvalue == NULL) { 993 jniThrowException(env, "java/lang/IllegalArgumentException", 994 "property value String is null"); 995 return; 996 } 997 998 String8 name = JStringToString8(env, jname); 999 String8 value = JStringToString8(env, jvalue); 1000 1001 status_t err = drm->setPropertyString(name, value); 1002 1003 throwExceptionAsNecessary(env, err, "Failed to set property"); 1004} 1005 1006static void android_media_MediaDrm_setPropertyByteArray( 1007 JNIEnv *env, jobject thiz, jstring jname, jbyteArray jvalue) { 1008 sp<IDrm> drm = GetDrm(env, thiz); 1009 1010 if (drm == NULL) { 1011 jniThrowException(env, "java/lang/IllegalStateException", 1012 "MediaDrm obj is null"); 1013 return; 1014 } 1015 1016 if (jname == NULL) { 1017 jniThrowException(env, "java/lang/IllegalArgumentException", 1018 "property name String is null"); 1019 return; 1020 } 1021 1022 if (jvalue == NULL) { 1023 jniThrowException(env, "java/lang/IllegalArgumentException", 1024 "property value byte array is null"); 1025 return; 1026 } 1027 1028 String8 name = JStringToString8(env, jname); 1029 Vector<uint8_t> value = JByteArrayToVector(env, jvalue); 1030 1031 status_t err = drm->setPropertyByteArray(name, value); 1032 1033 throwExceptionAsNecessary(env, err, "Failed to set property"); 1034} 1035 1036static void android_media_MediaDrm_setCipherAlgorithmNative( 1037 JNIEnv *env, jobject thiz, jobject jdrm, jbyteArray jsessionId, 1038 jstring jalgorithm) { 1039 1040 sp<IDrm> drm = GetDrm(env, jdrm); 1041 1042 if (!CheckSession(env, drm, jsessionId)) { 1043 return; 1044 } 1045 1046 if (jalgorithm == NULL) { 1047 jniThrowException(env, "java/lang/IllegalArgumentException", 1048 "algorithm String is null"); 1049 return; 1050 } 1051 1052 Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId)); 1053 String8 algorithm = JStringToString8(env, jalgorithm); 1054 1055 status_t err = drm->setCipherAlgorithm(sessionId, algorithm); 1056 1057 throwExceptionAsNecessary(env, err, "Failed to set cipher algorithm"); 1058} 1059 1060static void android_media_MediaDrm_setMacAlgorithmNative( 1061 JNIEnv *env, jobject thiz, jobject jdrm, jbyteArray jsessionId, 1062 jstring jalgorithm) { 1063 1064 sp<IDrm> drm = GetDrm(env, jdrm); 1065 1066 if (!CheckSession(env, drm, jsessionId)) { 1067 return; 1068 } 1069 1070 if (jalgorithm == NULL) { 1071 jniThrowException(env, "java/lang/IllegalArgumentException", 1072 "algorithm String is null"); 1073 return; 1074 } 1075 1076 Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId)); 1077 String8 algorithm = JStringToString8(env, jalgorithm); 1078 1079 status_t err = drm->setMacAlgorithm(sessionId, algorithm); 1080 1081 throwExceptionAsNecessary(env, err, "Failed to set mac algorithm"); 1082} 1083 1084 1085static jbyteArray android_media_MediaDrm_encryptNative( 1086 JNIEnv *env, jobject thiz, jobject jdrm, jbyteArray jsessionId, 1087 jbyteArray jkeyId, jbyteArray jinput, jbyteArray jiv) { 1088 1089 sp<IDrm> drm = GetDrm(env, jdrm); 1090 1091 if (!CheckSession(env, drm, jsessionId)) { 1092 return NULL; 1093 } 1094 1095 if (jkeyId == NULL || jinput == NULL || jiv == NULL) { 1096 jniThrowException(env, "java/lang/IllegalArgumentException", 1097 "required argument is null"); 1098 return NULL; 1099 } 1100 1101 Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId)); 1102 Vector<uint8_t> keyId(JByteArrayToVector(env, jkeyId)); 1103 Vector<uint8_t> input(JByteArrayToVector(env, jinput)); 1104 Vector<uint8_t> iv(JByteArrayToVector(env, jiv)); 1105 Vector<uint8_t> output; 1106 1107 status_t err = drm->encrypt(sessionId, keyId, input, iv, output); 1108 1109 if (throwExceptionAsNecessary(env, err, "Failed to encrypt")) { 1110 return NULL; 1111 } 1112 1113 return VectorToJByteArray(env, output); 1114} 1115 1116static jbyteArray android_media_MediaDrm_decryptNative( 1117 JNIEnv *env, jobject thiz, jobject jdrm, jbyteArray jsessionId, 1118 jbyteArray jkeyId, jbyteArray jinput, jbyteArray jiv) { 1119 1120 sp<IDrm> drm = GetDrm(env, jdrm); 1121 1122 if (!CheckSession(env, drm, jsessionId)) { 1123 return NULL; 1124 } 1125 1126 if (jkeyId == NULL || jinput == NULL || jiv == NULL) { 1127 jniThrowException(env, "java/lang/IllegalArgumentException", 1128 "required argument is null"); 1129 return NULL; 1130 } 1131 1132 Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId)); 1133 Vector<uint8_t> keyId(JByteArrayToVector(env, jkeyId)); 1134 Vector<uint8_t> input(JByteArrayToVector(env, jinput)); 1135 Vector<uint8_t> iv(JByteArrayToVector(env, jiv)); 1136 Vector<uint8_t> output; 1137 1138 status_t err = drm->decrypt(sessionId, keyId, input, iv, output); 1139 if (throwExceptionAsNecessary(env, err, "Failed to decrypt")) { 1140 return NULL; 1141 } 1142 1143 return VectorToJByteArray(env, output); 1144} 1145 1146static jbyteArray android_media_MediaDrm_signNative( 1147 JNIEnv *env, jobject thiz, jobject jdrm, jbyteArray jsessionId, 1148 jbyteArray jkeyId, jbyteArray jmessage) { 1149 1150 sp<IDrm> drm = GetDrm(env, jdrm); 1151 1152 if (!CheckSession(env, drm, jsessionId)) { 1153 return NULL; 1154 } 1155 1156 if (jkeyId == NULL || jmessage == NULL) { 1157 jniThrowException(env, "java/lang/IllegalArgumentException", 1158 "required argument is null"); 1159 return NULL; 1160 } 1161 1162 Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId)); 1163 Vector<uint8_t> keyId(JByteArrayToVector(env, jkeyId)); 1164 Vector<uint8_t> message(JByteArrayToVector(env, jmessage)); 1165 Vector<uint8_t> signature; 1166 1167 status_t err = drm->sign(sessionId, keyId, message, signature); 1168 1169 if (throwExceptionAsNecessary(env, err, "Failed to sign")) { 1170 return NULL; 1171 } 1172 1173 return VectorToJByteArray(env, signature); 1174} 1175 1176static jboolean android_media_MediaDrm_verifyNative( 1177 JNIEnv *env, jobject thiz, jobject jdrm, jbyteArray jsessionId, 1178 jbyteArray jkeyId, jbyteArray jmessage, jbyteArray jsignature) { 1179 1180 sp<IDrm> drm = GetDrm(env, jdrm); 1181 1182 if (!CheckSession(env, drm, jsessionId)) { 1183 return false; 1184 } 1185 1186 if (jkeyId == NULL || jmessage == NULL || jsignature == NULL) { 1187 jniThrowException(env, "java/lang/IllegalArgumentException", 1188 "required argument is null"); 1189 return false; 1190 } 1191 1192 Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId)); 1193 Vector<uint8_t> keyId(JByteArrayToVector(env, jkeyId)); 1194 Vector<uint8_t> message(JByteArrayToVector(env, jmessage)); 1195 Vector<uint8_t> signature(JByteArrayToVector(env, jsignature)); 1196 bool match; 1197 1198 status_t err = drm->verify(sessionId, keyId, message, signature, match); 1199 1200 throwExceptionAsNecessary(env, err, "Failed to verify"); 1201 return match; 1202} 1203 1204 1205static JNINativeMethod gMethods[] = { 1206 { "release", "()V", (void *)android_media_MediaDrm_release }, 1207 { "native_init", "()V", (void *)android_media_MediaDrm_native_init }, 1208 1209 { "native_setup", "(Ljava/lang/Object;[B)V", 1210 (void *)android_media_MediaDrm_native_setup }, 1211 1212 { "native_finalize", "()V", 1213 (void *)android_media_MediaDrm_native_finalize }, 1214 1215 { "isCryptoSchemeSupportedNative", "([B)Z", 1216 (void *)android_media_MediaDrm_isCryptoSchemeSupportedNative }, 1217 1218 { "openSession", "()[B", 1219 (void *)android_media_MediaDrm_openSession }, 1220 1221 { "closeSession", "([B)V", 1222 (void *)android_media_MediaDrm_closeSession }, 1223 1224 { "getKeyRequest", "([B[BLjava/lang/String;ILjava/util/HashMap;)" 1225 "Landroid/media/MediaDrm$KeyRequest;", 1226 (void *)android_media_MediaDrm_getKeyRequest }, 1227 1228 { "provideKeyResponse", "([B[B)[B", 1229 (void *)android_media_MediaDrm_provideKeyResponse }, 1230 1231 { "removeKeys", "([B)V", 1232 (void *)android_media_MediaDrm_removeKeys }, 1233 1234 { "restoreKeys", "([B[B)V", 1235 (void *)android_media_MediaDrm_restoreKeys }, 1236 1237 { "queryKeyStatus", "([B)Ljava/util/HashMap;", 1238 (void *)android_media_MediaDrm_queryKeyStatus }, 1239 1240 { "getProvisionRequest", "()Landroid/media/MediaDrm$ProvisionRequest;", 1241 (void *)android_media_MediaDrm_getProvisionRequest }, 1242 1243 { "provideProvisionResponse", "([B)V", 1244 (void *)android_media_MediaDrm_provideProvisionResponse }, 1245 1246 { "getSecureStops", "()Ljava/util/List;", 1247 (void *)android_media_MediaDrm_getSecureStops }, 1248 1249 { "releaseSecureStops", "([B)V", 1250 (void *)android_media_MediaDrm_releaseSecureStops }, 1251 1252 { "getPropertyString", "(Ljava/lang/String;)Ljava/lang/String;", 1253 (void *)android_media_MediaDrm_getPropertyString }, 1254 1255 { "getPropertyByteArray", "(Ljava/lang/String;)[B", 1256 (void *)android_media_MediaDrm_getPropertyByteArray }, 1257 1258 { "setPropertyString", "(Ljava/lang/String;Ljava/lang/String;)V", 1259 (void *)android_media_MediaDrm_setPropertyString }, 1260 1261 { "setPropertyByteArray", "(Ljava/lang/String;[B)V", 1262 (void *)android_media_MediaDrm_setPropertyByteArray }, 1263 1264 { "setCipherAlgorithmNative", 1265 "(Landroid/media/MediaDrm;[BLjava/lang/String;)V", 1266 (void *)android_media_MediaDrm_setCipherAlgorithmNative }, 1267 1268 { "setMacAlgorithmNative", 1269 "(Landroid/media/MediaDrm;[BLjava/lang/String;)V", 1270 (void *)android_media_MediaDrm_setMacAlgorithmNative }, 1271 1272 { "encryptNative", "(Landroid/media/MediaDrm;[B[B[B[B)[B", 1273 (void *)android_media_MediaDrm_encryptNative }, 1274 1275 { "decryptNative", "(Landroid/media/MediaDrm;[B[B[B[B)[B", 1276 (void *)android_media_MediaDrm_decryptNative }, 1277 1278 { "signNative", "(Landroid/media/MediaDrm;[B[B[B)[B", 1279 (void *)android_media_MediaDrm_signNative }, 1280 1281 { "verifyNative", "(Landroid/media/MediaDrm;[B[B[B[B)Z", 1282 (void *)android_media_MediaDrm_verifyNative }, 1283}; 1284 1285int register_android_media_Drm(JNIEnv *env) { 1286 return AndroidRuntime::registerNativeMethods(env, 1287 "android/media/MediaDrm", gMethods, NELEM(gMethods)); 1288} 1289 1290