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