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