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