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     LOGV("Registering %s natives", className);
     72 
     73     scoped_local_ref<jclass> c(env, findClass(env, className));
     74     if (c.get() == NULL) {
     75         LOGE("Native registration unable to find class '%s', aborting", className);
     76         abort();
     77     }
     78 
     79     if ((*env)->RegisterNatives(e, c.get(), gMethods, numMethods) < 0) {
     80         LOGE("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             LOGW("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         LOGE("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         LOGE("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);
    261     if (exception == NULL) {
    262         exception = (*env)->ExceptionOccurred(e);
    263         if (exception == NULL) {
    264             return;
    265         }
    266 
    267         (*env)->ExceptionClear(e);
    268         currentException.reset(exception);
    269     }
    270 
    271     char* buffer = getStackTrace(env, exception);
    272     if (buffer == NULL) {
    273         (*env)->ExceptionClear(e);
    274         buffer = getExceptionSummary(env, exception);
    275     }
    276 
    277     __android_log_write(priority, tag, buffer);
    278     free(buffer);
    279 
    280     if (currentException.get() != NULL) {
    281         (*env)->Throw(e, exception); // rethrow
    282     }
    283 }
    284 
    285 const char* jniStrError(int errnum, char* buf, size_t buflen) {
    286     // Note: glibc has a nonstandard strerror_r that returns char* rather than POSIX's int.
    287     // char *strerror_r(int errnum, char *buf, size_t n);
    288     char* ret = (char*) strerror_r(errnum, buf, buflen);
    289     if (((int)ret) == 0) {
    290         // POSIX strerror_r, success
    291         return buf;
    292     } else if (((int)ret) == -1) {
    293         // POSIX strerror_r, failure
    294         // (Strictly, POSIX only guarantees a value other than 0. The safest
    295         // way to implement this function is to use C++ and overload on the
    296         // type of strerror_r to accurately distinguish GNU from POSIX. But
    297         // realistic implementations will always return -1.)
    298         snprintf(buf, buflen, "errno %d", errnum);
    299         return buf;
    300     } else {
    301         // glibc strerror_r returning a string
    302         return ret;
    303     }
    304 }
    305 
    306 static struct CachedFields {
    307     jclass fileDescriptorClass;
    308     jmethodID fileDescriptorCtor;
    309     jfieldID descriptorField;
    310 } gCachedFields;
    311 
    312 int registerJniHelp(JNIEnv* env) {
    313     gCachedFields.fileDescriptorClass =
    314             reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass("java/io/FileDescriptor")));
    315     if (gCachedFields.fileDescriptorClass == NULL) {
    316         return -1;
    317     }
    318 
    319     gCachedFields.fileDescriptorCtor =
    320             env->GetMethodID(gCachedFields.fileDescriptorClass, "<init>", "()V");
    321     if (gCachedFields.fileDescriptorCtor == NULL) {
    322         return -1;
    323     }
    324 
    325     gCachedFields.descriptorField =
    326             env->GetFieldID(gCachedFields.fileDescriptorClass, "descriptor", "I");
    327     if (gCachedFields.descriptorField == NULL) {
    328         return -1;
    329     }
    330 
    331     return 0;
    332 }
    333 
    334 jobject jniCreateFileDescriptor(C_JNIEnv* env, int fd) {
    335     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
    336     jobject fileDescriptor = (*env)->NewObject(e,
    337             gCachedFields.fileDescriptorClass, gCachedFields.fileDescriptorCtor);
    338     jniSetFileDescriptorOfFD(env, fileDescriptor, fd);
    339     return fileDescriptor;
    340 }
    341 
    342 int jniGetFDFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor) {
    343     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
    344     return (*env)->GetIntField(e, fileDescriptor, gCachedFields.descriptorField);
    345 }
    346 
    347 void jniSetFileDescriptorOfFD(C_JNIEnv* env, jobject fileDescriptor, int value) {
    348     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
    349     (*env)->SetIntField(e, fileDescriptor, gCachedFields.descriptorField, value);
    350 }
    351 
    352 /*
    353  * DO NOT USE THIS FUNCTION
    354  *
    355  * Get a pointer to the elements of a non-movable array.
    356  *
    357  * The semantics are similar to GetDirectBufferAddress.  Specifically, the VM
    358  * guarantees that the array will not move, and the caller must ensure that
    359  * it does not continue to use the pointer after the object is collected.
    360  *
    361  * We currently use an illegal sequence that trips up CheckJNI when
    362  * the "forcecopy" mode is enabled.  We pass in a magic value to work
    363  * around the problem.
    364  *
    365  * Returns NULL if the array is movable.
    366  */
    367 #define kNoCopyMagic 0xd5aab57f     /* also in CheckJni.c */
    368 extern "C" jbyte* jniGetNonMovableArrayElements(C_JNIEnv* env, jarray arrayObj) {
    369     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
    370 
    371     jbyteArray byteArray = reinterpret_cast<jbyteArray>(arrayObj);
    372 
    373     /*
    374      * Normally the "isCopy" parameter is for a return value only, so the
    375      * non-CheckJNI VM will ignore whatever we pass in.
    376      */
    377     uint32_t noCopy = kNoCopyMagic;
    378     jbyte* result = (*env)->GetByteArrayElements(e, byteArray, reinterpret_cast<jboolean*>(&noCopy));
    379 
    380     /*
    381      * The non-CheckJNI implementation only cares about the array object,
    382      * so we can replace the element pointer with the magic value.
    383      */
    384     (*env)->ReleaseByteArrayElements(e, byteArray, reinterpret_cast<jbyte*>(kNoCopyMagic), 0);
    385     return result;
    386 }
    387