Home | History | Annotate | Download | only in jni
      1 /*
      2  * Copyright 2017, 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 "MediaDescrambler-JNI"
     19 #include <utils/Log.h>
     20 
     21 #include "android_media_MediaDescrambler.h"
     22 #include "android_runtime/AndroidRuntime.h"
     23 #include "android_os_HwRemoteBinder.h"
     24 #include <nativehelper/JNIHelp.h>
     25 
     26 #include <android/hardware/cas/native/1.0/BpHwDescrambler.h>
     27 #include <android/hardware/cas/native/1.0/BnHwDescrambler.h>
     28 #include <android/hardware/cas/native/1.0/IDescrambler.h>
     29 #include <binder/MemoryDealer.h>
     30 #include <hidl/HidlSupport.h>
     31 #include <hidlmemory/FrameworkUtils.h>
     32 #include <media/stagefright/foundation/ADebug.h>
     33 #include <media/cas/DescramblerAPI.h>
     34 #include <nativehelper/ScopedLocalRef.h>
     35 
     36 namespace android {
     37 class IMemory;
     38 class MemoryDealer;
     39 
     40 namespace hardware {
     41 class HidlMemory;
     42 };
     43 using hardware::fromHeap;
     44 using hardware::HidlMemory;
     45 using hardware::hidl_string;
     46 using hardware::hidl_vec;
     47 using namespace hardware::cas::V1_0;
     48 using namespace hardware::cas::native::V1_0;
     49 
     50 struct JDescrambler : public RefBase {
     51     JDescrambler(JNIEnv *env, jobject descramberBinderObj);
     52 
     53     status_t descramble(
     54             uint32_t key,
     55             ssize_t totalLength,
     56             const hidl_vec<SubSample>& subSamples,
     57             const void *srcPtr,
     58             jint srcOffset,
     59             void *dstPtr,
     60             jint dstOffset,
     61             Status *status,
     62             uint32_t *bytesWritten,
     63             hidl_string *detailedError);
     64 
     65 
     66 protected:
     67     virtual ~JDescrambler();
     68 
     69 private:
     70     sp<IDescrambler> mDescrambler;
     71     sp<IMemory> mMem;
     72     sp<MemoryDealer> mDealer;
     73     sp<HidlMemory> mHidlMemory;
     74     SharedBuffer mDescramblerSrcBuffer;
     75 
     76     Mutex mSharedMemLock;
     77 
     78     bool ensureBufferCapacity(size_t neededSize);
     79 
     80     DISALLOW_EVIL_CONSTRUCTORS(JDescrambler);
     81 };
     82 
     83 struct fields_t {
     84     jfieldID context;
     85     jbyte flagPesHeader;
     86 };
     87 
     88 static fields_t gFields;
     89 
     90 static sp<JDescrambler> getDescrambler(JNIEnv *env, jobject thiz) {
     91     return (JDescrambler *)env->GetLongField(thiz, gFields.context);
     92 }
     93 
     94 static void setDescrambler(
     95         JNIEnv *env, jobject thiz, const sp<JDescrambler> &descrambler) {
     96     sp<JDescrambler> old = (JDescrambler *)env->GetLongField(thiz, gFields.context);
     97     if (descrambler != NULL) {
     98         descrambler->incStrong(thiz);
     99     }
    100     if (old != NULL) {
    101         old->decStrong(thiz);
    102     }
    103     env->SetLongField(thiz, gFields.context, (jlong)descrambler.get());
    104 }
    105 
    106 static status_t getBufferAndSize(
    107         JNIEnv *env, jobject byteBuf, jint offset, jint limit, size_t length,
    108         void **outPtr, jbyteArray *outByteArray) {
    109     void *ptr = env->GetDirectBufferAddress(byteBuf);
    110 
    111     jbyteArray byteArray = NULL;
    112 
    113     ScopedLocalRef<jclass> byteBufClass(env, env->FindClass("java/nio/ByteBuffer"));
    114     CHECK(byteBufClass.get() != NULL);
    115 
    116     if (ptr == NULL) {
    117         jmethodID arrayID =
    118             env->GetMethodID(byteBufClass.get(), "array", "()[B");
    119         CHECK(arrayID != NULL);
    120 
    121         byteArray =
    122             (jbyteArray)env->CallObjectMethod(byteBuf, arrayID);
    123 
    124         if (byteArray == NULL) {
    125             return INVALID_OPERATION;
    126         }
    127 
    128         jboolean isCopy;
    129         ptr = env->GetByteArrayElements(byteArray, &isCopy);
    130     }
    131 
    132     if ((jint)length + offset > limit) {
    133         if (byteArray != NULL) {
    134             env->ReleaseByteArrayElements(byteArray, (jbyte *)ptr, 0);
    135         }
    136 
    137         return -ERANGE;
    138     }
    139 
    140     *outPtr = ptr;
    141     *outByteArray = byteArray;
    142 
    143     return OK;
    144 }
    145 
    146 JDescrambler::JDescrambler(JNIEnv *env, jobject descramblerBinderObj) {
    147     mDescrambler = GetDescrambler(env, descramblerBinderObj);
    148     if (mDescrambler == NULL) {
    149         jniThrowException(env, "java/lang/NullPointerException", NULL);
    150     }
    151 }
    152 
    153 JDescrambler::~JDescrambler() {
    154     // Don't call release() here, it's called by Java class
    155     mDescrambler.clear();
    156     mMem.clear();
    157     mDealer.clear();
    158 }
    159 
    160 sp<IDescrambler> GetDescrambler(JNIEnv *env, jobject obj) {
    161     if (obj != NULL) {
    162         sp<hardware::IBinder> hwBinder =
    163                 JHwRemoteBinder::GetNativeContext(env, obj)->getBinder();
    164 
    165         if (hwBinder != NULL) {
    166             return hardware::fromBinder<
    167                     IDescrambler, BpHwDescrambler, BnHwDescrambler>(hwBinder);
    168         }
    169     }
    170     return NULL;
    171 }
    172 
    173 bool JDescrambler::ensureBufferCapacity(size_t neededSize) {
    174     if (mMem != NULL && mMem->size() >= neededSize) {
    175         return true;
    176     }
    177 
    178     ALOGV("ensureBufferCapacity: current size %zu, new size %zu",
    179             mMem == NULL ? 0 : mMem->size(), neededSize);
    180 
    181     size_t alignment = MemoryDealer::getAllocationAlignment();
    182     neededSize = (neededSize + (alignment - 1)) & ~(alignment - 1);
    183     // Align to multiples of 64K.
    184     neededSize = (neededSize + 65535) & ~65535;
    185     mDealer = new MemoryDealer(neededSize, "JDescrambler");
    186     mMem = mDealer->allocate(neededSize);
    187 
    188     ssize_t offset;
    189     size_t size;
    190     sp<IMemoryHeap> heap = mMem->getMemory(&offset, &size);
    191     if (heap == NULL) {
    192         return false;
    193     }
    194 
    195     mHidlMemory = fromHeap(heap);
    196     mDescramblerSrcBuffer.heapBase = *mHidlMemory;
    197     mDescramblerSrcBuffer.offset = (uint64_t) offset;
    198     mDescramblerSrcBuffer.size = (uint64_t) size;
    199     return true;
    200 }
    201 
    202 status_t JDescrambler::descramble(
    203         uint32_t key,
    204         ssize_t totalLength,
    205         const hidl_vec<SubSample>& subSamples,
    206         const void *srcPtr,
    207         jint srcOffset,
    208         void *dstPtr,
    209         jint dstOffset,
    210         Status *status,
    211         uint32_t *bytesWritten,
    212         hidl_string *detailedError) {
    213     // TODO: IDescrambler::descramble() is re-entrant, however because we
    214     // only have 1 shared mem buffer, we can only do 1 descramble at a time.
    215     // Concurrency might be improved by allowing on-demand allocation of up
    216     // to 2 shared mem buffers.
    217     Mutex::Autolock autolock(mSharedMemLock);
    218 
    219     if (!ensureBufferCapacity(totalLength)) {
    220         return NO_MEMORY;
    221     }
    222 
    223     memcpy(mMem->pointer(),
    224             (const void*)((const uint8_t*)srcPtr + srcOffset), totalLength);
    225 
    226     DestinationBuffer dstBuffer;
    227     dstBuffer.type = BufferType::SHARED_MEMORY;
    228     dstBuffer.nonsecureMemory = mDescramblerSrcBuffer;
    229 
    230     auto err = mDescrambler->descramble(
    231             (ScramblingControl) key,
    232             subSamples,
    233             mDescramblerSrcBuffer,
    234             0,
    235             dstBuffer,
    236             0,
    237             [&status, &bytesWritten, &detailedError] (
    238                     Status _status, uint32_t _bytesWritten,
    239                     const hidl_string& _detailedError) {
    240                 *status = _status;
    241                 *bytesWritten = _bytesWritten;
    242                 *detailedError = _detailedError;
    243             });
    244 
    245     if (!err.isOk()) {
    246         return FAILED_TRANSACTION;
    247     }
    248 
    249     if (*status == Status::OK) {
    250         if (*bytesWritten > 0 && (ssize_t) *bytesWritten <= totalLength) {
    251             memcpy((void*)((uint8_t*)dstPtr + dstOffset), mMem->pointer(), *bytesWritten);
    252         } else {
    253             // status seems OK but bytesWritten is invalid, we really
    254             // have no idea what is wrong.
    255             *status = Status::ERROR_CAS_UNKNOWN;
    256         }
    257     }
    258     return OK;
    259 }
    260 
    261 }  // namespace android
    262 
    263 using namespace android;
    264 
    265 static void android_media_MediaDescrambler_native_release(JNIEnv *env, jobject thiz) {
    266     setDescrambler(env, thiz, NULL);
    267 }
    268 
    269 static void android_media_MediaDescrambler_native_init(JNIEnv *env) {
    270     ScopedLocalRef<jclass> clazz(
    271             env, env->FindClass("android/media/MediaDescrambler"));
    272     CHECK(clazz.get() != NULL);
    273 
    274     gFields.context = env->GetFieldID(clazz.get(), "mNativeContext", "J");
    275     CHECK(gFields.context != NULL);
    276 
    277     jfieldID fieldPesHeader = env->GetStaticFieldID(
    278             clazz.get(), "SCRAMBLE_FLAG_PES_HEADER", "B");
    279     CHECK(fieldPesHeader != NULL);
    280 
    281     gFields.flagPesHeader = env->GetStaticByteField(clazz.get(), fieldPesHeader);
    282 }
    283 
    284 static void android_media_MediaDescrambler_native_setup(
    285         JNIEnv *env, jobject thiz, jobject descramblerBinderObj) {
    286     setDescrambler(env, thiz, new JDescrambler(env, descramblerBinderObj));
    287 }
    288 
    289 static ssize_t getSubSampleInfo(JNIEnv *env, jint numSubSamples,
    290         jintArray numBytesOfClearDataObj, jintArray numBytesOfEncryptedDataObj,
    291         hidl_vec<SubSample> *outSubSamples) {
    292 
    293     if (numSubSamples <= 0 ||
    294             numSubSamples >= (signed)(INT32_MAX / sizeof(SubSample))) {
    295         // subSamples array may silently overflow if number of samples are
    296         // too large.  Use INT32_MAX as maximum allocation size may be less
    297         // than SIZE_MAX on some platforms.
    298         ALOGE("numSubSamples is invalid!");
    299         return -1;
    300     }
    301 
    302     jboolean isCopy;
    303     ssize_t totalSize = 0;
    304 
    305     jint *numBytesOfClearData =
    306         (numBytesOfClearDataObj == NULL)
    307             ? NULL
    308             : env->GetIntArrayElements(numBytesOfClearDataObj, &isCopy);
    309 
    310     jint *numBytesOfEncryptedData =
    311         (numBytesOfEncryptedDataObj == NULL)
    312             ? NULL
    313             : env->GetIntArrayElements(numBytesOfEncryptedDataObj, &isCopy);
    314 
    315     outSubSamples->resize(numSubSamples);
    316     SubSample *subSamples = outSubSamples->data();
    317     if (subSamples == NULL) {
    318         ALOGE("Failed to allocate SubSample array!");
    319         return -1;
    320     }
    321 
    322     for (jint i = 0; i < numSubSamples; ++i) {
    323         subSamples[i].numBytesOfClearData =
    324             (numBytesOfClearData == NULL) ? 0 : numBytesOfClearData[i];
    325 
    326         subSamples[i].numBytesOfEncryptedData =
    327             (numBytesOfEncryptedData == NULL)
    328                 ? 0 : numBytesOfEncryptedData[i];
    329 
    330         totalSize += subSamples[i].numBytesOfClearData +
    331                 subSamples[i].numBytesOfEncryptedData;
    332     }
    333 
    334     if (numBytesOfEncryptedData != NULL) {
    335         env->ReleaseIntArrayElements(
    336                 numBytesOfEncryptedDataObj, numBytesOfEncryptedData, 0);
    337         numBytesOfEncryptedData = NULL;
    338     }
    339 
    340     if (numBytesOfClearData != NULL) {
    341         env->ReleaseIntArrayElements(
    342                 numBytesOfClearDataObj, numBytesOfClearData, 0);
    343         numBytesOfClearData = NULL;
    344     }
    345 
    346     if (totalSize < 0) {
    347         return -1;
    348     }
    349 
    350     return totalSize;
    351 }
    352 
    353 static jthrowable createServiceSpecificException(
    354         JNIEnv *env, int serviceSpecificError, const char *msg) {
    355     if (env->ExceptionCheck()) {
    356         ALOGW("Discarding pending exception");
    357         env->ExceptionDescribe();
    358         env->ExceptionClear();
    359     }
    360 
    361     ScopedLocalRef<jclass> clazz(
    362             env, env->FindClass("android/os/ServiceSpecificException"));
    363     CHECK(clazz.get() != NULL);
    364 
    365     const jmethodID ctor = env->GetMethodID(clazz.get(), "<init>", "(ILjava/lang/String;)V");
    366     CHECK(ctor != NULL);
    367 
    368     ScopedLocalRef<jstring> msgObj(
    369             env, env->NewStringUTF(msg != NULL ?
    370                     msg : String8::format("Error %#x", serviceSpecificError)));
    371 
    372     return (jthrowable)env->NewObject(
    373             clazz.get(), ctor, serviceSpecificError, msgObj.get());
    374 }
    375 
    376 static jint android_media_MediaDescrambler_native_descramble(
    377         JNIEnv *env, jobject thiz, jbyte key, jbyte flags, jint numSubSamples,
    378         jintArray numBytesOfClearDataObj, jintArray numBytesOfEncryptedDataObj,
    379         jobject srcBuf, jint srcOffset, jint srcLimit,
    380         jobject dstBuf, jint dstOffset, jint dstLimit) {
    381     sp<JDescrambler> descrambler = getDescrambler(env, thiz);
    382     if (descrambler == NULL) {
    383         jniThrowException(env, "java/lang/IllegalStateException",
    384                 "Invalid descrambler object!");
    385         return -1;
    386     }
    387 
    388     hidl_vec<SubSample> subSamples;
    389     ssize_t totalLength = getSubSampleInfo(
    390             env, numSubSamples, numBytesOfClearDataObj,
    391             numBytesOfEncryptedDataObj, &subSamples);
    392     if (totalLength < 0) {
    393         jniThrowException(env, "java/lang/IllegalArgumentException",
    394                 "Invalid subsample info!");
    395         return -1;
    396     }
    397 
    398     void *srcPtr = NULL, *dstPtr = NULL;
    399     jbyteArray srcArray = NULL, dstArray = NULL;
    400     status_t err = getBufferAndSize(
    401             env, srcBuf, srcOffset, srcLimit, totalLength, &srcPtr, &srcArray);
    402 
    403     if (err == OK) {
    404         if (dstBuf == NULL) {
    405             dstPtr = srcPtr;
    406         } else {
    407             err = getBufferAndSize(
    408                     env, dstBuf, dstOffset, dstLimit, totalLength, &dstPtr, &dstArray);
    409         }
    410     }
    411 
    412     if (err != OK) {
    413         jniThrowException(env, "java/lang/IllegalArgumentException",
    414                 "Invalid buffer offset and/or size for subsamples!");
    415         return -1;
    416     }
    417 
    418     uint32_t scramblingControl = (uint32_t)key;
    419 
    420     if (flags & gFields.flagPesHeader) {
    421         scramblingControl |= DescramblerPlugin::kScrambling_Flag_PesHeader;
    422     }
    423 
    424     Status status;
    425     uint32_t bytesWritten;
    426     hidl_string detailedError;
    427 
    428     err = descrambler->descramble(
    429             scramblingControl, totalLength, subSamples,
    430             srcPtr, srcOffset, dstPtr, dstOffset,
    431             &status, &bytesWritten, &detailedError);
    432 
    433     // Release byte array before throwing
    434     if (srcArray != NULL) {
    435         env->ReleaseByteArrayElements(srcArray, (jbyte *)srcPtr, 0);
    436     }
    437     if (dstArray != NULL) {
    438         env->ReleaseByteArrayElements(dstArray, (jbyte *)dstPtr, 0);
    439     }
    440 
    441     if (err == NO_MEMORY) {
    442         jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
    443     } else if (err == FAILED_TRANSACTION) {
    444         jniThrowException(env, "android/os/RemoteException", NULL);
    445     } else if (status != Status::OK) {
    446         // Throw ServiceSpecific with cas error code and detailed msg,
    447         // which will be re-thrown as MediaCasStateException.
    448         env->Throw(createServiceSpecificException(
    449                 env, (int) status, detailedError.c_str()));
    450     }
    451     return bytesWritten;
    452 }
    453 
    454 static const JNINativeMethod gMethods[] = {
    455     { "native_release", "()V",
    456             (void *)android_media_MediaDescrambler_native_release },
    457     { "native_init", "()V",
    458             (void *)android_media_MediaDescrambler_native_init },
    459     { "native_setup", "(Landroid/os/IHwBinder;)V",
    460             (void *)android_media_MediaDescrambler_native_setup },
    461     { "native_descramble", "(BBI[I[ILjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;II)I",
    462             (void *)android_media_MediaDescrambler_native_descramble },
    463 };
    464 
    465 int register_android_media_Descrambler(JNIEnv *env) {
    466     return AndroidRuntime::registerNativeMethods(env,
    467                 "android/media/MediaDescrambler", gMethods, NELEM(gMethods));
    468 }
    469 
    470