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