blob: 5aefd4200fabb1faae52afc4963c46961847c5ed [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
Elliott Hughes1ed11b42016-06-11 16:58:34 -070020/* ...but this code uses asprintf, which is a BSD/GNU extension. */
21#define _BSD_SOURCE
Elliott Hughes2fc9d0a2014-08-18 19:50:01 -070022#endif
23
Elliott Hughes87f62a82011-04-22 19:22:54 -070024#define LOG_TAG "JNIHelp"
25
Steven Moreland3544a932017-07-19 10:26:05 -070026#include <nativehelper/JniConstants.h>
27#include <nativehelper/JNIHelp.h>
Ruben Brunka77f3a22013-09-09 02:21:31 -070028#include "ALog-priv.h"
Elliott Hughes87f62a82011-04-22 19:22:54 -070029
Ruben Brunka77f3a22013-09-09 02:21:31 -070030#include <stdio.h>
Elliott Hughes87f62a82011-04-22 19:22:54 -070031#include <stdlib.h>
32#include <string.h>
33#include <assert.h>
34
Brian Carlstromdd8af232012-05-13 23:56:07 -070035#include <string>
36
Elliott Hughes87f62a82011-04-22 19:22:54 -070037/**
38 * Equivalent to ScopedLocalRef, but for C_JNIEnv instead. (And slightly more powerful.)
39 */
40template<typename T>
41class scoped_local_ref {
42public:
Chih-Hung Hsieh01332142016-05-02 11:47:43 -070043 explicit scoped_local_ref(C_JNIEnv* env, T localRef = NULL)
Elliott Hughes87f62a82011-04-22 19:22:54 -070044 : mEnv(env), mLocalRef(localRef)
45 {
46 }
47
48 ~scoped_local_ref() {
49 reset();
50 }
51
52 void reset(T localRef = NULL) {
53 if (mLocalRef != NULL) {
54 (*mEnv)->DeleteLocalRef(reinterpret_cast<JNIEnv*>(mEnv), mLocalRef);
55 mLocalRef = localRef;
56 }
57 }
58
59 T get() const {
60 return mLocalRef;
61 }
62
63private:
Ian Rogers8288dde2014-11-04 11:42:02 -080064 C_JNIEnv* const mEnv;
Elliott Hughes87f62a82011-04-22 19:22:54 -070065 T mLocalRef;
66
Ian Rogers8288dde2014-11-04 11:42:02 -080067 DISALLOW_COPY_AND_ASSIGN(scoped_local_ref);
Elliott Hughes87f62a82011-04-22 19:22:54 -070068};
69
70static jclass findClass(C_JNIEnv* env, const char* className) {
71 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
72 return (*env)->FindClass(e, className);
73}
74
75extern "C" int jniRegisterNativeMethods(C_JNIEnv* env, const char* className,
76 const JNINativeMethod* gMethods, int numMethods)
77{
78 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
79
Brian Carlstromdd8af232012-05-13 23:56:07 -070080 ALOGV("Registering %s's %d native methods...", className, numMethods);
Elliott Hughes87f62a82011-04-22 19:22:54 -070081
82 scoped_local_ref<jclass> c(env, findClass(env, className));
83 if (c.get() == NULL) {
Andreas Gampe6c5be192015-08-03 10:31:52 -070084 char* tmp;
85 const char* msg;
86 if (asprintf(&tmp,
87 "Native registration unable to find class '%s'; aborting...",
88 className) == -1) {
89 // Allocation failed, print default warning.
90 msg = "Native registration unable to find class; aborting...";
91 } else {
92 msg = tmp;
93 }
Elliott Hughesa3b57002013-01-22 09:35:09 -080094 e->FatalError(msg);
Elliott Hughes87f62a82011-04-22 19:22:54 -070095 }
96
97 if ((*env)->RegisterNatives(e, c.get(), gMethods, numMethods) < 0) {
Andreas Gampe6c5be192015-08-03 10:31:52 -070098 char* tmp;
99 const char* msg;
100 if (asprintf(&tmp, "RegisterNatives failed for '%s'; aborting...", className) == -1) {
101 // Allocation failed, print default warning.
102 msg = "RegisterNatives failed; aborting...";
103 } else {
104 msg = tmp;
105 }
Elliott Hughesa3b57002013-01-22 09:35:09 -0800106 e->FatalError(msg);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700107 }
108
109 return 0;
110}
111
112/*
113 * Returns a human-readable summary of an exception object. The buffer will
114 * be populated with the "binary" class name and, if present, the
115 * exception message.
116 */
Brian Carlstromdd8af232012-05-13 23:56:07 -0700117static bool getExceptionSummary(C_JNIEnv* env, jthrowable exception, std::string& result) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700118 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
119
120 /* get the name of the exception's class */
121 scoped_local_ref<jclass> exceptionClass(env, (*env)->GetObjectClass(e, exception)); // can't fail
122 scoped_local_ref<jclass> classClass(env,
123 (*env)->GetObjectClass(e, exceptionClass.get())); // java.lang.Class, can't fail
124 jmethodID classGetNameMethod =
125 (*env)->GetMethodID(e, classClass.get(), "getName", "()Ljava/lang/String;");
126 scoped_local_ref<jstring> classNameStr(env,
127 (jstring) (*env)->CallObjectMethod(e, exceptionClass.get(), classGetNameMethod));
128 if (classNameStr.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700129 (*env)->ExceptionClear(e);
130 result = "<error getting class name>";
131 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700132 }
Elliott Hughes87f62a82011-04-22 19:22:54 -0700133 const char* classNameChars = (*env)->GetStringUTFChars(e, classNameStr.get(), NULL);
134 if (classNameChars == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700135 (*env)->ExceptionClear(e);
136 result = "<error getting class name UTF-8>";
137 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700138 }
Brian Carlstromdd8af232012-05-13 23:56:07 -0700139 result += classNameChars;
140 (*env)->ReleaseStringUTFChars(e, classNameStr.get(), classNameChars);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700141
142 /* if the exception has a detail message, get that */
143 jmethodID getMessage =
144 (*env)->GetMethodID(e, exceptionClass.get(), "getMessage", "()Ljava/lang/String;");
145 scoped_local_ref<jstring> messageStr(env,
146 (jstring) (*env)->CallObjectMethod(e, exception, getMessage));
147 if (messageStr.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700148 return true;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700149 }
150
Brian Carlstromdd8af232012-05-13 23:56:07 -0700151 result += ": ";
152
Elliott Hughes87f62a82011-04-22 19:22:54 -0700153 const char* messageChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL);
154 if (messageChars != NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700155 result += messageChars;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700156 (*env)->ReleaseStringUTFChars(e, messageStr.get(), messageChars);
157 } else {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700158 result += "<error getting message>";
Elliott Hughes87f62a82011-04-22 19:22:54 -0700159 (*env)->ExceptionClear(e); // clear OOM
Elliott Hughes87f62a82011-04-22 19:22:54 -0700160 }
161
Brian Carlstromdd8af232012-05-13 23:56:07 -0700162 return true;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700163}
164
165/*
166 * Returns an exception (with stack trace) as a string.
167 */
Brian Carlstromdd8af232012-05-13 23:56:07 -0700168static bool getStackTrace(C_JNIEnv* env, jthrowable exception, std::string& result) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700169 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
170
171 scoped_local_ref<jclass> stringWriterClass(env, findClass(env, "java/io/StringWriter"));
172 if (stringWriterClass.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700173 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700174 }
175
176 jmethodID stringWriterCtor = (*env)->GetMethodID(e, stringWriterClass.get(), "<init>", "()V");
177 jmethodID stringWriterToStringMethod =
178 (*env)->GetMethodID(e, stringWriterClass.get(), "toString", "()Ljava/lang/String;");
179
180 scoped_local_ref<jclass> printWriterClass(env, findClass(env, "java/io/PrintWriter"));
181 if (printWriterClass.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700182 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700183 }
184
185 jmethodID printWriterCtor =
186 (*env)->GetMethodID(e, printWriterClass.get(), "<init>", "(Ljava/io/Writer;)V");
187
188 scoped_local_ref<jobject> stringWriter(env,
189 (*env)->NewObject(e, stringWriterClass.get(), stringWriterCtor));
190 if (stringWriter.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700191 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700192 }
193
Huanqi Chend47c3252014-06-24 13:31:31 +0200194 scoped_local_ref<jobject> printWriter(env,
195 (*env)->NewObject(e, printWriterClass.get(), printWriterCtor, stringWriter.get()));
196 if (printWriter.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700197 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700198 }
199
200 scoped_local_ref<jclass> exceptionClass(env, (*env)->GetObjectClass(e, exception)); // can't fail
201 jmethodID printStackTraceMethod =
202 (*env)->GetMethodID(e, exceptionClass.get(), "printStackTrace", "(Ljava/io/PrintWriter;)V");
Huanqi Chend47c3252014-06-24 13:31:31 +0200203 (*env)->CallVoidMethod(e, exception, printStackTraceMethod, printWriter.get());
Elliott Hughes87f62a82011-04-22 19:22:54 -0700204
205 if ((*env)->ExceptionCheck(e)) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700206 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700207 }
208
209 scoped_local_ref<jstring> messageStr(env,
210 (jstring) (*env)->CallObjectMethod(e, stringWriter.get(), stringWriterToStringMethod));
211 if (messageStr.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700212 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700213 }
214
215 const char* utfChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL);
216 if (utfChars == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700217 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700218 }
219
Brian Carlstromdd8af232012-05-13 23:56:07 -0700220 result = utfChars;
221
Elliott Hughes87f62a82011-04-22 19:22:54 -0700222 (*env)->ReleaseStringUTFChars(e, messageStr.get(), utfChars);
Brian Carlstromdd8af232012-05-13 23:56:07 -0700223 return true;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700224}
225
226extern "C" int jniThrowException(C_JNIEnv* env, const char* className, const char* msg) {
227 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
228
229 if ((*env)->ExceptionCheck(e)) {
230 /* TODO: consider creating the new exception with this as "cause" */
231 scoped_local_ref<jthrowable> exception(env, (*env)->ExceptionOccurred(e));
232 (*env)->ExceptionClear(e);
233
234 if (exception.get() != NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700235 std::string text;
236 getExceptionSummary(env, exception.get(), text);
237 ALOGW("Discarding pending exception (%s) to throw %s", text.c_str(), className);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700238 }
239 }
240
241 scoped_local_ref<jclass> exceptionClass(env, findClass(env, className));
242 if (exceptionClass.get() == NULL) {
Steve Block79a0d9c2012-01-06 19:16:58 +0000243 ALOGE("Unable to find exception class %s", className);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700244 /* ClassNotFoundException now pending */
245 return -1;
246 }
247
248 if ((*env)->ThrowNew(e, exceptionClass.get(), msg) != JNI_OK) {
Steve Block79a0d9c2012-01-06 19:16:58 +0000249 ALOGE("Failed throwing '%s' '%s'", className, msg);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700250 /* an exception, most likely OOM, will now be pending */
251 return -1;
252 }
253
254 return 0;
255}
256
257int jniThrowExceptionFmt(C_JNIEnv* env, const char* className, const char* fmt, va_list args) {
258 char msgBuf[512];
259 vsnprintf(msgBuf, sizeof(msgBuf), fmt, args);
260 return jniThrowException(env, className, msgBuf);
261}
262
263int jniThrowNullPointerException(C_JNIEnv* env, const char* msg) {
264 return jniThrowException(env, "java/lang/NullPointerException", msg);
265}
266
267int jniThrowRuntimeException(C_JNIEnv* env, const char* msg) {
268 return jniThrowException(env, "java/lang/RuntimeException", msg);
269}
270
271int jniThrowIOException(C_JNIEnv* env, int errnum) {
272 char buffer[80];
273 const char* message = jniStrError(errnum, buffer, sizeof(buffer));
274 return jniThrowException(env, "java/io/IOException", message);
275}
276
Elliott Hughesa4e4e742013-09-03 17:47:18 -0700277static std::string jniGetStackTrace(C_JNIEnv* env, jthrowable exception) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700278 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
279
Jeff Browne2b11e72012-04-10 20:34:39 -0700280 scoped_local_ref<jthrowable> currentException(env, (*env)->ExceptionOccurred(e));
Elliott Hughes87f62a82011-04-22 19:22:54 -0700281 if (exception == NULL) {
Jeff Browne2b11e72012-04-10 20:34:39 -0700282 exception = currentException.get();
Elliott Hughes87f62a82011-04-22 19:22:54 -0700283 if (exception == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700284 return "<no pending exception>";
Elliott Hughes87f62a82011-04-22 19:22:54 -0700285 }
Jeff Browne2b11e72012-04-10 20:34:39 -0700286 }
Elliott Hughes87f62a82011-04-22 19:22:54 -0700287
Jeff Browne2b11e72012-04-10 20:34:39 -0700288 if (currentException.get() != NULL) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700289 (*env)->ExceptionClear(e);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700290 }
291
Brian Carlstromdd8af232012-05-13 23:56:07 -0700292 std::string trace;
293 if (!getStackTrace(env, exception, trace)) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700294 (*env)->ExceptionClear(e);
Brian Carlstromdd8af232012-05-13 23:56:07 -0700295 getExceptionSummary(env, exception, trace);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700296 }
297
Elliott Hughes87f62a82011-04-22 19:22:54 -0700298 if (currentException.get() != NULL) {
Jeff Browne2b11e72012-04-10 20:34:39 -0700299 (*env)->Throw(e, currentException.get()); // rethrow
Elliott Hughes87f62a82011-04-22 19:22:54 -0700300 }
Brian Carlstromdd8af232012-05-13 23:56:07 -0700301
302 return trace;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700303}
304
Elliott Hughesa4e4e742013-09-03 17:47:18 -0700305void jniLogException(C_JNIEnv* env, int priority, const char* tag, jthrowable exception) {
306 std::string trace(jniGetStackTrace(env, exception));
307 __android_log_write(priority, tag, trace.c_str());
308}
309
Elliott Hughes87f62a82011-04-22 19:22:54 -0700310const char* jniStrError(int errnum, char* buf, size_t buflen) {
Elliott Hughesd60512c2013-10-08 14:15:18 -0700311#if __GLIBC__
Elliott Hughes87f62a82011-04-22 19:22:54 -0700312 // Note: glibc has a nonstandard strerror_r that returns char* rather than POSIX's int.
313 // char *strerror_r(int errnum, char *buf, size_t n);
Elliott Hughesd60512c2013-10-08 14:15:18 -0700314 return strerror_r(errnum, buf, buflen);
315#else
316 int rc = strerror_r(errnum, buf, buflen);
317 if (rc != 0) {
318 // (POSIX only guarantees a value other than 0. The safest
Elliott Hughes87f62a82011-04-22 19:22:54 -0700319 // way to implement this function is to use C++ and overload on the
Elliott Hughesd60512c2013-10-08 14:15:18 -0700320 // type of strerror_r to accurately distinguish GNU from POSIX.)
Elliott Hughes87f62a82011-04-22 19:22:54 -0700321 snprintf(buf, buflen, "errno %d", errnum);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700322 }
Elliott Hughesd60512c2013-10-08 14:15:18 -0700323 return buf;
324#endif
Elliott Hughes87f62a82011-04-22 19:22:54 -0700325}
326
Elliott Hughes87f62a82011-04-22 19:22:54 -0700327jobject jniCreateFileDescriptor(C_JNIEnv* env, int fd) {
328 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
Dimitry Ivanov13803f62016-03-21 13:37:12 -0700329 JniConstants::init(e);
Brian Carlstromdd8af232012-05-13 23:56:07 -0700330 static jmethodID ctor = e->GetMethodID(JniConstants::fileDescriptorClass, "<init>", "()V");
331 jobject fileDescriptor = (*env)->NewObject(e, JniConstants::fileDescriptorClass, ctor);
Narayan Kamathe29dbf52013-10-31 17:17:10 +0000332 // NOTE: NewObject ensures that an OutOfMemoryError will be seen by the Java
333 // caller if the alloc fails, so we just return NULL when that happens.
334 if (fileDescriptor != NULL) {
335 jniSetFileDescriptorOfFD(env, fileDescriptor, fd);
336 }
Elliott Hughes87f62a82011-04-22 19:22:54 -0700337 return fileDescriptor;
338}
339
340int jniGetFDFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor) {
341 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
Dimitry Ivanov13803f62016-03-21 13:37:12 -0700342 JniConstants::init(e);
Brian Carlstromdd8af232012-05-13 23:56:07 -0700343 static jfieldID fid = e->GetFieldID(JniConstants::fileDescriptorClass, "descriptor", "I");
Narayan Kamathe29dbf52013-10-31 17:17:10 +0000344 if (fileDescriptor != NULL) {
345 return (*env)->GetIntField(e, fileDescriptor, fid);
346 } else {
347 return -1;
348 }
Elliott Hughes87f62a82011-04-22 19:22:54 -0700349}
350
351void jniSetFileDescriptorOfFD(C_JNIEnv* env, jobject fileDescriptor, int value) {
352 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
Dimitry Ivanov13803f62016-03-21 13:37:12 -0700353 JniConstants::init(e);
Brian Carlstromdd8af232012-05-13 23:56:07 -0700354 static jfieldID fid = e->GetFieldID(JniConstants::fileDescriptorClass, "descriptor", "I");
355 (*env)->SetIntField(e, fileDescriptor, fid, value);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700356}
357
Jeff Browndc176c52013-04-02 18:09:29 -0700358jobject jniGetReferent(C_JNIEnv* env, jobject ref) {
359 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
Dimitry Ivanov13803f62016-03-21 13:37:12 -0700360 JniConstants::init(e);
Jeff Browndc176c52013-04-02 18:09:29 -0700361 static jmethodID get = e->GetMethodID(JniConstants::referenceClass, "get", "()Ljava/lang/Object;");
362 return (*env)->CallObjectMethod(e, ref, get);
363}
364
Fredrik Roubert2e312802017-07-04 21:53:08 +0200365jstring jniCreateString(C_JNIEnv* env, const jchar* unicodeChars, jsize len) {
366 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
367 return (*env)->NewString(e, unicodeChars, len);
368}