aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSorin Basca <sorinbasca@google.com>2021-03-15 12:55:04 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2021-03-15 12:55:04 +0000
commit168e9b4deb28fc10e014946a933973e1153a843c (patch)
treeeef70b9c63b0596bf187b09e5c6b6cf9b8cd2774
parent4f3fc360eb6759a3f8cbb002b1715e0aa541112c (diff)
parent40668b3dd0e679e0a6d74e57a3cd9348fac92cd2 (diff)
downloadlibnativehelper-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.c107
-rw-r--r--include/nativehelper/JNIHelp.h13
-rw-r--r--libnativehelper.map.txt1
-rw-r--r--libnativehelper_lazy.c9
-rw-r--r--tests/libnativehelper_lazy_test.cpp1
-rw-r--r--tests_mts/jni/jni_helper_jni.cpp8
-rw-r--r--tests_mts/src/com/android/art/libnativehelper/JniHelpTest.java17
7 files changed, 133 insertions, 23 deletions
diff --git a/JNIHelp.c b/JNIHelp.c
index 159feb0..fba7f8d 100644
--- a/JNIHelp.c
+++ b/JNIHelp.c
@@ -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");