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