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