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* msg; 83 asprintf(&msg, "Native registration unable to find class '%s'; aborting...", className); 84 e->FatalError(msg); 85 } 86 87 if ((*env)->RegisterNatives(e, c.get(), gMethods, numMethods) < 0) { 88 char* msg; 89 asprintf(&msg, "RegisterNatives failed for '%s'; aborting...", className); 90 e->FatalError(msg); 91 } 92 93 return 0; 94 } 95 96 /* 97 * Returns a human-readable summary of an exception object. The buffer will 98 * be populated with the "binary" class name and, if present, the 99 * exception message. 100 */ 101 static bool getExceptionSummary(C_JNIEnv* env, jthrowable exception, std::string& result) { 102 JNIEnv* e = reinterpret_cast<JNIEnv*>(env); 103 104 /* get the name of the exception's class */ 105 scoped_local_ref<jclass> exceptionClass(env, (*env)->GetObjectClass(e, exception)); // can't fail 106 scoped_local_ref<jclass> classClass(env, 107 (*env)->GetObjectClass(e, exceptionClass.get())); // java.lang.Class, can't fail 108 jmethodID classGetNameMethod = 109 (*env)->GetMethodID(e, classClass.get(), "getName", "()Ljava/lang/String;"); 110 scoped_local_ref<jstring> classNameStr(env, 111 (jstring) (*env)->CallObjectMethod(e, exceptionClass.get(), classGetNameMethod)); 112 if (classNameStr.get() == NULL) { 113 (*env)->ExceptionClear(e); 114 result = "<error getting class name>"; 115 return false; 116 } 117 const char* classNameChars = (*env)->GetStringUTFChars(e, classNameStr.get(), NULL); 118 if (classNameChars == NULL) { 119 (*env)->ExceptionClear(e); 120 result = "<error getting class name UTF-8>"; 121 return false; 122 } 123 result += classNameChars; 124 (*env)->ReleaseStringUTFChars(e, classNameStr.get(), classNameChars); 125 126 /* if the exception has a detail message, get that */ 127 jmethodID getMessage = 128 (*env)->GetMethodID(e, exceptionClass.get(), "getMessage", "()Ljava/lang/String;"); 129 scoped_local_ref<jstring> messageStr(env, 130 (jstring) (*env)->CallObjectMethod(e, exception, getMessage)); 131 if (messageStr.get() == NULL) { 132 return true; 133 } 134 135 result += ": "; 136 137 const char* messageChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL); 138 if (messageChars != NULL) { 139 result += messageChars; 140 (*env)->ReleaseStringUTFChars(e, messageStr.get(), messageChars); 141 } else { 142 result += "<error getting message>"; 143 (*env)->ExceptionClear(e); // clear OOM 144 } 145 146 return true; 147 } 148 149 /* 150 * Returns an exception (with stack trace) as a string. 151 */ 152 static bool getStackTrace(C_JNIEnv* env, jthrowable exception, std::string& result) { 153 JNIEnv* e = reinterpret_cast<JNIEnv*>(env); 154 155 scoped_local_ref<jclass> stringWriterClass(env, findClass(env, "java/io/StringWriter")); 156 if (stringWriterClass.get() == NULL) { 157 return false; 158 } 159 160 jmethodID stringWriterCtor = (*env)->GetMethodID(e, stringWriterClass.get(), "<init>", "()V"); 161 jmethodID stringWriterToStringMethod = 162 (*env)->GetMethodID(e, stringWriterClass.get(), "toString", "()Ljava/lang/String;"); 163 164 scoped_local_ref<jclass> printWriterClass(env, findClass(env, "java/io/PrintWriter")); 165 if (printWriterClass.get() == NULL) { 166 return false; 167 } 168 169 jmethodID printWriterCtor = 170 (*env)->GetMethodID(e, printWriterClass.get(), "<init>", "(Ljava/io/Writer;)V"); 171 172 scoped_local_ref<jobject> stringWriter(env, 173 (*env)->NewObject(e, stringWriterClass.get(), stringWriterCtor)); 174 if (stringWriter.get() == NULL) { 175 return false; 176 } 177 178 scoped_local_ref<jobject> printWriter(env, 179 (*env)->NewObject(e, printWriterClass.get(), printWriterCtor, stringWriter.get())); 180 if (printWriter.get() == NULL) { 181 return false; 182 } 183 184 scoped_local_ref<jclass> exceptionClass(env, (*env)->GetObjectClass(e, exception)); // can't fail 185 jmethodID printStackTraceMethod = 186 (*env)->GetMethodID(e, exceptionClass.get(), "printStackTrace", "(Ljava/io/PrintWriter;)V"); 187 (*env)->CallVoidMethod(e, exception, printStackTraceMethod, printWriter.get()); 188 189 if ((*env)->ExceptionCheck(e)) { 190 return false; 191 } 192 193 scoped_local_ref<jstring> messageStr(env, 194 (jstring) (*env)->CallObjectMethod(e, stringWriter.get(), stringWriterToStringMethod)); 195 if (messageStr.get() == NULL) { 196 return false; 197 } 198 199 const char* utfChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL); 200 if (utfChars == NULL) { 201 return false; 202 } 203 204 result = utfChars; 205 206 (*env)->ReleaseStringUTFChars(e, messageStr.get(), utfChars); 207 return true; 208 } 209 210 extern "C" int jniThrowException(C_JNIEnv* env, const char* className, const char* msg) { 211 JNIEnv* e = reinterpret_cast<JNIEnv*>(env); 212 213 if ((*env)->ExceptionCheck(e)) { 214 /* TODO: consider creating the new exception with this as "cause" */ 215 scoped_local_ref<jthrowable> exception(env, (*env)->ExceptionOccurred(e)); 216 (*env)->ExceptionClear(e); 217 218 if (exception.get() != NULL) { 219 std::string text; 220 getExceptionSummary(env, exception.get(), text); 221 ALOGW("Discarding pending exception (%s) to throw %s", text.c_str(), className); 222 } 223 } 224 225 scoped_local_ref<jclass> exceptionClass(env, findClass(env, className)); 226 if (exceptionClass.get() == NULL) { 227 ALOGE("Unable to find exception class %s", className); 228 /* ClassNotFoundException now pending */ 229 return -1; 230 } 231 232 if ((*env)->ThrowNew(e, exceptionClass.get(), msg) != JNI_OK) { 233 ALOGE("Failed throwing '%s' '%s'", className, msg); 234 /* an exception, most likely OOM, will now be pending */ 235 return -1; 236 } 237 238 return 0; 239 } 240 241 int jniThrowExceptionFmt(C_JNIEnv* env, const char* className, const char* fmt, va_list args) { 242 char msgBuf[512]; 243 vsnprintf(msgBuf, sizeof(msgBuf), fmt, args); 244 return jniThrowException(env, className, msgBuf); 245 } 246 247 int jniThrowNullPointerException(C_JNIEnv* env, const char* msg) { 248 return jniThrowException(env, "java/lang/NullPointerException", msg); 249 } 250 251 int jniThrowRuntimeException(C_JNIEnv* env, const char* msg) { 252 return jniThrowException(env, "java/lang/RuntimeException", msg); 253 } 254 255 int jniThrowIOException(C_JNIEnv* env, int errnum) { 256 char buffer[80]; 257 const char* message = jniStrError(errnum, buffer, sizeof(buffer)); 258 return jniThrowException(env, "java/io/IOException", message); 259 } 260 261 static std::string jniGetStackTrace(C_JNIEnv* env, jthrowable exception) { 262 JNIEnv* e = reinterpret_cast<JNIEnv*>(env); 263 264 scoped_local_ref<jthrowable> currentException(env, (*env)->ExceptionOccurred(e)); 265 if (exception == NULL) { 266 exception = currentException.get(); 267 if (exception == NULL) { 268 return "<no pending exception>"; 269 } 270 } 271 272 if (currentException.get() != NULL) { 273 (*env)->ExceptionClear(e); 274 } 275 276 std::string trace; 277 if (!getStackTrace(env, exception, trace)) { 278 (*env)->ExceptionClear(e); 279 getExceptionSummary(env, exception, trace); 280 } 281 282 if (currentException.get() != NULL) { 283 (*env)->Throw(e, currentException.get()); // rethrow 284 } 285 286 return trace; 287 } 288 289 void jniLogException(C_JNIEnv* env, int priority, const char* tag, jthrowable exception) { 290 std::string trace(jniGetStackTrace(env, exception)); 291 __android_log_write(priority, tag, trace.c_str()); 292 } 293 294 const char* jniStrError(int errnum, char* buf, size_t buflen) { 295 #if __GLIBC__ 296 // Note: glibc has a nonstandard strerror_r that returns char* rather than POSIX's int. 297 // char *strerror_r(int errnum, char *buf, size_t n); 298 return strerror_r(errnum, buf, buflen); 299 #else 300 int rc = strerror_r(errnum, buf, buflen); 301 if (rc != 0) { 302 // (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.) 305 snprintf(buf, buflen, "errno %d", errnum); 306 } 307 return buf; 308 #endif 309 } 310 311 jobject jniCreateFileDescriptor(C_JNIEnv* env, int fd) { 312 JNIEnv* e = reinterpret_cast<JNIEnv*>(env); 313 static jmethodID ctor = e->GetMethodID(JniConstants::fileDescriptorClass, "<init>", "()V"); 314 jobject fileDescriptor = (*env)->NewObject(e, JniConstants::fileDescriptorClass, ctor); 315 // NOTE: NewObject ensures that an OutOfMemoryError will be seen by the Java 316 // caller if the alloc fails, so we just return NULL when that happens. 317 if (fileDescriptor != NULL) { 318 jniSetFileDescriptorOfFD(env, fileDescriptor, fd); 319 } 320 return fileDescriptor; 321 } 322 323 int jniGetFDFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor) { 324 JNIEnv* e = reinterpret_cast<JNIEnv*>(env); 325 static jfieldID fid = e->GetFieldID(JniConstants::fileDescriptorClass, "descriptor", "I"); 326 if (fileDescriptor != NULL) { 327 return (*env)->GetIntField(e, fileDescriptor, fid); 328 } else { 329 return -1; 330 } 331 } 332 333 void jniSetFileDescriptorOfFD(C_JNIEnv* env, jobject fileDescriptor, int value) { 334 JNIEnv* e = reinterpret_cast<JNIEnv*>(env); 335 static jfieldID fid = e->GetFieldID(JniConstants::fileDescriptorClass, "descriptor", "I"); 336 (*env)->SetIntField(e, fileDescriptor, fid, value); 337 } 338 339 jobject jniGetReferent(C_JNIEnv* env, jobject ref) { 340 JNIEnv* e = reinterpret_cast<JNIEnv*>(env); 341 static jmethodID get = e->GetMethodID(JniConstants::referenceClass, "get", "()Ljava/lang/Object;"); 342 return (*env)->CallObjectMethod(e, ref, get); 343 } 344 345