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