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