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
31 namespace android {
32 using media::MediaDescrambler::DescrambleInfo;
33
34 struct fields_t {
35 jfieldID context;
36 };
37
38 static fields_t gFields;
39
getDescrambler(JNIEnv * env,jobject thiz)40 static sp<JDescrambler> getDescrambler(JNIEnv *env, jobject thiz) {
41 return (JDescrambler *)env->GetLongField(thiz, gFields.context);
42 }
43
setDescrambler(JNIEnv * env,jobject thiz,const sp<JDescrambler> & descrambler)44 static 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
getBufferAndSize(JNIEnv * env,jobject byteBuf,jint offset,jint limit,size_t length,void ** outPtr,jbyteArray * outByteArray)56 static 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
JDescrambler(JNIEnv * env,jobject descramblerBinderObj)96 JDescrambler::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
~JDescrambler()104 JDescrambler::~JDescrambler() {
105 // Don't call release() here, it's called by Java class
106 mDescrambler.clear();
107 mMem.clear();
108 mDealer.clear();
109 }
110
ensureBufferCapacity(size_t neededSize)111 void 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
descramble(jbyte key,size_t numSubSamples,ssize_t totalLength,DescramblerPlugin::SubSample * subSamples,const void * srcPtr,jint srcOffset,void * dstPtr,jint dstOffset,ssize_t * result)127 Status 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
172 using namespace android;
173
android_media_MediaDescrambler_native_release(JNIEnv * env,jobject thiz)174 static void android_media_MediaDescrambler_native_release(JNIEnv *env, jobject thiz) {
175 setDescrambler(env, thiz, NULL);
176 }
177
android_media_MediaDescrambler_native_init(JNIEnv * env)178 static 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
android_media_MediaDescrambler_native_setup(JNIEnv * env,jobject thiz,jobject descramblerBinderObj)187 static void android_media_MediaDescrambler_native_setup(
188 JNIEnv *env, jobject thiz, jobject descramblerBinderObj) {
189 setDescrambler(env, thiz, new JDescrambler(env, descramblerBinderObj));
190 }
191
getSubSampleInfo(JNIEnv * env,jint numSubSamples,jintArray numBytesOfClearDataObj,jintArray numBytesOfEncryptedDataObj,DescramblerPlugin::SubSample ** outSubSamples)192 static 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
createServiceSpecificException(JNIEnv * env,int serviceSpecificError,const char * msg)260 static 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
throwServiceSpecificException(JNIEnv * env,int serviceSpecificError,const char * msg)283 static void throwServiceSpecificException(
284 JNIEnv *env, int serviceSpecificError, const char *msg) {
285 jthrowable exception = createServiceSpecificException(env, serviceSpecificError, msg);
286 env->Throw(exception);
287 }
288
android_media_MediaDescrambler_native_descramble(JNIEnv * env,jobject thiz,jbyte key,jint numSubSamples,jintArray numBytesOfClearDataObj,jintArray numBytesOfEncryptedDataObj,jobject srcBuf,jint srcOffset,jint srcLimit,jobject dstBuf,jint dstOffset,jint dstLimit)289 static 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
393 static 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
register_android_media_Descrambler(JNIEnv * env)404 int register_android_media_Descrambler(JNIEnv *env) {
405 return AndroidRuntime::registerNativeMethods(env,
406 "android/media/MediaDescrambler", gMethods, NELEM(gMethods));
407 }
408
409