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 #define LOG_TAG "JNIHelp"
     18 
     19 #include "JNIHelp.h"
     20 #include "cutils/log.h"
     21 
     22 #include <stdlib.h>
     23 #include <string.h>
     24 #include <assert.h>
     25 
     26 /**
     27  * Equivalent to ScopedLocalRef, but for C_JNIEnv instead. (And slightly more powerful.)
     28  */
     29 template<typename T>
     30 class scoped_local_ref {
     31 public:
     32     scoped_local_ref(C_JNIEnv* env, T localRef = NULL)
     33     : mEnv(env), mLocalRef(localRef)
     34     {
     35     }
     36 
     37     ~scoped_local_ref() {
     38         reset();
     39     }
     40 
     41     void reset(T localRef = NULL) {
     42         if (mLocalRef != NULL) {
     43             (*mEnv)->DeleteLocalRef(reinterpret_cast<JNIEnv*>(mEnv), mLocalRef);
     44             mLocalRef = localRef;
     45         }
     46     }
     47 
     48     T get() const {
     49         return mLocalRef;
     50     }
     51 
     52 private:
     53     C_JNIEnv* mEnv;
     54     T mLocalRef;
     55 
     56     // Disallow copy and assignment.
     57     scoped_local_ref(const scoped_local_ref&);
     58     void operator=(const scoped_local_ref&);
     59 };
     60 
     61 static jclass findClass(C_JNIEnv* env, const char* className) {
     62     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
     63     return (*env)->FindClass(e, className);
     64 }
     65 
     66 extern "C" int jniRegisterNativeMethods(C_JNIEnv* env, const char* className,
     67     const JNINativeMethod* gMethods, int numMethods)
     68 {
     69     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
     70 
     71     ALOGV("Registering %s natives", className);
     72 
     73     scoped_local_ref<jclass> c(env, findClass(env, className));
     74     if (c.get() == NULL) {
     75         ALOGE("Native registration unable to find class '%s', aborting", className);
     76         abort();
     77     }
     78 
     79     if ((*env)->RegisterNatives(e, c.get(), gMethods, numMethods) < 0) {
     80         ALOGE("RegisterNatives failed for '%s', aborting", className);
     81         abort();
     82     }
     83 
     84     return 0;
     85 }
     86 
     87 /*
     88  * Returns a human-readable summary of an exception object.  The buffer will
     89  * be populated with the "binary" class name and, if present, the
     90  * exception message.
     91  */
     92 static char* getExceptionSummary0(C_JNIEnv* env, jthrowable exception) {
     93     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
     94 
     95     /* get the name of the exception's class */
     96     scoped_local_ref<jclass> exceptionClass(env, (*env)->GetObjectClass(e, exception)); // can't fail
     97     scoped_local_ref<jclass> classClass(env,
     98             (*env)->GetObjectClass(e, exceptionClass.get())); // java.lang.Class, can't fail
     99     jmethodID classGetNameMethod =
    100             (*env)->GetMethodID(e, classClass.get(), "getName", "()Ljava/lang/String;");
    101     scoped_local_ref<jstring> classNameStr(env,
    102             (jstring) (*env)->CallObjectMethod(e, exceptionClass.get(), classGetNameMethod));
    103     if (classNameStr.get() == NULL) {
    104         return NULL;
    105     }
    106 
    107     /* get printable string */
    108     const char* classNameChars = (*env)->GetStringUTFChars(e, classNameStr.get(), NULL);
    109     if (classNameChars == NULL) {
    110         return NULL;
    111     }
    112 
    113     /* if the exception has a detail message, get that */
    114     jmethodID getMessage =
    115             (*env)->GetMethodID(e, exceptionClass.get(), "getMessage", "()Ljava/lang/String;");
    116     scoped_local_ref<jstring> messageStr(env,
    117             (jstring) (*env)->CallObjectMethod(e, exception, getMessage));
    118     if (messageStr.get() == NULL) {
    119         return strdup(classNameChars);
    120     }
    121 
    122     char* result = NULL;
    123     const char* messageChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL);
    124     if (messageChars != NULL) {
    125         asprintf(&result, "%s: %s", classNameChars, messageChars);
    126         (*env)->ReleaseStringUTFChars(e, messageStr.get(), messageChars);
    127     } else {
    128         (*env)->ExceptionClear(e); // clear OOM
    129         asprintf(&result, "%s: <error getting message>", classNameChars);
    130     }
    131 
    132     (*env)->ReleaseStringUTFChars(e, classNameStr.get(), classNameChars);
    133     return result;
    134 }
    135 
    136 static char* getExceptionSummary(C_JNIEnv* env, jthrowable exception) {
    137     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
    138     char* result = getExceptionSummary0(env, exception);
    139     if (result == NULL) {
    140         (*env)->ExceptionClear(e);
    141         result = strdup("<error getting class name>");
    142     }
    143     return result;
    144 }
    145 
    146 /*
    147  * Returns an exception (with stack trace) as a string.
    148  */
    149 static char* getStackTrace(C_JNIEnv* env, jthrowable exception) {
    150     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
    151 
    152     scoped_local_ref<jclass> stringWriterClass(env, findClass(env, "java/io/StringWriter"));
    153     if (stringWriterClass.get() == NULL) {
    154         return NULL;
    155     }
    156 
    157     jmethodID stringWriterCtor = (*env)->GetMethodID(e, stringWriterClass.get(), "<init>", "()V");
    158     jmethodID stringWriterToStringMethod =
    159             (*env)->GetMethodID(e, stringWriterClass.get(), "toString", "()Ljava/lang/String;");
    160 
    161     scoped_local_ref<jclass> printWriterClass(env, findClass(env, "java/io/PrintWriter"));
    162     if (printWriterClass.get() == NULL) {
    163         return NULL;
    164     }
    165 
    166     jmethodID printWriterCtor =
    167             (*env)->GetMethodID(e, printWriterClass.get(), "<init>", "(Ljava/io/Writer;)V");
    168 
    169     scoped_local_ref<jobject> stringWriter(env,
    170             (*env)->NewObject(e, stringWriterClass.get(), stringWriterCtor));
    171     if (stringWriter.get() == NULL) {
    172         return NULL;
    173     }
    174 
    175     jobject printWriter =
    176             (*env)->NewObject(e, printWriterClass.get(), printWriterCtor, stringWriter.get());
    177     if (printWriter == NULL) {
    178         return NULL;
    179     }
    180 
    181     scoped_local_ref<jclass> exceptionClass(env, (*env)->GetObjectClass(e, exception)); // can't fail
    182     jmethodID printStackTraceMethod =
    183             (*env)->GetMethodID(e, exceptionClass.get(), "printStackTrace", "(Ljava/io/PrintWriter;)V");
    184     (*env)->CallVoidMethod(e, exception, printStackTraceMethod, printWriter);
    185 
    186     if ((*env)->ExceptionCheck(e)) {
    187         return NULL;
    188     }
    189 
    190     scoped_local_ref<jstring> messageStr(env,
    191             (jstring) (*env)->CallObjectMethod(e, stringWriter.get(), stringWriterToStringMethod));
    192     if (messageStr.get() == NULL) {
    193         return NULL;
    194     }
    195 
    196     const char* utfChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL);
    197     if (utfChars == NULL) {
    198         return NULL;
    199     }
    200 
    201     char* result = strdup(utfChars);
    202     (*env)->ReleaseStringUTFChars(e, messageStr.get(), utfChars);
    203     return result;
    204 }
    205 
    206 extern "C" int jniThrowException(C_JNIEnv* env, const char* className, const char* msg) {
    207     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
    208 
    209     if ((*env)->ExceptionCheck(e)) {
    210         /* TODO: consider creating the new exception with this as "cause" */
    211         scoped_local_ref<jthrowable> exception(env, (*env)->ExceptionOccurred(e));
    212         (*env)->ExceptionClear(e);
    213 
    214         if (exception.get() != NULL) {
    215             char* text = getExceptionSummary(env, exception.get());
    216             ALOGW("Discarding pending exception (%s) to throw %s", text, className);
    217             free(text);
    218         }
    219     }
    220 
    221     scoped_local_ref<jclass> exceptionClass(env, findClass(env, className));
    222     if (exceptionClass.get() == NULL) {
    223         ALOGE("Unable to find exception class %s", className);
    224         /* ClassNotFoundException now pending */
    225         return -1;
    226     }
    227 
    228     if ((*env)->ThrowNew(e, exceptionClass.get(), msg) != JNI_OK) {
    229         ALOGE("Failed throwing '%s' '%s'", className, msg);
    230         /* an exception, most likely OOM, will now be pending */
    231         return -1;
    232     }
    233 
    234     return 0;
    235 }
    236 
    237 int jniThrowExceptionFmt(C_JNIEnv* env, const char* className, const char* fmt, va_list args) {
    238     char msgBuf[512];
    239     vsnprintf(msgBuf, sizeof(msgBuf), fmt, args);
    240     return jniThrowException(env, className, msgBuf);
    241 }
    242 
    243 int jniThrowNullPointerException(C_JNIEnv* env, const char* msg) {
    244     return jniThrowException(env, "java/lang/NullPointerException", msg);
    245 }
    246 
    247 int jniThrowRuntimeException(C_JNIEnv* env, const char* msg) {
    248     return jniThrowException(env, "java/lang/RuntimeException", msg);
    249 }
    250 
    251 int jniThrowIOException(C_JNIEnv* env, int errnum) {
    252     char buffer[80];
    253     const char* message = jniStrError(errnum, buffer, sizeof(buffer));
    254     return jniThrowException(env, "java/io/IOException", message);
    255 }
    256 
    257 void jniLogException(C_JNIEnv* env, int priority, const char* tag, jthrowable exception) {
    258     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
    259 
    260     scoped_local_ref<jthrowable> currentException(env, (*env)->ExceptionOccurred(e));
    261     if (exception == NULL) {
    262         exception = currentException.get();
    263         if (exception == NULL) {
    264             return;
    265         }
    266     }
    267 
    268     if (currentException.get() != NULL) {
    269         (*env)->ExceptionClear(e);
    270     }
    271 
    272     char* buffer = getStackTrace(env, exception);
    273     if (buffer == NULL) {
    274         (*env)->ExceptionClear(e);
    275         buffer = getExceptionSummary(env, exception);
    276     }
    277 
    278     __android_log_write(priority, tag, buffer);
    279     free(buffer);
    280 
    281     if (currentException.get() != NULL) {
    282         (*env)->Throw(e, currentException.get()); // rethrow
    283     }
    284 }
    285 
    286 const char* jniStrError(int errnum, char* buf, size_t buflen) {
    287     // Note: glibc has a nonstandard strerror_r that returns char* rather than POSIX's int.
    288     // char *strerror_r(int errnum, char *buf, size_t n);
    289     char* ret = (char*) strerror_r(errnum, buf, buflen);
    290     if (((int)ret) == 0) {
    291         // POSIX strerror_r, success
    292         return buf;
    293     } else if (((int)ret) == -1) {
    294         // POSIX strerror_r, failure
    295         // (Strictly, POSIX only guarantees a value other than 0. The safest
    296         // way to implement this function is to use C++ and overload on the
    297         // type of strerror_r to accurately distinguish GNU from POSIX. But
    298         // realistic implementations will always return -1.)
    299         snprintf(buf, buflen, "errno %d", errnum);
    300         return buf;
    301     } else {
    302         // glibc strerror_r returning a string
    303         return ret;
    304     }
    305 }
    306 
    307 static struct CachedFields {
    308     jclass fileDescriptorClass;
    309     jmethodID fileDescriptorCtor;
    310     jfieldID descriptorField;
    311 } gCachedFields;
    312 
    313 jint JNI_OnLoad(JavaVM* vm, void*) {
    314     JNIEnv* env;
    315     if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
    316         ALOGE("JavaVM::GetEnv() failed");
    317         abort();
    318     }
    319 
    320     gCachedFields.fileDescriptorClass =
    321             reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass("java/io/FileDescriptor")));
    322     if (gCachedFields.fileDescriptorClass == NULL) {
    323         abort();
    324     }
    325 
    326     gCachedFields.fileDescriptorCtor =
    327             env->GetMethodID(gCachedFields.fileDescriptorClass, "<init>", "()V");
    328     if (gCachedFields.fileDescriptorCtor == NULL) {
    329         abort();
    330     }
    331 
    332     gCachedFields.descriptorField =
    333             env->GetFieldID(gCachedFields.fileDescriptorClass, "descriptor", "I");
    334     if (gCachedFields.descriptorField == NULL) {
    335         abort();
    336     }
    337 
    338     return JNI_VERSION_1_6;
    339 }
    340 
    341 jobject jniCreateFileDescriptor(C_JNIEnv* env, int fd) {
    342     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
    343     jobject fileDescriptor = (*env)->NewObject(e,
    344             gCachedFields.fileDescriptorClass, gCachedFields.fileDescriptorCtor);
    345     jniSetFileDescriptorOfFD(env, fileDescriptor, fd);
    346     return fileDescriptor;
    347 }
    348 
    349 int jniGetFDFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor) {
    350     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
    351     return (*env)->GetIntField(e, fileDescriptor, gCachedFields.descriptorField);
    352 }
    353 
    354 void jniSetFileDescriptorOfFD(C_JNIEnv* env, jobject fileDescriptor, int value) {
    355     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
    356     (*env)->SetIntField(e, fileDescriptor, gCachedFields.descriptorField, value);
    357 }
    358 
    359 /*
    360  * DO NOT USE THIS FUNCTION
    361  *
    362  * Get a pointer to the elements of a non-movable array.
    363  *
    364  * The semantics are similar to GetDirectBufferAddress.  Specifically, the VM
    365  * guarantees that the array will not move, and the caller must ensure that
    366  * it does not continue to use the pointer after the object is collected.
    367  *
    368  * We currently use an illegal sequence that trips up CheckJNI when
    369  * the "forcecopy" mode is enabled.  We pass in a magic value to work
    370  * around the problem.
    371  *
    372  * Returns NULL if the array is movable.
    373  */
    374 #define kNoCopyMagic 0xd5aab57f     /* also in CheckJni.c */
    375 extern "C" jbyte* jniGetNonMovableArrayElements(C_JNIEnv* env, jarray arrayObj) {
    376     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
    377 
    378     jbyteArray byteArray = reinterpret_cast<jbyteArray>(arrayObj);
    379 
    380     /*
    381      * Normally the "isCopy" parameter is for a return value only, so the
    382      * non-CheckJNI VM will ignore whatever we pass in.
    383      */
    384     uint32_t noCopy = kNoCopyMagic;
    385     jbyte* result = (*env)->GetByteArrayElements(e, byteArray, reinterpret_cast<jboolean*>(&noCopy));
    386 
    387     /*
    388      * The non-CheckJNI implementation only cares about the array object,
    389      * so we can replace the element pointer with the magic value.
    390      */
    391     (*env)->ReleaseByteArrayElements(e, byteArray, reinterpret_cast<jbyte*>(kNoCopyMagic), 0);
    392     return result;
    393 }
    394