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/* 18 * JNI helper functions. 19 */ 20#define LOG_TAG "JNIHelp" 21#include "JNIHelp.h" 22#include "utils/Log.h" 23 24#include <string.h> 25#include <assert.h> 26 27/* 28 * Register native JNI-callable methods. 29 * 30 * "className" looks like "java/lang/String". 31 */ 32int jniRegisterNativeMethods(JNIEnv* env, const char* className, 33 const JNINativeMethod* gMethods, int numMethods) 34{ 35 jclass clazz; 36 37 LOGV("Registering %s natives\n", className); 38 clazz = (*env)->FindClass(env, className); 39 if (clazz == NULL) { 40 LOGE("Native registration unable to find class '%s'\n", className); 41 return -1; 42 } 43 44 int result = 0; 45 if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) { 46 LOGE("RegisterNatives failed for '%s'\n", className); 47 result = -1; 48 } 49 50 (*env)->DeleteLocalRef(env, clazz); 51 return result; 52} 53 54/* 55 * Get a human-readable summary of an exception object. The buffer will 56 * be populated with the "binary" class name and, if present, the 57 * exception message. 58 */ 59static void getExceptionSummary(JNIEnv* env, jthrowable exception, char* buf, size_t bufLen) 60{ 61 int success = 0; 62 63 /* get the name of the exception's class */ 64 jclass exceptionClazz = (*env)->GetObjectClass(env, exception); // can't fail 65 jclass classClazz = (*env)->GetObjectClass(env, exceptionClazz); // java.lang.Class, can't fail 66 jmethodID classGetNameMethod = (*env)->GetMethodID( 67 env, classClazz, "getName", "()Ljava/lang/String;"); 68 jstring classNameStr = (*env)->CallObjectMethod(env, exceptionClazz, classGetNameMethod); 69 if (classNameStr != NULL) { 70 /* get printable string */ 71 const char* classNameChars = (*env)->GetStringUTFChars(env, classNameStr, NULL); 72 if (classNameChars != NULL) { 73 /* if the exception has a message string, get that */ 74 jmethodID throwableGetMessageMethod = (*env)->GetMethodID( 75 env, exceptionClazz, "getMessage", "()Ljava/lang/String;"); 76 jstring messageStr = (*env)->CallObjectMethod( 77 env, exception, throwableGetMessageMethod); 78 79 if (messageStr != NULL) { 80 const char* messageChars = (*env)->GetStringUTFChars(env, messageStr, NULL); 81 if (messageChars != NULL) { 82 snprintf(buf, bufLen, "%s: %s", classNameChars, messageChars); 83 (*env)->ReleaseStringUTFChars(env, messageStr, messageChars); 84 } else { 85 (*env)->ExceptionClear(env); // clear OOM 86 snprintf(buf, bufLen, "%s: <error getting message>", classNameChars); 87 } 88 (*env)->DeleteLocalRef(env, messageStr); 89 } else { 90 strncpy(buf, classNameChars, bufLen); 91 buf[bufLen - 1] = '\0'; 92 } 93 94 (*env)->ReleaseStringUTFChars(env, classNameStr, classNameChars); 95 success = 1; 96 } 97 (*env)->DeleteLocalRef(env, classNameStr); 98 } 99 (*env)->DeleteLocalRef(env, classClazz); 100 (*env)->DeleteLocalRef(env, exceptionClazz); 101 102 if (! success) { 103 (*env)->ExceptionClear(env); 104 snprintf(buf, bufLen, "%s", "<error getting class name>"); 105 } 106} 107 108/* 109 * Formats an exception as a string with its stack trace. 110 */ 111static void printStackTrace(JNIEnv* env, jthrowable exception, char* buf, size_t bufLen) 112{ 113 int success = 0; 114 115 jclass stringWriterClazz = (*env)->FindClass(env, "java/io/StringWriter"); 116 if (stringWriterClazz != NULL) { 117 jmethodID stringWriterCtor = (*env)->GetMethodID(env, stringWriterClazz, 118 "<init>", "()V"); 119 jmethodID stringWriterToStringMethod = (*env)->GetMethodID(env, stringWriterClazz, 120 "toString", "()Ljava/lang/String;"); 121 122 jclass printWriterClazz = (*env)->FindClass(env, "java/io/PrintWriter"); 123 if (printWriterClazz != NULL) { 124 jmethodID printWriterCtor = (*env)->GetMethodID(env, printWriterClazz, 125 "<init>", "(Ljava/io/Writer;)V"); 126 127 jobject stringWriterObj = (*env)->NewObject(env, stringWriterClazz, stringWriterCtor); 128 if (stringWriterObj != NULL) { 129 jobject printWriterObj = (*env)->NewObject(env, printWriterClazz, printWriterCtor, 130 stringWriterObj); 131 if (printWriterObj != NULL) { 132 jclass exceptionClazz = (*env)->GetObjectClass(env, exception); // can't fail 133 jmethodID printStackTraceMethod = (*env)->GetMethodID( 134 env, exceptionClazz, "printStackTrace", "(Ljava/io/PrintWriter;)V"); 135 136 (*env)->CallVoidMethod( 137 env, exception, printStackTraceMethod, printWriterObj); 138 if (! (*env)->ExceptionCheck(env)) { 139 jstring messageStr = (*env)->CallObjectMethod( 140 env, stringWriterObj, stringWriterToStringMethod); 141 if (messageStr != NULL) { 142 jsize messageStrLength = (*env)->GetStringLength(env, messageStr); 143 if (messageStrLength >= (jsize) bufLen) { 144 messageStrLength = bufLen - 1; 145 } 146 (*env)->GetStringUTFRegion(env, messageStr, 0, messageStrLength, buf); 147 (*env)->DeleteLocalRef(env, messageStr); 148 buf[messageStrLength] = '\0'; 149 success = 1; 150 } 151 } 152 (*env)->DeleteLocalRef(env, exceptionClazz); 153 (*env)->DeleteLocalRef(env, printWriterObj); 154 } 155 (*env)->DeleteLocalRef(env, stringWriterObj); 156 } 157 (*env)->DeleteLocalRef(env, printWriterClazz); 158 } 159 (*env)->DeleteLocalRef(env, stringWriterClazz); 160 } 161 162 if (! success) { 163 (*env)->ExceptionClear(env); 164 getExceptionSummary(env, exception, buf, bufLen); 165 } 166} 167 168/* 169 * Throw an exception with the specified class and an optional message. 170 * 171 * If an exception is currently pending, we log a warning message and 172 * clear it. 173 * 174 * Returns 0 if the specified exception was successfully thrown. (Some 175 * sort of exception will always be pending when this returns.) 176 */ 177int jniThrowException(JNIEnv* env, const char* className, const char* msg) 178{ 179 jclass exceptionClass; 180 181 if ((*env)->ExceptionCheck(env)) { 182 /* TODO: consider creating the new exception with this as "cause" */ 183 char buf[256]; 184 185 jthrowable exception = (*env)->ExceptionOccurred(env); 186 (*env)->ExceptionClear(env); 187 188 if (exception != NULL) { 189 getExceptionSummary(env, exception, buf, sizeof(buf)); 190 LOGW("Discarding pending exception (%s) to throw %s\n", buf, className); 191 (*env)->DeleteLocalRef(env, exception); 192 } 193 } 194 195 exceptionClass = (*env)->FindClass(env, className); 196 if (exceptionClass == NULL) { 197 LOGE("Unable to find exception class %s\n", className); 198 /* ClassNotFoundException now pending */ 199 return -1; 200 } 201 202 int result = 0; 203 if ((*env)->ThrowNew(env, exceptionClass, msg) != JNI_OK) { 204 LOGE("Failed throwing '%s' '%s'\n", className, msg); 205 /* an exception, most likely OOM, will now be pending */ 206 result = -1; 207 } 208 209 (*env)->DeleteLocalRef(env, exceptionClass); 210 return result; 211} 212 213/* 214 * Throw a java.lang.NullPointerException, with an optional message. 215 */ 216int jniThrowNullPointerException(JNIEnv* env, const char* msg) 217{ 218 return jniThrowException(env, "java/lang/NullPointerException", msg); 219} 220 221/* 222 * Throw a java.lang.RuntimeException, with an optional message. 223 */ 224int jniThrowRuntimeException(JNIEnv* env, const char* msg) 225{ 226 return jniThrowException(env, "java/lang/RuntimeException", msg); 227} 228 229/* 230 * Throw a java.io.IOException, generating the message from errno. 231 */ 232int jniThrowIOException(JNIEnv* env, int errnum) 233{ 234 char buffer[80]; 235 const char* message = jniStrError(errnum, buffer, sizeof(buffer)); 236 return jniThrowException(env, "java/io/IOException", message); 237} 238 239/* 240 * Log an exception. 241 * If exception is NULL, logs the current exception in the JNI environment, if any. 242 */ 243void jniLogException(JNIEnv* env, int priority, const char* tag, jthrowable exception) 244{ 245 int currentException = 0; 246 if (exception == NULL) { 247 exception = (*env)->ExceptionOccurred(env); 248 if (exception == NULL) { 249 return; 250 } 251 252 (*env)->ExceptionClear(env); 253 currentException = 1; 254 } 255 256 char buffer[1024]; 257 printStackTrace(env, exception, buffer, sizeof(buffer)); 258 __android_log_write(priority, tag, buffer); 259 260 if (currentException) { 261 (*env)->Throw(env, exception); // rethrow 262 (*env)->DeleteLocalRef(env, exception); 263 } 264} 265 266const char* jniStrError(int errnum, char* buf, size_t buflen) 267{ 268 // note: glibc has a nonstandard strerror_r that returns char* rather 269 // than POSIX's int. 270 // char *strerror_r(int errnum, char *buf, size_t n); 271 char* ret = (char*) strerror_r(errnum, buf, buflen); 272 if (((int)ret) == 0) { 273 //POSIX strerror_r, success 274 return buf; 275 } else if (((int)ret) == -1) { 276 //POSIX strerror_r, failure 277 // (Strictly, POSIX only guarantees a value other than 0. The safest 278 // way to implement this function is to use C++ and overload on the 279 // type of strerror_r to accurately distinguish GNU from POSIX. But 280 // realistic implementations will always return -1.) 281 snprintf(buf, buflen, "errno %d", errnum); 282 return buf; 283 } else { 284 //glibc strerror_r returning a string 285 return ret; 286 } 287} 288 289static struct CachedFields { 290 jclass fileDescriptorClass; 291 jmethodID fileDescriptorCtor; 292 jfieldID descriptorField; 293} gCachedFields; 294 295int registerJniHelp(JNIEnv* env) { 296 gCachedFields.fileDescriptorClass = 297 (*env)->NewGlobalRef(env, (*env)->FindClass(env, "java/io/FileDescriptor")); 298 if (gCachedFields.fileDescriptorClass == NULL) { 299 return -1; 300 } 301 302 gCachedFields.fileDescriptorCtor = 303 (*env)->GetMethodID(env, gCachedFields.fileDescriptorClass, "<init>", "()V"); 304 if (gCachedFields.fileDescriptorCtor == NULL) { 305 return -1; 306 } 307 308 gCachedFields.descriptorField = 309 (*env)->GetFieldID(env, gCachedFields.fileDescriptorClass, "descriptor", "I"); 310 if (gCachedFields.descriptorField == NULL) { 311 return -1; 312 } 313 314 return 0; 315} 316 317/* 318 * Create a java.io.FileDescriptor given an integer fd 319 */ 320jobject jniCreateFileDescriptor(JNIEnv* env, int fd) { 321 jobject fileDescriptor = (*env)->NewObject(env, 322 gCachedFields.fileDescriptorClass, gCachedFields.fileDescriptorCtor); 323 jniSetFileDescriptorOfFD(env, fileDescriptor, fd); 324 return fileDescriptor; 325} 326 327/* 328 * Get an int file descriptor from a java.io.FileDescriptor 329 */ 330int jniGetFDFromFileDescriptor(JNIEnv* env, jobject fileDescriptor) { 331 return (*env)->GetIntField(env, fileDescriptor, gCachedFields.descriptorField); 332} 333 334/* 335 * Set the descriptor of a java.io.FileDescriptor 336 */ 337void jniSetFileDescriptorOfFD(JNIEnv* env, jobject fileDescriptor, int value) { 338 (*env)->SetIntField(env, fileDescriptor, gCachedFields.descriptorField, value); 339} 340