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