• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
getDescrambler(JNIEnv * env,jobject thiz)90 static sp<JDescrambler> getDescrambler(JNIEnv *env, jobject thiz) {
91     return (JDescrambler *)env->GetLongField(thiz, gFields.context);
92 }
93 
setDescrambler(JNIEnv * env,jobject thiz,const sp<JDescrambler> & descrambler)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 
getBufferAndSize(JNIEnv * env,jobject byteBuf,jint offset,jint limit,size_t length,void ** outPtr,jbyteArray * outByteArray)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 
JDescrambler(JNIEnv * env,jobject descramblerBinderObj)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 
~JDescrambler()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 
GetDescrambler(JNIEnv * env,jobject obj)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 
ensureBufferCapacity(size_t neededSize)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 
descramble(uint32_t key,ssize_t totalLength,const hidl_vec<SubSample> & subSamples,const void * srcPtr,jint srcOffset,void * dstPtr,jint dstOffset,Status * status,uint32_t * bytesWritten,hidl_string * detailedError)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->unsecurePointer(),
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->unsecurePointer(),
252                 *bytesWritten);
253         } else {
254             // status seems OK but bytesWritten is invalid, we really
255             // have no idea what is wrong.
256             *status = Status::ERROR_CAS_UNKNOWN;
257         }
258     }
259     return OK;
260 }
261 
262 }  // namespace android
263 
264 using namespace android;
265 
android_media_MediaDescrambler_native_release(JNIEnv * env,jobject thiz)266 static void android_media_MediaDescrambler_native_release(JNIEnv *env, jobject thiz) {
267     setDescrambler(env, thiz, NULL);
268 }
269 
android_media_MediaDescrambler_native_init(JNIEnv * env)270 static void android_media_MediaDescrambler_native_init(JNIEnv *env) {
271     ScopedLocalRef<jclass> clazz(
272             env, env->FindClass("android/media/MediaDescrambler"));
273     CHECK(clazz.get() != NULL);
274 
275     gFields.context = env->GetFieldID(clazz.get(), "mNativeContext", "J");
276     CHECK(gFields.context != NULL);
277 
278     jfieldID fieldPesHeader = env->GetStaticFieldID(
279             clazz.get(), "SCRAMBLE_FLAG_PES_HEADER", "B");
280     CHECK(fieldPesHeader != NULL);
281 
282     gFields.flagPesHeader = env->GetStaticByteField(clazz.get(), fieldPesHeader);
283 }
284 
android_media_MediaDescrambler_native_setup(JNIEnv * env,jobject thiz,jobject descramblerBinderObj)285 static void android_media_MediaDescrambler_native_setup(
286         JNIEnv *env, jobject thiz, jobject descramblerBinderObj) {
287     setDescrambler(env, thiz, new JDescrambler(env, descramblerBinderObj));
288 }
289 
getSubSampleInfo(JNIEnv * env,jint numSubSamples,jintArray numBytesOfClearDataObj,jintArray numBytesOfEncryptedDataObj,hidl_vec<SubSample> * outSubSamples)290 static ssize_t getSubSampleInfo(JNIEnv *env, jint numSubSamples,
291         jintArray numBytesOfClearDataObj, jintArray numBytesOfEncryptedDataObj,
292         hidl_vec<SubSample> *outSubSamples) {
293 
294     if (numSubSamples <= 0 ||
295             numSubSamples >= (signed)(INT32_MAX / sizeof(SubSample))) {
296         // subSamples array may silently overflow if number of samples are
297         // too large.  Use INT32_MAX as maximum allocation size may be less
298         // than SIZE_MAX on some platforms.
299         ALOGE("numSubSamples is invalid!");
300         return -1;
301     }
302 
303     jboolean isCopy;
304     ssize_t totalSize = 0;
305 
306     jint *numBytesOfClearData =
307         (numBytesOfClearDataObj == NULL)
308             ? NULL
309             : env->GetIntArrayElements(numBytesOfClearDataObj, &isCopy);
310 
311     jint *numBytesOfEncryptedData =
312         (numBytesOfEncryptedDataObj == NULL)
313             ? NULL
314             : env->GetIntArrayElements(numBytesOfEncryptedDataObj, &isCopy);
315 
316     outSubSamples->resize(numSubSamples);
317     SubSample *subSamples = outSubSamples->data();
318     if (subSamples == NULL) {
319         ALOGE("Failed to allocate SubSample array!");
320         return -1;
321     }
322 
323     for (jint i = 0; i < numSubSamples; ++i) {
324         subSamples[i].numBytesOfClearData =
325             (numBytesOfClearData == NULL) ? 0 : numBytesOfClearData[i];
326 
327         subSamples[i].numBytesOfEncryptedData =
328             (numBytesOfEncryptedData == NULL)
329                 ? 0 : numBytesOfEncryptedData[i];
330 
331         totalSize += subSamples[i].numBytesOfClearData +
332                 subSamples[i].numBytesOfEncryptedData;
333     }
334 
335     if (numBytesOfEncryptedData != NULL) {
336         env->ReleaseIntArrayElements(
337                 numBytesOfEncryptedDataObj, numBytesOfEncryptedData, 0);
338         numBytesOfEncryptedData = NULL;
339     }
340 
341     if (numBytesOfClearData != NULL) {
342         env->ReleaseIntArrayElements(
343                 numBytesOfClearDataObj, numBytesOfClearData, 0);
344         numBytesOfClearData = NULL;
345     }
346 
347     if (totalSize < 0) {
348         return -1;
349     }
350 
351     return totalSize;
352 }
353 
createServiceSpecificException(JNIEnv * env,int serviceSpecificError,const char * msg)354 static jthrowable createServiceSpecificException(
355         JNIEnv *env, int serviceSpecificError, const char *msg) {
356     if (env->ExceptionCheck()) {
357         ALOGW("Discarding pending exception");
358         env->ExceptionDescribe();
359         env->ExceptionClear();
360     }
361 
362     ScopedLocalRef<jclass> clazz(
363             env, env->FindClass("android/os/ServiceSpecificException"));
364     CHECK(clazz.get() != NULL);
365 
366     const jmethodID ctor = env->GetMethodID(clazz.get(), "<init>", "(ILjava/lang/String;)V");
367     CHECK(ctor != NULL);
368 
369     ScopedLocalRef<jstring> msgObj(
370             env, env->NewStringUTF(msg != NULL ?
371                     msg : String8::format("Error %#x", serviceSpecificError)));
372 
373     return (jthrowable)env->NewObject(
374             clazz.get(), ctor, serviceSpecificError, msgObj.get());
375 }
376 
android_media_MediaDescrambler_native_descramble(JNIEnv * env,jobject thiz,jbyte key,jbyte flags,jint numSubSamples,jintArray numBytesOfClearDataObj,jintArray numBytesOfEncryptedDataObj,jobject srcBuf,jint srcOffset,jint srcLimit,jobject dstBuf,jint dstOffset,jint dstLimit)377 static jint android_media_MediaDescrambler_native_descramble(
378         JNIEnv *env, jobject thiz, jbyte key, jbyte flags, jint numSubSamples,
379         jintArray numBytesOfClearDataObj, jintArray numBytesOfEncryptedDataObj,
380         jobject srcBuf, jint srcOffset, jint srcLimit,
381         jobject dstBuf, jint dstOffset, jint dstLimit) {
382     sp<JDescrambler> descrambler = getDescrambler(env, thiz);
383     if (descrambler == NULL) {
384         jniThrowException(env, "java/lang/IllegalStateException",
385                 "Invalid descrambler object!");
386         return -1;
387     }
388 
389     hidl_vec<SubSample> subSamples;
390     ssize_t totalLength = getSubSampleInfo(
391             env, numSubSamples, numBytesOfClearDataObj,
392             numBytesOfEncryptedDataObj, &subSamples);
393     if (totalLength < 0) {
394         jniThrowException(env, "java/lang/IllegalArgumentException",
395                 "Invalid subsample info!");
396         return -1;
397     }
398 
399     void *srcPtr = NULL, *dstPtr = NULL;
400     jbyteArray srcArray = NULL, dstArray = NULL;
401     status_t err = getBufferAndSize(
402             env, srcBuf, srcOffset, srcLimit, totalLength, &srcPtr, &srcArray);
403 
404     if (err == OK) {
405         if (dstBuf == NULL) {
406             dstPtr = srcPtr;
407         } else {
408             err = getBufferAndSize(
409                     env, dstBuf, dstOffset, dstLimit, totalLength, &dstPtr, &dstArray);
410         }
411     }
412 
413     if (err != OK) {
414         jniThrowException(env, "java/lang/IllegalArgumentException",
415                 "Invalid buffer offset and/or size for subsamples!");
416         return -1;
417     }
418 
419     uint32_t scramblingControl = (uint32_t)key;
420 
421     if (flags & gFields.flagPesHeader) {
422         scramblingControl |= DescramblerPlugin::kScrambling_Flag_PesHeader;
423     }
424 
425     Status status;
426     uint32_t bytesWritten;
427     hidl_string detailedError;
428 
429     err = descrambler->descramble(
430             scramblingControl, totalLength, subSamples,
431             srcPtr, srcOffset, dstPtr, dstOffset,
432             &status, &bytesWritten, &detailedError);
433 
434     // Release byte array before throwing
435     if (srcArray != NULL) {
436         env->ReleaseByteArrayElements(srcArray, (jbyte *)srcPtr, 0);
437     }
438     if (dstArray != NULL) {
439         env->ReleaseByteArrayElements(dstArray, (jbyte *)dstPtr, 0);
440     }
441 
442     if (err == NO_MEMORY) {
443         jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
444     } else if (err == FAILED_TRANSACTION) {
445         jniThrowException(env, "android/os/RemoteException", NULL);
446     } else if (status != Status::OK) {
447         // Throw ServiceSpecific with cas error code and detailed msg,
448         // which will be re-thrown as MediaCasStateException.
449         env->Throw(createServiceSpecificException(
450                 env, (int) status, detailedError.c_str()));
451     }
452     return bytesWritten;
453 }
454 
455 static const JNINativeMethod gMethods[] = {
456     { "native_release", "()V",
457             (void *)android_media_MediaDescrambler_native_release },
458     { "native_init", "()V",
459             (void *)android_media_MediaDescrambler_native_init },
460     { "native_setup", "(Landroid/os/IHwBinder;)V",
461             (void *)android_media_MediaDescrambler_native_setup },
462     { "native_descramble", "(BBI[I[ILjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;II)I",
463             (void *)android_media_MediaDescrambler_native_descramble },
464 };
465 
register_android_media_Descrambler(JNIEnv * env)466 int register_android_media_Descrambler(JNIEnv *env) {
467     return AndroidRuntime::registerNativeMethods(env,
468                 "android/media/MediaDescrambler", gMethods, NELEM(gMethods));
469 }
470 
471