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_util_Binder.h"
24#include "JNIHelp.h"
25
26#include <android/media/IDescrambler.h>
27#include <binder/MemoryDealer.h>
28#include <media/stagefright/foundation/ADebug.h>
29#include <nativehelper/ScopedLocalRef.h>
30
31namespace android {
32using media::MediaDescrambler::DescrambleInfo;
33
34struct fields_t {
35    jfieldID context;
36};
37
38static fields_t gFields;
39
40static sp<JDescrambler> getDescrambler(JNIEnv *env, jobject thiz) {
41    return (JDescrambler *)env->GetLongField(thiz, gFields.context);
42}
43
44static void setDescrambler(
45        JNIEnv *env, jobject thiz, const sp<JDescrambler> &descrambler) {
46    sp<JDescrambler> old = (JDescrambler *)env->GetLongField(thiz, gFields.context);
47    if (descrambler != NULL) {
48        descrambler->incStrong(thiz);
49    }
50    if (old != NULL) {
51        old->decStrong(thiz);
52    }
53    env->SetLongField(thiz, gFields.context, (jlong)descrambler.get());
54}
55
56static status_t getBufferAndSize(
57        JNIEnv *env, jobject byteBuf, jint offset, jint limit, size_t length,
58        void **outPtr, jbyteArray *outByteArray) {
59    void *ptr = env->GetDirectBufferAddress(byteBuf);
60
61    jbyteArray byteArray = NULL;
62
63    ScopedLocalRef<jclass> byteBufClass(env, env->FindClass("java/nio/ByteBuffer"));
64    CHECK(byteBufClass.get() != NULL);
65
66    if (ptr == NULL) {
67        jmethodID arrayID =
68            env->GetMethodID(byteBufClass.get(), "array", "()[B");
69        CHECK(arrayID != NULL);
70
71        byteArray =
72            (jbyteArray)env->CallObjectMethod(byteBuf, arrayID);
73
74        if (byteArray == NULL) {
75            return INVALID_OPERATION;
76        }
77
78        jboolean isCopy;
79        ptr = env->GetByteArrayElements(byteArray, &isCopy);
80    }
81
82    if ((jint)length + offset > limit) {
83        if (byteArray != NULL) {
84            env->ReleaseByteArrayElements(byteArray, (jbyte *)ptr, 0);
85        }
86
87        return -ERANGE;
88    }
89
90    *outPtr = ptr;
91    *outByteArray = byteArray;
92
93    return OK;
94}
95
96JDescrambler::JDescrambler(JNIEnv *env, jobject descramblerBinderObj) {
97    sp<IDescrambler> cas;
98    if (descramblerBinderObj != NULL) {
99        sp<IBinder> binder = ibinderForJavaObject(env, descramblerBinderObj);
100        mDescrambler = interface_cast<IDescrambler>(binder);
101    }
102}
103
104JDescrambler::~JDescrambler() {
105    // Don't call release() here, it's called by Java class
106    mDescrambler.clear();
107    mMem.clear();
108    mDealer.clear();
109}
110
111void JDescrambler::ensureBufferCapacity(size_t neededSize) {
112    if (mMem != NULL && mMem->size() >= neededSize) {
113        return;
114    }
115
116    ALOGV("ensureBufferCapacity: current size %zu, new size %zu",
117            mMem == NULL ? 0 : mMem->size(), neededSize);
118
119    size_t alignment = MemoryDealer::getAllocationAlignment();
120    neededSize = (neededSize + (alignment - 1)) & ~(alignment - 1);
121    // Align to multiples of 64K.
122    neededSize = (neededSize + 65535) & ~65535;
123    mDealer = new MemoryDealer(neededSize, "JDescrambler");
124    mMem = mDealer->allocate(neededSize);
125}
126
127Status JDescrambler::descramble(
128        jbyte key,
129        size_t numSubSamples,
130        ssize_t totalLength,
131        DescramblerPlugin::SubSample *subSamples,
132        const void *srcPtr,
133        jint srcOffset,
134        void *dstPtr,
135        jint dstOffset,
136        ssize_t *result) {
137    // TODO: IDescrambler::descramble() is re-entrant, however because we
138    // only have 1 shared mem buffer, we can only do 1 descramble at a time.
139    // Concurrency might be improved by allowing on-demand allocation of up
140    // to 2 shared mem buffers.
141    Mutex::Autolock autolock(mSharedMemLock);
142
143    ensureBufferCapacity(totalLength);
144
145    memcpy(mMem->pointer(),
146            (const void*)((const uint8_t*)srcPtr + srcOffset), totalLength);
147
148    DescrambleInfo info;
149    info.dstType = DescrambleInfo::kDestinationTypeVmPointer;
150    info.numSubSamples = numSubSamples;
151    info.scramblingControl = (DescramblerPlugin::ScramblingControl) key;
152    info.subSamples = subSamples;
153    info.srcMem = mMem;
154    info.srcOffset = 0;
155    info.dstPtr = NULL;
156    info.dstOffset = 0;
157
158    int32_t descrambleResult;
159    Status status = mDescrambler->descramble(info, &descrambleResult);
160
161    if (status.isOk()) {
162        *result = (descrambleResult <= totalLength) ? descrambleResult : -1;
163        if (*result > 0) {
164            memcpy((void*)((uint8_t*)dstPtr + dstOffset), mMem->pointer(), *result);
165        }
166    }
167    return status;
168}
169
170}  // namespace android
171
172using namespace android;
173
174static void android_media_MediaDescrambler_native_release(JNIEnv *env, jobject thiz) {
175    setDescrambler(env, thiz, NULL);
176}
177
178static void android_media_MediaDescrambler_native_init(JNIEnv *env) {
179    ScopedLocalRef<jclass> clazz(
180            env, env->FindClass("android/media/MediaDescrambler"));
181    CHECK(clazz.get() != NULL);
182
183    gFields.context = env->GetFieldID(clazz.get(), "mNativeContext", "J");
184    CHECK(gFields.context != NULL);
185}
186
187static void android_media_MediaDescrambler_native_setup(
188        JNIEnv *env, jobject thiz, jobject descramblerBinderObj) {
189    setDescrambler(env, thiz, new JDescrambler(env, descramblerBinderObj));
190}
191
192static ssize_t getSubSampleInfo(JNIEnv *env, jint numSubSamples,
193        jintArray numBytesOfClearDataObj, jintArray numBytesOfEncryptedDataObj,
194        DescramblerPlugin::SubSample **outSubSamples) {
195
196    if (numSubSamples <= 0 || numSubSamples >=
197            (signed)(INT32_MAX / sizeof(DescramblerPlugin::SubSample)) ) {
198        // subSamples array may silently overflow if number of samples are
199        // too large.  Use INT32_MAX as maximum allocation size may be less
200        // than SIZE_MAX on some platforms.
201        ALOGE("numSubSamples is invalid!");
202        return -1;
203    }
204
205    jboolean isCopy;
206    ssize_t totalSize = 0;
207
208    jint *numBytesOfClearData =
209        (numBytesOfClearDataObj == NULL)
210            ? NULL
211            : env->GetIntArrayElements(numBytesOfClearDataObj, &isCopy);
212
213    jint *numBytesOfEncryptedData =
214        (numBytesOfEncryptedDataObj == NULL)
215            ? NULL
216            : env->GetIntArrayElements(numBytesOfEncryptedDataObj, &isCopy);
217
218    DescramblerPlugin::SubSample *subSamples =
219            new(std::nothrow) DescramblerPlugin::SubSample[numSubSamples];
220
221    if (subSamples == NULL) {
222        ALOGE("Failed to allocate SubSample array!");
223        return -1;
224    }
225
226    for (jint i = 0; i < numSubSamples; ++i) {
227        subSamples[i].mNumBytesOfClearData =
228            (numBytesOfClearData == NULL) ? 0 : numBytesOfClearData[i];
229
230        subSamples[i].mNumBytesOfEncryptedData =
231            (numBytesOfEncryptedData == NULL)
232                ? 0 : numBytesOfEncryptedData[i];
233
234        totalSize += subSamples[i].mNumBytesOfClearData +
235                subSamples[i].mNumBytesOfEncryptedData;
236    }
237
238    if (numBytesOfEncryptedData != NULL) {
239        env->ReleaseIntArrayElements(
240                numBytesOfEncryptedDataObj, numBytesOfEncryptedData, 0);
241        numBytesOfEncryptedData = NULL;
242    }
243
244    if (numBytesOfClearData != NULL) {
245        env->ReleaseIntArrayElements(
246                numBytesOfClearDataObj, numBytesOfClearData, 0);
247        numBytesOfClearData = NULL;
248    }
249
250    if (totalSize < 0) {
251        delete[] subSamples;
252        return -1;
253    }
254
255    *outSubSamples = subSamples;
256
257    return totalSize;
258}
259
260static jthrowable createServiceSpecificException(
261        JNIEnv *env, int serviceSpecificError, const char *msg) {
262    if (env->ExceptionCheck()) {
263        ALOGW("Discarding pending exception");
264        env->ExceptionDescribe();
265        env->ExceptionClear();
266    }
267
268    ScopedLocalRef<jclass> clazz(
269            env, env->FindClass("android/os/ServiceSpecificException"));
270    CHECK(clazz.get() != NULL);
271
272    const jmethodID ctor = env->GetMethodID(clazz.get(), "<init>", "(ILjava/lang/String;)V");
273    CHECK(ctor != NULL);
274
275    ScopedLocalRef<jstring> msgObj(
276            env, env->NewStringUTF(msg != NULL ?
277                    msg : String8::format("Error %#x", serviceSpecificError)));
278
279    return (jthrowable)env->NewObject(
280            clazz.get(), ctor, serviceSpecificError, msgObj.get());
281}
282
283static void throwServiceSpecificException(
284        JNIEnv *env, int serviceSpecificError, const char *msg) {
285    jthrowable exception = createServiceSpecificException(env, serviceSpecificError, msg);
286    env->Throw(exception);
287}
288
289static jint android_media_MediaDescrambler_native_descramble(
290        JNIEnv *env, jobject thiz, jbyte key, jint numSubSamples,
291        jintArray numBytesOfClearDataObj, jintArray numBytesOfEncryptedDataObj,
292        jobject srcBuf, jint srcOffset, jint srcLimit,
293        jobject dstBuf, jint dstOffset, jint dstLimit) {
294    sp<JDescrambler> descrambler = getDescrambler(env, thiz);
295    if (descrambler == NULL) {
296        jniThrowException(env, "java/lang/IllegalStateException", NULL);
297        return -1;
298    }
299
300    DescramblerPlugin::SubSample *subSamples = NULL;
301    ssize_t totalLength = getSubSampleInfo(
302            env, numSubSamples, numBytesOfClearDataObj,
303            numBytesOfEncryptedDataObj, &subSamples);
304    if (totalLength < 0) {
305        jniThrowException(env, "java/lang/IllegalArgumentException",
306                "Invalid subsample info!");
307        return -1;
308    }
309
310    ssize_t result = -1;
311    void *srcPtr = NULL, *dstPtr = NULL;
312    jbyteArray srcArray = NULL, dstArray = NULL;
313    status_t err = getBufferAndSize(
314            env, srcBuf, srcOffset, srcLimit, totalLength, &srcPtr, &srcArray);
315
316    if (err == OK) {
317        if (dstBuf == NULL) {
318            dstPtr = srcPtr;
319        } else {
320            err = getBufferAndSize(
321                    env, dstBuf, dstOffset, dstLimit, totalLength, &dstPtr, &dstArray);
322        }
323    }
324
325    if (err != OK) {
326        jniThrowException(env, "java/lang/IllegalArgumentException",
327                "Invalid buffer offset and/or size for subsamples!");
328        return -1;
329    }
330
331    Status status;
332    if (err == OK) {
333        status = descrambler->descramble(
334                key, numSubSamples, totalLength, subSamples,
335                srcPtr, srcOffset, dstPtr, dstOffset, &result);
336    }
337
338    delete[] subSamples;
339    if (srcArray != NULL) {
340        env->ReleaseByteArrayElements(srcArray, (jbyte *)srcPtr, 0);
341    }
342    if (dstArray != NULL) {
343        env->ReleaseByteArrayElements(dstArray, (jbyte *)dstPtr, 0);
344    }
345
346    if (!status.isOk()) {
347        switch (status.exceptionCode()) {
348            case Status::EX_SECURITY:
349                jniThrowException(env, "java/lang/SecurityException",
350                        status.exceptionMessage());
351                break;
352            case Status::EX_BAD_PARCELABLE:
353                jniThrowException(env, "java/lang/BadParcelableException",
354                        status.exceptionMessage());
355                break;
356            case Status::EX_ILLEGAL_ARGUMENT:
357                jniThrowException(env, "java/lang/IllegalArgumentException",
358                        status.exceptionMessage());
359                break;
360            case Status::EX_NULL_POINTER:
361                jniThrowException(env, "java/lang/NullPointerException",
362                        status.exceptionMessage());
363                break;
364            case Status::EX_ILLEGAL_STATE:
365                jniThrowException(env, "java/lang/IllegalStateException",
366                        status.exceptionMessage());
367                break;
368            case Status::EX_NETWORK_MAIN_THREAD:
369                jniThrowException(env, "java/lang/NetworkOnMainThreadException",
370                        status.exceptionMessage());
371                break;
372            case Status::EX_UNSUPPORTED_OPERATION:
373                jniThrowException(env, "java/lang/UnsupportedOperationException",
374                        status.exceptionMessage());
375                break;
376            case Status::EX_SERVICE_SPECIFIC:
377                throwServiceSpecificException(env, status.serviceSpecificErrorCode(),
378                        status.exceptionMessage());
379                break;
380            default:
381            {
382                String8 msg;
383                msg.appendFormat("Unknown exception code: %d, msg: %s",
384                        status.exceptionCode(), status.exceptionMessage().string());
385                jniThrowException(env, "java/lang/RuntimeException", msg.string());
386                break;
387            }
388        }
389    }
390    return result;
391}
392
393static const JNINativeMethod gMethods[] = {
394    { "native_release", "()V",
395            (void *)android_media_MediaDescrambler_native_release },
396    { "native_init", "()V",
397            (void *)android_media_MediaDescrambler_native_init },
398    { "native_setup", "(Landroid/os/IBinder;)V",
399            (void *)android_media_MediaDescrambler_native_setup },
400    { "native_descramble", "(BI[I[ILjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;II)I",
401            (void *)android_media_MediaDescrambler_native_descramble },
402};
403
404int register_android_media_Descrambler(JNIEnv *env) {
405    return AndroidRuntime::registerNativeMethods(env,
406                "android/media/MediaDescrambler", gMethods, NELEM(gMethods));
407}
408
409