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