am b0b521ea: Update exception handling for MediaCodec

* commit 'b0b521eabde47301d1ff6d5b14ef54ffcdf24cdf':
  Update exception handling for MediaCodec
This commit is contained in:
Andy Hung
2014-08-08 16:23:28 +00:00
committed by Android Git Automerger
2 changed files with 117 additions and 75 deletions

View File

@@ -58,12 +58,17 @@ enum {
EVENT_SET_CALLBACK = 2,
};
struct CryptoErrorCodes {
static struct CryptoErrorCodes {
jint cryptoErrorNoKey;
jint cryptoErrorKeyExpired;
jint cryptoErrorResourceBusy;
} gCryptoErrorCodes;
static struct CodecActionCodes {
jint codecActionTransient;
jint codecActionRecoverable;
} gCodecActionCodes;
struct fields_t {
jfieldID context;
jmethodID postEventFromNativeID;
@@ -101,10 +106,11 @@ JMediaCodec::JMediaCodec(
PRIORITY_FOREGROUND);
if (nameIsType) {
mCodec = MediaCodec::CreateByType(mLooper, name, encoder);
mCodec = MediaCodec::CreateByType(mLooper, name, encoder, &mInitStatus);
} else {
mCodec = MediaCodec::CreateByComponentName(mLooper, name);
mCodec = MediaCodec::CreateByComponentName(mLooper, name, &mInitStatus);
}
CHECK((mCodec != NULL) != (mInitStatus != OK));
}
void JMediaCodec::cacheJavaObjects(JNIEnv *env) {
@@ -147,7 +153,7 @@ void JMediaCodec::cacheJavaObjects(JNIEnv *env) {
}
status_t JMediaCodec::initCheck() const {
return mCodec != NULL ? OK : NO_INIT;
return mInitStatus;
}
void JMediaCodec::registerSelf() {
@@ -158,6 +164,7 @@ void JMediaCodec::release() {
if (mCodec != NULL) {
mCodec->release();
mCodec.clear();
mInitStatus = NO_INIT;
}
if (mLooper != NULL) {
@@ -554,6 +561,34 @@ void JMediaCodec::setVideoScalingMode(int mode) {
}
}
static jthrowable createCodecException(
JNIEnv *env, status_t err, int32_t actionCode, const char *msg = NULL) {
ScopedLocalRef<jclass> clazz(
env, env->FindClass("android/media/MediaCodec$CodecException"));
CHECK(clazz.get() != NULL);
const jmethodID ctor = env->GetMethodID(clazz.get(), "<init>", "(IILjava/lang/String;)V");
CHECK(ctor != NULL);
ScopedLocalRef<jstring> msgObj(
env, env->NewStringUTF(msg != NULL ? msg : String8::format("Error %#x", err)));
// translate action code to Java equivalent
switch (actionCode) {
case ACTION_CODE_TRANSIENT:
actionCode = gCodecActionCodes.codecActionTransient;
break;
case ACTION_CODE_RECOVERABLE:
actionCode = gCodecActionCodes.codecActionRecoverable;
break;
default:
actionCode = 0; // everything else is fatal
break;
}
return (jthrowable)env->NewObject(clazz.get(), ctor, err, actionCode, msgObj.get());
}
void JMediaCodec::handleCallback(const sp<AMessage> &msg) {
int32_t arg1, arg2 = 0;
jobject obj = NULL;
@@ -605,19 +640,8 @@ void JMediaCodec::handleCallback(const sp<AMessage> &msg) {
CHECK(msg->findInt32("err", &err));
CHECK(msg->findInt32("actionCode", &actionCode));
// use Integer object to pass the action code
ScopedLocalRef<jclass> clazz(
env, env->FindClass("android/media/MediaCodec$CodecException"));
jmethodID ctor = env->GetMethodID(clazz.get(), "<init>", "(IILjava/lang/String;)V");
AString str;
const char *detail = "Unknown error";
if (msg->findString("detail", &str)) {
detail = str.c_str();
}
jstring msgObj = env->NewStringUTF(detail);
obj = env->NewObject(clazz.get(), ctor, err, actionCode, msgObj);
// note that DRM errors could conceivably alias into a CodecException
obj = (jobject)createCodecException(env, err, actionCode);
if (obj == NULL) {
if (env->ExceptionCheck()) {
@@ -705,6 +729,11 @@ static void android_media_MediaCodec_release(JNIEnv *env, jobject thiz) {
setMediaCodec(env, thiz, NULL);
}
static void throwCodecException(JNIEnv *env, status_t err, int32_t actionCode, const char *msg) {
jthrowable exception = createCodecException(env, err, actionCode, msg);
env->Throw(exception);
}
static void throwCryptoException(JNIEnv *env, status_t err, const char *msg) {
ScopedLocalRef<jclass> clazz(
env, env->FindClass("android/media/MediaCodec$CryptoException"));
@@ -716,7 +745,7 @@ static void throwCryptoException(JNIEnv *env, status_t err, const char *msg) {
jstring msgObj = env->NewStringUTF(msg != NULL ? msg : "Unknown Error");
/* translate OS errors to Java API CryptoException errorCodes */
/* translate OS errors to Java API CryptoException errorCodes (which are positive) */
switch (err) {
case ERROR_DRM_NO_LICENSE:
err = gCryptoErrorCodes.cryptoErrorNoKey;
@@ -727,7 +756,7 @@ static void throwCryptoException(JNIEnv *env, status_t err, const char *msg) {
case ERROR_DRM_RESOURCE_BUSY:
err = gCryptoErrorCodes.cryptoErrorResourceBusy;
break;
default:
default: /* Other negative DRM error codes go out as is. */
break;
}
@@ -738,13 +767,8 @@ static void throwCryptoException(JNIEnv *env, status_t err, const char *msg) {
}
static jint throwExceptionAsNecessary(
JNIEnv *env, status_t err, const char *msg = NULL) {
if (err >= ERROR_DRM_VENDOR_MIN && err <= ERROR_DRM_VENDOR_MAX) {
// We'll throw our custom MediaCodec.CryptoException
throwCryptoException(env, err, msg);
return 0;
}
JNIEnv *env, status_t err, int32_t actionCode = ACTION_CODE_FATAL,
const char *msg = NULL) {
switch (err) {
case OK:
return 0;
@@ -758,20 +782,18 @@ static jint throwExceptionAsNecessary(
case INFO_OUTPUT_BUFFERS_CHANGED:
return DEQUEUE_INFO_OUTPUT_BUFFERS_CHANGED;
case ERROR_DRM_NO_LICENSE:
case ERROR_DRM_LICENSE_EXPIRED:
case ERROR_DRM_RESOURCE_BUSY:
throwCryptoException(env, err, msg);
break;
case INVALID_OPERATION:
jniThrowException(env, "java/lang/IllegalStateException", msg);
return 0;
default:
{
jniThrowException(env, "java/lang/IllegalStateException", msg);
break;
}
if (isCryptoError(err)) {
throwCryptoException(env, err, msg);
return 0;
}
throwCodecException(env, err, actionCode, msg);
return 0;
}
return 0;
}
static void android_media_MediaCodec_native_setCallback(
@@ -781,7 +803,7 @@ static void android_media_MediaCodec_native_setCallback(
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
throwExceptionAsNecessary(env, INVALID_OPERATION);
return;
}
@@ -800,7 +822,7 @@ static void android_media_MediaCodec_native_configure(
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
throwExceptionAsNecessary(env, INVALID_OPERATION);
return;
}
@@ -842,7 +864,7 @@ static jobject android_media_MediaCodec_createInputSurface(JNIEnv* env,
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
throwExceptionAsNecessary(env, INVALID_OPERATION);
return NULL;
}
@@ -865,13 +887,13 @@ static void android_media_MediaCodec_start(JNIEnv *env, jobject thiz) {
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL) {
jniThrowException(env, "java/lang/IllegalStateException", "no codec found");
throwExceptionAsNecessary(env, INVALID_OPERATION);
return;
}
status_t err = codec->start();
throwExceptionAsNecessary(env, err, "start failed");
throwExceptionAsNecessary(env, err, ACTION_CODE_FATAL, "start failed");
}
static void android_media_MediaCodec_stop(JNIEnv *env, jobject thiz) {
@@ -880,7 +902,7 @@ static void android_media_MediaCodec_stop(JNIEnv *env, jobject thiz) {
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
throwExceptionAsNecessary(env, INVALID_OPERATION);
return;
}
@@ -895,8 +917,7 @@ static void android_media_MediaCodec_reset(JNIEnv *env, jobject thiz) {
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL) {
// should never be here
jniThrowException(env, "java/lang/IllegalStateException", NULL);
throwExceptionAsNecessary(env, INVALID_OPERATION);
return;
}
@@ -904,7 +925,10 @@ static void android_media_MediaCodec_reset(JNIEnv *env, jobject thiz) {
if (err != OK) {
// treat all errors as fatal for now, though resource not available
// errors could be treated as transient.
err = 0x80000000;
// we also should avoid sending INVALID_OPERATION here due to
// the transitory nature of reset(), it should not inadvertently
// trigger an IllegalStateException.
err = UNKNOWN_ERROR;
}
throwExceptionAsNecessary(env, err);
}
@@ -915,7 +939,7 @@ static void android_media_MediaCodec_flush(JNIEnv *env, jobject thiz) {
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
throwExceptionAsNecessary(env, INVALID_OPERATION);
return;
}
@@ -937,7 +961,7 @@ static void android_media_MediaCodec_queueInputBuffer(
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
throwExceptionAsNecessary(env, INVALID_OPERATION);
return;
}
@@ -947,7 +971,7 @@ static void android_media_MediaCodec_queueInputBuffer(
index, offset, size, timestampUs, flags, &errorDetailMsg);
throwExceptionAsNecessary(
env, err, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str());
env, err, ACTION_CODE_FATAL, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str());
}
static void android_media_MediaCodec_queueSecureInputBuffer(
@@ -963,7 +987,7 @@ static void android_media_MediaCodec_queueSecureInputBuffer(
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
throwExceptionAsNecessary(env, INVALID_OPERATION);
return;
}
@@ -1005,7 +1029,7 @@ static void android_media_MediaCodec_queueSecureInputBuffer(
err = -ERANGE;
// subSamples array may silently overflow if number of samples are too large. Use
// INT32_MAX as maximum allocation size may be less than SIZE_MAX on some platforms
} else if ( CC_UNLIKELY(numSubSamples >= INT32_MAX / sizeof(*subSamples)) ) {
} else if ( CC_UNLIKELY(numSubSamples >= (signed)(INT32_MAX / sizeof(*subSamples))) ) {
err = -EINVAL;
} else {
jboolean isCopy;
@@ -1089,7 +1113,7 @@ static void android_media_MediaCodec_queueSecureInputBuffer(
subSamples = NULL;
throwExceptionAsNecessary(
env, err, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str());
env, err, ACTION_CODE_FATAL, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str());
}
static jint android_media_MediaCodec_dequeueInputBuffer(
@@ -1099,7 +1123,7 @@ static jint android_media_MediaCodec_dequeueInputBuffer(
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
throwExceptionAsNecessary(env, INVALID_OPERATION);
return -1;
}
@@ -1120,7 +1144,7 @@ static jint android_media_MediaCodec_dequeueOutputBuffer(
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
throwExceptionAsNecessary(env, INVALID_OPERATION);
return 0;
}
@@ -1143,7 +1167,7 @@ static void android_media_MediaCodec_releaseOutputBuffer(
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
throwExceptionAsNecessary(env, INVALID_OPERATION);
return;
}
@@ -1158,7 +1182,7 @@ static void android_media_MediaCodec_signalEndOfInputStream(JNIEnv* env,
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
throwExceptionAsNecessary(env, INVALID_OPERATION);
return;
}
@@ -1174,7 +1198,7 @@ static jobject android_media_MediaCodec_getFormatNative(
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
throwExceptionAsNecessary(env, INVALID_OPERATION);
return NULL;
}
@@ -1197,7 +1221,7 @@ static jobject android_media_MediaCodec_getOutputFormatForIndexNative(
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
throwExceptionAsNecessary(env, INVALID_OPERATION);
return NULL;
}
@@ -1220,7 +1244,7 @@ static jobjectArray android_media_MediaCodec_getBuffers(
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
throwExceptionAsNecessary(env, INVALID_OPERATION);
return NULL;
}
@@ -1246,7 +1270,7 @@ static jobject android_media_MediaCodec_getBuffer(
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
throwExceptionAsNecessary(env, INVALID_OPERATION);
return NULL;
}
@@ -1272,7 +1296,7 @@ static jobject android_media_MediaCodec_getImage(
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
throwExceptionAsNecessary(env, INVALID_OPERATION);
return NULL;
}
@@ -1298,7 +1322,7 @@ static jobject android_media_MediaCodec_getName(
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
throwExceptionAsNecessary(env, INVALID_OPERATION);
return NULL;
}
@@ -1321,7 +1345,7 @@ static void android_media_MediaCodec_setParameters(
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
throwExceptionAsNecessary(env, INVALID_OPERATION);
return;
}
@@ -1340,7 +1364,7 @@ static void android_media_MediaCodec_setVideoScalingMode(
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
throwExceptionAsNecessary(env, INVALID_OPERATION);
return;
}
@@ -1409,13 +1433,25 @@ static void android_media_MediaCodec_native_init(JNIEnv *env) {
CHECK(field != NULL);
gCryptoErrorCodes.cryptoErrorResourceBusy =
env->GetStaticIntField(clazz.get(), field);
clazz.reset(env->FindClass("android/media/MediaCodec$CodecException"));
CHECK(clazz.get() != NULL);
field = env->GetStaticFieldID(clazz.get(), "ACTION_TRANSIENT", "I");
CHECK(field != NULL);
gCodecActionCodes.codecActionTransient =
env->GetStaticIntField(clazz.get(), field);
field = env->GetStaticFieldID(clazz.get(), "ACTION_RECOVERABLE", "I");
CHECK(field != NULL);
gCodecActionCodes.codecActionRecoverable =
env->GetStaticIntField(clazz.get(), field);
}
static void android_media_MediaCodec_native_setup(
JNIEnv *env, jobject thiz,
jstring name, jboolean nameIsType, jboolean encoder) {
if (name == NULL) {
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
jniThrowException(env, "java/lang/NullPointerException", NULL);
return;
}
@@ -1427,19 +1463,23 @@ static void android_media_MediaCodec_native_setup(
sp<JMediaCodec> codec = new JMediaCodec(env, thiz, tmp, nameIsType, encoder);
status_t err = codec->initCheck();
env->ReleaseStringUTFChars(name, tmp);
tmp = NULL;
if (err != OK) {
jniThrowException(
env,
"java/io/IOException",
"Failed to allocate component instance");
const status_t err = codec->initCheck();
if (err == NAME_NOT_FOUND) {
// fail and do not try again.
jniThrowException(env, "java/lang/IllegalArgumentException",
String8::format("Failed to initialize %s, error %#x", tmp, err));
env->ReleaseStringUTFChars(name, tmp);
return;
} else if (err != OK) {
// believed possible to try again
jniThrowException(env, "java/io/IOException",
String8::format("Failed to find matching codec %s, error %#x", tmp, err));
env->ReleaseStringUTFChars(name, tmp);
return;
}
env->ReleaseStringUTFChars(name, tmp);
codec->registerSelf();
setMediaCodec(env,thiz, codec);

View File

@@ -135,6 +135,8 @@ private:
sp<AMessage> mCallbackNotification;
status_t mInitStatus;
status_t createByteBufferFromABuffer(
JNIEnv *env, bool readOnly, bool clearBuffer, const sp<ABuffer> &buffer,
jobject *buf) const;