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 "JNIHelp.h" 20 #include "cutils/log.h" 21 22 #include <stdlib.h> 23 #include <string.h> 24 #include <assert.h> 25 26 /** 27 * Equivalent to ScopedLocalRef, but for C_JNIEnv instead. (And slightly more powerful.) 28 */ 29 template<typename T> 30 class scoped_local_ref { 31 public: 32 scoped_local_ref(C_JNIEnv* env, T localRef = NULL) 33 : mEnv(env), mLocalRef(localRef) 34 { 35 } 36 37 ~scoped_local_ref() { 38 reset(); 39 } 40 41 void reset(T localRef = NULL) { 42 if (mLocalRef != NULL) { 43 (*mEnv)->DeleteLocalRef(reinterpret_cast<JNIEnv*>(mEnv), mLocalRef); 44 mLocalRef = localRef; 45 } 46 } 47 48 T get() const { 49 return mLocalRef; 50 } 51 52 private: 53 C_JNIEnv* mEnv; 54 T mLocalRef; 55 56 // Disallow copy and assignment. 57 scoped_local_ref(const scoped_local_ref&); 58 void operator=(const scoped_local_ref&); 59 }; 60 61 static jclass findClass(C_JNIEnv* env, const char* className) { 62 JNIEnv* e = reinterpret_cast<JNIEnv*>(env); 63 return (*env)->FindClass(e, className); 64 } 65 66 extern "C" int jniRegisterNativeMethods(C_JNIEnv* env, const char* className, 67 const JNINativeMethod* gMethods, int numMethods) 68 { 69 JNIEnv* e = reinterpret_cast<JNIEnv*>(env); 70 71 LOGV("Registering %s natives", className); 72 73 scoped_local_ref<jclass> c(env, findClass(env, className)); 74 if (c.get() == NULL) { 75 LOGE("Native registration unable to find class '%s', aborting", className); 76 abort(); 77 } 78 79 if ((*env)->RegisterNatives(e, c.get(), gMethods, numMethods) < 0) { 80 LOGE("RegisterNatives failed for '%s', aborting", className); 81 abort(); 82 } 83 84 return 0; 85 } 86 87 /* 88 * Returns a human-readable summary of an exception object. The buffer will 89 * be populated with the "binary" class name and, if present, the 90 * exception message. 91 */ 92 static char* getExceptionSummary0(C_JNIEnv* env, jthrowable exception) { 93 JNIEnv* e = reinterpret_cast<JNIEnv*>(env); 94 95 /* get the name of the exception's class */ 96 scoped_local_ref<jclass> exceptionClass(env, (*env)->GetObjectClass(e, exception)); // can't fail 97 scoped_local_ref<jclass> classClass(env, 98 (*env)->GetObjectClass(e, exceptionClass.get())); // java.lang.Class, can't fail 99 jmethodID classGetNameMethod = 100 (*env)->GetMethodID(e, classClass.get(), "getName", "()Ljava/lang/String;"); 101 scoped_local_ref<jstring> classNameStr(env, 102 (jstring) (*env)->CallObjectMethod(e, exceptionClass.get(), classGetNameMethod)); 103 if (classNameStr.get() == NULL) { 104 return NULL; 105 } 106 107 /* get printable string */ 108 const char* classNameChars = (*env)->GetStringUTFChars(e, classNameStr.get(), NULL); 109 if (classNameChars == NULL) { 110 return NULL; 111 } 112 113 /* if the exception has a detail message, get that */ 114 jmethodID getMessage = 115 (*env)->GetMethodID(e, exceptionClass.get(), "getMessage", "()Ljava/lang/String;"); 116 scoped_local_ref<jstring> messageStr(env, 117 (jstring) (*env)->CallObjectMethod(e, exception, getMessage)); 118 if (messageStr.get() == NULL) { 119 return strdup(classNameChars); 120 } 121 122 char* result = NULL; 123 const char* messageChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL); 124 if (messageChars != NULL) { 125 asprintf(&result, "%s: %s", classNameChars, messageChars); 126 (*env)->ReleaseStringUTFChars(e, messageStr.get(), messageChars); 127 } else { 128 (*env)->ExceptionClear(e); // clear OOM 129 asprintf(&result, "%s: <error getting message>", classNameChars); 130 } 131 132 (*env)->ReleaseStringUTFChars(e, classNameStr.get(), classNameChars); 133 return result; 134 } 135 136 static char* getExceptionSummary(C_JNIEnv* env, jthrowable exception) { 137 JNIEnv* e = reinterpret_cast<JNIEnv*>(env); 138 char* result = getExceptionSummary0(env, exception); 139 if (result == NULL) { 140 (*env)->ExceptionClear(e); 141 result = strdup("<error getting class name>"); 142 } 143 return result; 144 } 145 146 /* 147 * Returns an exception (with stack trace) as a string. 148 */ 149 static char* getStackTrace(C_JNIEnv* env, jthrowable exception) { 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 NULL; 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 NULL; 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 NULL; 173 } 174 175 jobject printWriter = 176 (*env)->NewObject(e, printWriterClass.get(), printWriterCtor, stringWriter.get()); 177 if (printWriter == NULL) { 178 return NULL; 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); 185 186 if ((*env)->ExceptionCheck(e)) { 187 return NULL; 188 } 189 190 scoped_local_ref<jstring> messageStr(env, 191 (jstring) (*env)->CallObjectMethod(e, stringWriter.get(), stringWriterToStringMethod)); 192 if (messageStr.get() == NULL) { 193 return NULL; 194 } 195 196 const char* utfChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL); 197 if (utfChars == NULL) { 198 return NULL; 199 } 200 201 char* result = strdup(utfChars); 202 (*env)->ReleaseStringUTFChars(e, messageStr.get(), utfChars); 203 return result; 204 } 205 206 extern "C" int jniThrowException(C_JNIEnv* env, const char* className, const char* msg) { 207 JNIEnv* e = reinterpret_cast<JNIEnv*>(env); 208 209 if ((*env)->ExceptionCheck(e)) { 210 /* TODO: consider creating the new exception with this as "cause" */ 211 scoped_local_ref<jthrowable> exception(env, (*env)->ExceptionOccurred(e)); 212 (*env)->ExceptionClear(e); 213 214 if (exception.get() != NULL) { 215 char* text = getExceptionSummary(env, exception.get()); 216 LOGW("Discarding pending exception (%s) to throw %s", text, className); 217 free(text); 218 } 219 } 220 221 scoped_local_ref<jclass> exceptionClass(env, findClass(env, className)); 222 if (exceptionClass.get() == NULL) { 223 LOGE("Unable to find exception class %s", className); 224 /* ClassNotFoundException now pending */ 225 return -1; 226 } 227 228 if ((*env)->ThrowNew(e, exceptionClass.get(), msg) != JNI_OK) { 229 LOGE("Failed throwing '%s' '%s'", className, msg); 230 /* an exception, most likely OOM, will now be pending */ 231 return -1; 232 } 233 234 return 0; 235 } 236 237 int jniThrowExceptionFmt(C_JNIEnv* env, const char* className, const char* fmt, va_list args) { 238 char msgBuf[512]; 239 vsnprintf(msgBuf, sizeof(msgBuf), fmt, args); 240 return jniThrowException(env, className, msgBuf); 241 } 242 243 int jniThrowNullPointerException(C_JNIEnv* env, const char* msg) { 244 return jniThrowException(env, "java/lang/NullPointerException", msg); 245 } 246 247 int jniThrowRuntimeException(C_JNIEnv* env, const char* msg) { 248 return jniThrowException(env, "java/lang/RuntimeException", msg); 249 } 250 251 int jniThrowIOException(C_JNIEnv* env, int errnum) { 252 char buffer[80]; 253 const char* message = jniStrError(errnum, buffer, sizeof(buffer)); 254 return jniThrowException(env, "java/io/IOException", message); 255 } 256 257 void jniLogException(C_JNIEnv* env, int priority, const char* tag, jthrowable exception) { 258 JNIEnv* e = reinterpret_cast<JNIEnv*>(env); 259 260 scoped_local_ref<jthrowable> currentException(env); 261 if (exception == NULL) { 262 exception = (*env)->ExceptionOccurred(e); 263 if (exception == NULL) { 264 return; 265 } 266 267 (*env)->ExceptionClear(e); 268 currentException.reset(exception); 269 } 270 271 char* buffer = getStackTrace(env, exception); 272 if (buffer == NULL) { 273 (*env)->ExceptionClear(e); 274 buffer = getExceptionSummary(env, exception); 275 } 276 277 __android_log_write(priority, tag, buffer); 278 free(buffer); 279 280 if (currentException.get() != NULL) { 281 (*env)->Throw(e, exception); // rethrow 282 } 283 } 284 285 const char* jniStrError(int errnum, char* buf, size_t buflen) { 286 // Note: glibc has a nonstandard strerror_r that returns char* rather than POSIX's int. 287 // char *strerror_r(int errnum, char *buf, size_t n); 288 char* ret = (char*) strerror_r(errnum, buf, buflen); 289 if (((int)ret) == 0) { 290 // POSIX strerror_r, success 291 return buf; 292 } else if (((int)ret) == -1) { 293 // POSIX strerror_r, failure 294 // (Strictly, POSIX only guarantees a value other than 0. The safest 295 // way to implement this function is to use C++ and overload on the 296 // type of strerror_r to accurately distinguish GNU from POSIX. But 297 // realistic implementations will always return -1.) 298 snprintf(buf, buflen, "errno %d", errnum); 299 return buf; 300 } else { 301 // glibc strerror_r returning a string 302 return ret; 303 } 304 } 305 306 static struct CachedFields { 307 jclass fileDescriptorClass; 308 jmethodID fileDescriptorCtor; 309 jfieldID descriptorField; 310 } gCachedFields; 311 312 int registerJniHelp(JNIEnv* env) { 313 gCachedFields.fileDescriptorClass = 314 reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass("java/io/FileDescriptor"))); 315 if (gCachedFields.fileDescriptorClass == NULL) { 316 return -1; 317 } 318 319 gCachedFields.fileDescriptorCtor = 320 env->GetMethodID(gCachedFields.fileDescriptorClass, "<init>", "()V"); 321 if (gCachedFields.fileDescriptorCtor == NULL) { 322 return -1; 323 } 324 325 gCachedFields.descriptorField = 326 env->GetFieldID(gCachedFields.fileDescriptorClass, "descriptor", "I"); 327 if (gCachedFields.descriptorField == NULL) { 328 return -1; 329 } 330 331 return 0; 332 } 333 334 jobject jniCreateFileDescriptor(C_JNIEnv* env, int fd) { 335 JNIEnv* e = reinterpret_cast<JNIEnv*>(env); 336 jobject fileDescriptor = (*env)->NewObject(e, 337 gCachedFields.fileDescriptorClass, gCachedFields.fileDescriptorCtor); 338 jniSetFileDescriptorOfFD(env, fileDescriptor, fd); 339 return fileDescriptor; 340 } 341 342 int jniGetFDFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor) { 343 JNIEnv* e = reinterpret_cast<JNIEnv*>(env); 344 return (*env)->GetIntField(e, fileDescriptor, gCachedFields.descriptorField); 345 } 346 347 void jniSetFileDescriptorOfFD(C_JNIEnv* env, jobject fileDescriptor, int value) { 348 JNIEnv* e = reinterpret_cast<JNIEnv*>(env); 349 (*env)->SetIntField(e, fileDescriptor, gCachedFields.descriptorField, value); 350 } 351 352 /* 353 * DO NOT USE THIS FUNCTION 354 * 355 * Get a pointer to the elements of a non-movable array. 356 * 357 * The semantics are similar to GetDirectBufferAddress. Specifically, the VM 358 * guarantees that the array will not move, and the caller must ensure that 359 * it does not continue to use the pointer after the object is collected. 360 * 361 * We currently use an illegal sequence that trips up CheckJNI when 362 * the "forcecopy" mode is enabled. We pass in a magic value to work 363 * around the problem. 364 * 365 * Returns NULL if the array is movable. 366 */ 367 #define kNoCopyMagic 0xd5aab57f /* also in CheckJni.c */ 368 extern "C" jbyte* jniGetNonMovableArrayElements(C_JNIEnv* env, jarray arrayObj) { 369 JNIEnv* e = reinterpret_cast<JNIEnv*>(env); 370 371 jbyteArray byteArray = reinterpret_cast<jbyteArray>(arrayObj); 372 373 /* 374 * Normally the "isCopy" parameter is for a return value only, so the 375 * non-CheckJNI VM will ignore whatever we pass in. 376 */ 377 uint32_t noCopy = kNoCopyMagic; 378 jbyte* result = (*env)->GetByteArrayElements(e, byteArray, reinterpret_cast<jboolean*>(&noCopy)); 379 380 /* 381 * The non-CheckJNI implementation only cares about the array object, 382 * so we can replace the element pointer with the magic value. 383 */ 384 (*env)->ReleaseByteArrayElements(e, byteArray, reinterpret_cast<jbyte*>(kNoCopyMagic), 0); 385 return result; 386 } 387