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 #endif 21 22 #define LOG_TAG "JNIHelp" 23 24 #include "JniConstants.h" 25 #include "JNIHelp.h" 26 #include "ALog-priv.h" 27 28 #include <stdio.h> 29 #include <stdlib.h> 30 #include <string.h> 31 #include <assert.h> 32 33 #include <string> 34 35 /** 36 * Equivalent to ScopedLocalRef, but for C_JNIEnv instead. (And slightly more powerful.) 37 */ 38 template<typename T> 39 class scoped_local_ref { 40 public: 41 scoped_local_ref(C_JNIEnv* env, T localRef = NULL) 42 : mEnv(env), mLocalRef(localRef) 43 { 44 } 45 46 ~scoped_local_ref() { 47 reset(); 48 } 49 50 void reset(T localRef = NULL) { 51 if (mLocalRef != NULL) { 52 (*mEnv)->DeleteLocalRef(reinterpret_cast<JNIEnv*>(mEnv), mLocalRef); 53 mLocalRef = localRef; 54 } 55 } 56 57 T get() const { 58 return mLocalRef; 59 } 60 61 private: 62 C_JNIEnv* const mEnv; 63 T mLocalRef; 64 65 DISALLOW_COPY_AND_ASSIGN(scoped_local_ref); 66 }; 67 68 static jclass findClass(C_JNIEnv* env, const char* className) { 69 JNIEnv* e = reinterpret_cast<JNIEnv*>(env); 70 return (*env)->FindClass(e, className); 71 } 72 73 extern "C" int jniRegisterNativeMethods(C_JNIEnv* env, const char* className, 74 const JNINativeMethod* gMethods, int numMethods) 75 { 76 JNIEnv* e = reinterpret_cast<JNIEnv*>(env); 77 78 ALOGV("Registering %s's %d native methods...", className, numMethods); 79 80 scoped_local_ref<jclass> c(env, findClass(env, className)); 81 if (c.get() == NULL) { 82 char* tmp; 83 const char* msg; 84 if (asprintf(&tmp, 85 "Native registration unable to find class '%s'; aborting...", 86 className) == -1) { 87 // Allocation failed, print default warning. 88 msg = "Native registration unable to find class; aborting..."; 89 } else { 90 msg = tmp; 91 } 92 e->FatalError(msg); 93 } 94 95 if ((*env)->RegisterNatives(e, c.get(), gMethods, numMethods) < 0) { 96 char* tmp; 97 const char* msg; 98 if (asprintf(&tmp, "RegisterNatives failed for '%s'; aborting...", className) == -1) { 99 // Allocation failed, print default warning. 100 msg = "RegisterNatives failed; aborting..."; 101 } else { 102 msg = tmp; 103 } 104 e->FatalError(msg); 105 } 106 107 return 0; 108 } 109 110 /* 111 * Returns a human-readable summary of an exception object. The buffer will 112 * be populated with the "binary" class name and, if present, the 113 * exception message. 114 */ 115 static bool getExceptionSummary(C_JNIEnv* env, jthrowable exception, std::string& result) { 116 JNIEnv* e = reinterpret_cast<JNIEnv*>(env); 117 118 /* get the name of the exception's class */ 119 scoped_local_ref<jclass> exceptionClass(env, (*env)->GetObjectClass(e, exception)); // can't fail 120 scoped_local_ref<jclass> classClass(env, 121 (*env)->GetObjectClass(e, exceptionClass.get())); // java.lang.Class, can't fail 122 jmethodID classGetNameMethod = 123 (*env)->GetMethodID(e, classClass.get(), "getName", "()Ljava/lang/String;"); 124 scoped_local_ref<jstring> classNameStr(env, 125 (jstring) (*env)->CallObjectMethod(e, exceptionClass.get(), classGetNameMethod)); 126 if (classNameStr.get() == NULL) { 127 (*env)->ExceptionClear(e); 128 result = "<error getting class name>"; 129 return false; 130 } 131 const char* classNameChars = (*env)->GetStringUTFChars(e, classNameStr.get(), NULL); 132 if (classNameChars == NULL) { 133 (*env)->ExceptionClear(e); 134 result = "<error getting class name UTF-8>"; 135 return false; 136 } 137 result += classNameChars; 138 (*env)->ReleaseStringUTFChars(e, classNameStr.get(), classNameChars); 139 140 /* if the exception has a detail message, get that */ 141 jmethodID getMessage = 142 (*env)->GetMethodID(e, exceptionClass.get(), "getMessage", "()Ljava/lang/String;"); 143 scoped_local_ref<jstring> messageStr(env, 144 (jstring) (*env)->CallObjectMethod(e, exception, getMessage)); 145 if (messageStr.get() == NULL) { 146 return true; 147 } 148 149 result += ": "; 150 151 const char* messageChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL); 152 if (messageChars != NULL) { 153 result += messageChars; 154 (*env)->ReleaseStringUTFChars(e, messageStr.get(), messageChars); 155 } else { 156 result += "<error getting message>"; 157 (*env)->ExceptionClear(e); // clear OOM 158 } 159 160 return true; 161 } 162 163 /* 164 * Returns an exception (with stack trace) as a string. 165 */ 166 static bool getStackTrace(C_JNIEnv* env, jthrowable exception, std::string& result) { 167 JNIEnv* e = reinterpret_cast<JNIEnv*>(env); 168 169 scoped_local_ref<jclass> stringWriterClass(env, findClass(env, "java/io/StringWriter")); 170 if (stringWriterClass.get() == NULL) { 171 return false; 172 } 173 174 jmethodID stringWriterCtor = (*env)->GetMethodID(e, stringWriterClass.get(), "<init>", "()V"); 175 jmethodID stringWriterToStringMethod = 176 (*env)->GetMethodID(e, stringWriterClass.get(), "toString", "()Ljava/lang/String;"); 177 178 scoped_local_ref<jclass> printWriterClass(env, findClass(env, "java/io/PrintWriter")); 179 if (printWriterClass.get() == NULL) { 180 return false; 181 } 182 183 jmethodID printWriterCtor = 184 (*env)->GetMethodID(e, printWriterClass.get(), "<init>", "(Ljava/io/Writer;)V"); 185 186 scoped_local_ref<jobject> stringWriter(env, 187 (*env)->NewObject(e, stringWriterClass.get(), stringWriterCtor)); 188 if (stringWriter.get() == NULL) { 189 return false; 190 } 191 192 scoped_local_ref<jobject> printWriter(env, 193 (*env)->NewObject(e, printWriterClass.get(), printWriterCtor, stringWriter.get())); 194 if (printWriter.get() == NULL) { 195 return false; 196 } 197 198 scoped_local_ref<jclass> exceptionClass(env, (*env)->GetObjectClass(e, exception)); // can't fail 199 jmethodID printStackTraceMethod = 200 (*env)->GetMethodID(e, exceptionClass.get(), "printStackTrace", "(Ljava/io/PrintWriter;)V"); 201 (*env)->CallVoidMethod(e, exception, printStackTraceMethod, printWriter.get()); 202 203 if ((*env)->ExceptionCheck(e)) { 204 return false; 205 } 206 207 scoped_local_ref<jstring> messageStr(env, 208 (jstring) (*env)->CallObjectMethod(e, stringWriter.get(), stringWriterToStringMethod)); 209 if (messageStr.get() == NULL) { 210 return false; 211 } 212 213 const char* utfChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL); 214 if (utfChars == NULL) { 215 return false; 216 } 217 218 result = utfChars; 219 220 (*env)->ReleaseStringUTFChars(e, messageStr.get(), utfChars); 221 return true; 222 } 223 224 extern "C" int jniThrowException(C_JNIEnv* env, const char* className, const char* msg) { 225 JNIEnv* e = reinterpret_cast<JNIEnv*>(env); 226 227 if ((*env)->ExceptionCheck(e)) { 228 /* TODO: consider creating the new exception with this as "cause" */ 229 scoped_local_ref<jthrowable> exception(env, (*env)->ExceptionOccurred(e)); 230 (*env)->ExceptionClear(e); 231 232 if (exception.get() != NULL) { 233 std::string text; 234 getExceptionSummary(env, exception.get(), text); 235 ALOGW("Discarding pending exception (%s) to throw %s", text.c_str(), className); 236 } 237 } 238 239 scoped_local_ref<jclass> exceptionClass(env, findClass(env, className)); 240 if (exceptionClass.get() == NULL) { 241 ALOGE("Unable to find exception class %s", className); 242 /* ClassNotFoundException now pending */ 243 return -1; 244 } 245 246 if ((*env)->ThrowNew(e, exceptionClass.get(), msg) != JNI_OK) { 247 ALOGE("Failed throwing '%s' '%s'", className, msg); 248 /* an exception, most likely OOM, will now be pending */ 249 return -1; 250 } 251 252 return 0; 253 } 254 255 int jniThrowExceptionFmt(C_JNIEnv* env, const char* className, const char* fmt, va_list args) { 256 char msgBuf[512]; 257 vsnprintf(msgBuf, sizeof(msgBuf), fmt, args); 258 return jniThrowException(env, className, msgBuf); 259 } 260 261 int jniThrowNullPointerException(C_JNIEnv* env, const char* msg) { 262 return jniThrowException(env, "java/lang/NullPointerException", msg); 263 } 264 265 int jniThrowRuntimeException(C_JNIEnv* env, const char* msg) { 266 return jniThrowException(env, "java/lang/RuntimeException", msg); 267 } 268 269 int jniThrowIOException(C_JNIEnv* env, int errnum) { 270 char buffer[80]; 271 const char* message = jniStrError(errnum, buffer, sizeof(buffer)); 272 return jniThrowException(env, "java/io/IOException", message); 273 } 274 275 static std::string jniGetStackTrace(C_JNIEnv* env, jthrowable exception) { 276 JNIEnv* e = reinterpret_cast<JNIEnv*>(env); 277 278 scoped_local_ref<jthrowable> currentException(env, (*env)->ExceptionOccurred(e)); 279 if (exception == NULL) { 280 exception = currentException.get(); 281 if (exception == NULL) { 282 return "<no pending exception>"; 283 } 284 } 285 286 if (currentException.get() != NULL) { 287 (*env)->ExceptionClear(e); 288 } 289 290 std::string trace; 291 if (!getStackTrace(env, exception, trace)) { 292 (*env)->ExceptionClear(e); 293 getExceptionSummary(env, exception, trace); 294 } 295 296 if (currentException.get() != NULL) { 297 (*env)->Throw(e, currentException.get()); // rethrow 298 } 299 300 return trace; 301 } 302 303 void jniLogException(C_JNIEnv* env, int priority, const char* tag, jthrowable exception) { 304 std::string trace(jniGetStackTrace(env, exception)); 305 __android_log_write(priority, tag, trace.c_str()); 306 } 307 308 const char* jniStrError(int errnum, char* buf, size_t buflen) { 309 #if __GLIBC__ 310 // Note: glibc has a nonstandard strerror_r that returns char* rather than POSIX's int. 311 // char *strerror_r(int errnum, char *buf, size_t n); 312 return strerror_r(errnum, buf, buflen); 313 #else 314 int rc = strerror_r(errnum, buf, buflen); 315 if (rc != 0) { 316 // (POSIX only guarantees a value other than 0. The safest 317 // way to implement this function is to use C++ and overload on the 318 // type of strerror_r to accurately distinguish GNU from POSIX.) 319 snprintf(buf, buflen, "errno %d", errnum); 320 } 321 return buf; 322 #endif 323 } 324 325 jobject jniCreateFileDescriptor(C_JNIEnv* env, int fd) { 326 JNIEnv* e = reinterpret_cast<JNIEnv*>(env); 327 JniConstants::init(e); 328 static jmethodID ctor = e->GetMethodID(JniConstants::fileDescriptorClass, "<init>", "()V"); 329 jobject fileDescriptor = (*env)->NewObject(e, JniConstants::fileDescriptorClass, ctor); 330 // NOTE: NewObject ensures that an OutOfMemoryError will be seen by the Java 331 // caller if the alloc fails, so we just return NULL when that happens. 332 if (fileDescriptor != NULL) { 333 jniSetFileDescriptorOfFD(env, fileDescriptor, fd); 334 } 335 return fileDescriptor; 336 } 337 338 int jniGetFDFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor) { 339 JNIEnv* e = reinterpret_cast<JNIEnv*>(env); 340 JniConstants::init(e); 341 static jfieldID fid = e->GetFieldID(JniConstants::fileDescriptorClass, "descriptor", "I"); 342 if (fileDescriptor != NULL) { 343 return (*env)->GetIntField(e, fileDescriptor, fid); 344 } else { 345 return -1; 346 } 347 } 348 349 void jniSetFileDescriptorOfFD(C_JNIEnv* env, jobject fileDescriptor, int value) { 350 JNIEnv* e = reinterpret_cast<JNIEnv*>(env); 351 JniConstants::init(e); 352 static jfieldID fid = e->GetFieldID(JniConstants::fileDescriptorClass, "descriptor", "I"); 353 (*env)->SetIntField(e, fileDescriptor, fid, value); 354 } 355 356 jobject jniGetReferent(C_JNIEnv* env, jobject ref) { 357 JNIEnv* e = reinterpret_cast<JNIEnv*>(env); 358 JniConstants::init(e); 359 static jmethodID get = e->GetMethodID(JniConstants::referenceClass, "get", "()Ljava/lang/Object;"); 360 return (*env)->CallObjectMethod(e, ref, get); 361 } 362 363