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 */ 40template<typename T> 41class scoped_local_ref { 42public: 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 63private: 64 C_JNIEnv* const mEnv; 65 T mLocalRef; 66 67 DISALLOW_COPY_AND_ASSIGN(scoped_local_ref); 68}; 69 70static jclass findClass(C_JNIEnv* env, const char* className) { 71 JNIEnv* e = reinterpret_cast<JNIEnv*>(env); 72 return (*env)->FindClass(e, className); 73} 74 75extern "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 */ 117static 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 */ 168static 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 226extern "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 257int 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 263int jniThrowNullPointerException(C_JNIEnv* env, const char* msg) { 264 return jniThrowException(env, "java/lang/NullPointerException", msg); 265} 266 267int jniThrowRuntimeException(C_JNIEnv* env, const char* msg) { 268 return jniThrowException(env, "java/lang/RuntimeException", msg); 269} 270 271int 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 277static 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 305void 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 310const 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 327jobject 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 340int 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 351void 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 358jobject 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