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