| /* |
| * Copyright (C) 2006 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /* |
| * JNI helper functions. |
| */ |
| #define LOG_TAG "JNIHelp" |
| #include "JNIHelp.h" |
| #include "utils/Log.h" |
| |
| #include <string.h> |
| #include <assert.h> |
| |
| /* |
| * Register native JNI-callable methods. |
| * |
| * "className" looks like "java/lang/String". |
| */ |
| int jniRegisterNativeMethods(JNIEnv* env, const char* className, |
| const JNINativeMethod* gMethods, int numMethods) |
| { |
| jclass clazz; |
| |
| LOGV("Registering %s natives\n", className); |
| clazz = (*env)->FindClass(env, className); |
| if (clazz == NULL) { |
| LOGE("Native registration unable to find class '%s'\n", className); |
| return -1; |
| } |
| if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) { |
| LOGE("RegisterNatives failed for '%s'\n", className); |
| return -1; |
| } |
| return 0; |
| } |
| |
| /* |
| * Get a human-readable summary of an exception object. The buffer will |
| * be populated with the "binary" class name and, if present, the |
| * exception message. |
| */ |
| static void getExceptionSummary(JNIEnv* env, jthrowable excep, char* buf, |
| size_t bufLen) |
| { |
| if (excep == NULL) |
| return; |
| |
| /* get the name of the exception's class; none of these should fail */ |
| jclass clazz = (*env)->GetObjectClass(env, excep); // exception's class |
| jclass jlc = (*env)->GetObjectClass(env, clazz); // java.lang.Class |
| jmethodID getNameMethod = |
| (*env)->GetMethodID(env, jlc, "getName", "()Ljava/lang/String;"); |
| jstring className = (*env)->CallObjectMethod(env, clazz, getNameMethod); |
| |
| /* get printable string */ |
| const char* nameStr = (*env)->GetStringUTFChars(env, className, NULL); |
| if (nameStr == NULL) { |
| snprintf(buf, bufLen, "%s", "out of memory generating summary"); |
| (*env)->ExceptionClear(env); // clear OOM |
| return; |
| } |
| |
| /* if the exception has a message string, get that */ |
| jmethodID getThrowableMessage = |
| (*env)->GetMethodID(env, clazz, "getMessage", "()Ljava/lang/String;"); |
| jstring message = (*env)->CallObjectMethod(env, excep, getThrowableMessage); |
| |
| if (message != NULL) { |
| const char* messageStr = (*env)->GetStringUTFChars(env, message, NULL); |
| snprintf(buf, bufLen, "%s: %s", nameStr, messageStr); |
| if (messageStr != NULL) |
| (*env)->ReleaseStringUTFChars(env, message, messageStr); |
| else |
| (*env)->ExceptionClear(env); // clear OOM |
| } else { |
| strncpy(buf, nameStr, bufLen); |
| buf[bufLen-1] = '\0'; |
| } |
| |
| (*env)->ReleaseStringUTFChars(env, className, nameStr); |
| } |
| |
| /* |
| * Throw an exception with the specified class and an optional message. |
| * |
| * If an exception is currently pending, we log a warning message and |
| * clear it. |
| * |
| * Returns 0 if the specified exception was successfully thrown. (Some |
| * sort of exception will always be pending when this returns.) |
| */ |
| int jniThrowException(JNIEnv* env, const char* className, const char* msg) |
| { |
| jclass exceptionClass; |
| |
| if ((*env)->ExceptionCheck(env)) { |
| /* TODO: consider creating the new exception with this as "cause" */ |
| char buf[256]; |
| |
| jthrowable excep = (*env)->ExceptionOccurred(env); |
| (*env)->ExceptionClear(env); |
| getExceptionSummary(env, excep, buf, sizeof(buf)); |
| LOGW("Discarding pending exception (%s) to throw %s\n", |
| buf, className); |
| } |
| |
| exceptionClass = (*env)->FindClass(env, className); |
| if (exceptionClass == NULL) { |
| LOGE("Unable to find exception class %s\n", className); |
| /* ClassNotFoundException now pending */ |
| return -1; |
| } |
| |
| if ((*env)->ThrowNew(env, exceptionClass, msg) != JNI_OK) { |
| LOGE("Failed throwing '%s' '%s'\n", className, msg); |
| /* an exception, most likely OOM, will now be pending */ |
| return -1; |
| } |
| return 0; |
| } |
| |
| /* |
| * Throw a java.lang.NullPointerException, with an optional message. |
| */ |
| int jniThrowNullPointerException(JNIEnv* env, const char* msg) |
| { |
| return jniThrowException(env, "java/lang/NullPointerException", msg); |
| } |
| |
| /* |
| * Throw a java.lang.RuntimeException, with an optional message. |
| */ |
| int jniThrowRuntimeException(JNIEnv* env, const char* msg) |
| { |
| return jniThrowException(env, "java/lang/RuntimeException", msg); |
| } |
| |
| /* |
| * Throw a java.io.IOException, generating the message from errno. |
| */ |
| int jniThrowIOException(JNIEnv* env, int errnum) |
| { |
| char buffer[80]; |
| const char* message = jniStrError(errnum, buffer, sizeof(buffer)); |
| return jniThrowException(env, "java/io/IOException", message); |
| } |
| |
| const char* jniStrError(int errnum, char* buf, size_t buflen) |
| { |
| // note: glibc has a nonstandard strerror_r that returns char* rather |
| // than POSIX's int. |
| // char *strerror_r(int errnum, char *buf, size_t n); |
| char* ret = (char*) strerror_r(errnum, buf, buflen); |
| if (((int)ret) == 0) { |
| //POSIX strerror_r, success |
| return buf; |
| } else if (((int)ret) == -1) { |
| //POSIX strerror_r, failure |
| // (Strictly, POSIX only guarantees a value other than 0. The safest |
| // way to implement this function is to use C++ and overload on the |
| // type of strerror_r to accurately distinguish GNU from POSIX. But |
| // realistic implementations will always return -1.) |
| snprintf(buf, buflen, "errno %d", errnum); |
| return buf; |
| } else { |
| //glibc strerror_r returning a string |
| return ret; |
| } |
| } |