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