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