Loading core/api/current.txt +3 −0 Original line number Diff line number Diff line Loading @@ -22527,6 +22527,7 @@ package android.media { field @Deprecated public static final int BUFFER_FLAG_SYNC_FRAME = 1; // 0x1 field public static final int CONFIGURE_FLAG_ENCODE = 1; // 0x1 field public static final int CONFIGURE_FLAG_USE_BLOCK_MODEL = 2; // 0x2 field public static final int CONFIGURE_FLAG_USE_CRYPTO_ASYNC = 4; // 0x4 field public static final int CRYPTO_MODE_AES_CBC = 2; // 0x2 field public static final int CRYPTO_MODE_AES_CTR = 1; // 0x1 field public static final int CRYPTO_MODE_UNENCRYPTED = 0; // 0x0 Loading Loading @@ -22556,6 +22557,7 @@ package android.media { public abstract static class MediaCodec.Callback { ctor public MediaCodec.Callback(); method public void onCryptoError(@NonNull android.media.MediaCodec, @NonNull android.media.MediaCodec.CryptoException); method public abstract void onError(@NonNull android.media.MediaCodec, @NonNull android.media.MediaCodec.CodecException); method public abstract void onInputBufferAvailable(@NonNull android.media.MediaCodec, int); method public abstract void onOutputBufferAvailable(@NonNull android.media.MediaCodec, int, @NonNull android.media.MediaCodec.BufferInfo); Loading @@ -22573,6 +22575,7 @@ package android.media { public static final class MediaCodec.CryptoException extends java.lang.RuntimeException implements android.media.MediaDrmThrowable { ctor public MediaCodec.CryptoException(int, @Nullable String); method @Nullable public android.media.MediaCodec.CryptoInfo getCryptoInfo(); method public int getErrorCode(); field @Deprecated public static final int ERROR_FRAME_TOO_LARGE = 8; // 0x8 field @Deprecated public static final int ERROR_INSUFFICIENT_OUTPUT_PROTECTION = 4; // 0x4 media/java/android/media/MediaCodec.java +57 −2 Original line number Diff line number Diff line Loading @@ -571,6 +571,10 @@ import java.util.concurrent.locks.ReentrantLock; void onError(…) { … } {@literal @Override} void onCryptoError(…) { … } }); codec.configure(format, …); mOutputFormat = codec.getOutputFormat(); // option B Loading Loading @@ -1774,6 +1778,7 @@ final public class MediaCodec { private static final int CB_OUTPUT_FORMAT_CHANGE = 4; private static final String EOS_AND_DECODE_ONLY_ERROR_MESSAGE = "An input buffer cannot have " + "both BUFFER_FLAG_END_OF_STREAM and BUFFER_FLAG_DECODE_ONLY flags"; private static final int CB_CRYPTO_ERROR = 6; private class EventHandler extends Handler { private MediaCodec mCodec; Loading Loading @@ -1901,6 +1906,12 @@ final public class MediaCodec { break; } case CB_CRYPTO_ERROR: { mCallback.onCryptoError(mCodec, (MediaCodec.CryptoException) msg.obj); break; } case CB_OUTPUT_FORMAT_CHANGE: { mCallback.onOutputFormatChanged(mCodec, Loading Loading @@ -2104,12 +2115,25 @@ final public class MediaCodec { */ public static final int CONFIGURE_FLAG_USE_BLOCK_MODEL = 2; /** * This flag should be used on a secure decoder only. MediaCodec configured with this * flag does decryption in a separate thread. The flag requires MediaCodec to operate * asynchronously and will throw CryptoException if any, in the onCryptoError() * callback. Applications should override the default implementation of * onCryptoError() and access the associated CryptoException. * * CryptoException thrown will contain {@link MediaCodec.CryptoInfo} * This can be accessed using getCryptoInfo() */ public static final int CONFIGURE_FLAG_USE_CRYPTO_ASYNC = 4; /** @hide */ @IntDef( flag = true, value = { CONFIGURE_FLAG_ENCODE, CONFIGURE_FLAG_USE_BLOCK_MODEL, CONFIGURE_FLAG_USE_CRYPTO_ASYNC, }) @Retention(RetentionPolicy.SOURCE) public @interface ConfigureFlag {} Loading Loading @@ -2523,19 +2547,20 @@ final public class MediaCodec { public final static class CryptoException extends RuntimeException implements MediaDrmThrowable { public CryptoException(int errorCode, @Nullable String detailMessage) { this(detailMessage, errorCode, 0, 0, 0); this(detailMessage, errorCode, 0, 0, 0, null); } /** * @hide */ public CryptoException(String message, int errorCode, int vendorError, int oemError, int errorContext) { int errorContext, @Nullable CryptoInfo cryptoInfo) { super(message); mErrorCode = errorCode; mVendorError = vendorError; mOemError = oemError; mErrorContext = errorContext; mCryptoInfo = cryptoInfo; } /** Loading Loading @@ -2654,6 +2679,16 @@ final public class MediaCodec { return mErrorCode; } /** * Returns CryptoInfo associated with this {@link CryptoException} * if any * * @return CryptoInfo object if any. {@link MediaCodec.CryptoException} */ public @Nullable CryptoInfo getCryptoInfo() { return mCryptoInfo; } @Override public int getVendorError() { return mVendorError; Loading @@ -2670,6 +2705,7 @@ final public class MediaCodec { } private final int mErrorCode, mVendorError, mOemError, mErrorContext; private CryptoInfo mCryptoInfo; } /** Loading Loading @@ -5087,6 +5123,25 @@ final public class MediaCodec { */ public abstract void onError(@NonNull MediaCodec codec, @NonNull CodecException e); /** * Called only when MediaCodec encountered a crypto(decryption) error when using * a decoder configured with CONFIGURE_FLAG_USE_CRYPTO_ASYNC flag along with crypto * or descrambler object. * * @param codec The MediaCodec object * @param e The {@link MediaCodec.CryptoException} object with error details. */ public void onCryptoError(@NonNull MediaCodec codec, @NonNull CryptoException e) { /* * A default implementation for backward compatibility. * Use of CONFIGURE_FLAG_USE_CRYPTO_ASYNC requires override of this callback * to receive CrytoInfo. Without an orverride an exception is thrown. */ throw new IllegalStateException( "Client must override onCryptoError when the codec is " + "configured with CONFIGURE_FLAG_USE_CRYPTO_ASYNC.", e); } /** * Called when the output format has changed * Loading media/jni/android_media_MediaCodec.cpp +196 −66 Original line number Diff line number Diff line Loading @@ -184,6 +184,8 @@ struct fields_t { jmethodID postEventFromNativeID; jmethodID lockAndGetContextID; jmethodID setAndUnlockContextID; jmethodID cryptoInfoSetID; jmethodID cryptoInfoSetPatternID; jfieldID cryptoInfoNumSubSamplesID; jfieldID cryptoInfoNumBytesOfClearDataID; jfieldID cryptoInfoNumBytesOfEncryptedDataID; Loading @@ -203,6 +205,7 @@ struct fields_t { static fields_t gFields; static const void *sRefBaseOwner; jint MediaErrorToJavaError(status_t err); //////////////////////////////////////////////////////////////////////////////// Loading Loading @@ -1068,6 +1071,180 @@ static jthrowable createCodecException( 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; Loading Loading @@ -1107,6 +1284,17 @@ void JMediaCodec::handleCallback(const sp<AMessage> &msg) { 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; Loading Loading @@ -1144,7 +1332,6 @@ void JMediaCodec::handleCallback(const sp<AMessage> &msg) { default: TRESPASS(); } env->CallVoidMethod( mObject, gFields.postEventFromNativeID, Loading Loading @@ -1229,7 +1416,6 @@ void JMediaCodec::onMessageReceived(const sp<AMessage> &msg) { } } jint MediaErrorToJavaError(status_t err); } // namespace android Loading Loading @@ -1280,70 +1466,8 @@ static void throwCodecException(JNIEnv *env, status_t err, int32_t actionCode, c 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); } Loading Loading @@ -2963,6 +3087,12 @@ static void android_media_MediaCodec_native_init(JNIEnv *env, jclass) { 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); Loading Loading
core/api/current.txt +3 −0 Original line number Diff line number Diff line Loading @@ -22527,6 +22527,7 @@ package android.media { field @Deprecated public static final int BUFFER_FLAG_SYNC_FRAME = 1; // 0x1 field public static final int CONFIGURE_FLAG_ENCODE = 1; // 0x1 field public static final int CONFIGURE_FLAG_USE_BLOCK_MODEL = 2; // 0x2 field public static final int CONFIGURE_FLAG_USE_CRYPTO_ASYNC = 4; // 0x4 field public static final int CRYPTO_MODE_AES_CBC = 2; // 0x2 field public static final int CRYPTO_MODE_AES_CTR = 1; // 0x1 field public static final int CRYPTO_MODE_UNENCRYPTED = 0; // 0x0 Loading Loading @@ -22556,6 +22557,7 @@ package android.media { public abstract static class MediaCodec.Callback { ctor public MediaCodec.Callback(); method public void onCryptoError(@NonNull android.media.MediaCodec, @NonNull android.media.MediaCodec.CryptoException); method public abstract void onError(@NonNull android.media.MediaCodec, @NonNull android.media.MediaCodec.CodecException); method public abstract void onInputBufferAvailable(@NonNull android.media.MediaCodec, int); method public abstract void onOutputBufferAvailable(@NonNull android.media.MediaCodec, int, @NonNull android.media.MediaCodec.BufferInfo); Loading @@ -22573,6 +22575,7 @@ package android.media { public static final class MediaCodec.CryptoException extends java.lang.RuntimeException implements android.media.MediaDrmThrowable { ctor public MediaCodec.CryptoException(int, @Nullable String); method @Nullable public android.media.MediaCodec.CryptoInfo getCryptoInfo(); method public int getErrorCode(); field @Deprecated public static final int ERROR_FRAME_TOO_LARGE = 8; // 0x8 field @Deprecated public static final int ERROR_INSUFFICIENT_OUTPUT_PROTECTION = 4; // 0x4
media/java/android/media/MediaCodec.java +57 −2 Original line number Diff line number Diff line Loading @@ -571,6 +571,10 @@ import java.util.concurrent.locks.ReentrantLock; void onError(…) { … } {@literal @Override} void onCryptoError(…) { … } }); codec.configure(format, …); mOutputFormat = codec.getOutputFormat(); // option B Loading Loading @@ -1774,6 +1778,7 @@ final public class MediaCodec { private static final int CB_OUTPUT_FORMAT_CHANGE = 4; private static final String EOS_AND_DECODE_ONLY_ERROR_MESSAGE = "An input buffer cannot have " + "both BUFFER_FLAG_END_OF_STREAM and BUFFER_FLAG_DECODE_ONLY flags"; private static final int CB_CRYPTO_ERROR = 6; private class EventHandler extends Handler { private MediaCodec mCodec; Loading Loading @@ -1901,6 +1906,12 @@ final public class MediaCodec { break; } case CB_CRYPTO_ERROR: { mCallback.onCryptoError(mCodec, (MediaCodec.CryptoException) msg.obj); break; } case CB_OUTPUT_FORMAT_CHANGE: { mCallback.onOutputFormatChanged(mCodec, Loading Loading @@ -2104,12 +2115,25 @@ final public class MediaCodec { */ public static final int CONFIGURE_FLAG_USE_BLOCK_MODEL = 2; /** * This flag should be used on a secure decoder only. MediaCodec configured with this * flag does decryption in a separate thread. The flag requires MediaCodec to operate * asynchronously and will throw CryptoException if any, in the onCryptoError() * callback. Applications should override the default implementation of * onCryptoError() and access the associated CryptoException. * * CryptoException thrown will contain {@link MediaCodec.CryptoInfo} * This can be accessed using getCryptoInfo() */ public static final int CONFIGURE_FLAG_USE_CRYPTO_ASYNC = 4; /** @hide */ @IntDef( flag = true, value = { CONFIGURE_FLAG_ENCODE, CONFIGURE_FLAG_USE_BLOCK_MODEL, CONFIGURE_FLAG_USE_CRYPTO_ASYNC, }) @Retention(RetentionPolicy.SOURCE) public @interface ConfigureFlag {} Loading Loading @@ -2523,19 +2547,20 @@ final public class MediaCodec { public final static class CryptoException extends RuntimeException implements MediaDrmThrowable { public CryptoException(int errorCode, @Nullable String detailMessage) { this(detailMessage, errorCode, 0, 0, 0); this(detailMessage, errorCode, 0, 0, 0, null); } /** * @hide */ public CryptoException(String message, int errorCode, int vendorError, int oemError, int errorContext) { int errorContext, @Nullable CryptoInfo cryptoInfo) { super(message); mErrorCode = errorCode; mVendorError = vendorError; mOemError = oemError; mErrorContext = errorContext; mCryptoInfo = cryptoInfo; } /** Loading Loading @@ -2654,6 +2679,16 @@ final public class MediaCodec { return mErrorCode; } /** * Returns CryptoInfo associated with this {@link CryptoException} * if any * * @return CryptoInfo object if any. {@link MediaCodec.CryptoException} */ public @Nullable CryptoInfo getCryptoInfo() { return mCryptoInfo; } @Override public int getVendorError() { return mVendorError; Loading @@ -2670,6 +2705,7 @@ final public class MediaCodec { } private final int mErrorCode, mVendorError, mOemError, mErrorContext; private CryptoInfo mCryptoInfo; } /** Loading Loading @@ -5087,6 +5123,25 @@ final public class MediaCodec { */ public abstract void onError(@NonNull MediaCodec codec, @NonNull CodecException e); /** * Called only when MediaCodec encountered a crypto(decryption) error when using * a decoder configured with CONFIGURE_FLAG_USE_CRYPTO_ASYNC flag along with crypto * or descrambler object. * * @param codec The MediaCodec object * @param e The {@link MediaCodec.CryptoException} object with error details. */ public void onCryptoError(@NonNull MediaCodec codec, @NonNull CryptoException e) { /* * A default implementation for backward compatibility. * Use of CONFIGURE_FLAG_USE_CRYPTO_ASYNC requires override of this callback * to receive CrytoInfo. Without an orverride an exception is thrown. */ throw new IllegalStateException( "Client must override onCryptoError when the codec is " + "configured with CONFIGURE_FLAG_USE_CRYPTO_ASYNC.", e); } /** * Called when the output format has changed * Loading
media/jni/android_media_MediaCodec.cpp +196 −66 Original line number Diff line number Diff line Loading @@ -184,6 +184,8 @@ struct fields_t { jmethodID postEventFromNativeID; jmethodID lockAndGetContextID; jmethodID setAndUnlockContextID; jmethodID cryptoInfoSetID; jmethodID cryptoInfoSetPatternID; jfieldID cryptoInfoNumSubSamplesID; jfieldID cryptoInfoNumBytesOfClearDataID; jfieldID cryptoInfoNumBytesOfEncryptedDataID; Loading @@ -203,6 +205,7 @@ struct fields_t { static fields_t gFields; static const void *sRefBaseOwner; jint MediaErrorToJavaError(status_t err); //////////////////////////////////////////////////////////////////////////////// Loading Loading @@ -1068,6 +1071,180 @@ static jthrowable createCodecException( 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; Loading Loading @@ -1107,6 +1284,17 @@ void JMediaCodec::handleCallback(const sp<AMessage> &msg) { 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; Loading Loading @@ -1144,7 +1332,6 @@ void JMediaCodec::handleCallback(const sp<AMessage> &msg) { default: TRESPASS(); } env->CallVoidMethod( mObject, gFields.postEventFromNativeID, Loading Loading @@ -1229,7 +1416,6 @@ void JMediaCodec::onMessageReceived(const sp<AMessage> &msg) { } } jint MediaErrorToJavaError(status_t err); } // namespace android Loading Loading @@ -1280,70 +1466,8 @@ static void throwCodecException(JNIEnv *env, status_t err, int32_t actionCode, c 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); } Loading Loading @@ -2963,6 +3087,12 @@ static void android_media_MediaCodec_native_init(JNIEnv *env, jclass) { 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); Loading