CryptoAsync: Creating MediaCodec.CryptoException
CryptoException is created with MediaCodec.CryptoInfo
Added callback onCryptoError(MediaCodec.CryptoException)
as another MediaCodec callback to signal decryption errors.
Bug: 254050543
Change-Id: Ifb8bfb44b38178ec99e6dbb607825c642bd4e46f
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 5b0c2a2..9a4aa33 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -184,6 +184,8 @@
jmethodID postEventFromNativeID;
jmethodID lockAndGetContextID;
jmethodID setAndUnlockContextID;
+ jmethodID cryptoInfoSetID;
+ jmethodID cryptoInfoSetPatternID;
jfieldID cryptoInfoNumSubSamplesID;
jfieldID cryptoInfoNumBytesOfClearDataID;
jfieldID cryptoInfoNumBytesOfEncryptedDataID;
@@ -203,6 +205,7 @@
static fields_t gFields;
static const void *sRefBaseOwner;
+jint MediaErrorToJavaError(status_t err);
////////////////////////////////////////////////////////////////////////////////
@@ -1068,6 +1071,180 @@
return (jthrowable)env->NewObject(clazz.get(), ctor, err, actionCode, msgObj.get());
}
+static void AMessageToCryptoInfo(JNIEnv * env, const jobject & obj,
+ const sp<AMessage> & msg) {
+ if(msg == nullptr || obj == nullptr) {
+ ALOGE("CryptoAsync Nothing to do in AMessagetoCryptoInfo");
+ return;
+ }
+ size_t numSubSamples = 0;
+ sp<ABuffer> subSamplesBuffer;
+ sp<ABuffer> keyBuffer;
+ sp<ABuffer> ivBuffer;
+ CryptoPlugin::Mode mode;
+ CryptoPlugin::Pattern pattern;
+ CHECK(msg->findInt32("mode", (int*)&mode));
+ CHECK(msg->findSize("numSubSamples", &numSubSamples));
+ CHECK(msg->findBuffer("subSamples", &subSamplesBuffer));
+ CHECK(msg->findInt32("encryptBlocks", (int32_t *)&pattern.mEncryptBlocks));
+ CHECK(msg->findInt32("skipBlocks", (int32_t *)&pattern.mSkipBlocks));
+ CHECK(msg->findBuffer("iv", &ivBuffer));
+ CHECK(msg->findBuffer("key", &keyBuffer));
+
+ // subsamples
+ ScopedLocalRef<jintArray> samplesOfEncryptedDataArr(env, env->NewIntArray(numSubSamples));
+ ScopedLocalRef<jintArray> samplesOfClearDataArr(env, env->NewIntArray(numSubSamples));
+ jboolean isCopy;
+ jint *dstEncryptedSamples =
+ env->GetIntArrayElements(samplesOfEncryptedDataArr.get(), &isCopy);
+ jint * dstClearSamples =
+ env->GetIntArrayElements(samplesOfClearDataArr.get(), &isCopy);
+
+ CryptoPlugin::SubSample * samplesArray =
+ (CryptoPlugin::SubSample*)(subSamplesBuffer.get()->data());
+
+ for(int i = 0 ; i < numSubSamples ; i++) {
+ dstEncryptedSamples[i] = samplesArray[i].mNumBytesOfEncryptedData;
+ dstClearSamples[i] = samplesArray[i].mNumBytesOfClearData;
+ }
+ env->ReleaseIntArrayElements(samplesOfEncryptedDataArr.get(), dstEncryptedSamples, 0);
+ env->ReleaseIntArrayElements(samplesOfClearDataArr.get(), dstClearSamples, 0);
+ // key and iv
+ jbyteArray keyArray = NULL;
+ jbyteArray ivArray = NULL;
+ if (keyBuffer.get() != nullptr && keyBuffer->size() > 0) {
+ keyArray = env->NewByteArray(keyBuffer->size());
+ jbyte * dstKey = env->GetByteArrayElements(keyArray, &isCopy);
+ memcpy(dstKey, keyBuffer->data(), keyBuffer->size());
+ env->ReleaseByteArrayElements(keyArray,dstKey,0);
+ }
+ if (ivBuffer.get() != nullptr && ivBuffer->size() > 0) {
+ ivArray = env->NewByteArray(ivBuffer->size());
+ jbyte *dstIv = env->GetByteArrayElements(ivArray, &isCopy);
+ memcpy(dstIv, ivBuffer->data(), ivBuffer->size());
+ env->ReleaseByteArrayElements(ivArray, dstIv,0);
+ }
+ // set samples, key and iv
+ env->CallVoidMethod(
+ obj,
+ gFields.cryptoInfoSetID,
+ (jint)numSubSamples,
+ samplesOfClearDataArr.get(),
+ samplesOfEncryptedDataArr.get(),
+ keyArray,
+ ivArray,
+ mode);
+ if (keyArray != NULL) {
+ env->DeleteLocalRef(keyArray);
+ }
+ if (ivArray != NULL) {
+ env->DeleteLocalRef(ivArray);
+ }
+ // set pattern
+ env->CallVoidMethod(
+ obj,
+ gFields.cryptoInfoSetPatternID,
+ pattern.mEncryptBlocks,
+ pattern.mSkipBlocks);
+}
+
+static void CryptoErrorToJavaError(status_t err, jint& jerr, std::string& defaultMsg) {
+ switch(err) {
+ case ERROR_DRM_NO_LICENSE:
+ jerr = gCryptoErrorCodes.cryptoErrorNoKey;
+ defaultMsg = "Crypto key not available";
+ break;
+ case ERROR_DRM_LICENSE_EXPIRED:
+ jerr = gCryptoErrorCodes.cryptoErrorKeyExpired;
+ defaultMsg = "License expired";
+ break;
+ case ERROR_DRM_RESOURCE_BUSY:
+ jerr = gCryptoErrorCodes.cryptoErrorResourceBusy;
+ defaultMsg = "Resource busy or unavailable";
+ break;
+ case ERROR_DRM_INSUFFICIENT_OUTPUT_PROTECTION:
+ jerr = gCryptoErrorCodes.cryptoErrorInsufficientOutputProtection;
+ defaultMsg = "Required output protections are not active";
+ break;
+ case ERROR_DRM_SESSION_NOT_OPENED:
+ jerr = gCryptoErrorCodes.cryptoErrorSessionNotOpened;
+ defaultMsg = "Attempted to use a closed session";
+ break;
+ case ERROR_DRM_INSUFFICIENT_SECURITY:
+ jerr = gCryptoErrorCodes.cryptoErrorInsufficientSecurity;
+ defaultMsg = "Required security level is not met";
+ break;
+ case ERROR_DRM_CANNOT_HANDLE:
+ jerr = gCryptoErrorCodes.cryptoErrorUnsupportedOperation;
+ defaultMsg = "Operation not supported in this configuration";
+ break;
+ case ERROR_DRM_FRAME_TOO_LARGE:
+ jerr = gCryptoErrorCodes.cryptoErrorFrameTooLarge;
+ defaultMsg = "Decrytped frame exceeds size of output buffer";
+ break;
+ case ERROR_DRM_SESSION_LOST_STATE:
+ jerr = gCryptoErrorCodes.cryptoErrorLostState;
+ defaultMsg = "Session state was lost, open a new session and retry";
+ break;
+ default: // Other negative DRM error codes go out best-effort.
+ jerr = MediaErrorToJavaError(err);
+ defaultMsg = StrCryptoError(err);
+ break;
+ }
+}
+static jthrowable createCryptoException(JNIEnv *env, status_t err,
+ const char * msg = NULL, const sp<ICrypto> & crypto = NULL,
+ const sp<AMessage> & cryptoInfo = NULL) {
+ jthrowable exception = nullptr;
+ jmethodID constructID = nullptr;
+ ScopedLocalRef<jobject> cryptoInfoObject(env);
+ std::string defaultMsg = "Unknown Error";
+ jint jerr = 0;
+ // Get a class ref for CryptoException
+ ScopedLocalRef<jclass> clazz(
+ env, env->FindClass("android/media/MediaCodec$CryptoException"));
+ CHECK(clazz.get() != NULL);
+
+ // Get constructor ref for CryptoException
+ constructID = env->GetMethodID(clazz.get(), "<init>",
+ "(Ljava/lang/String;IIIILandroid/media/MediaCodec$CryptoInfo;)V");
+ CHECK(constructID != NULL);
+
+ // create detailed message for exception
+ CryptoErrorToJavaError(err, jerr, defaultMsg);
+ std::string originalMsg(msg != NULL ? msg : defaultMsg.c_str());
+ DrmStatus dStatus(err, originalMsg.c_str());
+ std::string detailedMsg(
+ DrmUtils::GetExceptionMessage(dStatus, defaultMsg.c_str(), crypto));
+ jstring msgObj = env->NewStringUTF(detailedMsg.c_str());
+
+ if (cryptoInfo != nullptr) {
+ // Class ref for CryptoInfo
+ ScopedLocalRef<jclass> clazzCryptoInfo(
+ env, env->FindClass("android/media/MediaCodec$CryptoInfo"));
+ CHECK(clazzCryptoInfo.get() != NULL);
+
+ // Constructor reference for CryptoInfo
+ jmethodID constructCryptoInfo =
+ env->GetMethodID(clazzCryptoInfo.get(), "<init>", "()V");
+ CHECK(constructCryptoInfo != NULL);
+
+ // Create CryptoInfo jobject
+ cryptoInfoObject.reset(
+ env->NewObject(clazzCryptoInfo.get(), constructCryptoInfo));
+ CHECK(cryptoInfoObject.get() != NULL);
+
+ // Translate AMesage to CryptoInfo
+ AMessageToCryptoInfo(env, cryptoInfoObject.get(), cryptoInfo);
+ }
+
+ exception = (jthrowable)env->NewObject(
+ clazz.get(), constructID, msgObj, jerr,
+ dStatus.getCdmErr(), dStatus.getOemErr(), dStatus.getContext(),
+ cryptoInfoObject.get());
+
+ return exception;
+}
void JMediaCodec::handleCallback(const sp<AMessage> &msg) {
int32_t arg1, arg2 = 0;
jobject obj = NULL;
@@ -1107,6 +1284,17 @@
break;
}
+ case MediaCodec::CB_CRYPTO_ERROR:
+ {
+ int32_t err, actionCode;
+ AString errorDetail;
+ CHECK(msg->findInt32("err", &err));
+ CHECK(msg->findInt32("actionCode",&actionCode));
+ CHECK(msg->findString("errorDetail", &errorDetail));
+ obj = (jobject)createCryptoException(env, err, errorDetail.c_str(), NULL, msg);
+ break;
+ }
+
case MediaCodec::CB_ERROR:
{
int32_t err, actionCode;
@@ -1144,7 +1332,6 @@
default:
TRESPASS();
}
-
env->CallVoidMethod(
mObject,
gFields.postEventFromNativeID,
@@ -1229,7 +1416,6 @@
}
}
-jint MediaErrorToJavaError(status_t err);
} // namespace android
@@ -1280,70 +1466,8 @@
static void throwCryptoException(JNIEnv *env, status_t err, const char *msg,
const sp<ICrypto> &crypto) {
- ScopedLocalRef<jclass> clazz(
- env, env->FindClass("android/media/MediaCodec$CryptoException"));
- CHECK(clazz.get() != NULL);
-
- jmethodID constructID =
- env->GetMethodID(clazz.get(), "<init>", "(Ljava/lang/String;IIII)V");
- CHECK(constructID != NULL);
-
- std::string defaultMsg = "Unknown Error";
-
- /* translate OS errors to Java API CryptoException errorCodes (which are positive) */
- jint jerr = 0;
- switch (err) {
- case ERROR_DRM_NO_LICENSE:
- jerr = gCryptoErrorCodes.cryptoErrorNoKey;
- defaultMsg = "Crypto key not available";
- break;
- case ERROR_DRM_LICENSE_EXPIRED:
- jerr = gCryptoErrorCodes.cryptoErrorKeyExpired;
- defaultMsg = "License expired";
- break;
- case ERROR_DRM_RESOURCE_BUSY:
- jerr = gCryptoErrorCodes.cryptoErrorResourceBusy;
- defaultMsg = "Resource busy or unavailable";
- break;
- case ERROR_DRM_INSUFFICIENT_OUTPUT_PROTECTION:
- jerr = gCryptoErrorCodes.cryptoErrorInsufficientOutputProtection;
- defaultMsg = "Required output protections are not active";
- break;
- case ERROR_DRM_SESSION_NOT_OPENED:
- jerr = gCryptoErrorCodes.cryptoErrorSessionNotOpened;
- defaultMsg = "Attempted to use a closed session";
- break;
- case ERROR_DRM_INSUFFICIENT_SECURITY:
- jerr = gCryptoErrorCodes.cryptoErrorInsufficientSecurity;
- defaultMsg = "Required security level is not met";
- break;
- case ERROR_DRM_CANNOT_HANDLE:
- jerr = gCryptoErrorCodes.cryptoErrorUnsupportedOperation;
- defaultMsg = "Operation not supported in this configuration";
- break;
- case ERROR_DRM_FRAME_TOO_LARGE:
- jerr = gCryptoErrorCodes.cryptoErrorFrameTooLarge;
- defaultMsg = "Decrytped frame exceeds size of output buffer";
- break;
- case ERROR_DRM_SESSION_LOST_STATE:
- jerr = gCryptoErrorCodes.cryptoErrorLostState;
- defaultMsg = "Session state was lost, open a new session and retry";
- break;
- default: /* Other negative DRM error codes go out best-effort. */
- jerr = MediaErrorToJavaError(err);
- defaultMsg = StrCryptoError(err);
- break;
- }
-
- std::string originalMsg(msg != NULL ? msg : defaultMsg.c_str());
- DrmStatus dStatus(err, originalMsg.c_str());
- std::string detailedMsg(DrmUtils::GetExceptionMessage(dStatus, defaultMsg.c_str(), crypto));
- jstring msgObj = env->NewStringUTF(detailedMsg.c_str());
-
- jthrowable exception =
- (jthrowable)env->NewObject(clazz.get(), constructID, msgObj, jerr,
- dStatus.getCdmErr(), dStatus.getOemErr(), dStatus.getContext());
-
+ jthrowable exception = createCryptoException(
+ env, err, msg, crypto, /* cryptoInfo */ NULL);
env->Throw(exception);
}
@@ -2963,6 +3087,12 @@
clazz.reset(env->FindClass("android/media/MediaCodec$CryptoInfo"));
CHECK(clazz.get() != NULL);
+ gFields.cryptoInfoSetID = env->GetMethodID(clazz.get(), "set", "(I[I[I[B[BI)V");
+ CHECK(gFields.cryptoInfoSetID != NULL);
+
+ gFields.cryptoInfoSetPatternID = env->GetMethodID(clazz.get(), "setPattern", "(II)V");
+ CHECK(gFields.cryptoInfoSetPatternID != NULL);
+
gFields.cryptoInfoNumSubSamplesID =
env->GetFieldID(clazz.get(), "numSubSamples", "I");
CHECK(gFields.cryptoInfoNumSubSamplesID != NULL);