Home | History | Annotate | Download | only in libnativehelper
      1 /*
      2  * Copyright (C) 2006 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 /*
     18  * JNI helper functions.
     19  */
     20 #define LOG_TAG "JNIHelp"
     21 #include "JNIHelp.h"
     22 #include "utils/Log.h"
     23 
     24 #include <string.h>
     25 #include <assert.h>
     26 
     27 /*
     28  * Register native JNI-callable methods.
     29  *
     30  * "className" looks like "java/lang/String".
     31  */
     32 int jniRegisterNativeMethods(JNIEnv* env, const char* className,
     33     const JNINativeMethod* gMethods, int numMethods)
     34 {
     35     jclass clazz;
     36 
     37     LOGV("Registering %s natives\n", className);
     38     clazz = (*env)->FindClass(env, className);
     39     if (clazz == NULL) {
     40         LOGE("Native registration unable to find class '%s'\n", className);
     41         return -1;
     42     }
     43 
     44     int result = 0;
     45     if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) {
     46         LOGE("RegisterNatives failed for '%s'\n", className);
     47         result = -1;
     48     }
     49 
     50     (*env)->DeleteLocalRef(env, clazz);
     51     return result;
     52 }
     53 
     54 /*
     55  * Get a human-readable summary of an exception object.  The buffer will
     56  * be populated with the "binary" class name and, if present, the
     57  * exception message.
     58  */
     59 static void getExceptionSummary(JNIEnv* env, jthrowable exception, char* buf, size_t bufLen)
     60 {
     61     int success = 0;
     62 
     63     /* get the name of the exception's class */
     64     jclass exceptionClazz = (*env)->GetObjectClass(env, exception); // can't fail
     65     jclass classClazz = (*env)->GetObjectClass(env, exceptionClazz); // java.lang.Class, can't fail
     66     jmethodID classGetNameMethod = (*env)->GetMethodID(
     67             env, classClazz, "getName", "()Ljava/lang/String;");
     68     jstring classNameStr = (*env)->CallObjectMethod(env, exceptionClazz, classGetNameMethod);
     69     if (classNameStr != NULL) {
     70         /* get printable string */
     71         const char* classNameChars = (*env)->GetStringUTFChars(env, classNameStr, NULL);
     72         if (classNameChars != NULL) {
     73             /* if the exception has a message string, get that */
     74             jmethodID throwableGetMessageMethod = (*env)->GetMethodID(
     75                     env, exceptionClazz, "getMessage", "()Ljava/lang/String;");
     76             jstring messageStr = (*env)->CallObjectMethod(
     77                     env, exception, throwableGetMessageMethod);
     78 
     79             if (messageStr != NULL) {
     80                 const char* messageChars = (*env)->GetStringUTFChars(env, messageStr, NULL);
     81                 if (messageChars != NULL) {
     82                     snprintf(buf, bufLen, "%s: %s", classNameChars, messageChars);
     83                     (*env)->ReleaseStringUTFChars(env, messageStr, messageChars);
     84                 } else {
     85                     (*env)->ExceptionClear(env); // clear OOM
     86                     snprintf(buf, bufLen, "%s: <error getting message>", classNameChars);
     87                 }
     88                 (*env)->DeleteLocalRef(env, messageStr);
     89             } else {
     90                 strncpy(buf, classNameChars, bufLen);
     91                 buf[bufLen - 1] = '\0';
     92             }
     93 
     94             (*env)->ReleaseStringUTFChars(env, classNameStr, classNameChars);
     95             success = 1;
     96         }
     97         (*env)->DeleteLocalRef(env, classNameStr);
     98     }
     99     (*env)->DeleteLocalRef(env, classClazz);
    100     (*env)->DeleteLocalRef(env, exceptionClazz);
    101 
    102     if (! success) {
    103         (*env)->ExceptionClear(env);
    104         snprintf(buf, bufLen, "%s", "<error getting class name>");
    105     }
    106 }
    107 
    108 /*
    109  * Formats an exception as a string with its stack trace.
    110  */
    111 static void printStackTrace(JNIEnv* env, jthrowable exception, char* buf, size_t bufLen)
    112 {
    113     int success = 0;
    114 
    115     jclass stringWriterClazz = (*env)->FindClass(env, "java/io/StringWriter");
    116     if (stringWriterClazz != NULL) {
    117         jmethodID stringWriterCtor = (*env)->GetMethodID(env, stringWriterClazz,
    118                 "<init>", "()V");
    119         jmethodID stringWriterToStringMethod = (*env)->GetMethodID(env, stringWriterClazz,
    120                 "toString", "()Ljava/lang/String;");
    121 
    122         jclass printWriterClazz = (*env)->FindClass(env, "java/io/PrintWriter");
    123         if (printWriterClazz != NULL) {
    124             jmethodID printWriterCtor = (*env)->GetMethodID(env, printWriterClazz,
    125                     "<init>", "(Ljava/io/Writer;)V");
    126 
    127             jobject stringWriterObj = (*env)->NewObject(env, stringWriterClazz, stringWriterCtor);
    128             if (stringWriterObj != NULL) {
    129                 jobject printWriterObj = (*env)->NewObject(env, printWriterClazz, printWriterCtor,
    130                         stringWriterObj);
    131                 if (printWriterObj != NULL) {
    132                     jclass exceptionClazz = (*env)->GetObjectClass(env, exception); // can't fail
    133                     jmethodID printStackTraceMethod = (*env)->GetMethodID(
    134                             env, exceptionClazz, "printStackTrace", "(Ljava/io/PrintWriter;)V");
    135 
    136                     (*env)->CallVoidMethod(
    137                             env, exception, printStackTraceMethod, printWriterObj);
    138                     if (! (*env)->ExceptionCheck(env)) {
    139                         jstring messageStr = (*env)->CallObjectMethod(
    140                                 env, stringWriterObj, stringWriterToStringMethod);
    141                         if (messageStr != NULL) {
    142                             jsize messageStrLength = (*env)->GetStringLength(env, messageStr);
    143                             if (messageStrLength >= (jsize) bufLen) {
    144                                 messageStrLength = bufLen - 1;
    145                             }
    146                             (*env)->GetStringUTFRegion(env, messageStr, 0, messageStrLength, buf);
    147                             (*env)->DeleteLocalRef(env, messageStr);
    148                             buf[messageStrLength] = '\0';
    149                             success = 1;
    150                         }
    151                     }
    152                     (*env)->DeleteLocalRef(env, exceptionClazz);
    153                     (*env)->DeleteLocalRef(env, printWriterObj);
    154                 }
    155                 (*env)->DeleteLocalRef(env, stringWriterObj);
    156             }
    157             (*env)->DeleteLocalRef(env, printWriterClazz);
    158         }
    159         (*env)->DeleteLocalRef(env, stringWriterClazz);
    160     }
    161 
    162     if (! success) {
    163         (*env)->ExceptionClear(env);
    164         getExceptionSummary(env, exception, buf, bufLen);
    165     }
    166 }
    167 
    168 /*
    169  * Throw an exception with the specified class and an optional message.
    170  *
    171  * If an exception is currently pending, we log a warning message and
    172  * clear it.
    173  *
    174  * Returns 0 if the specified exception was successfully thrown.  (Some
    175  * sort of exception will always be pending when this returns.)
    176  */
    177 int jniThrowException(JNIEnv* env, const char* className, const char* msg)
    178 {
    179     jclass exceptionClass;
    180 
    181     if ((*env)->ExceptionCheck(env)) {
    182         /* TODO: consider creating the new exception with this as "cause" */
    183         char buf[256];
    184 
    185         jthrowable exception = (*env)->ExceptionOccurred(env);
    186         (*env)->ExceptionClear(env);
    187 
    188         if (exception != NULL) {
    189             getExceptionSummary(env, exception, buf, sizeof(buf));
    190             LOGW("Discarding pending exception (%s) to throw %s\n", buf, className);
    191             (*env)->DeleteLocalRef(env, exception);
    192         }
    193     }
    194 
    195     exceptionClass = (*env)->FindClass(env, className);
    196     if (exceptionClass == NULL) {
    197         LOGE("Unable to find exception class %s\n", className);
    198         /* ClassNotFoundException now pending */
    199         return -1;
    200     }
    201 
    202     int result = 0;
    203     if ((*env)->ThrowNew(env, exceptionClass, msg) != JNI_OK) {
    204         LOGE("Failed throwing '%s' '%s'\n", className, msg);
    205         /* an exception, most likely OOM, will now be pending */
    206         result = -1;
    207     }
    208 
    209     (*env)->DeleteLocalRef(env, exceptionClass);
    210     return result;
    211 }
    212 
    213 /*
    214  * Throw a java.lang.NullPointerException, with an optional message.
    215  */
    216 int jniThrowNullPointerException(JNIEnv* env, const char* msg)
    217 {
    218     return jniThrowException(env, "java/lang/NullPointerException", msg);
    219 }
    220 
    221 /*
    222  * Throw a java.lang.RuntimeException, with an optional message.
    223  */
    224 int jniThrowRuntimeException(JNIEnv* env, const char* msg)
    225 {
    226     return jniThrowException(env, "java/lang/RuntimeException", msg);
    227 }
    228 
    229 /*
    230  * Throw a java.io.IOException, generating the message from errno.
    231  */
    232 int jniThrowIOException(JNIEnv* env, int errnum)
    233 {
    234     char buffer[80];
    235     const char* message = jniStrError(errnum, buffer, sizeof(buffer));
    236     return jniThrowException(env, "java/io/IOException", message);
    237 }
    238 
    239 /*
    240  * Log an exception.
    241  * If exception is NULL, logs the current exception in the JNI environment, if any.
    242  */
    243 void jniLogException(JNIEnv* env, int priority, const char* tag, jthrowable exception)
    244 {
    245     int currentException = 0;
    246     if (exception == NULL) {
    247         exception = (*env)->ExceptionOccurred(env);
    248         if (exception == NULL) {
    249             return;
    250         }
    251 
    252         (*env)->ExceptionClear(env);
    253         currentException = 1;
    254     }
    255 
    256     char buffer[1024];
    257     printStackTrace(env, exception, buffer, sizeof(buffer));
    258     __android_log_write(priority, tag, buffer);
    259 
    260     if (currentException) {
    261         (*env)->Throw(env, exception); // rethrow
    262         (*env)->DeleteLocalRef(env, exception);
    263     }
    264 }
    265 
    266 const char* jniStrError(int errnum, char* buf, size_t buflen)
    267 {
    268     // note: glibc has a nonstandard strerror_r that returns char* rather
    269     // than POSIX's int.
    270     // char *strerror_r(int errnum, char *buf, size_t n);
    271     char* ret = (char*) strerror_r(errnum, buf, buflen);
    272     if (((int)ret) == 0) {
    273         //POSIX strerror_r, success
    274         return buf;
    275     } else if (((int)ret) == -1) {
    276         //POSIX strerror_r, failure
    277         // (Strictly, POSIX only guarantees a value other than 0. The safest
    278         // way to implement this function is to use C++ and overload on the
    279         // type of strerror_r to accurately distinguish GNU from POSIX. But
    280         // realistic implementations will always return -1.)
    281         snprintf(buf, buflen, "errno %d", errnum);
    282         return buf;
    283     } else {
    284         //glibc strerror_r returning a string
    285         return ret;
    286     }
    287 }
    288 
    289 static struct CachedFields {
    290     jclass fileDescriptorClass;
    291     jmethodID fileDescriptorCtor;
    292     jfieldID descriptorField;
    293 } gCachedFields;
    294 
    295 int registerJniHelp(JNIEnv* env) {
    296     gCachedFields.fileDescriptorClass =
    297             (*env)->NewGlobalRef(env, (*env)->FindClass(env, "java/io/FileDescriptor"));
    298     if (gCachedFields.fileDescriptorClass == NULL) {
    299         return -1;
    300     }
    301 
    302     gCachedFields.fileDescriptorCtor =
    303             (*env)->GetMethodID(env, gCachedFields.fileDescriptorClass, "<init>", "()V");
    304     if (gCachedFields.fileDescriptorCtor == NULL) {
    305         return -1;
    306     }
    307 
    308     gCachedFields.descriptorField =
    309             (*env)->GetFieldID(env, gCachedFields.fileDescriptorClass, "descriptor", "I");
    310     if (gCachedFields.descriptorField == NULL) {
    311         return -1;
    312     }
    313 
    314     return 0;
    315 }
    316 
    317 /*
    318  * Create a java.io.FileDescriptor given an integer fd
    319  */
    320 jobject jniCreateFileDescriptor(JNIEnv* env, int fd) {
    321     jobject fileDescriptor = (*env)->NewObject(env,
    322             gCachedFields.fileDescriptorClass, gCachedFields.fileDescriptorCtor);
    323     jniSetFileDescriptorOfFD(env, fileDescriptor, fd);
    324     return fileDescriptor;
    325 }
    326 
    327 /*
    328  * Get an int file descriptor from a java.io.FileDescriptor
    329  */
    330 int jniGetFDFromFileDescriptor(JNIEnv* env, jobject fileDescriptor) {
    331     return (*env)->GetIntField(env, fileDescriptor, gCachedFields.descriptorField);
    332 }
    333 
    334 /*
    335  * Set the descriptor of a java.io.FileDescriptor
    336  */
    337 void jniSetFileDescriptorOfFD(JNIEnv* env, jobject fileDescriptor, int value) {
    338     (*env)->SetIntField(env, fileDescriptor, gCachedFields.descriptorField, value);
    339 }
    340