blob: c61365a448d3f75897f9ccc45ec4f309a0ac9bb0 [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"
Chong Zhang2659c2f2017-04-27 13:18:20 -070023#include "android_os_HwRemoteBinder.h"
Steven Moreland60cc6c02017-08-25 15:49:25 -070024#include <nativehelper/JNIHelp.h>
Chong Zhangd5927ae2017-01-03 11:07:18 -080025
Chong Zhang2659c2f2017-04-27 13:18:20 -070026#include <android/hardware/cas/native/1.0/BpHwDescrambler.h>
27#include <android/hardware/cas/native/1.0/BnHwDescrambler.h>
Chong Zhangcd538552018-02-21 17:22:19 -080028#include <android/hardware/cas/native/1.0/IDescrambler.h>
Chong Zhangd5927ae2017-01-03 11:07:18 -080029#include <binder/MemoryDealer.h>
Chong Zhang2659c2f2017-04-27 13:18:20 -070030#include <hidl/HidlSupport.h>
Chong Zhangf98fc1c2018-02-13 17:55:34 -080031#include <hidlmemory/FrameworkUtils.h>
Chong Zhangd5927ae2017-01-03 11:07:18 -080032#include <media/stagefright/foundation/ADebug.h>
Chong Zhangcd538552018-02-21 17:22:19 -080033#include <media/cas/DescramblerAPI.h>
Chong Zhangd5927ae2017-01-03 11:07:18 -080034#include <nativehelper/ScopedLocalRef.h>
35
36namespace android {
Chong Zhangcd538552018-02-21 17:22:19 -080037class IMemory;
38class MemoryDealer;
Chong Zhang2659c2f2017-04-27 13:18:20 -070039
Chong Zhangcd538552018-02-21 17:22:19 -080040namespace hardware {
41class HidlMemory;
42};
Chong Zhangf98fc1c2018-02-13 17:55:34 -080043using hardware::fromHeap;
Chong Zhangcd538552018-02-21 17:22:19 -080044using hardware::HidlMemory;
45using hardware::hidl_string;
46using hardware::hidl_vec;
47using namespace hardware::cas::V1_0;
48using namespace hardware::cas::native::V1_0;
49
50struct 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
66protected:
67 virtual ~JDescrambler();
68
69private:
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};
Chong Zhangd5927ae2017-01-03 11:07:18 -080082
83struct fields_t {
84 jfieldID context;
Chong Zhangcd538552018-02-21 17:22:19 -080085 jbyte flagPesHeader;
Chong Zhangd5927ae2017-01-03 11:07:18 -080086};
87
88static fields_t gFields;
89
90static sp<JDescrambler> getDescrambler(JNIEnv *env, jobject thiz) {
91 return (JDescrambler *)env->GetLongField(thiz, gFields.context);
92}
93
94static 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
106static status_t getBufferAndSize(
Chong Zhangaddc39e2017-03-31 14:52:52 -0700107 JNIEnv *env, jobject byteBuf, jint offset, jint limit, size_t length,
Chong Zhangd5927ae2017-01-03 11:07:18 -0800108 void **outPtr, jbyteArray *outByteArray) {
109 void *ptr = env->GetDirectBufferAddress(byteBuf);
110
Chong Zhangd5927ae2017-01-03 11:07:18 -0800111 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);
Chong Zhangd5927ae2017-01-03 11:07:18 -0800130 }
131
Chong Zhangaddc39e2017-03-31 14:52:52 -0700132 if ((jint)length + offset > limit) {
Chong Zhangd5927ae2017-01-03 11:07:18 -0800133 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
146JDescrambler::JDescrambler(JNIEnv *env, jobject descramblerBinderObj) {
Chong Zhang2659c2f2017-04-27 13:18:20 -0700147 mDescrambler = GetDescrambler(env, descramblerBinderObj);
148 if (mDescrambler == NULL) {
149 jniThrowException(env, "java/lang/NullPointerException", NULL);
Chong Zhangd5927ae2017-01-03 11:07:18 -0800150 }
151}
152
153JDescrambler::~JDescrambler() {
154 // Don't call release() here, it's called by Java class
155 mDescrambler.clear();
156 mMem.clear();
157 mDealer.clear();
158}
159
Chong Zhangcd538552018-02-21 17:22:19 -0800160sp<IDescrambler> GetDescrambler(JNIEnv *env, jobject obj) {
Chong Zhang2659c2f2017-04-27 13:18:20 -0700161 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
173bool JDescrambler::ensureBufferCapacity(size_t neededSize) {
Chong Zhangd5927ae2017-01-03 11:07:18 -0800174 if (mMem != NULL && mMem->size() >= neededSize) {
Chong Zhang2659c2f2017-04-27 13:18:20 -0700175 return true;
Chong Zhangd5927ae2017-01-03 11:07:18 -0800176 }
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);
Chong Zhang2659c2f2017-04-27 13:18:20 -0700187
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
Chong Zhangf98fc1c2018-02-13 17:55:34 -0800195 mHidlMemory = fromHeap(heap);
196 mDescramblerSrcBuffer.heapBase = *mHidlMemory;
Chong Zhang2659c2f2017-04-27 13:18:20 -0700197 mDescramblerSrcBuffer.offset = (uint64_t) offset;
198 mDescramblerSrcBuffer.size = (uint64_t) size;
199 return true;
Chong Zhangd5927ae2017-01-03 11:07:18 -0800200}
201
Chong Zhang2659c2f2017-04-27 13:18:20 -0700202status_t JDescrambler::descramble(
Chong Zhangcd538552018-02-21 17:22:19 -0800203 uint32_t key,
Chong Zhangd5927ae2017-01-03 11:07:18 -0800204 ssize_t totalLength,
Chong Zhang2659c2f2017-04-27 13:18:20 -0700205 const hidl_vec<SubSample>& subSamples,
Chong Zhangd5927ae2017-01-03 11:07:18 -0800206 const void *srcPtr,
207 jint srcOffset,
208 void *dstPtr,
Chong Zhangdadee0c2017-03-14 10:05:36 -0700209 jint dstOffset,
Chong Zhang2659c2f2017-04-27 13:18:20 -0700210 Status *status,
211 uint32_t *bytesWritten,
212 hidl_string *detailedError) {
Chong Zhangd5927ae2017-01-03 11:07:18 -0800213 // 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
Chong Zhang2659c2f2017-04-27 13:18:20 -0700219 if (!ensureBufferCapacity(totalLength)) {
220 return NO_MEMORY;
221 }
Chong Zhangd5927ae2017-01-03 11:07:18 -0800222
Ytai Ben-Tsvi2c9d8f62019-09-05 15:14:31 -0700223 memcpy(mMem->unsecurePointer(),
Chong Zhangd5927ae2017-01-03 11:07:18 -0800224 (const void*)((const uint8_t*)srcPtr + srcOffset), totalLength);
225
Chong Zhang2659c2f2017-04-27 13:18:20 -0700226 DestinationBuffer dstBuffer;
227 dstBuffer.type = BufferType::SHARED_MEMORY;
228 dstBuffer.nonsecureMemory = mDescramblerSrcBuffer;
Chong Zhangd5927ae2017-01-03 11:07:18 -0800229
Chong Zhang2659c2f2017-04-27 13:18:20 -0700230 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 });
Chong Zhangd5927ae2017-01-03 11:07:18 -0800244
Chong Zhang2659c2f2017-04-27 13:18:20 -0700245 if (!err.isOk()) {
246 return FAILED_TRANSACTION;
247 }
248
249 if (*status == Status::OK) {
250 if (*bytesWritten > 0 && (ssize_t) *bytesWritten <= totalLength) {
Ytai Ben-Tsvi2c9d8f62019-09-05 15:14:31 -0700251 memcpy((void*)((uint8_t*)dstPtr + dstOffset), mMem->unsecurePointer(),
252 *bytesWritten);
Chong Zhang2659c2f2017-04-27 13:18:20 -0700253 } else {
254 // status seems OK but bytesWritten is invalid, we really
255 // have no idea what is wrong.
256 *status = Status::ERROR_CAS_UNKNOWN;
Chong Zhangdadee0c2017-03-14 10:05:36 -0700257 }
Chong Zhangd5927ae2017-01-03 11:07:18 -0800258 }
Chong Zhang2659c2f2017-04-27 13:18:20 -0700259 return OK;
Chong Zhangd5927ae2017-01-03 11:07:18 -0800260}
261
262} // namespace android
263
264using namespace android;
265
266static void android_media_MediaDescrambler_native_release(JNIEnv *env, jobject thiz) {
267 setDescrambler(env, thiz, NULL);
268}
269
270static 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);
Chong Zhangcd538552018-02-21 17:22:19 -0800277
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);
Chong Zhangd5927ae2017-01-03 11:07:18 -0800283}
284
285static void android_media_MediaDescrambler_native_setup(
286 JNIEnv *env, jobject thiz, jobject descramblerBinderObj) {
287 setDescrambler(env, thiz, new JDescrambler(env, descramblerBinderObj));
288}
289
290static ssize_t getSubSampleInfo(JNIEnv *env, jint numSubSamples,
291 jintArray numBytesOfClearDataObj, jintArray numBytesOfEncryptedDataObj,
Chong Zhang2659c2f2017-04-27 13:18:20 -0700292 hidl_vec<SubSample> *outSubSamples) {
Chong Zhangd5927ae2017-01-03 11:07:18 -0800293
Chong Zhang2659c2f2017-04-27 13:18:20 -0700294 if (numSubSamples <= 0 ||
295 numSubSamples >= (signed)(INT32_MAX / sizeof(SubSample))) {
Chong Zhangd5927ae2017-01-03 11:07:18 -0800296 // 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
Chong Zhang2659c2f2017-04-27 13:18:20 -0700316 outSubSamples->resize(numSubSamples);
317 SubSample *subSamples = outSubSamples->data();
Chong Zhangd5927ae2017-01-03 11:07:18 -0800318 if (subSamples == NULL) {
319 ALOGE("Failed to allocate SubSample array!");
320 return -1;
321 }
322
323 for (jint i = 0; i < numSubSamples; ++i) {
Chong Zhang2659c2f2017-04-27 13:18:20 -0700324 subSamples[i].numBytesOfClearData =
Chong Zhangd5927ae2017-01-03 11:07:18 -0800325 (numBytesOfClearData == NULL) ? 0 : numBytesOfClearData[i];
326
Chong Zhang2659c2f2017-04-27 13:18:20 -0700327 subSamples[i].numBytesOfEncryptedData =
Chong Zhangd5927ae2017-01-03 11:07:18 -0800328 (numBytesOfEncryptedData == NULL)
329 ? 0 : numBytesOfEncryptedData[i];
330
Chong Zhang2659c2f2017-04-27 13:18:20 -0700331 totalSize += subSamples[i].numBytesOfClearData +
332 subSamples[i].numBytesOfEncryptedData;
Chong Zhangd5927ae2017-01-03 11:07:18 -0800333 }
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
Chong Zhangdadee0c2017-03-14 10:05:36 -0700347 if (totalSize < 0) {
Chong Zhangdadee0c2017-03-14 10:05:36 -0700348 return -1;
349 }
350
Chong Zhangd5927ae2017-01-03 11:07:18 -0800351 return totalSize;
352}
353
Chong Zhangdadee0c2017-03-14 10:05:36 -0700354static 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
Chong Zhangd5927ae2017-01-03 11:07:18 -0800377static jint android_media_MediaDescrambler_native_descramble(
Chong Zhangcd538552018-02-21 17:22:19 -0800378 JNIEnv *env, jobject thiz, jbyte key, jbyte flags, jint numSubSamples,
Chong Zhangd5927ae2017-01-03 11:07:18 -0800379 jintArray numBytesOfClearDataObj, jintArray numBytesOfEncryptedDataObj,
Chong Zhangaddc39e2017-03-31 14:52:52 -0700380 jobject srcBuf, jint srcOffset, jint srcLimit,
381 jobject dstBuf, jint dstOffset, jint dstLimit) {
Chong Zhangd5927ae2017-01-03 11:07:18 -0800382 sp<JDescrambler> descrambler = getDescrambler(env, thiz);
383 if (descrambler == NULL) {
Chong Zhang2659c2f2017-04-27 13:18:20 -0700384 jniThrowException(env, "java/lang/IllegalStateException",
385 "Invalid descrambler object!");
Chong Zhangd5927ae2017-01-03 11:07:18 -0800386 return -1;
387 }
388
Chong Zhang2659c2f2017-04-27 13:18:20 -0700389 hidl_vec<SubSample> subSamples;
Chong Zhangd5927ae2017-01-03 11:07:18 -0800390 ssize_t totalLength = getSubSampleInfo(
391 env, numSubSamples, numBytesOfClearDataObj,
392 numBytesOfEncryptedDataObj, &subSamples);
393 if (totalLength < 0) {
394 jniThrowException(env, "java/lang/IllegalArgumentException",
Chong Zhangaddc39e2017-03-31 14:52:52 -0700395 "Invalid subsample info!");
Chong Zhangd5927ae2017-01-03 11:07:18 -0800396 return -1;
397 }
398
Chong Zhangd5927ae2017-01-03 11:07:18 -0800399 void *srcPtr = NULL, *dstPtr = NULL;
400 jbyteArray srcArray = NULL, dstArray = NULL;
401 status_t err = getBufferAndSize(
Chong Zhangaddc39e2017-03-31 14:52:52 -0700402 env, srcBuf, srcOffset, srcLimit, totalLength, &srcPtr, &srcArray);
Chong Zhangd5927ae2017-01-03 11:07:18 -0800403
404 if (err == OK) {
405 if (dstBuf == NULL) {
406 dstPtr = srcPtr;
407 } else {
408 err = getBufferAndSize(
Chong Zhangaddc39e2017-03-31 14:52:52 -0700409 env, dstBuf, dstOffset, dstLimit, totalLength, &dstPtr, &dstArray);
Chong Zhangd5927ae2017-01-03 11:07:18 -0800410 }
411 }
Chong Zhangaddc39e2017-03-31 14:52:52 -0700412
413 if (err != OK) {
414 jniThrowException(env, "java/lang/IllegalArgumentException",
415 "Invalid buffer offset and/or size for subsamples!");
416 return -1;
417 }
418
Chong Zhangcd538552018-02-21 17:22:19 -0800419 uint32_t scramblingControl = (uint32_t)key;
420
421 if (flags & gFields.flagPesHeader) {
422 scramblingControl |= DescramblerPlugin::kScrambling_Flag_PesHeader;
423 }
424
Chong Zhangdadee0c2017-03-14 10:05:36 -0700425 Status status;
Chong Zhang2659c2f2017-04-27 13:18:20 -0700426 uint32_t bytesWritten;
427 hidl_string detailedError;
Chong Zhangd5927ae2017-01-03 11:07:18 -0800428
Chong Zhang2659c2f2017-04-27 13:18:20 -0700429 err = descrambler->descramble(
Chong Zhangcd538552018-02-21 17:22:19 -0800430 scramblingControl, totalLength, subSamples,
Chong Zhang2659c2f2017-04-27 13:18:20 -0700431 srcPtr, srcOffset, dstPtr, dstOffset,
432 &status, &bytesWritten, &detailedError);
433
434 // Release byte array before throwing
Chong Zhangd5927ae2017-01-03 11:07:18 -0800435 if (srcArray != NULL) {
436 env->ReleaseByteArrayElements(srcArray, (jbyte *)srcPtr, 0);
437 }
438 if (dstArray != NULL) {
439 env->ReleaseByteArrayElements(dstArray, (jbyte *)dstPtr, 0);
440 }
Chong Zhangdadee0c2017-03-14 10:05:36 -0700441
Chong Zhang2659c2f2017-04-27 13:18:20 -0700442 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()));
Chong Zhangdadee0c2017-03-14 10:05:36 -0700451 }
Chong Zhang2659c2f2017-04-27 13:18:20 -0700452 return bytesWritten;
Chong Zhangd5927ae2017-01-03 11:07:18 -0800453}
454
455static 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 },
Chong Zhang2659c2f2017-04-27 13:18:20 -0700460 { "native_setup", "(Landroid/os/IHwBinder;)V",
Chong Zhangd5927ae2017-01-03 11:07:18 -0800461 (void *)android_media_MediaDescrambler_native_setup },
Chong Zhangcd538552018-02-21 17:22:19 -0800462 { "native_descramble", "(BBI[I[ILjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;II)I",
Chong Zhangd5927ae2017-01-03 11:07:18 -0800463 (void *)android_media_MediaDescrambler_native_descramble },
464};
465
466int register_android_media_Descrambler(JNIEnv *env) {
467 return AndroidRuntime::registerNativeMethods(env,
468 "android/media/MediaDescrambler", gMethods, NELEM(gMethods));
469}
470