blob: ef4d86299aaa54762921b25bbf7eadfe7bc366d1 [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
Brian Carlstromdd8af232012-05-13 23:56:07 -070019#define LIBCORE_CPP_JNI_HELPERS
20
21#include "JniConstants.h"
Elliott Hughes87f62a82011-04-22 19:22:54 -070022#include "JNIHelp.h"
Ruben Brunka77f3a22013-09-09 02:21:31 -070023#include "ALog-priv.h"
Elliott Hughes87f62a82011-04-22 19:22:54 -070024
Ruben Brunka77f3a22013-09-09 02:21:31 -070025#include <stdio.h>
Elliott Hughes87f62a82011-04-22 19:22:54 -070026#include <stdlib.h>
27#include <string.h>
28#include <assert.h>
29
Brian Carlstromdd8af232012-05-13 23:56:07 -070030#include <string>
31
Elliott Hughes87f62a82011-04-22 19:22:54 -070032/**
33 * Equivalent to ScopedLocalRef, but for C_JNIEnv instead. (And slightly more powerful.)
34 */
35template<typename T>
36class scoped_local_ref {
37public:
38 scoped_local_ref(C_JNIEnv* env, T localRef = NULL)
39 : mEnv(env), mLocalRef(localRef)
40 {
41 }
42
43 ~scoped_local_ref() {
44 reset();
45 }
46
47 void reset(T localRef = NULL) {
48 if (mLocalRef != NULL) {
49 (*mEnv)->DeleteLocalRef(reinterpret_cast<JNIEnv*>(mEnv), mLocalRef);
50 mLocalRef = localRef;
51 }
52 }
53
54 T get() const {
55 return mLocalRef;
56 }
57
58private:
59 C_JNIEnv* mEnv;
60 T mLocalRef;
61
62 // Disallow copy and assignment.
63 scoped_local_ref(const scoped_local_ref&);
64 void operator=(const scoped_local_ref&);
65};
66
67static jclass findClass(C_JNIEnv* env, const char* className) {
68 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
69 return (*env)->FindClass(e, className);
70}
71
72extern "C" int jniRegisterNativeMethods(C_JNIEnv* env, const char* className,
73 const JNINativeMethod* gMethods, int numMethods)
74{
75 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
76
Brian Carlstromdd8af232012-05-13 23:56:07 -070077 ALOGV("Registering %s's %d native methods...", className, numMethods);
Elliott Hughes87f62a82011-04-22 19:22:54 -070078
79 scoped_local_ref<jclass> c(env, findClass(env, className));
80 if (c.get() == NULL) {
Elliott Hughesa3b57002013-01-22 09:35:09 -080081 char* msg;
Brian Carlstromdeb6d5d2013-05-10 09:50:08 -070082 asprintf(&msg, "Native registration unable to find class '%s'; aborting...", className);
Elliott Hughesa3b57002013-01-22 09:35:09 -080083 e->FatalError(msg);
Elliott Hughes87f62a82011-04-22 19:22:54 -070084 }
85
86 if ((*env)->RegisterNatives(e, c.get(), gMethods, numMethods) < 0) {
Elliott Hughesa3b57002013-01-22 09:35:09 -080087 char* msg;
Brian Carlstromdeb6d5d2013-05-10 09:50:08 -070088 asprintf(&msg, "RegisterNatives failed for '%s'; aborting...", className);
Elliott Hughesa3b57002013-01-22 09:35:09 -080089 e->FatalError(msg);
Elliott Hughes87f62a82011-04-22 19:22:54 -070090 }
91
92 return 0;
93}
94
95/*
96 * Returns a human-readable summary of an exception object. The buffer will
97 * be populated with the "binary" class name and, if present, the
98 * exception message.
99 */
Brian Carlstromdd8af232012-05-13 23:56:07 -0700100static bool getExceptionSummary(C_JNIEnv* env, jthrowable exception, std::string& result) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700101 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
102
103 /* get the name of the exception's class */
104 scoped_local_ref<jclass> exceptionClass(env, (*env)->GetObjectClass(e, exception)); // can't fail
105 scoped_local_ref<jclass> classClass(env,
106 (*env)->GetObjectClass(e, exceptionClass.get())); // java.lang.Class, can't fail
107 jmethodID classGetNameMethod =
108 (*env)->GetMethodID(e, classClass.get(), "getName", "()Ljava/lang/String;");
109 scoped_local_ref<jstring> classNameStr(env,
110 (jstring) (*env)->CallObjectMethod(e, exceptionClass.get(), classGetNameMethod));
111 if (classNameStr.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700112 (*env)->ExceptionClear(e);
113 result = "<error getting class name>";
114 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700115 }
Elliott Hughes87f62a82011-04-22 19:22:54 -0700116 const char* classNameChars = (*env)->GetStringUTFChars(e, classNameStr.get(), NULL);
117 if (classNameChars == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700118 (*env)->ExceptionClear(e);
119 result = "<error getting class name UTF-8>";
120 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700121 }
Brian Carlstromdd8af232012-05-13 23:56:07 -0700122 result += classNameChars;
123 (*env)->ReleaseStringUTFChars(e, classNameStr.get(), classNameChars);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700124
125 /* if the exception has a detail message, get that */
126 jmethodID getMessage =
127 (*env)->GetMethodID(e, exceptionClass.get(), "getMessage", "()Ljava/lang/String;");
128 scoped_local_ref<jstring> messageStr(env,
129 (jstring) (*env)->CallObjectMethod(e, exception, getMessage));
130 if (messageStr.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700131 return true;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700132 }
133
Brian Carlstromdd8af232012-05-13 23:56:07 -0700134 result += ": ";
135
Elliott Hughes87f62a82011-04-22 19:22:54 -0700136 const char* messageChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL);
137 if (messageChars != NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700138 result += messageChars;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700139 (*env)->ReleaseStringUTFChars(e, messageStr.get(), messageChars);
140 } else {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700141 result += "<error getting message>";
Elliott Hughes87f62a82011-04-22 19:22:54 -0700142 (*env)->ExceptionClear(e); // clear OOM
Elliott Hughes87f62a82011-04-22 19:22:54 -0700143 }
144
Brian Carlstromdd8af232012-05-13 23:56:07 -0700145 return true;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700146}
147
148/*
149 * Returns an exception (with stack trace) as a string.
150 */
Brian Carlstromdd8af232012-05-13 23:56:07 -0700151static bool getStackTrace(C_JNIEnv* env, jthrowable exception, std::string& result) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700152 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
153
154 scoped_local_ref<jclass> stringWriterClass(env, findClass(env, "java/io/StringWriter"));
155 if (stringWriterClass.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700156 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700157 }
158
159 jmethodID stringWriterCtor = (*env)->GetMethodID(e, stringWriterClass.get(), "<init>", "()V");
160 jmethodID stringWriterToStringMethod =
161 (*env)->GetMethodID(e, stringWriterClass.get(), "toString", "()Ljava/lang/String;");
162
163 scoped_local_ref<jclass> printWriterClass(env, findClass(env, "java/io/PrintWriter"));
164 if (printWriterClass.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700165 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700166 }
167
168 jmethodID printWriterCtor =
169 (*env)->GetMethodID(e, printWriterClass.get(), "<init>", "(Ljava/io/Writer;)V");
170
171 scoped_local_ref<jobject> stringWriter(env,
172 (*env)->NewObject(e, stringWriterClass.get(), stringWriterCtor));
173 if (stringWriter.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700174 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700175 }
176
177 jobject printWriter =
178 (*env)->NewObject(e, printWriterClass.get(), printWriterCtor, stringWriter.get());
179 if (printWriter == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700180 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700181 }
182
183 scoped_local_ref<jclass> exceptionClass(env, (*env)->GetObjectClass(e, exception)); // can't fail
184 jmethodID printStackTraceMethod =
185 (*env)->GetMethodID(e, exceptionClass.get(), "printStackTrace", "(Ljava/io/PrintWriter;)V");
186 (*env)->CallVoidMethod(e, exception, printStackTraceMethod, printWriter);
187
188 if ((*env)->ExceptionCheck(e)) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700189 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700190 }
191
192 scoped_local_ref<jstring> messageStr(env,
193 (jstring) (*env)->CallObjectMethod(e, stringWriter.get(), stringWriterToStringMethod));
194 if (messageStr.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700195 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700196 }
197
198 const char* utfChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL);
199 if (utfChars == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700200 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700201 }
202
Brian Carlstromdd8af232012-05-13 23:56:07 -0700203 result = utfChars;
204
Elliott Hughes87f62a82011-04-22 19:22:54 -0700205 (*env)->ReleaseStringUTFChars(e, messageStr.get(), utfChars);
Brian Carlstromdd8af232012-05-13 23:56:07 -0700206 return true;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700207}
208
209extern "C" int jniThrowException(C_JNIEnv* env, const char* className, const char* msg) {
210 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
211
212 if ((*env)->ExceptionCheck(e)) {
213 /* TODO: consider creating the new exception with this as "cause" */
214 scoped_local_ref<jthrowable> exception(env, (*env)->ExceptionOccurred(e));
215 (*env)->ExceptionClear(e);
216
217 if (exception.get() != NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700218 std::string text;
219 getExceptionSummary(env, exception.get(), text);
220 ALOGW("Discarding pending exception (%s) to throw %s", text.c_str(), className);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700221 }
222 }
223
224 scoped_local_ref<jclass> exceptionClass(env, findClass(env, className));
225 if (exceptionClass.get() == NULL) {
Steve Block79a0d9c2012-01-06 19:16:58 +0000226 ALOGE("Unable to find exception class %s", className);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700227 /* ClassNotFoundException now pending */
228 return -1;
229 }
230
231 if ((*env)->ThrowNew(e, exceptionClass.get(), msg) != JNI_OK) {
Steve Block79a0d9c2012-01-06 19:16:58 +0000232 ALOGE("Failed throwing '%s' '%s'", className, msg);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700233 /* an exception, most likely OOM, will now be pending */
234 return -1;
235 }
236
237 return 0;
238}
239
240int jniThrowExceptionFmt(C_JNIEnv* env, const char* className, const char* fmt, va_list args) {
241 char msgBuf[512];
242 vsnprintf(msgBuf, sizeof(msgBuf), fmt, args);
243 return jniThrowException(env, className, msgBuf);
244}
245
246int jniThrowNullPointerException(C_JNIEnv* env, const char* msg) {
247 return jniThrowException(env, "java/lang/NullPointerException", msg);
248}
249
250int jniThrowRuntimeException(C_JNIEnv* env, const char* msg) {
251 return jniThrowException(env, "java/lang/RuntimeException", msg);
252}
253
254int jniThrowIOException(C_JNIEnv* env, int errnum) {
255 char buffer[80];
256 const char* message = jniStrError(errnum, buffer, sizeof(buffer));
257 return jniThrowException(env, "java/io/IOException", message);
258}
259
260void jniLogException(C_JNIEnv* env, int priority, const char* tag, jthrowable exception) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700261 std::string trace(jniGetStackTrace(env, exception));
262 __android_log_write(priority, tag, trace.c_str());
263}
264
265extern "C" std::string jniGetStackTrace(C_JNIEnv* env, jthrowable exception) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700266 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
267
Jeff Browne2b11e72012-04-10 20:34:39 -0700268 scoped_local_ref<jthrowable> currentException(env, (*env)->ExceptionOccurred(e));
Elliott Hughes87f62a82011-04-22 19:22:54 -0700269 if (exception == NULL) {
Jeff Browne2b11e72012-04-10 20:34:39 -0700270 exception = currentException.get();
Elliott Hughes87f62a82011-04-22 19:22:54 -0700271 if (exception == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700272 return "<no pending exception>";
Elliott Hughes87f62a82011-04-22 19:22:54 -0700273 }
Jeff Browne2b11e72012-04-10 20:34:39 -0700274 }
Elliott Hughes87f62a82011-04-22 19:22:54 -0700275
Jeff Browne2b11e72012-04-10 20:34:39 -0700276 if (currentException.get() != NULL) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700277 (*env)->ExceptionClear(e);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700278 }
279
Brian Carlstromdd8af232012-05-13 23:56:07 -0700280 std::string trace;
281 if (!getStackTrace(env, exception, trace)) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700282 (*env)->ExceptionClear(e);
Brian Carlstromdd8af232012-05-13 23:56:07 -0700283 getExceptionSummary(env, exception, trace);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700284 }
285
Elliott Hughes87f62a82011-04-22 19:22:54 -0700286 if (currentException.get() != NULL) {
Jeff Browne2b11e72012-04-10 20:34:39 -0700287 (*env)->Throw(e, currentException.get()); // rethrow
Elliott Hughes87f62a82011-04-22 19:22:54 -0700288 }
Brian Carlstromdd8af232012-05-13 23:56:07 -0700289
290 return trace;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700291}
292
293const char* jniStrError(int errnum, char* buf, size_t buflen) {
294 // Note: glibc has a nonstandard strerror_r that returns char* rather than POSIX's int.
295 // char *strerror_r(int errnum, char *buf, size_t n);
296 char* ret = (char*) strerror_r(errnum, buf, buflen);
297 if (((int)ret) == 0) {
298 // POSIX strerror_r, success
299 return buf;
300 } else if (((int)ret) == -1) {
301 // POSIX strerror_r, failure
302 // (Strictly, 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. But
305 // realistic implementations will always return -1.)
306 snprintf(buf, buflen, "errno %d", errnum);
307 return buf;
308 } else {
309 // glibc strerror_r returning a string
310 return ret;
311 }
312}
313
Elliott Hughes87f62a82011-04-22 19:22:54 -0700314jobject jniCreateFileDescriptor(C_JNIEnv* env, int fd) {
315 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
Brian Carlstromdd8af232012-05-13 23:56:07 -0700316 static jmethodID ctor = e->GetMethodID(JniConstants::fileDescriptorClass, "<init>", "()V");
317 jobject fileDescriptor = (*env)->NewObject(e, JniConstants::fileDescriptorClass, ctor);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700318 jniSetFileDescriptorOfFD(env, fileDescriptor, fd);
319 return fileDescriptor;
320}
321
322int jniGetFDFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor) {
323 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
Brian Carlstromdd8af232012-05-13 23:56:07 -0700324 static jfieldID fid = e->GetFieldID(JniConstants::fileDescriptorClass, "descriptor", "I");
325 return (*env)->GetIntField(e, fileDescriptor, fid);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700326}
327
328void jniSetFileDescriptorOfFD(C_JNIEnv* env, jobject fileDescriptor, int value) {
329 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
Brian Carlstromdd8af232012-05-13 23:56:07 -0700330 static jfieldID fid = e->GetFieldID(JniConstants::fileDescriptorClass, "descriptor", "I");
331 (*env)->SetIntField(e, fileDescriptor, fid, value);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700332}
333
Jeff Browndc176c52013-04-02 18:09:29 -0700334jobject jniGetReferent(C_JNIEnv* env, jobject ref) {
335 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
336 static jmethodID get = e->GetMethodID(JniConstants::referenceClass, "get", "()Ljava/lang/Object;");
337 return (*env)->CallObjectMethod(e, ref, get);
338}
339
Elliott Hughes87f62a82011-04-22 19:22:54 -0700340/*
341 * DO NOT USE THIS FUNCTION
342 *
343 * Get a pointer to the elements of a non-movable array.
344 *
345 * The semantics are similar to GetDirectBufferAddress. Specifically, the VM
346 * guarantees that the array will not move, and the caller must ensure that
347 * it does not continue to use the pointer after the object is collected.
348 *
349 * We currently use an illegal sequence that trips up CheckJNI when
350 * the "forcecopy" mode is enabled. We pass in a magic value to work
351 * around the problem.
352 *
353 * Returns NULL if the array is movable.
354 */
355#define kNoCopyMagic 0xd5aab57f /* also in CheckJni.c */
356extern "C" jbyte* jniGetNonMovableArrayElements(C_JNIEnv* env, jarray arrayObj) {
357 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
358
359 jbyteArray byteArray = reinterpret_cast<jbyteArray>(arrayObj);
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* result = (*env)->GetByteArrayElements(e, byteArray, reinterpret_cast<jboolean*>(&noCopy));
367
368 /*
369 * The non-CheckJNI implementation only cares about the array object,
370 * so we can replace the element pointer with the magic value.
371 */
372 (*env)->ReleaseByteArrayElements(e, byteArray, reinterpret_cast<jbyte*>(kNoCopyMagic), 0);
373 return result;
374}