blob: fcad2f4817b064db9392e92e44a070fd8b01e137 [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#include "JniConstants.h"
Elliott Hughes87f62a82011-04-22 19:22:54 -070020#include "JNIHelp.h"
Ruben Brunka77f3a22013-09-09 02:21:31 -070021#include "ALog-priv.h"
Elliott Hughes87f62a82011-04-22 19:22:54 -070022
Ruben Brunka77f3a22013-09-09 02:21:31 -070023#include <stdio.h>
Elliott Hughes87f62a82011-04-22 19:22:54 -070024#include <stdlib.h>
25#include <string.h>
26#include <assert.h>
27
Brian Carlstromdd8af232012-05-13 23:56:07 -070028#include <string>
29
Elliott Hughes87f62a82011-04-22 19:22:54 -070030/**
31 * Equivalent to ScopedLocalRef, but for C_JNIEnv instead. (And slightly more powerful.)
32 */
33template<typename T>
34class scoped_local_ref {
35public:
36 scoped_local_ref(C_JNIEnv* env, T localRef = NULL)
37 : mEnv(env), mLocalRef(localRef)
38 {
39 }
40
41 ~scoped_local_ref() {
42 reset();
43 }
44
45 void reset(T localRef = NULL) {
46 if (mLocalRef != NULL) {
47 (*mEnv)->DeleteLocalRef(reinterpret_cast<JNIEnv*>(mEnv), mLocalRef);
48 mLocalRef = localRef;
49 }
50 }
51
52 T get() const {
53 return mLocalRef;
54 }
55
56private:
57 C_JNIEnv* mEnv;
58 T mLocalRef;
59
60 // Disallow copy and assignment.
61 scoped_local_ref(const scoped_local_ref&);
62 void operator=(const scoped_local_ref&);
63};
64
65static jclass findClass(C_JNIEnv* env, const char* className) {
66 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
67 return (*env)->FindClass(e, className);
68}
69
70extern "C" int jniRegisterNativeMethods(C_JNIEnv* env, const char* className,
71 const JNINativeMethod* gMethods, int numMethods)
72{
73 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
74
Brian Carlstromdd8af232012-05-13 23:56:07 -070075 ALOGV("Registering %s's %d native methods...", className, numMethods);
Elliott Hughes87f62a82011-04-22 19:22:54 -070076
77 scoped_local_ref<jclass> c(env, findClass(env, className));
78 if (c.get() == NULL) {
Elliott Hughesa3b57002013-01-22 09:35:09 -080079 char* msg;
Brian Carlstromdeb6d5d2013-05-10 09:50:08 -070080 asprintf(&msg, "Native registration unable to find class '%s'; aborting...", className);
Elliott Hughesa3b57002013-01-22 09:35:09 -080081 e->FatalError(msg);
Elliott Hughes87f62a82011-04-22 19:22:54 -070082 }
83
84 if ((*env)->RegisterNatives(e, c.get(), gMethods, numMethods) < 0) {
Elliott Hughesa3b57002013-01-22 09:35:09 -080085 char* msg;
Brian Carlstromdeb6d5d2013-05-10 09:50:08 -070086 asprintf(&msg, "RegisterNatives failed for '%s'; aborting...", className);
Elliott Hughesa3b57002013-01-22 09:35:09 -080087 e->FatalError(msg);
Elliott Hughes87f62a82011-04-22 19:22:54 -070088 }
89
90 return 0;
91}
92
93/*
94 * Returns a human-readable summary of an exception object. The buffer will
95 * be populated with the "binary" class name and, if present, the
96 * exception message.
97 */
Brian Carlstromdd8af232012-05-13 23:56:07 -070098static bool getExceptionSummary(C_JNIEnv* env, jthrowable exception, std::string& result) {
Elliott Hughes87f62a82011-04-22 19:22:54 -070099 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
100
101 /* get the name of the exception's class */
102 scoped_local_ref<jclass> exceptionClass(env, (*env)->GetObjectClass(e, exception)); // can't fail
103 scoped_local_ref<jclass> classClass(env,
104 (*env)->GetObjectClass(e, exceptionClass.get())); // java.lang.Class, can't fail
105 jmethodID classGetNameMethod =
106 (*env)->GetMethodID(e, classClass.get(), "getName", "()Ljava/lang/String;");
107 scoped_local_ref<jstring> classNameStr(env,
108 (jstring) (*env)->CallObjectMethod(e, exceptionClass.get(), classGetNameMethod));
109 if (classNameStr.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700110 (*env)->ExceptionClear(e);
111 result = "<error getting class name>";
112 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700113 }
Elliott Hughes87f62a82011-04-22 19:22:54 -0700114 const char* classNameChars = (*env)->GetStringUTFChars(e, classNameStr.get(), NULL);
115 if (classNameChars == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700116 (*env)->ExceptionClear(e);
117 result = "<error getting class name UTF-8>";
118 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700119 }
Brian Carlstromdd8af232012-05-13 23:56:07 -0700120 result += classNameChars;
121 (*env)->ReleaseStringUTFChars(e, classNameStr.get(), classNameChars);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700122
123 /* if the exception has a detail message, get that */
124 jmethodID getMessage =
125 (*env)->GetMethodID(e, exceptionClass.get(), "getMessage", "()Ljava/lang/String;");
126 scoped_local_ref<jstring> messageStr(env,
127 (jstring) (*env)->CallObjectMethod(e, exception, getMessage));
128 if (messageStr.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700129 return true;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700130 }
131
Brian Carlstromdd8af232012-05-13 23:56:07 -0700132 result += ": ";
133
Elliott Hughes87f62a82011-04-22 19:22:54 -0700134 const char* messageChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL);
135 if (messageChars != NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700136 result += messageChars;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700137 (*env)->ReleaseStringUTFChars(e, messageStr.get(), messageChars);
138 } else {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700139 result += "<error getting message>";
Elliott Hughes87f62a82011-04-22 19:22:54 -0700140 (*env)->ExceptionClear(e); // clear OOM
Elliott Hughes87f62a82011-04-22 19:22:54 -0700141 }
142
Brian Carlstromdd8af232012-05-13 23:56:07 -0700143 return true;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700144}
145
146/*
147 * Returns an exception (with stack trace) as a string.
148 */
Brian Carlstromdd8af232012-05-13 23:56:07 -0700149static bool getStackTrace(C_JNIEnv* env, jthrowable exception, std::string& result) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700150 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
151
152 scoped_local_ref<jclass> stringWriterClass(env, findClass(env, "java/io/StringWriter"));
153 if (stringWriterClass.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700154 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700155 }
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) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700163 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700164 }
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) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700172 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700173 }
174
Huanqi Chend47c3252014-06-24 13:31:31 +0200175 scoped_local_ref<jobject> printWriter(env,
176 (*env)->NewObject(e, printWriterClass.get(), printWriterCtor, stringWriter.get()));
177 if (printWriter.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700178 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700179 }
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");
Huanqi Chend47c3252014-06-24 13:31:31 +0200184 (*env)->CallVoidMethod(e, exception, printStackTraceMethod, printWriter.get());
Elliott Hughes87f62a82011-04-22 19:22:54 -0700185
186 if ((*env)->ExceptionCheck(e)) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700187 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700188 }
189
190 scoped_local_ref<jstring> messageStr(env,
191 (jstring) (*env)->CallObjectMethod(e, stringWriter.get(), stringWriterToStringMethod));
192 if (messageStr.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700193 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700194 }
195
196 const char* utfChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL);
197 if (utfChars == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700198 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700199 }
200
Brian Carlstromdd8af232012-05-13 23:56:07 -0700201 result = utfChars;
202
Elliott Hughes87f62a82011-04-22 19:22:54 -0700203 (*env)->ReleaseStringUTFChars(e, messageStr.get(), utfChars);
Brian Carlstromdd8af232012-05-13 23:56:07 -0700204 return true;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700205}
206
207extern "C" int jniThrowException(C_JNIEnv* env, const char* className, const char* msg) {
208 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
209
210 if ((*env)->ExceptionCheck(e)) {
211 /* TODO: consider creating the new exception with this as "cause" */
212 scoped_local_ref<jthrowable> exception(env, (*env)->ExceptionOccurred(e));
213 (*env)->ExceptionClear(e);
214
215 if (exception.get() != NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700216 std::string text;
217 getExceptionSummary(env, exception.get(), text);
218 ALOGW("Discarding pending exception (%s) to throw %s", text.c_str(), className);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700219 }
220 }
221
222 scoped_local_ref<jclass> exceptionClass(env, findClass(env, className));
223 if (exceptionClass.get() == NULL) {
Steve Block79a0d9c2012-01-06 19:16:58 +0000224 ALOGE("Unable to find exception class %s", className);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700225 /* ClassNotFoundException now pending */
226 return -1;
227 }
228
229 if ((*env)->ThrowNew(e, exceptionClass.get(), msg) != JNI_OK) {
Steve Block79a0d9c2012-01-06 19:16:58 +0000230 ALOGE("Failed throwing '%s' '%s'", className, msg);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700231 /* an exception, most likely OOM, will now be pending */
232 return -1;
233 }
234
235 return 0;
236}
237
238int jniThrowExceptionFmt(C_JNIEnv* env, const char* className, const char* fmt, va_list args) {
239 char msgBuf[512];
240 vsnprintf(msgBuf, sizeof(msgBuf), fmt, args);
241 return jniThrowException(env, className, msgBuf);
242}
243
244int jniThrowNullPointerException(C_JNIEnv* env, const char* msg) {
245 return jniThrowException(env, "java/lang/NullPointerException", msg);
246}
247
248int jniThrowRuntimeException(C_JNIEnv* env, const char* msg) {
249 return jniThrowException(env, "java/lang/RuntimeException", msg);
250}
251
252int jniThrowIOException(C_JNIEnv* env, int errnum) {
253 char buffer[80];
254 const char* message = jniStrError(errnum, buffer, sizeof(buffer));
255 return jniThrowException(env, "java/io/IOException", message);
256}
257
Elliott Hughesa4e4e742013-09-03 17:47:18 -0700258static std::string jniGetStackTrace(C_JNIEnv* env, jthrowable exception) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700259 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
260
Jeff Browne2b11e72012-04-10 20:34:39 -0700261 scoped_local_ref<jthrowable> currentException(env, (*env)->ExceptionOccurred(e));
Elliott Hughes87f62a82011-04-22 19:22:54 -0700262 if (exception == NULL) {
Jeff Browne2b11e72012-04-10 20:34:39 -0700263 exception = currentException.get();
Elliott Hughes87f62a82011-04-22 19:22:54 -0700264 if (exception == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700265 return "<no pending exception>";
Elliott Hughes87f62a82011-04-22 19:22:54 -0700266 }
Jeff Browne2b11e72012-04-10 20:34:39 -0700267 }
Elliott Hughes87f62a82011-04-22 19:22:54 -0700268
Jeff Browne2b11e72012-04-10 20:34:39 -0700269 if (currentException.get() != NULL) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700270 (*env)->ExceptionClear(e);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700271 }
272
Brian Carlstromdd8af232012-05-13 23:56:07 -0700273 std::string trace;
274 if (!getStackTrace(env, exception, trace)) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700275 (*env)->ExceptionClear(e);
Brian Carlstromdd8af232012-05-13 23:56:07 -0700276 getExceptionSummary(env, exception, trace);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700277 }
278
Elliott Hughes87f62a82011-04-22 19:22:54 -0700279 if (currentException.get() != NULL) {
Jeff Browne2b11e72012-04-10 20:34:39 -0700280 (*env)->Throw(e, currentException.get()); // rethrow
Elliott Hughes87f62a82011-04-22 19:22:54 -0700281 }
Brian Carlstromdd8af232012-05-13 23:56:07 -0700282
283 return trace;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700284}
285
Elliott Hughesa4e4e742013-09-03 17:47:18 -0700286void jniLogException(C_JNIEnv* env, int priority, const char* tag, jthrowable exception) {
287 std::string trace(jniGetStackTrace(env, exception));
288 __android_log_write(priority, tag, trace.c_str());
289}
290
Elliott Hughes87f62a82011-04-22 19:22:54 -0700291const char* jniStrError(int errnum, char* buf, size_t buflen) {
Elliott Hughesd60512c2013-10-08 14:15:18 -0700292#if __GLIBC__
Elliott Hughes87f62a82011-04-22 19:22:54 -0700293 // Note: glibc has a nonstandard strerror_r that returns char* rather than POSIX's int.
294 // char *strerror_r(int errnum, char *buf, size_t n);
Elliott Hughesd60512c2013-10-08 14:15:18 -0700295 return strerror_r(errnum, buf, buflen);
296#else
297 int rc = strerror_r(errnum, buf, buflen);
298 if (rc != 0) {
299 // (POSIX only guarantees a value other than 0. The safest
Elliott Hughes87f62a82011-04-22 19:22:54 -0700300 // way to implement this function is to use C++ and overload on the
Elliott Hughesd60512c2013-10-08 14:15:18 -0700301 // type of strerror_r to accurately distinguish GNU from POSIX.)
Elliott Hughes87f62a82011-04-22 19:22:54 -0700302 snprintf(buf, buflen, "errno %d", errnum);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700303 }
Elliott Hughesd60512c2013-10-08 14:15:18 -0700304 return buf;
305#endif
Elliott Hughes87f62a82011-04-22 19:22:54 -0700306}
307
Elliott Hughes87f62a82011-04-22 19:22:54 -0700308jobject jniCreateFileDescriptor(C_JNIEnv* env, int fd) {
309 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
Brian Carlstromdd8af232012-05-13 23:56:07 -0700310 static jmethodID ctor = e->GetMethodID(JniConstants::fileDescriptorClass, "<init>", "()V");
311 jobject fileDescriptor = (*env)->NewObject(e, JniConstants::fileDescriptorClass, ctor);
Narayan Kamathe29dbf52013-10-31 17:17:10 +0000312 // NOTE: NewObject ensures that an OutOfMemoryError will be seen by the Java
313 // caller if the alloc fails, so we just return NULL when that happens.
314 if (fileDescriptor != NULL) {
315 jniSetFileDescriptorOfFD(env, fileDescriptor, fd);
316 }
Elliott Hughes87f62a82011-04-22 19:22:54 -0700317 return fileDescriptor;
318}
319
320int jniGetFDFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor) {
321 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
Brian Carlstromdd8af232012-05-13 23:56:07 -0700322 static jfieldID fid = e->GetFieldID(JniConstants::fileDescriptorClass, "descriptor", "I");
Narayan Kamathe29dbf52013-10-31 17:17:10 +0000323 if (fileDescriptor != NULL) {
324 return (*env)->GetIntField(e, fileDescriptor, fid);
325 } else {
326 return -1;
327 }
Elliott Hughes87f62a82011-04-22 19:22:54 -0700328}
329
330void jniSetFileDescriptorOfFD(C_JNIEnv* env, jobject fileDescriptor, int value) {
331 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
Brian Carlstromdd8af232012-05-13 23:56:07 -0700332 static jfieldID fid = e->GetFieldID(JniConstants::fileDescriptorClass, "descriptor", "I");
333 (*env)->SetIntField(e, fileDescriptor, fid, value);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700334}
335
Jeff Browndc176c52013-04-02 18:09:29 -0700336jobject jniGetReferent(C_JNIEnv* env, jobject ref) {
337 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
338 static jmethodID get = e->GetMethodID(JniConstants::referenceClass, "get", "()Ljava/lang/Object;");
339 return (*env)->CallObjectMethod(e, ref, get);
340}
341