blob: e5abc0f5d2b3e95b8d89d68e47b17425d28ddfb9 [file] [log] [blame]
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001/*
Andy McFaddend1f4cf72009-10-21 15:36:48 -07002 * Copyright (C) 2006 The Android Open Source Project
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003 *
Andy McFaddend1f4cf72009-10-21 15:36:48 -07004 * 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/*
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080018 * JNI helper functions.
19 */
20#define LOG_TAG "JNIHelp"
21#include "JNIHelp.h"
22#include "utils/Log.h"
23
Andy McFadden37b80052011-02-08 12:07:32 -080024#include <stdlib.h>
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080025#include <string.h>
26#include <assert.h>
27
28/*
29 * Register native JNI-callable methods.
30 *
31 * "className" looks like "java/lang/String".
32 */
33int jniRegisterNativeMethods(JNIEnv* env, const char* className,
34 const JNINativeMethod* gMethods, int numMethods)
35{
36 jclass clazz;
37
38 LOGV("Registering %s natives\n", className);
39 clazz = (*env)->FindClass(env, className);
40 if (clazz == NULL) {
Andy McFadden5e261cd2011-01-07 13:34:47 -080041 LOGE("Native registration unable to find class '%s', aborting\n",
42 className);
43 abort();
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080044 }
Jeff Brown431da182010-06-01 21:07:08 -070045
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080046 if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) {
Andy McFadden5e261cd2011-01-07 13:34:47 -080047 LOGE("RegisterNatives failed for '%s', aborting\n", className);
48 abort();
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080049 }
Jeff Brown431da182010-06-01 21:07:08 -070050
51 (*env)->DeleteLocalRef(env, clazz);
Andy McFadden5e261cd2011-01-07 13:34:47 -080052 return 0;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080053}
54
55/*
Andy McFaddend1f4cf72009-10-21 15:36:48 -070056 * Get a human-readable summary of an exception object. The buffer will
57 * be populated with the "binary" class name and, if present, the
58 * exception message.
59 */
Jeff Brown431da182010-06-01 21:07:08 -070060static void getExceptionSummary(JNIEnv* env, jthrowable exception, char* buf, size_t bufLen)
Andy McFaddend1f4cf72009-10-21 15:36:48 -070061{
Jeff Brown431da182010-06-01 21:07:08 -070062 int success = 0;
Andy McFaddend1f4cf72009-10-21 15:36:48 -070063
Jeff Brown431da182010-06-01 21:07:08 -070064 /* 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);
Andy McFaddend1f4cf72009-10-21 15:36:48 -070079
Jeff Brown431da182010-06-01 21:07:08 -070080 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);
85 } else {
86 (*env)->ExceptionClear(env); // clear OOM
87 snprintf(buf, bufLen, "%s: <error getting message>", classNameChars);
88 }
89 (*env)->DeleteLocalRef(env, messageStr);
90 } else {
91 strncpy(buf, classNameChars, bufLen);
92 buf[bufLen - 1] = '\0';
93 }
94
95 (*env)->ReleaseStringUTFChars(env, classNameStr, classNameChars);
96 success = 1;
97 }
98 (*env)->DeleteLocalRef(env, classNameStr);
99 }
100 (*env)->DeleteLocalRef(env, classClazz);
101 (*env)->DeleteLocalRef(env, exceptionClazz);
102
103 if (! success) {
104 (*env)->ExceptionClear(env);
105 snprintf(buf, bufLen, "%s", "<error getting class name>");
106 }
107}
108
109/*
110 * Formats an exception as a string with its stack trace.
111 */
112static void printStackTrace(JNIEnv* env, jthrowable exception, char* buf, size_t bufLen)
113{
114 int success = 0;
115
116 jclass stringWriterClazz = (*env)->FindClass(env, "java/io/StringWriter");
117 if (stringWriterClazz != NULL) {
118 jmethodID stringWriterCtor = (*env)->GetMethodID(env, stringWriterClazz,
119 "<init>", "()V");
120 jmethodID stringWriterToStringMethod = (*env)->GetMethodID(env, stringWriterClazz,
121 "toString", "()Ljava/lang/String;");
122
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");
127
128 jobject stringWriterObj = (*env)->NewObject(env, stringWriterClazz, stringWriterCtor);
129 if (stringWriterObj != NULL) {
130 jobject printWriterObj = (*env)->NewObject(env, printWriterClazz, printWriterCtor,
131 stringWriterObj);
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");
136
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;
146 }
147 (*env)->GetStringUTFRegion(env, messageStr, 0, messageStrLength, buf);
148 (*env)->DeleteLocalRef(env, messageStr);
149 buf[messageStrLength] = '\0';
150 success = 1;
151 }
152 }
153 (*env)->DeleteLocalRef(env, exceptionClazz);
154 (*env)->DeleteLocalRef(env, printWriterObj);
155 }
156 (*env)->DeleteLocalRef(env, stringWriterObj);
157 }
158 (*env)->DeleteLocalRef(env, printWriterClazz);
159 }
160 (*env)->DeleteLocalRef(env, stringWriterClazz);
Andy McFaddend1f4cf72009-10-21 15:36:48 -0700161 }
162
Jeff Brown431da182010-06-01 21:07:08 -0700163 if (! success) {
164 (*env)->ExceptionClear(env);
165 getExceptionSummary(env, exception, buf, bufLen);
Andy McFaddend1f4cf72009-10-21 15:36:48 -0700166 }
Andy McFaddend1f4cf72009-10-21 15:36:48 -0700167}
168
169/*
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800170 * Throw an exception with the specified class and an optional message.
Andy McFaddend1f4cf72009-10-21 15:36:48 -0700171 *
172 * If an exception is currently pending, we log a warning message and
173 * clear it.
174 *
175 * Returns 0 if the specified exception was successfully thrown. (Some
176 * sort of exception will always be pending when this returns.)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800177 */
178int jniThrowException(JNIEnv* env, const char* className, const char* msg)
179{
180 jclass exceptionClass;
181
Andy McFaddend1f4cf72009-10-21 15:36:48 -0700182 if ((*env)->ExceptionCheck(env)) {
183 /* TODO: consider creating the new exception with this as "cause" */
184 char buf[256];
185
Jeff Brown431da182010-06-01 21:07:08 -0700186 jthrowable exception = (*env)->ExceptionOccurred(env);
Andy McFaddend1f4cf72009-10-21 15:36:48 -0700187 (*env)->ExceptionClear(env);
Jeff Brown431da182010-06-01 21:07:08 -0700188
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);
193 }
Andy McFaddend1f4cf72009-10-21 15:36:48 -0700194 }
195
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800196 exceptionClass = (*env)->FindClass(env, className);
197 if (exceptionClass == NULL) {
198 LOGE("Unable to find exception class %s\n", className);
Andy McFaddend1f4cf72009-10-21 15:36:48 -0700199 /* ClassNotFoundException now pending */
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800200 return -1;
201 }
202
Jeff Brown431da182010-06-01 21:07:08 -0700203 int result = 0;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800204 if ((*env)->ThrowNew(env, exceptionClass, msg) != JNI_OK) {
205 LOGE("Failed throwing '%s' '%s'\n", className, msg);
Andy McFaddend1f4cf72009-10-21 15:36:48 -0700206 /* an exception, most likely OOM, will now be pending */
Jeff Brown431da182010-06-01 21:07:08 -0700207 result = -1;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800208 }
Jeff Brown431da182010-06-01 21:07:08 -0700209
210 (*env)->DeleteLocalRef(env, exceptionClass);
211 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800212}
213
214/*
Elliott Hughes7f4a5ec2010-01-28 13:43:39 -0800215 * Throw a java.lang.NullPointerException, with an optional message.
216 */
217int jniThrowNullPointerException(JNIEnv* env, const char* msg)
218{
219 return jniThrowException(env, "java/lang/NullPointerException", msg);
220}
221
222/*
Dan Bornstein4a888b02009-12-07 15:46:23 -0800223 * Throw a java.lang.RuntimeException, with an optional message.
224 */
225int jniThrowRuntimeException(JNIEnv* env, const char* msg)
226{
227 return jniThrowException(env, "java/lang/RuntimeException", msg);
228}
229
230/*
231 * Throw a java.io.IOException, generating the message from errno.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800232 */
233int jniThrowIOException(JNIEnv* env, int errnum)
234{
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800235 char buffer[80];
Elliott Hughes54b596c2009-10-20 16:59:01 -0700236 const char* message = jniStrError(errnum, buffer, sizeof(buffer));
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800237 return jniThrowException(env, "java/io/IOException", message);
238}
239
Jeff Brown431da182010-06-01 21:07:08 -0700240/*
241 * Log an exception.
242 * If exception is NULL, logs the current exception in the JNI environment, if any.
243 */
244void jniLogException(JNIEnv* env, int priority, const char* tag, jthrowable exception)
245{
246 int currentException = 0;
247 if (exception == NULL) {
248 exception = (*env)->ExceptionOccurred(env);
249 if (exception == NULL) {
250 return;
251 }
252
253 (*env)->ExceptionClear(env);
254 currentException = 1;
255 }
256
257 char buffer[1024];
258 printStackTrace(env, exception, buffer, sizeof(buffer));
259 __android_log_write(priority, tag, buffer);
260
261 if (currentException) {
262 (*env)->Throw(env, exception); // rethrow
263 (*env)->DeleteLocalRef(env, exception);
264 }
265}
266
Elliott Hughes54b596c2009-10-20 16:59:01 -0700267const char* jniStrError(int errnum, char* buf, size_t buflen)
268{
269 // note: glibc has a nonstandard strerror_r that returns char* rather
270 // than POSIX's int.
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
275 return buf;
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);
283 return buf;
284 } else {
285 //glibc strerror_r returning a string
286 return ret;
287 }
288}
Elliott Hughese9179f12010-06-17 13:19:01 -0700289
290static struct CachedFields {
291 jclass fileDescriptorClass;
292 jmethodID fileDescriptorCtor;
293 jfieldID descriptorField;
294} gCachedFields;
295
296int registerJniHelp(JNIEnv* env) {
297 gCachedFields.fileDescriptorClass =
298 (*env)->NewGlobalRef(env, (*env)->FindClass(env, "java/io/FileDescriptor"));
299 if (gCachedFields.fileDescriptorClass == NULL) {
300 return -1;
301 }
302
303 gCachedFields.fileDescriptorCtor =
304 (*env)->GetMethodID(env, gCachedFields.fileDescriptorClass, "<init>", "()V");
305 if (gCachedFields.fileDescriptorCtor == NULL) {
306 return -1;
307 }
308
309 gCachedFields.descriptorField =
310 (*env)->GetFieldID(env, gCachedFields.fileDescriptorClass, "descriptor", "I");
311 if (gCachedFields.descriptorField == NULL) {
312 return -1;
313 }
314
315 return 0;
316}
317
318/*
319 * Create a java.io.FileDescriptor given an integer fd
320 */
321jobject jniCreateFileDescriptor(JNIEnv* env, int fd) {
322 jobject fileDescriptor = (*env)->NewObject(env,
323 gCachedFields.fileDescriptorClass, gCachedFields.fileDescriptorCtor);
324 jniSetFileDescriptorOfFD(env, fileDescriptor, fd);
325 return fileDescriptor;
326}
327
328/*
329 * Get an int file descriptor from a java.io.FileDescriptor
330 */
331int jniGetFDFromFileDescriptor(JNIEnv* env, jobject fileDescriptor) {
332 return (*env)->GetIntField(env, fileDescriptor, gCachedFields.descriptorField);
333}
334
335/*
336 * Set the descriptor of a java.io.FileDescriptor
337 */
338void jniSetFileDescriptorOfFD(JNIEnv* env, jobject fileDescriptor, int value) {
339 (*env)->SetIntField(env, fileDescriptor, gCachedFields.descriptorField, value);
340}
Andy McFadden37b80052011-02-08 12:07:32 -0800341
342/*
343 * DO NOT USE THIS FUNCTION
344 *
345 * Get a pointer to the elements of a non-movable array.
346 *
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.
350 *
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.
354 *
355 * Returns NULL if the array is movable.
356 */
357jbyte* jniGetNonMovableArrayElements(JNIEnv* env, jarray arrayObj)
358{
359#define kNoCopyMagic 0xd5aab57f /* also in CheckJni.c */
360
361 /*
362 * Normally the "isCopy" parameter is for a return value only, so the
363 * non-CheckJNI VM will ignore whatever we pass in.
364 */
365 uint32_t noCopy = kNoCopyMagic;
366 jbyte *addr = (*env)->GetByteArrayElements(env, arrayObj,
367 (jboolean*)&noCopy);
368
369 /*
370 * The non-CheckJNI implementation only cares about the array object,
371 * so we can replace the element pointer with the magic value.
372 */
373 (*env)->ReleaseByteArrayElements(env, arrayObj, (jbyte*) kNoCopyMagic, 0);
374 return addr;
375}