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