blob: bf50930c33114f16819edec4ccc4a8247beadfb9 [file] [log] [blame]
Elliott Hughes87f62a82011-04-22 19:22:54 -07001/*
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#define LOG_TAG "JNIHelp"
18
19#include "JNIHelp.h"
Elliott Hughesa6f951c2011-06-08 15:54:05 -070020#include "cutils/log.h"
Elliott Hughes87f62a82011-04-22 19:22:54 -070021
22#include <stdlib.h>
23#include <string.h>
24#include <assert.h>
25
26/**
27 * Equivalent to ScopedLocalRef, but for C_JNIEnv instead. (And slightly more powerful.)
28 */
29template<typename T>
30class scoped_local_ref {
31public:
32 scoped_local_ref(C_JNIEnv* env, T localRef = NULL)
33 : mEnv(env), mLocalRef(localRef)
34 {
35 }
36
37 ~scoped_local_ref() {
38 reset();
39 }
40
41 void reset(T localRef = NULL) {
42 if (mLocalRef != NULL) {
43 (*mEnv)->DeleteLocalRef(reinterpret_cast<JNIEnv*>(mEnv), mLocalRef);
44 mLocalRef = localRef;
45 }
46 }
47
48 T get() const {
49 return mLocalRef;
50 }
51
52private:
53 C_JNIEnv* mEnv;
54 T mLocalRef;
55
56 // Disallow copy and assignment.
57 scoped_local_ref(const scoped_local_ref&);
58 void operator=(const scoped_local_ref&);
59};
60
61static jclass findClass(C_JNIEnv* env, const char* className) {
62 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
63 return (*env)->FindClass(e, className);
64}
65
66extern "C" int jniRegisterNativeMethods(C_JNIEnv* env, const char* className,
67 const JNINativeMethod* gMethods, int numMethods)
68{
69 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
70
Steve Block292b1c22011-10-20 11:55:54 +010071 ALOGV("Registering %s natives", className);
Elliott Hughes87f62a82011-04-22 19:22:54 -070072
73 scoped_local_ref<jclass> c(env, findClass(env, className));
74 if (c.get() == NULL) {
Steve Block79a0d9c2012-01-06 19:16:58 +000075 ALOGE("Native registration unable to find class '%s', aborting", className);
Elliott Hughes87f62a82011-04-22 19:22:54 -070076 abort();
77 }
78
79 if ((*env)->RegisterNatives(e, c.get(), gMethods, numMethods) < 0) {
Steve Block79a0d9c2012-01-06 19:16:58 +000080 ALOGE("RegisterNatives failed for '%s', aborting", className);
Elliott Hughes87f62a82011-04-22 19:22:54 -070081 abort();
82 }
83
84 return 0;
85}
86
87/*
88 * Returns a human-readable summary of an exception object. The buffer will
89 * be populated with the "binary" class name and, if present, the
90 * exception message.
91 */
92static char* getExceptionSummary0(C_JNIEnv* env, jthrowable exception) {
93 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
94
95 /* get the name of the exception's class */
96 scoped_local_ref<jclass> exceptionClass(env, (*env)->GetObjectClass(e, exception)); // can't fail
97 scoped_local_ref<jclass> classClass(env,
98 (*env)->GetObjectClass(e, exceptionClass.get())); // java.lang.Class, can't fail
99 jmethodID classGetNameMethod =
100 (*env)->GetMethodID(e, classClass.get(), "getName", "()Ljava/lang/String;");
101 scoped_local_ref<jstring> classNameStr(env,
102 (jstring) (*env)->CallObjectMethod(e, exceptionClass.get(), classGetNameMethod));
103 if (classNameStr.get() == NULL) {
104 return NULL;
105 }
106
107 /* get printable string */
108 const char* classNameChars = (*env)->GetStringUTFChars(e, classNameStr.get(), NULL);
109 if (classNameChars == NULL) {
110 return NULL;
111 }
112
113 /* if the exception has a detail message, get that */
114 jmethodID getMessage =
115 (*env)->GetMethodID(e, exceptionClass.get(), "getMessage", "()Ljava/lang/String;");
116 scoped_local_ref<jstring> messageStr(env,
117 (jstring) (*env)->CallObjectMethod(e, exception, getMessage));
118 if (messageStr.get() == NULL) {
119 return strdup(classNameChars);
120 }
121
122 char* result = NULL;
123 const char* messageChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL);
124 if (messageChars != NULL) {
125 asprintf(&result, "%s: %s", classNameChars, messageChars);
126 (*env)->ReleaseStringUTFChars(e, messageStr.get(), messageChars);
127 } else {
128 (*env)->ExceptionClear(e); // clear OOM
129 asprintf(&result, "%s: <error getting message>", classNameChars);
130 }
131
132 (*env)->ReleaseStringUTFChars(e, classNameStr.get(), classNameChars);
133 return result;
134}
135
136static char* getExceptionSummary(C_JNIEnv* env, jthrowable exception) {
137 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
138 char* result = getExceptionSummary0(env, exception);
139 if (result == NULL) {
140 (*env)->ExceptionClear(e);
141 result = strdup("<error getting class name>");
142 }
143 return result;
144}
145
146/*
147 * Returns an exception (with stack trace) as a string.
148 */
149static char* getStackTrace(C_JNIEnv* env, jthrowable exception) {
150 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
151
152 scoped_local_ref<jclass> stringWriterClass(env, findClass(env, "java/io/StringWriter"));
153 if (stringWriterClass.get() == NULL) {
154 return NULL;
155 }
156
157 jmethodID stringWriterCtor = (*env)->GetMethodID(e, stringWriterClass.get(), "<init>", "()V");
158 jmethodID stringWriterToStringMethod =
159 (*env)->GetMethodID(e, stringWriterClass.get(), "toString", "()Ljava/lang/String;");
160
161 scoped_local_ref<jclass> printWriterClass(env, findClass(env, "java/io/PrintWriter"));
162 if (printWriterClass.get() == NULL) {
163 return NULL;
164 }
165
166 jmethodID printWriterCtor =
167 (*env)->GetMethodID(e, printWriterClass.get(), "<init>", "(Ljava/io/Writer;)V");
168
169 scoped_local_ref<jobject> stringWriter(env,
170 (*env)->NewObject(e, stringWriterClass.get(), stringWriterCtor));
171 if (stringWriter.get() == NULL) {
172 return NULL;
173 }
174
175 jobject printWriter =
176 (*env)->NewObject(e, printWriterClass.get(), printWriterCtor, stringWriter.get());
177 if (printWriter == NULL) {
178 return NULL;
179 }
180
181 scoped_local_ref<jclass> exceptionClass(env, (*env)->GetObjectClass(e, exception)); // can't fail
182 jmethodID printStackTraceMethod =
183 (*env)->GetMethodID(e, exceptionClass.get(), "printStackTrace", "(Ljava/io/PrintWriter;)V");
184 (*env)->CallVoidMethod(e, exception, printStackTraceMethod, printWriter);
185
186 if ((*env)->ExceptionCheck(e)) {
187 return NULL;
188 }
189
190 scoped_local_ref<jstring> messageStr(env,
191 (jstring) (*env)->CallObjectMethod(e, stringWriter.get(), stringWriterToStringMethod));
192 if (messageStr.get() == NULL) {
193 return NULL;
194 }
195
196 const char* utfChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL);
197 if (utfChars == NULL) {
198 return NULL;
199 }
200
201 char* result = strdup(utfChars);
202 (*env)->ReleaseStringUTFChars(e, messageStr.get(), utfChars);
203 return result;
204}
205
206extern "C" int jniThrowException(C_JNIEnv* env, const char* className, const char* msg) {
207 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
208
209 if ((*env)->ExceptionCheck(e)) {
210 /* TODO: consider creating the new exception with this as "cause" */
211 scoped_local_ref<jthrowable> exception(env, (*env)->ExceptionOccurred(e));
212 (*env)->ExceptionClear(e);
213
214 if (exception.get() != NULL) {
215 char* text = getExceptionSummary(env, exception.get());
Steve Block7f18b362012-01-05 23:21:27 +0000216 ALOGW("Discarding pending exception (%s) to throw %s", text, className);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700217 free(text);
218 }
219 }
220
221 scoped_local_ref<jclass> exceptionClass(env, findClass(env, className));
222 if (exceptionClass.get() == NULL) {
Steve Block79a0d9c2012-01-06 19:16:58 +0000223 ALOGE("Unable to find exception class %s", className);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700224 /* ClassNotFoundException now pending */
225 return -1;
226 }
227
228 if ((*env)->ThrowNew(e, exceptionClass.get(), msg) != JNI_OK) {
Steve Block79a0d9c2012-01-06 19:16:58 +0000229 ALOGE("Failed throwing '%s' '%s'", className, msg);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700230 /* an exception, most likely OOM, will now be pending */
231 return -1;
232 }
233
234 return 0;
235}
236
237int jniThrowExceptionFmt(C_JNIEnv* env, const char* className, const char* fmt, va_list args) {
238 char msgBuf[512];
239 vsnprintf(msgBuf, sizeof(msgBuf), fmt, args);
240 return jniThrowException(env, className, msgBuf);
241}
242
243int jniThrowNullPointerException(C_JNIEnv* env, const char* msg) {
244 return jniThrowException(env, "java/lang/NullPointerException", msg);
245}
246
247int jniThrowRuntimeException(C_JNIEnv* env, const char* msg) {
248 return jniThrowException(env, "java/lang/RuntimeException", msg);
249}
250
251int jniThrowIOException(C_JNIEnv* env, int errnum) {
252 char buffer[80];
253 const char* message = jniStrError(errnum, buffer, sizeof(buffer));
254 return jniThrowException(env, "java/io/IOException", message);
255}
256
257void jniLogException(C_JNIEnv* env, int priority, const char* tag, jthrowable exception) {
258 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
259
Jeff Browne2b11e72012-04-10 20:34:39 -0700260 scoped_local_ref<jthrowable> currentException(env, (*env)->ExceptionOccurred(e));
Elliott Hughes87f62a82011-04-22 19:22:54 -0700261 if (exception == NULL) {
Jeff Browne2b11e72012-04-10 20:34:39 -0700262 exception = currentException.get();
Elliott Hughes87f62a82011-04-22 19:22:54 -0700263 if (exception == NULL) {
264 return;
265 }
Jeff Browne2b11e72012-04-10 20:34:39 -0700266 }
Elliott Hughes87f62a82011-04-22 19:22:54 -0700267
Jeff Browne2b11e72012-04-10 20:34:39 -0700268 if (currentException.get() != NULL) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700269 (*env)->ExceptionClear(e);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700270 }
271
272 char* buffer = getStackTrace(env, exception);
273 if (buffer == NULL) {
274 (*env)->ExceptionClear(e);
275 buffer = getExceptionSummary(env, exception);
276 }
277
278 __android_log_write(priority, tag, buffer);
279 free(buffer);
280
281 if (currentException.get() != NULL) {
Jeff Browne2b11e72012-04-10 20:34:39 -0700282 (*env)->Throw(e, currentException.get()); // rethrow
Elliott Hughes87f62a82011-04-22 19:22:54 -0700283 }
284}
285
286const char* jniStrError(int errnum, char* buf, size_t buflen) {
287 // Note: glibc has a nonstandard strerror_r that returns char* rather than POSIX's int.
288 // char *strerror_r(int errnum, char *buf, size_t n);
289 char* ret = (char*) strerror_r(errnum, buf, buflen);
290 if (((int)ret) == 0) {
291 // POSIX strerror_r, success
292 return buf;
293 } else if (((int)ret) == -1) {
294 // POSIX strerror_r, failure
295 // (Strictly, POSIX only guarantees a value other than 0. The safest
296 // way to implement this function is to use C++ and overload on the
297 // type of strerror_r to accurately distinguish GNU from POSIX. But
298 // realistic implementations will always return -1.)
299 snprintf(buf, buflen, "errno %d", errnum);
300 return buf;
301 } else {
302 // glibc strerror_r returning a string
303 return ret;
304 }
305}
306
307static struct CachedFields {
308 jclass fileDescriptorClass;
309 jmethodID fileDescriptorCtor;
310 jfieldID descriptorField;
311} gCachedFields;
312
313int registerJniHelp(JNIEnv* env) {
314 gCachedFields.fileDescriptorClass =
315 reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass("java/io/FileDescriptor")));
316 if (gCachedFields.fileDescriptorClass == NULL) {
317 return -1;
318 }
319
320 gCachedFields.fileDescriptorCtor =
321 env->GetMethodID(gCachedFields.fileDescriptorClass, "<init>", "()V");
322 if (gCachedFields.fileDescriptorCtor == NULL) {
323 return -1;
324 }
325
326 gCachedFields.descriptorField =
327 env->GetFieldID(gCachedFields.fileDescriptorClass, "descriptor", "I");
328 if (gCachedFields.descriptorField == NULL) {
329 return -1;
330 }
331
332 return 0;
333}
334
335jobject jniCreateFileDescriptor(C_JNIEnv* env, int fd) {
336 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
337 jobject fileDescriptor = (*env)->NewObject(e,
338 gCachedFields.fileDescriptorClass, gCachedFields.fileDescriptorCtor);
339 jniSetFileDescriptorOfFD(env, fileDescriptor, fd);
340 return fileDescriptor;
341}
342
343int jniGetFDFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor) {
344 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
345 return (*env)->GetIntField(e, fileDescriptor, gCachedFields.descriptorField);
346}
347
348void jniSetFileDescriptorOfFD(C_JNIEnv* env, jobject fileDescriptor, int value) {
349 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
350 (*env)->SetIntField(e, fileDescriptor, gCachedFields.descriptorField, value);
351}
352
353/*
354 * DO NOT USE THIS FUNCTION
355 *
356 * Get a pointer to the elements of a non-movable array.
357 *
358 * The semantics are similar to GetDirectBufferAddress. Specifically, the VM
359 * guarantees that the array will not move, and the caller must ensure that
360 * it does not continue to use the pointer after the object is collected.
361 *
362 * We currently use an illegal sequence that trips up CheckJNI when
363 * the "forcecopy" mode is enabled. We pass in a magic value to work
364 * around the problem.
365 *
366 * Returns NULL if the array is movable.
367 */
368#define kNoCopyMagic 0xd5aab57f /* also in CheckJni.c */
369extern "C" jbyte* jniGetNonMovableArrayElements(C_JNIEnv* env, jarray arrayObj) {
370 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
371
372 jbyteArray byteArray = reinterpret_cast<jbyteArray>(arrayObj);
373
374 /*
375 * Normally the "isCopy" parameter is for a return value only, so the
376 * non-CheckJNI VM will ignore whatever we pass in.
377 */
378 uint32_t noCopy = kNoCopyMagic;
379 jbyte* result = (*env)->GetByteArrayElements(e, byteArray, reinterpret_cast<jboolean*>(&noCopy));
380
381 /*
382 * The non-CheckJNI implementation only cares about the array object,
383 * so we can replace the element pointer with the magic value.
384 */
385 (*env)->ReleaseByteArrayElements(e, byteArray, reinterpret_cast<jbyte*>(kNoCopyMagic), 0);
386 return result;
387}