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* msg;
     83         asprintf(&msg, "Native registration unable to find class '%s'; aborting...", className);
     84         e->FatalError(msg);
     85     }
     86 
     87     if ((*env)->RegisterNatives(e, c.get(), gMethods, numMethods) < 0) {
     88         char* msg;
     89         asprintf(&msg, "RegisterNatives failed for '%s'; aborting...", className);
     90         e->FatalError(msg);
     91     }
     92 
     93     return 0;
     94 }
     95 
     96 /*
     97  * Returns a human-readable summary of an exception object.  The buffer will
     98  * be populated with the "binary" class name and, if present, the
     99  * exception message.
    100  */
    101 static bool getExceptionSummary(C_JNIEnv* env, jthrowable exception, std::string& result) {
    102     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
    103 
    104     /* get the name of the exception's class */
    105     scoped_local_ref<jclass> exceptionClass(env, (*env)->GetObjectClass(e, exception)); // can't fail
    106     scoped_local_ref<jclass> classClass(env,
    107             (*env)->GetObjectClass(e, exceptionClass.get())); // java.lang.Class, can't fail
    108     jmethodID classGetNameMethod =
    109             (*env)->GetMethodID(e, classClass.get(), "getName", "()Ljava/lang/String;");
    110     scoped_local_ref<jstring> classNameStr(env,
    111             (jstring) (*env)->CallObjectMethod(e, exceptionClass.get(), classGetNameMethod));
    112     if (classNameStr.get() == NULL) {
    113         (*env)->ExceptionClear(e);
    114         result = "<error getting class name>";
    115         return false;
    116     }
    117     const char* classNameChars = (*env)->GetStringUTFChars(e, classNameStr.get(), NULL);
    118     if (classNameChars == NULL) {
    119         (*env)->ExceptionClear(e);
    120         result = "<error getting class name UTF-8>";
    121         return false;
    122     }
    123     result += classNameChars;
    124     (*env)->ReleaseStringUTFChars(e, classNameStr.get(), classNameChars);
    125 
    126     /* if the exception has a detail message, get that */
    127     jmethodID getMessage =
    128             (*env)->GetMethodID(e, exceptionClass.get(), "getMessage", "()Ljava/lang/String;");
    129     scoped_local_ref<jstring> messageStr(env,
    130             (jstring) (*env)->CallObjectMethod(e, exception, getMessage));
    131     if (messageStr.get() == NULL) {
    132         return true;
    133     }
    134 
    135     result += ": ";
    136 
    137     const char* messageChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL);
    138     if (messageChars != NULL) {
    139         result += messageChars;
    140         (*env)->ReleaseStringUTFChars(e, messageStr.get(), messageChars);
    141     } else {
    142         result += "<error getting message>";
    143         (*env)->ExceptionClear(e); // clear OOM
    144     }
    145 
    146     return true;
    147 }
    148 
    149 /*
    150  * Returns an exception (with stack trace) as a string.
    151  */
    152 static bool getStackTrace(C_JNIEnv* env, jthrowable exception, std::string& result) {
    153     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
    154 
    155     scoped_local_ref<jclass> stringWriterClass(env, findClass(env, "java/io/StringWriter"));
    156     if (stringWriterClass.get() == NULL) {
    157         return false;
    158     }
    159 
    160     jmethodID stringWriterCtor = (*env)->GetMethodID(e, stringWriterClass.get(), "<init>", "()V");
    161     jmethodID stringWriterToStringMethod =
    162             (*env)->GetMethodID(e, stringWriterClass.get(), "toString", "()Ljava/lang/String;");
    163 
    164     scoped_local_ref<jclass> printWriterClass(env, findClass(env, "java/io/PrintWriter"));
    165     if (printWriterClass.get() == NULL) {
    166         return false;
    167     }
    168 
    169     jmethodID printWriterCtor =
    170             (*env)->GetMethodID(e, printWriterClass.get(), "<init>", "(Ljava/io/Writer;)V");
    171 
    172     scoped_local_ref<jobject> stringWriter(env,
    173             (*env)->NewObject(e, stringWriterClass.get(), stringWriterCtor));
    174     if (stringWriter.get() == NULL) {
    175         return false;
    176     }
    177 
    178     scoped_local_ref<jobject> printWriter(env,
    179             (*env)->NewObject(e, printWriterClass.get(), printWriterCtor, stringWriter.get()));
    180     if (printWriter.get() == NULL) {
    181         return false;
    182     }
    183 
    184     scoped_local_ref<jclass> exceptionClass(env, (*env)->GetObjectClass(e, exception)); // can't fail
    185     jmethodID printStackTraceMethod =
    186             (*env)->GetMethodID(e, exceptionClass.get(), "printStackTrace", "(Ljava/io/PrintWriter;)V");
    187     (*env)->CallVoidMethod(e, exception, printStackTraceMethod, printWriter.get());
    188 
    189     if ((*env)->ExceptionCheck(e)) {
    190         return false;
    191     }
    192 
    193     scoped_local_ref<jstring> messageStr(env,
    194             (jstring) (*env)->CallObjectMethod(e, stringWriter.get(), stringWriterToStringMethod));
    195     if (messageStr.get() == NULL) {
    196         return false;
    197     }
    198 
    199     const char* utfChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL);
    200     if (utfChars == NULL) {
    201         return false;
    202     }
    203 
    204     result = utfChars;
    205 
    206     (*env)->ReleaseStringUTFChars(e, messageStr.get(), utfChars);
    207     return true;
    208 }
    209 
    210 extern "C" int jniThrowException(C_JNIEnv* env, const char* className, const char* msg) {
    211     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
    212 
    213     if ((*env)->ExceptionCheck(e)) {
    214         /* TODO: consider creating the new exception with this as "cause" */
    215         scoped_local_ref<jthrowable> exception(env, (*env)->ExceptionOccurred(e));
    216         (*env)->ExceptionClear(e);
    217 
    218         if (exception.get() != NULL) {
    219             std::string text;
    220             getExceptionSummary(env, exception.get(), text);
    221             ALOGW("Discarding pending exception (%s) to throw %s", text.c_str(), className);
    222         }
    223     }
    224 
    225     scoped_local_ref<jclass> exceptionClass(env, findClass(env, className));
    226     if (exceptionClass.get() == NULL) {
    227         ALOGE("Unable to find exception class %s", className);
    228         /* ClassNotFoundException now pending */
    229         return -1;
    230     }
    231 
    232     if ((*env)->ThrowNew(e, exceptionClass.get(), msg) != JNI_OK) {
    233         ALOGE("Failed throwing '%s' '%s'", className, msg);
    234         /* an exception, most likely OOM, will now be pending */
    235         return -1;
    236     }
    237 
    238     return 0;
    239 }
    240 
    241 int jniThrowExceptionFmt(C_JNIEnv* env, const char* className, const char* fmt, va_list args) {
    242     char msgBuf[512];
    243     vsnprintf(msgBuf, sizeof(msgBuf), fmt, args);
    244     return jniThrowException(env, className, msgBuf);
    245 }
    246 
    247 int jniThrowNullPointerException(C_JNIEnv* env, const char* msg) {
    248     return jniThrowException(env, "java/lang/NullPointerException", msg);
    249 }
    250 
    251 int jniThrowRuntimeException(C_JNIEnv* env, const char* msg) {
    252     return jniThrowException(env, "java/lang/RuntimeException", msg);
    253 }
    254 
    255 int jniThrowIOException(C_JNIEnv* env, int errnum) {
    256     char buffer[80];
    257     const char* message = jniStrError(errnum, buffer, sizeof(buffer));
    258     return jniThrowException(env, "java/io/IOException", message);
    259 }
    260 
    261 static std::string jniGetStackTrace(C_JNIEnv* env, jthrowable exception) {
    262     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
    263 
    264     scoped_local_ref<jthrowable> currentException(env, (*env)->ExceptionOccurred(e));
    265     if (exception == NULL) {
    266         exception = currentException.get();
    267         if (exception == NULL) {
    268           return "<no pending exception>";
    269         }
    270     }
    271 
    272     if (currentException.get() != NULL) {
    273         (*env)->ExceptionClear(e);
    274     }
    275 
    276     std::string trace;
    277     if (!getStackTrace(env, exception, trace)) {
    278         (*env)->ExceptionClear(e);
    279         getExceptionSummary(env, exception, trace);
    280     }
    281 
    282     if (currentException.get() != NULL) {
    283         (*env)->Throw(e, currentException.get()); // rethrow
    284     }
    285 
    286     return trace;
    287 }
    288 
    289 void jniLogException(C_JNIEnv* env, int priority, const char* tag, jthrowable exception) {
    290     std::string trace(jniGetStackTrace(env, exception));
    291     __android_log_write(priority, tag, trace.c_str());
    292 }
    293 
    294 const char* jniStrError(int errnum, char* buf, size_t buflen) {
    295 #if __GLIBC__
    296     // Note: glibc has a nonstandard strerror_r that returns char* rather than POSIX's int.
    297     // char *strerror_r(int errnum, char *buf, size_t n);
    298     return strerror_r(errnum, buf, buflen);
    299 #else
    300     int rc = strerror_r(errnum, buf, buflen);
    301     if (rc != 0) {
    302         // (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.)
    305         snprintf(buf, buflen, "errno %d", errnum);
    306     }
    307     return buf;
    308 #endif
    309 }
    310 
    311 jobject jniCreateFileDescriptor(C_JNIEnv* env, int fd) {
    312     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
    313     static jmethodID ctor = e->GetMethodID(JniConstants::fileDescriptorClass, "<init>", "()V");
    314     jobject fileDescriptor = (*env)->NewObject(e, JniConstants::fileDescriptorClass, ctor);
    315     // NOTE: NewObject ensures that an OutOfMemoryError will be seen by the Java
    316     // caller if the alloc fails, so we just return NULL when that happens.
    317     if (fileDescriptor != NULL)  {
    318         jniSetFileDescriptorOfFD(env, fileDescriptor, fd);
    319     }
    320     return fileDescriptor;
    321 }
    322 
    323 int jniGetFDFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor) {
    324     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
    325     static jfieldID fid = e->GetFieldID(JniConstants::fileDescriptorClass, "descriptor", "I");
    326     if (fileDescriptor != NULL) {
    327         return (*env)->GetIntField(e, fileDescriptor, fid);
    328     } else {
    329         return -1;
    330     }
    331 }
    332 
    333 void jniSetFileDescriptorOfFD(C_JNIEnv* env, jobject fileDescriptor, int value) {
    334     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
    335     static jfieldID fid = e->GetFieldID(JniConstants::fileDescriptorClass, "descriptor", "I");
    336     (*env)->SetIntField(e, fileDescriptor, fid, value);
    337 }
    338 
    339 jobject jniGetReferent(C_JNIEnv* env, jobject ref) {
    340     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
    341     static jmethodID get = e->GetMethodID(JniConstants::referenceClass, "get", "()Ljava/lang/Object;");
    342     return (*env)->CallObjectMethod(e, ref, get);
    343 }
    344 
    345