blob: 75856648f498f84de19c0a85a91ed46f6cc4b598 [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
132ssize_t JDescrambler::descramble(
133 jbyte key,
134 size_t numSubSamples,
135 ssize_t totalLength,
136 DescramblerPlugin::SubSample *subSamples,
137 const void *srcPtr,
138 jint srcOffset,
139 void *dstPtr,
140 jint dstOffset) {
141 // TODO: IDescrambler::descramble() is re-entrant, however because we
142 // only have 1 shared mem buffer, we can only do 1 descramble at a time.
143 // Concurrency might be improved by allowing on-demand allocation of up
144 // to 2 shared mem buffers.
145 Mutex::Autolock autolock(mSharedMemLock);
146
147 ensureBufferCapacity(totalLength);
148
149 memcpy(mMem->pointer(),
150 (const void*)((const uint8_t*)srcPtr + srcOffset), totalLength);
151
152 DescrambleInfo info;
153 info.dstType = DescrambleInfo::kDestinationTypeVmPointer;
154 info.numSubSamples = numSubSamples;
155 info.scramblingControl = (DescramblerPlugin::ScramblingControl) key;
156 info.subSamples = subSamples;
157 info.srcMem = mMem;
158 info.srcOffset = 0;
159 info.dstPtr = NULL;
160 info.dstOffset = 0;
161
162 int32_t result;
163 binder::Status status = mDescrambler->descramble(info, &result);
164
165 if (!status.isOk() || result > totalLength) {
166 return -1;
167 }
168 if (result > 0) {
169 memcpy((void*)((uint8_t*)dstPtr + dstOffset), mMem->pointer(), result);
170 }
171 return result;
172}
173
174} // namespace android
175
176using namespace android;
177
178static void android_media_MediaDescrambler_native_release(JNIEnv *env, jobject thiz) {
179 setDescrambler(env, thiz, NULL);
180}
181
182static void android_media_MediaDescrambler_native_init(JNIEnv *env) {
183 ScopedLocalRef<jclass> clazz(
184 env, env->FindClass("android/media/MediaDescrambler"));
185 CHECK(clazz.get() != NULL);
186
187 gFields.context = env->GetFieldID(clazz.get(), "mNativeContext", "J");
188 CHECK(gFields.context != NULL);
189}
190
191static void android_media_MediaDescrambler_native_setup(
192 JNIEnv *env, jobject thiz, jobject descramblerBinderObj) {
193 setDescrambler(env, thiz, new JDescrambler(env, descramblerBinderObj));
194}
195
196static ssize_t getSubSampleInfo(JNIEnv *env, jint numSubSamples,
197 jintArray numBytesOfClearDataObj, jintArray numBytesOfEncryptedDataObj,
198 DescramblerPlugin::SubSample **outSubSamples) {
199
200 if (numSubSamples <= 0 || numSubSamples >=
201 (signed)(INT32_MAX / sizeof(DescramblerPlugin::SubSample)) ) {
202 // subSamples array may silently overflow if number of samples are
203 // too large. Use INT32_MAX as maximum allocation size may be less
204 // than SIZE_MAX on some platforms.
205 ALOGE("numSubSamples is invalid!");
206 return -1;
207 }
208
209 jboolean isCopy;
210 ssize_t totalSize = 0;
211
212 jint *numBytesOfClearData =
213 (numBytesOfClearDataObj == NULL)
214 ? NULL
215 : env->GetIntArrayElements(numBytesOfClearDataObj, &isCopy);
216
217 jint *numBytesOfEncryptedData =
218 (numBytesOfEncryptedDataObj == NULL)
219 ? NULL
220 : env->GetIntArrayElements(numBytesOfEncryptedDataObj, &isCopy);
221
222 DescramblerPlugin::SubSample *subSamples =
223 new(std::nothrow) DescramblerPlugin::SubSample[numSubSamples];
224
225 if (subSamples == NULL) {
226 ALOGE("Failed to allocate SubSample array!");
227 return -1;
228 }
229
230 for (jint i = 0; i < numSubSamples; ++i) {
231 subSamples[i].mNumBytesOfClearData =
232 (numBytesOfClearData == NULL) ? 0 : numBytesOfClearData[i];
233
234 subSamples[i].mNumBytesOfEncryptedData =
235 (numBytesOfEncryptedData == NULL)
236 ? 0 : numBytesOfEncryptedData[i];
237
238 totalSize += subSamples[i].mNumBytesOfClearData +
239 subSamples[i].mNumBytesOfEncryptedData;
240 }
241
242 if (numBytesOfEncryptedData != NULL) {
243 env->ReleaseIntArrayElements(
244 numBytesOfEncryptedDataObj, numBytesOfEncryptedData, 0);
245 numBytesOfEncryptedData = NULL;
246 }
247
248 if (numBytesOfClearData != NULL) {
249 env->ReleaseIntArrayElements(
250 numBytesOfClearDataObj, numBytesOfClearData, 0);
251 numBytesOfClearData = NULL;
252 }
253
254 *outSubSamples = subSamples;
255
256 return totalSize;
257}
258
259static jint android_media_MediaDescrambler_native_descramble(
260 JNIEnv *env, jobject thiz, jbyte key, jint numSubSamples,
261 jintArray numBytesOfClearDataObj, jintArray numBytesOfEncryptedDataObj,
262 jobject srcBuf, jint srcOffset, jobject dstBuf, jint dstOffset) {
263 sp<JDescrambler> descrambler = getDescrambler(env, thiz);
264 if (descrambler == NULL) {
265 jniThrowException(env, "java/lang/IllegalStateException", NULL);
266 return -1;
267 }
268
269 DescramblerPlugin::SubSample *subSamples = NULL;
270 ssize_t totalLength = getSubSampleInfo(
271 env, numSubSamples, numBytesOfClearDataObj,
272 numBytesOfEncryptedDataObj, &subSamples);
273 if (totalLength < 0) {
274 jniThrowException(env, "java/lang/IllegalArgumentException",
275 "Invalid sub sample info!");
276 return -1;
277 }
278
279 ssize_t result = -1;
280 void *srcPtr = NULL, *dstPtr = NULL;
281 jbyteArray srcArray = NULL, dstArray = NULL;
282 status_t err = getBufferAndSize(
283 env, srcBuf, srcOffset, totalLength, &srcPtr, &srcArray);
284
285 if (err == OK) {
286 if (dstBuf == NULL) {
287 dstPtr = srcPtr;
288 } else {
289 err = getBufferAndSize(
290 env, dstBuf, dstOffset, totalLength, &dstPtr, &dstArray);
291 }
292 }
293
294 if (err == OK) {
295 result = descrambler->descramble(
296 key, numSubSamples, totalLength, subSamples,
297 srcPtr, srcOffset, dstPtr, dstOffset);
298 }
299
300 delete[] subSamples;
301 if (srcArray != NULL) {
302 env->ReleaseByteArrayElements(srcArray, (jbyte *)srcPtr, 0);
303 }
304 if (dstArray != NULL) {
305 env->ReleaseByteArrayElements(dstArray, (jbyte *)dstPtr, 0);
306 }
307 return result;
308}
309
310static const JNINativeMethod gMethods[] = {
311 { "native_release", "()V",
312 (void *)android_media_MediaDescrambler_native_release },
313 { "native_init", "()V",
314 (void *)android_media_MediaDescrambler_native_init },
315 { "native_setup", "(Landroid/os/IBinder;)V",
316 (void *)android_media_MediaDescrambler_native_setup },
317 { "native_descramble", "(BI[I[ILjava/nio/ByteBuffer;ILjava/nio/ByteBuffer;I)I",
318 (void *)android_media_MediaDescrambler_native_descramble },
319};
320
321int register_android_media_Descrambler(JNIEnv *env) {
322 return AndroidRuntime::registerNativeMethods(env,
323 "android/media/MediaDescrambler", gMethods, NELEM(gMethods));
324}
325