2 * Copyright (C) 2006 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 * JNI helper functions.
20 #define LOG_TAG "JNIHelp"
22 #include "utils/Log.h"
29 * Register native JNI-callable methods.
31 * "className" looks like "java/lang/String".
33 int jniRegisterNativeMethods(JNIEnv* env, const char* className,
34 const JNINativeMethod* gMethods, int numMethods)
38 LOGV("Registering %s natives\n", className);
39 clazz = (*env)->FindClass(env, className);
41 LOGE("Native registration unable to find class '%s', aborting\n",
46 if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) {
47 LOGE("RegisterNatives failed for '%s', aborting\n", className);
51 (*env)->DeleteLocalRef(env, clazz);
56 * Get a human-readable summary of an exception object. The buffer will
57 * be populated with the "binary" class name and, if present, the
60 static void getExceptionSummary(JNIEnv* env, jthrowable exception, char* buf, size_t bufLen)
64 /* get the name of the exception's class */
65 jclass exceptionClazz = (*env)->GetObjectClass(env, exception); // can't fail
66 jclass classClazz = (*env)->GetObjectClass(env, exceptionClazz); // java.lang.Class, can't fail
67 jmethodID classGetNameMethod = (*env)->GetMethodID(
68 env, classClazz, "getName", "()Ljava/lang/String;");
69 jstring classNameStr = (*env)->CallObjectMethod(env, exceptionClazz, classGetNameMethod);
70 if (classNameStr != NULL) {
71 /* get printable string */
72 const char* classNameChars = (*env)->GetStringUTFChars(env, classNameStr, NULL);
73 if (classNameChars != NULL) {
74 /* if the exception has a message string, get that */
75 jmethodID throwableGetMessageMethod = (*env)->GetMethodID(
76 env, exceptionClazz, "getMessage", "()Ljava/lang/String;");
77 jstring messageStr = (*env)->CallObjectMethod(
78 env, exception, throwableGetMessageMethod);
80 if (messageStr != NULL) {
81 const char* messageChars = (*env)->GetStringUTFChars(env, messageStr, NULL);
82 if (messageChars != NULL) {
83 snprintf(buf, bufLen, "%s: %s", classNameChars, messageChars);
84 (*env)->ReleaseStringUTFChars(env, messageStr, messageChars);
86 (*env)->ExceptionClear(env); // clear OOM
87 snprintf(buf, bufLen, "%s: <error getting message>", classNameChars);
89 (*env)->DeleteLocalRef(env, messageStr);
91 strncpy(buf, classNameChars, bufLen);
92 buf[bufLen - 1] = '\0';
95 (*env)->ReleaseStringUTFChars(env, classNameStr, classNameChars);
98 (*env)->DeleteLocalRef(env, classNameStr);
100 (*env)->DeleteLocalRef(env, classClazz);
101 (*env)->DeleteLocalRef(env, exceptionClazz);
104 (*env)->ExceptionClear(env);
105 snprintf(buf, bufLen, "%s", "<error getting class name>");
110 * Formats an exception as a string with its stack trace.
112 static void printStackTrace(JNIEnv* env, jthrowable exception, char* buf, size_t bufLen)
116 jclass stringWriterClazz = (*env)->FindClass(env, "java/io/StringWriter");
117 if (stringWriterClazz != NULL) {
118 jmethodID stringWriterCtor = (*env)->GetMethodID(env, stringWriterClazz,
120 jmethodID stringWriterToStringMethod = (*env)->GetMethodID(env, stringWriterClazz,
121 "toString", "()Ljava/lang/String;");
123 jclass printWriterClazz = (*env)->FindClass(env, "java/io/PrintWriter");
124 if (printWriterClazz != NULL) {
125 jmethodID printWriterCtor = (*env)->GetMethodID(env, printWriterClazz,
126 "<init>", "(Ljava/io/Writer;)V");
128 jobject stringWriterObj = (*env)->NewObject(env, stringWriterClazz, stringWriterCtor);
129 if (stringWriterObj != NULL) {
130 jobject printWriterObj = (*env)->NewObject(env, printWriterClazz, printWriterCtor,
132 if (printWriterObj != NULL) {
133 jclass exceptionClazz = (*env)->GetObjectClass(env, exception); // can't fail
134 jmethodID printStackTraceMethod = (*env)->GetMethodID(
135 env, exceptionClazz, "printStackTrace", "(Ljava/io/PrintWriter;)V");
137 (*env)->CallVoidMethod(
138 env, exception, printStackTraceMethod, printWriterObj);
139 if (! (*env)->ExceptionCheck(env)) {
140 jstring messageStr = (*env)->CallObjectMethod(
141 env, stringWriterObj, stringWriterToStringMethod);
142 if (messageStr != NULL) {
143 jsize messageStrLength = (*env)->GetStringLength(env, messageStr);
144 if (messageStrLength >= (jsize) bufLen) {
145 messageStrLength = bufLen - 1;
147 (*env)->GetStringUTFRegion(env, messageStr, 0, messageStrLength, buf);
148 (*env)->DeleteLocalRef(env, messageStr);
149 buf[messageStrLength] = '\0';
153 (*env)->DeleteLocalRef(env, exceptionClazz);
154 (*env)->DeleteLocalRef(env, printWriterObj);
156 (*env)->DeleteLocalRef(env, stringWriterObj);
158 (*env)->DeleteLocalRef(env, printWriterClazz);
160 (*env)->DeleteLocalRef(env, stringWriterClazz);
164 (*env)->ExceptionClear(env);
165 getExceptionSummary(env, exception, buf, bufLen);
170 * Throw an exception with the specified class and an optional message.
172 * If an exception is currently pending, we log a warning message and
175 * Returns 0 if the specified exception was successfully thrown. (Some
176 * sort of exception will always be pending when this returns.)
178 int jniThrowException(JNIEnv* env, const char* className, const char* msg)
180 jclass exceptionClass;
182 if ((*env)->ExceptionCheck(env)) {
183 /* TODO: consider creating the new exception with this as "cause" */
186 jthrowable exception = (*env)->ExceptionOccurred(env);
187 (*env)->ExceptionClear(env);
189 if (exception != NULL) {
190 getExceptionSummary(env, exception, buf, sizeof(buf));
191 LOGW("Discarding pending exception (%s) to throw %s\n", buf, className);
192 (*env)->DeleteLocalRef(env, exception);
196 exceptionClass = (*env)->FindClass(env, className);
197 if (exceptionClass == NULL) {
198 LOGE("Unable to find exception class %s\n", className);
199 /* ClassNotFoundException now pending */
204 if ((*env)->ThrowNew(env, exceptionClass, msg) != JNI_OK) {
205 LOGE("Failed throwing '%s' '%s'\n", className, msg);
206 /* an exception, most likely OOM, will now be pending */
210 (*env)->DeleteLocalRef(env, exceptionClass);
215 * Throw a java.lang.NullPointerException, with an optional message.
217 int jniThrowNullPointerException(JNIEnv* env, const char* msg)
219 return jniThrowException(env, "java/lang/NullPointerException", msg);
223 * Throw a java.lang.RuntimeException, with an optional message.
225 int jniThrowRuntimeException(JNIEnv* env, const char* msg)
227 return jniThrowException(env, "java/lang/RuntimeException", msg);
231 * Throw a java.io.IOException, generating the message from errno.
233 int jniThrowIOException(JNIEnv* env, int errnum)
236 const char* message = jniStrError(errnum, buffer, sizeof(buffer));
237 return jniThrowException(env, "java/io/IOException", message);
242 * If exception is NULL, logs the current exception in the JNI environment, if any.
244 void jniLogException(JNIEnv* env, int priority, const char* tag, jthrowable exception)
246 int currentException = 0;
247 if (exception == NULL) {
248 exception = (*env)->ExceptionOccurred(env);
249 if (exception == NULL) {
253 (*env)->ExceptionClear(env);
254 currentException = 1;
258 printStackTrace(env, exception, buffer, sizeof(buffer));
259 __android_log_write(priority, tag, buffer);
261 if (currentException) {
262 (*env)->Throw(env, exception); // rethrow
263 (*env)->DeleteLocalRef(env, exception);
267 const char* jniStrError(int errnum, char* buf, size_t buflen)
269 // note: glibc has a nonstandard strerror_r that returns char* rather
271 // char *strerror_r(int errnum, char *buf, size_t n);
272 char* ret = (char*) strerror_r(errnum, buf, buflen);
273 if (((int)ret) == 0) {
274 //POSIX strerror_r, success
276 } else if (((int)ret) == -1) {
277 //POSIX strerror_r, failure
278 // (Strictly, POSIX only guarantees a value other than 0. The safest
279 // way to implement this function is to use C++ and overload on the
280 // type of strerror_r to accurately distinguish GNU from POSIX. But
281 // realistic implementations will always return -1.)
282 snprintf(buf, buflen, "errno %d", errnum);
285 //glibc strerror_r returning a string
290 static struct CachedFields {
291 jclass fileDescriptorClass;
292 jmethodID fileDescriptorCtor;
293 jfieldID descriptorField;
296 int registerJniHelp(JNIEnv* env) {
297 gCachedFields.fileDescriptorClass =
298 (*env)->NewGlobalRef(env, (*env)->FindClass(env, "java/io/FileDescriptor"));
299 if (gCachedFields.fileDescriptorClass == NULL) {
303 gCachedFields.fileDescriptorCtor =
304 (*env)->GetMethodID(env, gCachedFields.fileDescriptorClass, "<init>", "()V");
305 if (gCachedFields.fileDescriptorCtor == NULL) {
309 gCachedFields.descriptorField =
310 (*env)->GetFieldID(env, gCachedFields.fileDescriptorClass, "descriptor", "I");
311 if (gCachedFields.descriptorField == NULL) {
319 * Create a java.io.FileDescriptor given an integer fd
321 jobject jniCreateFileDescriptor(JNIEnv* env, int fd) {
322 jobject fileDescriptor = (*env)->NewObject(env,
323 gCachedFields.fileDescriptorClass, gCachedFields.fileDescriptorCtor);
324 jniSetFileDescriptorOfFD(env, fileDescriptor, fd);
325 return fileDescriptor;
329 * Get an int file descriptor from a java.io.FileDescriptor
331 int jniGetFDFromFileDescriptor(JNIEnv* env, jobject fileDescriptor) {
332 return (*env)->GetIntField(env, fileDescriptor, gCachedFields.descriptorField);
336 * Set the descriptor of a java.io.FileDescriptor
338 void jniSetFileDescriptorOfFD(JNIEnv* env, jobject fileDescriptor, int value) {
339 (*env)->SetIntField(env, fileDescriptor, gCachedFields.descriptorField, value);
343 * DO NOT USE THIS FUNCTION
345 * Get a pointer to the elements of a non-movable array.
347 * The semantics are similar to GetDirectBufferAddress. Specifically, the VM
348 * guarantees that the array will not move, and the caller must ensure that
349 * it does not continue to use the pointer after the object is collected.
351 * We currently use an illegal sequence that trips up CheckJNI when
352 * the "forcecopy" mode is enabled. We pass in a magic value to work
353 * around the problem.
355 * Returns NULL if the array is movable.
357 jbyte* jniGetNonMovableArrayElements(JNIEnv* env, jarray arrayObj)
359 #define kNoCopyMagic 0xd5aab57f /* also in CheckJni.c */
362 * Normally the "isCopy" parameter is for a return value only, so the
363 * non-CheckJNI VM will ignore whatever we pass in.
365 uint32_t noCopy = kNoCopyMagic;
366 jbyte *addr = (*env)->GetByteArrayElements(env, arrayObj,
370 * The non-CheckJNI implementation only cares about the array object,
371 * so we can replace the element pointer with the magic value.
373 (*env)->ReleaseByteArrayElements(env, arrayObj, (jbyte*) kNoCopyMagic, 0);