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 #if defined(__ANDROID__)
     18 /* libnativehelper is built by NDK 19 in one variant, which doesn't yet have the GNU strerror_r. */
     19 #undef _GNU_SOURCE
     20 #endif
     21 
     22 #define LOG_TAG "JNIHelp"
     23 
     24 #include "JniConstants.h"
     25 #include "JNIHelp.h"
     26 #include "ALog-priv.h"
     27 
     28 #include <stdio.h>
     29 #include <stdlib.h>
     30 #include <string.h>
     31 #include <assert.h>
     32 
     33 #include <string>
     34 
     35 /**
     36  * Equivalent to ScopedLocalRef, but for C_JNIEnv instead. (And slightly more powerful.)
     37  */
     38 template<typename T>
     39 class scoped_local_ref {
     40 public:
     41     scoped_local_ref(C_JNIEnv* env, T localRef = NULL)
     42     : mEnv(env), mLocalRef(localRef)
     43     {
     44     }
     45 
     46     ~scoped_local_ref() {
     47         reset();
     48     }
     49 
     50     void reset(T localRef = NULL) {
     51         if (mLocalRef != NULL) {
     52             (*mEnv)->DeleteLocalRef(reinterpret_cast<JNIEnv*>(mEnv), mLocalRef);
     53             mLocalRef = localRef;
     54         }
     55     }
     56 
     57     T get() const {
     58         return mLocalRef;
     59     }
     60 
     61 private:
     62     C_JNIEnv* const mEnv;
     63     T mLocalRef;
     64 
     65     DISALLOW_COPY_AND_ASSIGN(scoped_local_ref);
     66 };
     67 
     68 static jclass findClass(C_JNIEnv* env, const char* className) {
     69     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
     70     return (*env)->FindClass(e, className);
     71 }
     72 
     73 extern "C" int jniRegisterNativeMethods(C_JNIEnv* env, const char* className,
     74     const JNINativeMethod* gMethods, int numMethods)
     75 {
     76     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
     77 
     78     ALOGV("Registering %s's %d native methods...", className, numMethods);
     79 
     80     scoped_local_ref<jclass> c(env, findClass(env, className));
     81     if (c.get() == NULL) {
     82         char* tmp;
     83         const char* msg;
     84         if (asprintf(&tmp,
     85                      "Native registration unable to find class '%s'; aborting...",
     86                      className) == -1) {
     87             // Allocation failed, print default warning.
     88             msg = "Native registration unable to find class; aborting...";
     89         } else {
     90             msg = tmp;
     91         }
     92         e->FatalError(msg);
     93     }
     94 
     95     if ((*env)->RegisterNatives(e, c.get(), gMethods, numMethods) < 0) {
     96         char* tmp;
     97         const char* msg;
     98         if (asprintf(&tmp, "RegisterNatives failed for '%s'; aborting...", className) == -1) {
     99             // Allocation failed, print default warning.
    100             msg = "RegisterNatives failed; aborting...";
    101         } else {
    102             msg = tmp;
    103         }
    104         e->FatalError(msg);
    105     }
    106 
    107     return 0;
    108 }
    109 
    110 /*
    111  * Returns a human-readable summary of an exception object.  The buffer will
    112  * be populated with the "binary" class name and, if present, the
    113  * exception message.
    114  */
    115 static bool getExceptionSummary(C_JNIEnv* env, jthrowable exception, std::string& result) {
    116     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
    117 
    118     /* get the name of the exception's class */
    119     scoped_local_ref<jclass> exceptionClass(env, (*env)->GetObjectClass(e, exception)); // can't fail
    120     scoped_local_ref<jclass> classClass(env,
    121             (*env)->GetObjectClass(e, exceptionClass.get())); // java.lang.Class, can't fail
    122     jmethodID classGetNameMethod =
    123             (*env)->GetMethodID(e, classClass.get(), "getName", "()Ljava/lang/String;");
    124     scoped_local_ref<jstring> classNameStr(env,
    125             (jstring) (*env)->CallObjectMethod(e, exceptionClass.get(), classGetNameMethod));
    126     if (classNameStr.get() == NULL) {
    127         (*env)->ExceptionClear(e);
    128         result = "<error getting class name>";
    129         return false;
    130     }
    131     const char* classNameChars = (*env)->GetStringUTFChars(e, classNameStr.get(), NULL);
    132     if (classNameChars == NULL) {
    133         (*env)->ExceptionClear(e);
    134         result = "<error getting class name UTF-8>";
    135         return false;
    136     }
    137     result += classNameChars;
    138     (*env)->ReleaseStringUTFChars(e, classNameStr.get(), classNameChars);
    139 
    140     /* if the exception has a detail message, get that */
    141     jmethodID getMessage =
    142             (*env)->GetMethodID(e, exceptionClass.get(), "getMessage", "()Ljava/lang/String;");
    143     scoped_local_ref<jstring> messageStr(env,
    144             (jstring) (*env)->CallObjectMethod(e, exception, getMessage));
    145     if (messageStr.get() == NULL) {
    146         return true;
    147     }
    148 
    149     result += ": ";
    150 
    151     const char* messageChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL);
    152     if (messageChars != NULL) {
    153         result += messageChars;
    154         (*env)->ReleaseStringUTFChars(e, messageStr.get(), messageChars);
    155     } else {
    156         result += "<error getting message>";
    157         (*env)->ExceptionClear(e); // clear OOM
    158     }
    159 
    160     return true;
    161 }
    162 
    163 /*
    164  * Returns an exception (with stack trace) as a string.
    165  */
    166 static bool getStackTrace(C_JNIEnv* env, jthrowable exception, std::string& result) {
    167     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
    168 
    169     scoped_local_ref<jclass> stringWriterClass(env, findClass(env, "java/io/StringWriter"));
    170     if (stringWriterClass.get() == NULL) {
    171         return false;
    172     }
    173 
    174     jmethodID stringWriterCtor = (*env)->GetMethodID(e, stringWriterClass.get(), "<init>", "()V");
    175     jmethodID stringWriterToStringMethod =
    176             (*env)->GetMethodID(e, stringWriterClass.get(), "toString", "()Ljava/lang/String;");
    177 
    178     scoped_local_ref<jclass> printWriterClass(env, findClass(env, "java/io/PrintWriter"));
    179     if (printWriterClass.get() == NULL) {
    180         return false;
    181     }
    182 
    183     jmethodID printWriterCtor =
    184             (*env)->GetMethodID(e, printWriterClass.get(), "<init>", "(Ljava/io/Writer;)V");
    185 
    186     scoped_local_ref<jobject> stringWriter(env,
    187             (*env)->NewObject(e, stringWriterClass.get(), stringWriterCtor));
    188     if (stringWriter.get() == NULL) {
    189         return false;
    190     }
    191 
    192     scoped_local_ref<jobject> printWriter(env,
    193             (*env)->NewObject(e, printWriterClass.get(), printWriterCtor, stringWriter.get()));
    194     if (printWriter.get() == NULL) {
    195         return false;
    196     }
    197 
    198     scoped_local_ref<jclass> exceptionClass(env, (*env)->GetObjectClass(e, exception)); // can't fail
    199     jmethodID printStackTraceMethod =
    200             (*env)->GetMethodID(e, exceptionClass.get(), "printStackTrace", "(Ljava/io/PrintWriter;)V");
    201     (*env)->CallVoidMethod(e, exception, printStackTraceMethod, printWriter.get());
    202 
    203     if ((*env)->ExceptionCheck(e)) {
    204         return false;
    205     }
    206 
    207     scoped_local_ref<jstring> messageStr(env,
    208             (jstring) (*env)->CallObjectMethod(e, stringWriter.get(), stringWriterToStringMethod));
    209     if (messageStr.get() == NULL) {
    210         return false;
    211     }
    212 
    213     const char* utfChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL);
    214     if (utfChars == NULL) {
    215         return false;
    216     }
    217 
    218     result = utfChars;
    219 
    220     (*env)->ReleaseStringUTFChars(e, messageStr.get(), utfChars);
    221     return true;
    222 }
    223 
    224 extern "C" int jniThrowException(C_JNIEnv* env, const char* className, const char* msg) {
    225     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
    226 
    227     if ((*env)->ExceptionCheck(e)) {
    228         /* TODO: consider creating the new exception with this as "cause" */
    229         scoped_local_ref<jthrowable> exception(env, (*env)->ExceptionOccurred(e));
    230         (*env)->ExceptionClear(e);
    231 
    232         if (exception.get() != NULL) {
    233             std::string text;
    234             getExceptionSummary(env, exception.get(), text);
    235             ALOGW("Discarding pending exception (%s) to throw %s", text.c_str(), className);
    236         }
    237     }
    238 
    239     scoped_local_ref<jclass> exceptionClass(env, findClass(env, className));
    240     if (exceptionClass.get() == NULL) {
    241         ALOGE("Unable to find exception class %s", className);
    242         /* ClassNotFoundException now pending */
    243         return -1;
    244     }
    245 
    246     if ((*env)->ThrowNew(e, exceptionClass.get(), msg) != JNI_OK) {
    247         ALOGE("Failed throwing '%s' '%s'", className, msg);
    248         /* an exception, most likely OOM, will now be pending */
    249         return -1;
    250     }
    251 
    252     return 0;
    253 }
    254 
    255 int jniThrowExceptionFmt(C_JNIEnv* env, const char* className, const char* fmt, va_list args) {
    256     char msgBuf[512];
    257     vsnprintf(msgBuf, sizeof(msgBuf), fmt, args);
    258     return jniThrowException(env, className, msgBuf);
    259 }
    260 
    261 int jniThrowNullPointerException(C_JNIEnv* env, const char* msg) {
    262     return jniThrowException(env, "java/lang/NullPointerException", msg);
    263 }
    264 
    265 int jniThrowRuntimeException(C_JNIEnv* env, const char* msg) {
    266     return jniThrowException(env, "java/lang/RuntimeException", msg);
    267 }
    268 
    269 int jniThrowIOException(C_JNIEnv* env, int errnum) {
    270     char buffer[80];
    271     const char* message = jniStrError(errnum, buffer, sizeof(buffer));
    272     return jniThrowException(env, "java/io/IOException", message);
    273 }
    274 
    275 static std::string jniGetStackTrace(C_JNIEnv* env, jthrowable exception) {
    276     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
    277 
    278     scoped_local_ref<jthrowable> currentException(env, (*env)->ExceptionOccurred(e));
    279     if (exception == NULL) {
    280         exception = currentException.get();
    281         if (exception == NULL) {
    282           return "<no pending exception>";
    283         }
    284     }
    285 
    286     if (currentException.get() != NULL) {
    287         (*env)->ExceptionClear(e);
    288     }
    289 
    290     std::string trace;
    291     if (!getStackTrace(env, exception, trace)) {
    292         (*env)->ExceptionClear(e);
    293         getExceptionSummary(env, exception, trace);
    294     }
    295 
    296     if (currentException.get() != NULL) {
    297         (*env)->Throw(e, currentException.get()); // rethrow
    298     }
    299 
    300     return trace;
    301 }
    302 
    303 void jniLogException(C_JNIEnv* env, int priority, const char* tag, jthrowable exception) {
    304     std::string trace(jniGetStackTrace(env, exception));
    305     __android_log_write(priority, tag, trace.c_str());
    306 }
    307 
    308 const char* jniStrError(int errnum, char* buf, size_t buflen) {
    309 #if __GLIBC__
    310     // Note: glibc has a nonstandard strerror_r that returns char* rather than POSIX's int.
    311     // char *strerror_r(int errnum, char *buf, size_t n);
    312     return strerror_r(errnum, buf, buflen);
    313 #else
    314     int rc = strerror_r(errnum, buf, buflen);
    315     if (rc != 0) {
    316         // (POSIX only guarantees a value other than 0. The safest
    317         // way to implement this function is to use C++ and overload on the
    318         // type of strerror_r to accurately distinguish GNU from POSIX.)
    319         snprintf(buf, buflen, "errno %d", errnum);
    320     }
    321     return buf;
    322 #endif
    323 }
    324 
    325 jobject jniCreateFileDescriptor(C_JNIEnv* env, int fd) {
    326     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
    327     JniConstants::init(e);
    328     static jmethodID ctor = e->GetMethodID(JniConstants::fileDescriptorClass, "<init>", "()V");
    329     jobject fileDescriptor = (*env)->NewObject(e, JniConstants::fileDescriptorClass, ctor);
    330     // NOTE: NewObject ensures that an OutOfMemoryError will be seen by the Java
    331     // caller if the alloc fails, so we just return NULL when that happens.
    332     if (fileDescriptor != NULL)  {
    333         jniSetFileDescriptorOfFD(env, fileDescriptor, fd);
    334     }
    335     return fileDescriptor;
    336 }
    337 
    338 int jniGetFDFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor) {
    339     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
    340     JniConstants::init(e);
    341     static jfieldID fid = e->GetFieldID(JniConstants::fileDescriptorClass, "descriptor", "I");
    342     if (fileDescriptor != NULL) {
    343         return (*env)->GetIntField(e, fileDescriptor, fid);
    344     } else {
    345         return -1;
    346     }
    347 }
    348 
    349 void jniSetFileDescriptorOfFD(C_JNIEnv* env, jobject fileDescriptor, int value) {
    350     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
    351     JniConstants::init(e);
    352     static jfieldID fid = e->GetFieldID(JniConstants::fileDescriptorClass, "descriptor", "I");
    353     (*env)->SetIntField(e, fileDescriptor, fid, value);
    354 }
    355 
    356 jobject jniGetReferent(C_JNIEnv* env, jobject ref) {
    357     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
    358     JniConstants::init(e);
    359     static jmethodID get = e->GetMethodID(JniConstants::referenceClass, "get", "()Ljava/lang/Object;");
    360     return (*env)->CallObjectMethod(e, ref, get);
    361 }
    362 
    363