diff options
author | Sorin Basca <sorinbasca@google.com> | 2021-03-15 12:55:04 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2021-03-15 12:55:04 +0000 |
commit | 168e9b4deb28fc10e014946a933973e1153a843c (patch) | |
tree | eef70b9c63b0596bf187b09e5c6b6cf9b8cd2774 | |
parent | 4f3fc360eb6759a3f8cbb002b1715e0aa541112c (diff) | |
parent | 40668b3dd0e679e0a6d74e57a3cd9348fac92cd2 (diff) | |
download | libnativehelper-168e9b4deb28fc10e014946a933973e1153a843c.tar.gz |
Merge "Add jniThrowErrnoException in libnativehelper" am: 5e86d7b367 am: e7b9d351c7 am: 40668b3dd0
Original change: https://android-review.googlesource.com/c/platform/libnativehelper/+/1619401
MUST ONLY BE SUBMITTED BY AUTOMERGER
Change-Id: I53558e709250a46a9023678388f4f2db94f57249
-rw-r--r-- | JNIHelp.c | 107 | ||||
-rw-r--r-- | include/nativehelper/JNIHelp.h | 13 | ||||
-rw-r--r-- | libnativehelper.map.txt | 1 | ||||
-rw-r--r-- | libnativehelper_lazy.c | 9 | ||||
-rw-r--r-- | tests/libnativehelper_lazy_test.cpp | 1 | ||||
-rw-r--r-- | tests_mts/jni/jni_helper_jni.cpp | 8 | ||||
-rw-r--r-- | tests_mts/src/com/android/art/libnativehelper/JniHelpTest.java | 17 |
7 files changed, 133 insertions, 23 deletions
@@ -16,6 +16,7 @@ #include "include/nativehelper/JNIHelp.h" +#include <stdarg.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> @@ -233,6 +234,84 @@ static void DiscardPendingException(JNIEnv* env, const char* className) { (*env)->DeleteLocalRef(env, exception); } +static int ThrowException(JNIEnv* env, const char* className, const char* ctorSig, ...) { + int status = -1; + jclass exceptionClass = NULL; + + va_list args; + va_start(args, ctorSig); + + DiscardPendingException(env, className); + + { + /* We want to clean up local references before returning from this function, so, + * regardless of return status, the end block must run. Have the work done in a + * nested block to avoid using any uninitialized variables in the end block. */ + exceptionClass = (*env)->FindClass(env, className); + if (exceptionClass == NULL) { + ALOGE("Unable to find exception class %s", className); + /* an exception, most likely ClassNotFoundException, will now be pending */ + goto end; + } + + jmethodID init = (*env)->GetMethodID(env, exceptionClass, "<init>", ctorSig); + if(init == NULL) { + ALOGE("Failed to find constructor for '%s' '%s'", className, ctorSig); + goto end; + } + + jobject instance = (*env)->NewObjectV(env, exceptionClass, init, args); + if (instance == NULL) { + ALOGE("Failed to construct '%s'", className); + goto end; + } + + if ((*env)->Throw(env, (jthrowable)instance) != JNI_OK) { + ALOGE("Failed to throw '%s'", className); + /* an exception, most likely OOM, will now be pending */ + goto end; + } + + /* everything worked fine, just update status to success and clean up */ + status = 0; + } + +end: + va_end(args); + if (exceptionClass != NULL) { + (*env)->DeleteLocalRef(env, exceptionClass); + } + return status; +} + +static jstring CreateExceptionMsg(JNIEnv* env, const char* msg) { + jstring detailMessage = (*env)->NewStringUTF(env, msg); + if (detailMessage == NULL) { + /* Not really much we can do here. We're probably dead in the water, + but let's try to stumble on... */ + (*env)->ExceptionClear(env); + } + return detailMessage; +} + +/* Helper macro to deal with conversion of the exception message from a C string + * to jstring. + * + * This is useful because most exceptions have a message as the first parameter + * and delegating the conversion to all the callers of ThrowException results in + * code duplication. However, since we try to allow variable number of arguments + * for the exception constructor we'd either need to do the conversion inside + * the macro, or manipulate the va_list to replace the C string to a jstring. + * This seems like the cleaner solution. + */ +#define THROW_EXCEPTION_WITH_MESSAGE(env, className, ctorSig, msg, ...) ({ \ + jstring _detailMessage = CreateExceptionMsg(env, msg); \ + int _status = ThrowException(env, className, ctorSig, _detailMessage, ## __VA_ARGS__); \ + if (_detailMessage != NULL) { \ + (*env)->DeleteLocalRef(env, _detailMessage); \ + } \ + _status; }) + // // JNIHelp external API // @@ -277,24 +356,7 @@ void jniLogException(JNIEnv* env, int priority, const char* tag, jthrowable thro } int jniThrowException(JNIEnv* env, const char* className, const char* message) { - DiscardPendingException(env, className); - - jclass exceptionClass = (*env)->FindClass(env, className); - if (exceptionClass == NULL) { - ALOGE("Unable to find exception class %s", className); - /* ClassNotFoundException now pending */ - return -1; - } - - int status = 0; - if ((*env)->ThrowNew(env, exceptionClass, message) != JNI_OK) { - ALOGE("Failed throwing '%s' '%s'", className, message); - /* an exception, most likely OOM, will now be pending */ - status = -1; - } - (*env)->DeleteLocalRef(env, exceptionClass); - - return status; + return THROW_EXCEPTION_WITH_MESSAGE(env, className, "(Ljava/lang/String;)V", message); } int jniThrowExceptionFmt(JNIEnv* env, const char* className, const char* fmt, va_list args) { @@ -311,12 +373,17 @@ int jniThrowRuntimeException(JNIEnv* env, const char* msg) { return jniThrowException(env, "java/lang/RuntimeException", msg); } -int jniThrowIOException(JNIEnv* env, int errnum) { +int jniThrowIOException(JNIEnv* env, int errno_value) { char buffer[80]; - const char* message = platformStrError(errnum, buffer, sizeof(buffer)); + const char* message = platformStrError(errno_value, buffer, sizeof(buffer)); return jniThrowException(env, "java/io/IOException", message); } +int jniThrowErrnoException(JNIEnv* env, const char* functionName, int errno_value) { + return THROW_EXCEPTION_WITH_MESSAGE(env, "android/system/ErrnoException", + "(Ljava/lang/String;I)V", functionName, errno_value); +} + jstring jniCreateString(JNIEnv* env, const jchar* unicodeChars, jsize len) { return (*env)->NewString(env, unicodeChars, len); } diff --git a/include/nativehelper/JNIHelp.h b/include/nativehelper/JNIHelp.h index d95670c..de2976f 100644 --- a/include/nativehelper/JNIHelp.h +++ b/include/nativehelper/JNIHelp.h @@ -88,9 +88,14 @@ int jniThrowNullPointerException(C_JNIEnv* env, const char* msg); int jniThrowRuntimeException(C_JNIEnv* env, const char* msg); /* - * Throw a java.io.IOException, generating the message from errno. + * Throw a java.io.IOException, generating the message from the given errno value. */ -int jniThrowIOException(C_JNIEnv* env, int errnum); +int jniThrowIOException(C_JNIEnv* env, int errno_value); + +/* + * Throw an android.system.ErrnoException, with the given function name and errno value. + */ +int jniThrowErrnoException(C_JNIEnv* env, const char* functionName, int errno_value); /* * Returns a Java String object created from UTF-16 data either from jchar or, @@ -143,6 +148,10 @@ inline int jniThrowIOException(JNIEnv* env, int errnum) { return jniThrowIOException(&env->functions, errnum); } +inline int jniThrowErrnoException(JNIEnv* env, const char* functionName, int errnum) { + return jniThrowErrnoException(&env->functions, functionName, errnum); +} + inline jstring jniCreateString(JNIEnv* env, const jchar* unicodeChars, jsize len) { return jniCreateString(&env->functions, unicodeChars, len); } diff --git a/libnativehelper.map.txt b/libnativehelper.map.txt index 2664647..0f5c3fa 100644 --- a/libnativehelper.map.txt +++ b/libnativehelper.map.txt @@ -17,6 +17,7 @@ LIBNATIVEHELPER_S { # introduced=S jniThrowNullPointerException; # apex jniThrowRuntimeException; # apex jniThrowIOException; # apex + jniThrowErrnoException; # apex jniCreateFileDescriptor; # apex jniGetFDFromFileDescriptor; # apex jniSetFileDescriptorOfFD; # apex diff --git a/libnativehelper_lazy.c b/libnativehelper_lazy.c index 9b76aba..e4795a0 100644 --- a/libnativehelper_lazy.c +++ b/libnativehelper_lazy.c @@ -51,6 +51,7 @@ enum MethodIndex { k_jniCreateString, k_jniLogException, k_jniRegisterNativeMethods, + k_jniThrowErrnoException, k_jniThrowException, k_jniThrowExceptionFmt, k_jniThrowIOException, @@ -133,6 +134,7 @@ static void InitializeOnce() { BIND_SYMBOL(jniCreateString); BIND_SYMBOL(jniLogException); BIND_SYMBOL(jniRegisterNativeMethods); + BIND_SYMBOL(jniThrowErrnoException); BIND_SYMBOL(jniThrowException); BIND_SYMBOL(jniThrowExceptionFmt); BIND_SYMBOL(jniThrowIOException); @@ -267,6 +269,11 @@ int jniRegisterNativeMethods(JNIEnv* env, INVOKE_METHOD(jniRegisterNativeMethods, M, env, className, gMethods, numMethods); } +int jniThrowErrnoException(JNIEnv* env, const char* functionName, int errnum) { + typedef int (*M)(JNIEnv*, const char*, int); + INVOKE_METHOD(jniThrowErrnoException, M, env, functionName, errnum); +} + int jniThrowException(JNIEnv* env, const char* className, const char* msg) { typedef int (*M)(JNIEnv*, const char*, const char*); INVOKE_METHOD(jniThrowException, M, env, className, msg); @@ -360,4 +367,4 @@ bool JniInvocationInit(struct JniInvocationImpl* instance, const char* library) const char* JniInvocationGetLibrary(const char* library, char* buffer) { typedef const char* (*M)(const char*, char*); INVOKE_METHOD(JniInvocationGetLibrary, M, library, buffer); -}
\ No newline at end of file +} diff --git a/tests/libnativehelper_lazy_test.cpp b/tests/libnativehelper_lazy_test.cpp index f26088d..0a41acd 100644 --- a/tests/libnativehelper_lazy_test.cpp +++ b/tests/libnativehelper_lazy_test.cpp @@ -45,6 +45,7 @@ TEST_F(LibnativehelperLazyTest, NoLibnativehelperIsForJNIHelp) { EXPECT_DEATH(jniThrowIOException(env, 1), kLoadFailed); EXPECT_DEATH(jniThrowNullPointerException(env, "msg"), kLoadFailed); EXPECT_DEATH(jniThrowRuntimeException(env, "msg"), kLoadFailed); + EXPECT_DEATH(jniThrowErrnoException(env, "fn", 1), kLoadFailed); } TEST_F(LibnativehelperLazyTest, NoLibnativehelperIsForJNIPlatformHelp) { diff --git a/tests_mts/jni/jni_helper_jni.cpp b/tests_mts/jni/jni_helper_jni.cpp index e5d1bb8..2aecc16 100644 --- a/tests_mts/jni/jni_helper_jni.cpp +++ b/tests_mts/jni/jni_helper_jni.cpp @@ -80,6 +80,11 @@ static void throwIOException(JNIEnv* env, jclass /*clazz*/, jint cause) { jniThrowIOException(env, cause); } +static void throwErrnoException(JNIEnv* env, jclass /*clazz*/, jstring functionName, jint cause) { + ScopedUtfChars m(env, functionName); + jniThrowErrnoException(env, m.c_str(), cause); +} + static void logException(JNIEnv* env, jclass /*clazz*/, jthrowable throwable) { jniLogException(env, ANDROID_LOG_VERBOSE, __FILE__, throwable); } @@ -197,6 +202,9 @@ JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { MAKE_JNI_NATIVE_METHOD("throwIOException", "(I)V", throwIOException), + MAKE_JNI_NATIVE_METHOD("throwErrnoException", + "(Ljava/lang/String;I)V", + throwErrnoException), MAKE_JNI_NATIVE_METHOD("logException", "(Ljava/lang/Throwable;)V", logException), diff --git a/tests_mts/src/com/android/art/libnativehelper/JniHelpTest.java b/tests_mts/src/com/android/art/libnativehelper/JniHelpTest.java index 0c068e0..1cda688 100644 --- a/tests_mts/src/com/android/art/libnativehelper/JniHelpTest.java +++ b/tests_mts/src/com/android/art/libnativehelper/JniHelpTest.java @@ -29,6 +29,8 @@ import java.nio.IntBuffer; import java.nio.FloatBuffer; import java.nio.DoubleBuffer; +import android.system.ErrnoException; + import org.junit.Assert; public class JniHelpTest extends AndroidTestCase { @@ -39,6 +41,7 @@ public class JniHelpTest extends AndroidTestCase { private static native void throwNullPointerException(String message); private static native void throwRuntimeException(String message); private static native void throwIOException(int cause) throws IOException; + private static native void throwErrnoException(String fileName, int cause) throws ErrnoException; private static native void logException(Throwable throwable); private static native FileDescriptor fileDescriptorCreate(int unixFd); @@ -118,6 +121,20 @@ public class JniHelpTest extends AndroidTestCase { assertFalse(s1.equals(s2)); } + public void testErrnoException() { + final String functionName = "execve"; + final int err = 42; + try { + throwErrnoException(functionName, err); + fail("Unreachable"); + } catch (ErrnoException e) { + // The message contains the function name as well as the string for the errno, just only + // check the first part of the message + assertTrue("Function name", e.getMessage().startsWith(functionName)); + assertEquals(err, e.errno); + } + } + public void testLogException() { try { throw new RuntimeException("Exception for logging test"); |