blob: 02ca02c87c75086dedb1d8d45b8cc0f08be6fc32 [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
Elliott Hughes2fc9d0a2014-08-18 19:50:01 -070017#if defined(__ANDROID__)
18/* libnativehelper is built by NDK 19 in one variant, which doesn't yet have the GNU strerror_r. */
19#undef _GNU_SOURCE
20#endif
21
Elliott Hughes87f62a82011-04-22 19:22:54 -070022#define LOG_TAG "JNIHelp"
23
Brian Carlstromdd8af232012-05-13 23:56:07 -070024#include "JniConstants.h"
Elliott Hughes87f62a82011-04-22 19:22:54 -070025#include "JNIHelp.h"
Ruben Brunka77f3a22013-09-09 02:21:31 -070026#include "ALog-priv.h"
Elliott Hughes87f62a82011-04-22 19:22:54 -070027
Ruben Brunka77f3a22013-09-09 02:21:31 -070028#include <stdio.h>
Elliott Hughes87f62a82011-04-22 19:22:54 -070029#include <stdlib.h>
30#include <string.h>
31#include <assert.h>
32
Brian Carlstromdd8af232012-05-13 23:56:07 -070033#include <string>
34
Elliott Hughes87f62a82011-04-22 19:22:54 -070035/**
36 * Equivalent to ScopedLocalRef, but for C_JNIEnv instead. (And slightly more powerful.)
37 */
38template<typename T>
39class scoped_local_ref {
40public:
Chih-Hung Hsieh01332142016-05-02 11:47:43 -070041 explicit scoped_local_ref(C_JNIEnv* env, T localRef = NULL)
Elliott Hughes87f62a82011-04-22 19:22:54 -070042 : mEnv(env), mLocalRef(localRef)
43 {
44 }
45
46 ~scoped_local_ref() {
47 reset();
48 }
49
50 void reset(T localRef = NULL) {
51 if (mLocalRef != NULL) {
52 (*mEnv)->DeleteLocalRef(reinterpret_cast<JNIEnv*>(mEnv), mLocalRef);
53 mLocalRef = localRef;
54 }
55 }
56
57 T get() const {
58 return mLocalRef;
59 }
60
61private:
Ian Rogers8288dde2014-11-04 11:42:02 -080062 C_JNIEnv* const mEnv;
Elliott Hughes87f62a82011-04-22 19:22:54 -070063 T mLocalRef;
64
Ian Rogers8288dde2014-11-04 11:42:02 -080065 DISALLOW_COPY_AND_ASSIGN(scoped_local_ref);
Elliott Hughes87f62a82011-04-22 19:22:54 -070066};
67
68static jclass findClass(C_JNIEnv* env, const char* className) {
69 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
70 return (*env)->FindClass(e, className);
71}
72
73extern "C" int jniRegisterNativeMethods(C_JNIEnv* env, const char* className,
74 const JNINativeMethod* gMethods, int numMethods)
75{
76 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
77
Brian Carlstromdd8af232012-05-13 23:56:07 -070078 ALOGV("Registering %s's %d native methods...", className, numMethods);
Elliott Hughes87f62a82011-04-22 19:22:54 -070079
80 scoped_local_ref<jclass> c(env, findClass(env, className));
81 if (c.get() == NULL) {
Andreas Gampe6c5be192015-08-03 10:31:52 -070082 char* tmp;
83 const char* msg;
84 if (asprintf(&tmp,
85 "Native registration unable to find class '%s'; aborting...",
86 className) == -1) {
87 // Allocation failed, print default warning.
88 msg = "Native registration unable to find class; aborting...";
89 } else {
90 msg = tmp;
91 }
Elliott Hughesa3b57002013-01-22 09:35:09 -080092 e->FatalError(msg);
Elliott Hughes87f62a82011-04-22 19:22:54 -070093 }
94
95 if ((*env)->RegisterNatives(e, c.get(), gMethods, numMethods) < 0) {
Andreas Gampe6c5be192015-08-03 10:31:52 -070096 char* tmp;
97 const char* msg;
98 if (asprintf(&tmp, "RegisterNatives failed for '%s'; aborting...", className) == -1) {
99 // Allocation failed, print default warning.
100 msg = "RegisterNatives failed; aborting...";
101 } else {
102 msg = tmp;
103 }
Elliott Hughesa3b57002013-01-22 09:35:09 -0800104 e->FatalError(msg);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700105 }
106
107 return 0;
108}
109
110/*
111 * Returns a human-readable summary of an exception object. The buffer will
112 * be populated with the "binary" class name and, if present, the
113 * exception message.
114 */
Brian Carlstromdd8af232012-05-13 23:56:07 -0700115static bool getExceptionSummary(C_JNIEnv* env, jthrowable exception, std::string& result) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700116 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
117
118 /* get the name of the exception's class */
119 scoped_local_ref<jclass> exceptionClass(env, (*env)->GetObjectClass(e, exception)); // can't fail
120 scoped_local_ref<jclass> classClass(env,
121 (*env)->GetObjectClass(e, exceptionClass.get())); // java.lang.Class, can't fail
122 jmethodID classGetNameMethod =
123 (*env)->GetMethodID(e, classClass.get(), "getName", "()Ljava/lang/String;");
124 scoped_local_ref<jstring> classNameStr(env,
125 (jstring) (*env)->CallObjectMethod(e, exceptionClass.get(), classGetNameMethod));
126 if (classNameStr.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700127 (*env)->ExceptionClear(e);
128 result = "<error getting class name>";
129 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700130 }
Elliott Hughes87f62a82011-04-22 19:22:54 -0700131 const char* classNameChars = (*env)->GetStringUTFChars(e, classNameStr.get(), NULL);
132 if (classNameChars == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700133 (*env)->ExceptionClear(e);
134 result = "<error getting class name UTF-8>";
135 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700136 }
Brian Carlstromdd8af232012-05-13 23:56:07 -0700137 result += classNameChars;
138 (*env)->ReleaseStringUTFChars(e, classNameStr.get(), classNameChars);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700139
140 /* if the exception has a detail message, get that */
141 jmethodID getMessage =
142 (*env)->GetMethodID(e, exceptionClass.get(), "getMessage", "()Ljava/lang/String;");
143 scoped_local_ref<jstring> messageStr(env,
144 (jstring) (*env)->CallObjectMethod(e, exception, getMessage));
145 if (messageStr.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700146 return true;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700147 }
148
Brian Carlstromdd8af232012-05-13 23:56:07 -0700149 result += ": ";
150
Elliott Hughes87f62a82011-04-22 19:22:54 -0700151 const char* messageChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL);
152 if (messageChars != NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700153 result += messageChars;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700154 (*env)->ReleaseStringUTFChars(e, messageStr.get(), messageChars);
155 } else {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700156 result += "<error getting message>";
Elliott Hughes87f62a82011-04-22 19:22:54 -0700157 (*env)->ExceptionClear(e); // clear OOM
Elliott Hughes87f62a82011-04-22 19:22:54 -0700158 }
159
Brian Carlstromdd8af232012-05-13 23:56:07 -0700160 return true;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700161}
162
163/*
164 * Returns an exception (with stack trace) as a string.
165 */
Brian Carlstromdd8af232012-05-13 23:56:07 -0700166static bool getStackTrace(C_JNIEnv* env, jthrowable exception, std::string& result) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700167 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
168
169 scoped_local_ref<jclass> stringWriterClass(env, findClass(env, "java/io/StringWriter"));
170 if (stringWriterClass.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700171 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700172 }
173
174 jmethodID stringWriterCtor = (*env)->GetMethodID(e, stringWriterClass.get(), "<init>", "()V");
175 jmethodID stringWriterToStringMethod =
176 (*env)->GetMethodID(e, stringWriterClass.get(), "toString", "()Ljava/lang/String;");
177
178 scoped_local_ref<jclass> printWriterClass(env, findClass(env, "java/io/PrintWriter"));
179 if (printWriterClass.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700180 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700181 }
182
183 jmethodID printWriterCtor =
184 (*env)->GetMethodID(e, printWriterClass.get(), "<init>", "(Ljava/io/Writer;)V");
185
186 scoped_local_ref<jobject> stringWriter(env,
187 (*env)->NewObject(e, stringWriterClass.get(), stringWriterCtor));
188 if (stringWriter.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700189 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700190 }
191
Huanqi Chend47c3252014-06-24 13:31:31 +0200192 scoped_local_ref<jobject> printWriter(env,
193 (*env)->NewObject(e, printWriterClass.get(), printWriterCtor, stringWriter.get()));
194 if (printWriter.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700195 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700196 }
197
198 scoped_local_ref<jclass> exceptionClass(env, (*env)->GetObjectClass(e, exception)); // can't fail
199 jmethodID printStackTraceMethod =
200 (*env)->GetMethodID(e, exceptionClass.get(), "printStackTrace", "(Ljava/io/PrintWriter;)V");
Huanqi Chend47c3252014-06-24 13:31:31 +0200201 (*env)->CallVoidMethod(e, exception, printStackTraceMethod, printWriter.get());
Elliott Hughes87f62a82011-04-22 19:22:54 -0700202
203 if ((*env)->ExceptionCheck(e)) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700204 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700205 }
206
207 scoped_local_ref<jstring> messageStr(env,
208 (jstring) (*env)->CallObjectMethod(e, stringWriter.get(), stringWriterToStringMethod));
209 if (messageStr.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700210 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700211 }
212
213 const char* utfChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL);
214 if (utfChars == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700215 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700216 }
217
Brian Carlstromdd8af232012-05-13 23:56:07 -0700218 result = utfChars;
219
Elliott Hughes87f62a82011-04-22 19:22:54 -0700220 (*env)->ReleaseStringUTFChars(e, messageStr.get(), utfChars);
Brian Carlstromdd8af232012-05-13 23:56:07 -0700221 return true;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700222}
223
224extern "C" int jniThrowException(C_JNIEnv* env, const char* className, const char* msg) {
225 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
226
227 if ((*env)->ExceptionCheck(e)) {
228 /* TODO: consider creating the new exception with this as "cause" */
229 scoped_local_ref<jthrowable> exception(env, (*env)->ExceptionOccurred(e));
230 (*env)->ExceptionClear(e);
231
232 if (exception.get() != NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700233 std::string text;
234 getExceptionSummary(env, exception.get(), text);
235 ALOGW("Discarding pending exception (%s) to throw %s", text.c_str(), className);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700236 }
237 }
238
239 scoped_local_ref<jclass> exceptionClass(env, findClass(env, className));
240 if (exceptionClass.get() == NULL) {
Steve Block79a0d9c2012-01-06 19:16:58 +0000241 ALOGE("Unable to find exception class %s", className);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700242 /* ClassNotFoundException now pending */
243 return -1;
244 }
245
246 if ((*env)->ThrowNew(e, exceptionClass.get(), msg) != JNI_OK) {
Steve Block79a0d9c2012-01-06 19:16:58 +0000247 ALOGE("Failed throwing '%s' '%s'", className, msg);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700248 /* an exception, most likely OOM, will now be pending */
249 return -1;
250 }
251
252 return 0;
253}
254
255int jniThrowExceptionFmt(C_JNIEnv* env, const char* className, const char* fmt, va_list args) {
256 char msgBuf[512];
257 vsnprintf(msgBuf, sizeof(msgBuf), fmt, args);
258 return jniThrowException(env, className, msgBuf);
259}
260
261int jniThrowNullPointerException(C_JNIEnv* env, const char* msg) {
262 return jniThrowException(env, "java/lang/NullPointerException", msg);
263}
264
265int jniThrowRuntimeException(C_JNIEnv* env, const char* msg) {
266 return jniThrowException(env, "java/lang/RuntimeException", msg);
267}
268
269int jniThrowIOException(C_JNIEnv* env, int errnum) {
270 char buffer[80];
271 const char* message = jniStrError(errnum, buffer, sizeof(buffer));
272 return jniThrowException(env, "java/io/IOException", message);
273}
274
Elliott Hughesa4e4e742013-09-03 17:47:18 -0700275static std::string jniGetStackTrace(C_JNIEnv* env, jthrowable exception) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700276 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
277
Jeff Browne2b11e72012-04-10 20:34:39 -0700278 scoped_local_ref<jthrowable> currentException(env, (*env)->ExceptionOccurred(e));
Elliott Hughes87f62a82011-04-22 19:22:54 -0700279 if (exception == NULL) {
Jeff Browne2b11e72012-04-10 20:34:39 -0700280 exception = currentException.get();
Elliott Hughes87f62a82011-04-22 19:22:54 -0700281 if (exception == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700282 return "<no pending exception>";
Elliott Hughes87f62a82011-04-22 19:22:54 -0700283 }
Jeff Browne2b11e72012-04-10 20:34:39 -0700284 }
Elliott Hughes87f62a82011-04-22 19:22:54 -0700285
Jeff Browne2b11e72012-04-10 20:34:39 -0700286 if (currentException.get() != NULL) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700287 (*env)->ExceptionClear(e);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700288 }
289
Brian Carlstromdd8af232012-05-13 23:56:07 -0700290 std::string trace;
291 if (!getStackTrace(env, exception, trace)) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700292 (*env)->ExceptionClear(e);
Brian Carlstromdd8af232012-05-13 23:56:07 -0700293 getExceptionSummary(env, exception, trace);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700294 }
295
Elliott Hughes87f62a82011-04-22 19:22:54 -0700296 if (currentException.get() != NULL) {
Jeff Browne2b11e72012-04-10 20:34:39 -0700297 (*env)->Throw(e, currentException.get()); // rethrow
Elliott Hughes87f62a82011-04-22 19:22:54 -0700298 }
Brian Carlstromdd8af232012-05-13 23:56:07 -0700299
300 return trace;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700301}
302
Elliott Hughesa4e4e742013-09-03 17:47:18 -0700303void jniLogException(C_JNIEnv* env, int priority, const char* tag, jthrowable exception) {
304 std::string trace(jniGetStackTrace(env, exception));
305 __android_log_write(priority, tag, trace.c_str());
306}
307
Elliott Hughes87f62a82011-04-22 19:22:54 -0700308const char* jniStrError(int errnum, char* buf, size_t buflen) {
Elliott Hughesd60512c2013-10-08 14:15:18 -0700309#if __GLIBC__
Elliott Hughes87f62a82011-04-22 19:22:54 -0700310 // Note: glibc has a nonstandard strerror_r that returns char* rather than POSIX's int.
311 // char *strerror_r(int errnum, char *buf, size_t n);
Elliott Hughesd60512c2013-10-08 14:15:18 -0700312 return strerror_r(errnum, buf, buflen);
313#else
314 int rc = strerror_r(errnum, buf, buflen);
315 if (rc != 0) {
316 // (POSIX only guarantees a value other than 0. The safest
Elliott Hughes87f62a82011-04-22 19:22:54 -0700317 // way to implement this function is to use C++ and overload on the
Elliott Hughesd60512c2013-10-08 14:15:18 -0700318 // type of strerror_r to accurately distinguish GNU from POSIX.)
Elliott Hughes87f62a82011-04-22 19:22:54 -0700319 snprintf(buf, buflen, "errno %d", errnum);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700320 }
Elliott Hughesd60512c2013-10-08 14:15:18 -0700321 return buf;
322#endif
Elliott Hughes87f62a82011-04-22 19:22:54 -0700323}
324
Elliott Hughes87f62a82011-04-22 19:22:54 -0700325jobject jniCreateFileDescriptor(C_JNIEnv* env, int fd) {
326 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
Brian Carlstromdd8af232012-05-13 23:56:07 -0700327 static jmethodID ctor = e->GetMethodID(JniConstants::fileDescriptorClass, "<init>", "()V");
328 jobject fileDescriptor = (*env)->NewObject(e, JniConstants::fileDescriptorClass, ctor);
Narayan Kamathe29dbf52013-10-31 17:17:10 +0000329 // NOTE: NewObject ensures that an OutOfMemoryError will be seen by the Java
330 // caller if the alloc fails, so we just return NULL when that happens.
331 if (fileDescriptor != NULL) {
332 jniSetFileDescriptorOfFD(env, fileDescriptor, fd);
333 }
Elliott Hughes87f62a82011-04-22 19:22:54 -0700334 return fileDescriptor;
335}
336
337int jniGetFDFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor) {
338 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
Brian Carlstromdd8af232012-05-13 23:56:07 -0700339 static jfieldID fid = e->GetFieldID(JniConstants::fileDescriptorClass, "descriptor", "I");
Narayan Kamathe29dbf52013-10-31 17:17:10 +0000340 if (fileDescriptor != NULL) {
341 return (*env)->GetIntField(e, fileDescriptor, fid);
342 } else {
343 return -1;
344 }
Elliott Hughes87f62a82011-04-22 19:22:54 -0700345}
346
347void jniSetFileDescriptorOfFD(C_JNIEnv* env, jobject fileDescriptor, int value) {
348 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
Brian Carlstromdd8af232012-05-13 23:56:07 -0700349 static jfieldID fid = e->GetFieldID(JniConstants::fileDescriptorClass, "descriptor", "I");
350 (*env)->SetIntField(e, fileDescriptor, fid, value);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700351}
352
Jeff Browndc176c52013-04-02 18:09:29 -0700353jobject jniGetReferent(C_JNIEnv* env, jobject ref) {
354 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
355 static jmethodID get = e->GetMethodID(JniConstants::referenceClass, "get", "()Ljava/lang/Object;");
356 return (*env)->CallObjectMethod(e, ref, get);
357}
358