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 ALOGV("Registering %s natives", className); 72 73 scoped_local_ref<jclass> c(env, findClass(env, className)); 74 if (c.get() == NULL) { 75 char* msg; 76 asprintf(&msg, "Native registration unable to find class '%s', aborting", className); 77 e->FatalError(msg); 78 } 79 80 if ((*env)->RegisterNatives(e, c.get(), gMethods, numMethods) < 0) { 81 char* msg; 82 asprintf(&msg, "RegisterNatives failed for '%s', aborting", className); 83 e->FatalError(msg); 84 } 85 86 return 0; 87 } 88 89 /* 90 * Returns a human-readable summary of an exception object. The buffer will 91 * be populated with the "binary" class name and, if present, the 92 * exception message. 93 */ 94 static char* getExceptionSummary0(C_JNIEnv* env, jthrowable exception) { 95 JNIEnv* e = reinterpret_cast<JNIEnv*>(env); 96 97 /* get the name of the exception's class */ 98 scoped_local_ref<jclass> exceptionClass(env, (*env)->GetObjectClass(e, exception)); // can't fail 99 scoped_local_ref<jclass> classClass(env, 100 (*env)->GetObjectClass(e, exceptionClass.get())); // java.lang.Class, can't fail 101 jmethodID classGetNameMethod = 102 (*env)->GetMethodID(e, classClass.get(), "getName", "()Ljava/lang/String;"); 103 scoped_local_ref<jstring> classNameStr(env, 104 (jstring) (*env)->CallObjectMethod(e, exceptionClass.get(), classGetNameMethod)); 105 if (classNameStr.get() == NULL) { 106 return NULL; 107 } 108 109 /* get printable string */ 110 const char* classNameChars = (*env)->GetStringUTFChars(e, classNameStr.get(), NULL); 111 if (classNameChars == NULL) { 112 return NULL; 113 } 114 115 /* if the exception has a detail message, get that */ 116 jmethodID getMessage = 117 (*env)->GetMethodID(e, exceptionClass.get(), "getMessage", "()Ljava/lang/String;"); 118 scoped_local_ref<jstring> messageStr(env, 119 (jstring) (*env)->CallObjectMethod(e, exception, getMessage)); 120 if (messageStr.get() == NULL) { 121 return strdup(classNameChars); 122 } 123 124 char* result = NULL; 125 const char* messageChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL); 126 if (messageChars != NULL) { 127 asprintf(&result, "%s: %s", classNameChars, messageChars); 128 (*env)->ReleaseStringUTFChars(e, messageStr.get(), messageChars); 129 } else { 130 (*env)->ExceptionClear(e); // clear OOM 131 asprintf(&result, "%s: <error getting message>", classNameChars); 132 } 133 134 (*env)->ReleaseStringUTFChars(e, classNameStr.get(), classNameChars); 135 return result; 136 } 137 138 static char* getExceptionSummary(C_JNIEnv* env, jthrowable exception) { 139 JNIEnv* e = reinterpret_cast<JNIEnv*>(env); 140 char* result = getExceptionSummary0(env, exception); 141 if (result == NULL) { 142 (*env)->ExceptionClear(e); 143 result = strdup("<error getting class name>"); 144 } 145 return result; 146 } 147 148 /* 149 * Returns an exception (with stack trace) as a string. 150 */ 151 static char* getStackTrace(C_JNIEnv* env, jthrowable exception) { 152 JNIEnv* e = reinterpret_cast<JNIEnv*>(env); 153 154 scoped_local_ref<jclass> stringWriterClass(env, findClass(env, "java/io/StringWriter")); 155 if (stringWriterClass.get() == NULL) { 156 return NULL; 157 } 158 159 jmethodID stringWriterCtor = (*env)->GetMethodID(e, stringWriterClass.get(), "<init>", "()V"); 160 jmethodID stringWriterToStringMethod = 161 (*env)->GetMethodID(e, stringWriterClass.get(), "toString", "()Ljava/lang/String;"); 162 163 scoped_local_ref<jclass> printWriterClass(env, findClass(env, "java/io/PrintWriter")); 164 if (printWriterClass.get() == NULL) { 165 return NULL; 166 } 167 168 jmethodID printWriterCtor = 169 (*env)->GetMethodID(e, printWriterClass.get(), "<init>", "(Ljava/io/Writer;)V"); 170 171 scoped_local_ref<jobject> stringWriter(env, 172 (*env)->NewObject(e, stringWriterClass.get(), stringWriterCtor)); 173 if (stringWriter.get() == NULL) { 174 return NULL; 175 } 176 177 jobject printWriter = 178 (*env)->NewObject(e, printWriterClass.get(), printWriterCtor, stringWriter.get()); 179 if (printWriter == NULL) { 180 return NULL; 181 } 182 183 scoped_local_ref<jclass> exceptionClass(env, (*env)->GetObjectClass(e, exception)); // can't fail 184 jmethodID printStackTraceMethod = 185 (*env)->GetMethodID(e, exceptionClass.get(), "printStackTrace", "(Ljava/io/PrintWriter;)V"); 186 (*env)->CallVoidMethod(e, exception, printStackTraceMethod, printWriter); 187 188 if ((*env)->ExceptionCheck(e)) { 189 return NULL; 190 } 191 192 scoped_local_ref<jstring> messageStr(env, 193 (jstring) (*env)->CallObjectMethod(e, stringWriter.get(), stringWriterToStringMethod)); 194 if (messageStr.get() == NULL) { 195 return NULL; 196 } 197 198 const char* utfChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL); 199 if (utfChars == NULL) { 200 return NULL; 201 } 202 203 char* result = strdup(utfChars); 204 (*env)->ReleaseStringUTFChars(e, messageStr.get(), utfChars); 205 return result; 206 } 207 208 extern "C" int jniThrowException(C_JNIEnv* env, const char* className, const char* msg) { 209 JNIEnv* e = reinterpret_cast<JNIEnv*>(env); 210 211 if ((*env)->ExceptionCheck(e)) { 212 /* TODO: consider creating the new exception with this as "cause" */ 213 scoped_local_ref<jthrowable> exception(env, (*env)->ExceptionOccurred(e)); 214 (*env)->ExceptionClear(e); 215 216 if (exception.get() != NULL) { 217 char* text = getExceptionSummary(env, exception.get()); 218 ALOGW("Discarding pending exception (%s) to throw %s", text, className); 219 free(text); 220 } 221 } 222 223 scoped_local_ref<jclass> exceptionClass(env, findClass(env, className)); 224 if (exceptionClass.get() == NULL) { 225 ALOGE("Unable to find exception class %s", className); 226 /* ClassNotFoundException now pending */ 227 return -1; 228 } 229 230 if ((*env)->ThrowNew(e, exceptionClass.get(), msg) != JNI_OK) { 231 ALOGE("Failed throwing '%s' '%s'", className, msg); 232 /* an exception, most likely OOM, will now be pending */ 233 return -1; 234 } 235 236 return 0; 237 } 238 239 int jniThrowExceptionFmt(C_JNIEnv* env, const char* className, const char* fmt, va_list args) { 240 char msgBuf[512]; 241 vsnprintf(msgBuf, sizeof(msgBuf), fmt, args); 242 return jniThrowException(env, className, msgBuf); 243 } 244 245 int jniThrowNullPointerException(C_JNIEnv* env, const char* msg) { 246 return jniThrowException(env, "java/lang/NullPointerException", msg); 247 } 248 249 int jniThrowRuntimeException(C_JNIEnv* env, const char* msg) { 250 return jniThrowException(env, "java/lang/RuntimeException", msg); 251 } 252 253 int jniThrowIOException(C_JNIEnv* env, int errnum) { 254 char buffer[80]; 255 const char* message = jniStrError(errnum, buffer, sizeof(buffer)); 256 return jniThrowException(env, "java/io/IOException", message); 257 } 258 259 void jniLogException(C_JNIEnv* env, int priority, const char* tag, jthrowable exception) { 260 JNIEnv* e = reinterpret_cast<JNIEnv*>(env); 261 262 scoped_local_ref<jthrowable> currentException(env, (*env)->ExceptionOccurred(e)); 263 if (exception == NULL) { 264 exception = currentException.get(); 265 if (exception == NULL) { 266 return; 267 } 268 } 269 270 if (currentException.get() != NULL) { 271 (*env)->ExceptionClear(e); 272 } 273 274 char* buffer = getStackTrace(env, exception); 275 if (buffer == NULL) { 276 (*env)->ExceptionClear(e); 277 buffer = getExceptionSummary(env, exception); 278 } 279 280 __android_log_write(priority, tag, buffer); 281 free(buffer); 282 283 if (currentException.get() != NULL) { 284 (*env)->Throw(e, currentException.get()); // rethrow 285 } 286 } 287 288 const char* jniStrError(int errnum, char* buf, size_t buflen) { 289 // Note: glibc has a nonstandard strerror_r that returns char* rather than POSIX's int. 290 // char *strerror_r(int errnum, char *buf, size_t n); 291 char* ret = (char*) strerror_r(errnum, buf, buflen); 292 if (((int)ret) == 0) { 293 // POSIX strerror_r, success 294 return buf; 295 } else if (((int)ret) == -1) { 296 // POSIX strerror_r, failure 297 // (Strictly, POSIX only guarantees a value other than 0. The safest 298 // way to implement this function is to use C++ and overload on the 299 // type of strerror_r to accurately distinguish GNU from POSIX. But 300 // realistic implementations will always return -1.) 301 snprintf(buf, buflen, "errno %d", errnum); 302 return buf; 303 } else { 304 // glibc strerror_r returning a string 305 return ret; 306 } 307 } 308 309 static struct { 310 jclass clazz; 311 jmethodID ctor; 312 jfieldID descriptor; 313 } gFileDescriptorClassInfo; 314 315 static struct { 316 jmethodID get; 317 } gReferenceClassInfo; 318 319 jint JNI_OnLoad(JavaVM* vm, void*) { 320 JNIEnv* env; 321 if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { 322 ALOGE("JavaVM::GetEnv() failed"); 323 abort(); 324 } 325 326 gFileDescriptorClassInfo.clazz = 327 reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass("java/io/FileDescriptor"))); 328 if (gFileDescriptorClassInfo.clazz == NULL) { 329 abort(); 330 } 331 332 gFileDescriptorClassInfo.ctor = 333 env->GetMethodID(gFileDescriptorClassInfo.clazz, "<init>", "()V"); 334 if (gFileDescriptorClassInfo.ctor == NULL) { 335 abort(); 336 } 337 338 gFileDescriptorClassInfo.descriptor = 339 env->GetFieldID(gFileDescriptorClassInfo.clazz, "descriptor", "I"); 340 if (gFileDescriptorClassInfo.descriptor == NULL) { 341 abort(); 342 } 343 344 jclass clazz = reinterpret_cast<jclass>(env->FindClass("java/lang/ref/Reference")); 345 if (clazz == NULL) { 346 abort(); 347 } 348 349 gReferenceClassInfo.get = env->GetMethodID(clazz, "get", "()Ljava/lang/Object;"); 350 if (gReferenceClassInfo.get == NULL) { 351 abort(); 352 } 353 354 return JNI_VERSION_1_6; 355 } 356 357 jobject jniCreateFileDescriptor(C_JNIEnv* env, int fd) { 358 JNIEnv* e = reinterpret_cast<JNIEnv*>(env); 359 jobject fileDescriptor = (*env)->NewObject(e, 360 gFileDescriptorClassInfo.clazz, gFileDescriptorClassInfo.ctor); 361 jniSetFileDescriptorOfFD(env, fileDescriptor, fd); 362 return fileDescriptor; 363 } 364 365 int jniGetFDFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor) { 366 JNIEnv* e = reinterpret_cast<JNIEnv*>(env); 367 return (*env)->GetIntField(e, fileDescriptor, gFileDescriptorClassInfo.descriptor); 368 } 369 370 void jniSetFileDescriptorOfFD(C_JNIEnv* env, jobject fileDescriptor, int value) { 371 JNIEnv* e = reinterpret_cast<JNIEnv*>(env); 372 (*env)->SetIntField(e, fileDescriptor, gFileDescriptorClassInfo.descriptor, value); 373 } 374 375 jobject jniGetReferent(C_JNIEnv* env, jobject ref) { 376 JNIEnv* e = reinterpret_cast<JNIEnv*>(env); 377 return (*env)->CallObjectMethod(e, ref, gReferenceClassInfo.get); 378 } 379 380 /* 381 * DO NOT USE THIS FUNCTION 382 * 383 * Get a pointer to the elements of a non-movable array. 384 * 385 * The semantics are similar to GetDirectBufferAddress. Specifically, the VM 386 * guarantees that the array will not move, and the caller must ensure that 387 * it does not continue to use the pointer after the object is collected. 388 * 389 * We currently use an illegal sequence that trips up CheckJNI when 390 * the "forcecopy" mode is enabled. We pass in a magic value to work 391 * around the problem. 392 * 393 * Returns NULL if the array is movable. 394 */ 395 #define kNoCopyMagic 0xd5aab57f /* also in CheckJni.c */ 396 extern "C" jbyte* jniGetNonMovableArrayElements(C_JNIEnv* env, jarray arrayObj) { 397 JNIEnv* e = reinterpret_cast<JNIEnv*>(env); 398 399 jbyteArray byteArray = reinterpret_cast<jbyteArray>(arrayObj); 400 401 /* 402 * Normally the "isCopy" parameter is for a return value only, so the 403 * non-CheckJNI VM will ignore whatever we pass in. 404 */ 405 uint32_t noCopy = kNoCopyMagic; 406 jbyte* result = (*env)->GetByteArrayElements(e, byteArray, reinterpret_cast<jboolean*>(&noCopy)); 407 408 /* 409 * The non-CheckJNI implementation only cares about the array object, 410 * so we can replace the element pointer with the magic value. 411 */ 412 (*env)->ReleaseByteArrayElements(e, byteArray, reinterpret_cast<jbyte*>(kNoCopyMagic), 0); 413 return result; 414 } 415