blob: 9c6cd743ab761a06c0e29bc4a87233cf58464747 [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
Logan Chien63e49172016-06-08 11:33:36 +080037namespace {
38
39// java.io.FileDescriptor.descriptor.
40jfieldID fileDescriptorDescriptorField = nullptr;
41
42// void java.io.FileDescriptor.<init>().
43jmethodID fileDescriptorInitMethod = nullptr;
44// Object java.lang.ref.Reference.get()
45jmethodID referenceGetMethod = nullptr;
46
47jfieldID FindField(JNIEnv* env, jclass klass, const char* name, const char* desc) {
48 jfieldID result = env->GetFieldID(klass, name, desc);
49 if (result == NULL) {
50 ALOGV("failed to find field '%s:%s'", name, desc);
51 abort();
52 }
53 return result;
54}
55
56jmethodID FindMethod(JNIEnv* env, jclass klass, const char* name, const char* signature) {
57 jmethodID result = env->GetMethodID(klass, name, signature);
58 if (result == NULL) {
59 ALOGV("failed to find method '%s%s'", name, signature);
60 abort();
61 }
62 return result;
63}
64
65void InitFieldsAndMethods(JNIEnv* env) {
66 JniConstants::init(env); // Ensure that classes are cached.
67 fileDescriptorDescriptorField = FindField(env, JniConstants::fileDescriptorClass, "descriptor",
68 "I");
69 fileDescriptorInitMethod = FindMethod(env, JniConstants::fileDescriptorClass, "<init>", "()V");
70 referenceGetMethod = FindMethod(env, JniConstants::referenceClass, "get",
71 "()Ljava/lang/Object;");
72}
73
74}
75
76namespace android {
77
78void ClearJNIHelpLocalCache() {
79 fileDescriptorDescriptorField = nullptr;
80 fileDescriptorInitMethod = nullptr;
81 referenceGetMethod = nullptr;
82}
83
84}
85
Elliott Hughes87f62a82011-04-22 19:22:54 -070086/**
87 * Equivalent to ScopedLocalRef, but for C_JNIEnv instead. (And slightly more powerful.)
88 */
89template<typename T>
90class scoped_local_ref {
91public:
Chih-Hung Hsieh01332142016-05-02 11:47:43 -070092 explicit scoped_local_ref(C_JNIEnv* env, T localRef = NULL)
Elliott Hughes87f62a82011-04-22 19:22:54 -070093 : mEnv(env), mLocalRef(localRef)
94 {
95 }
96
97 ~scoped_local_ref() {
98 reset();
99 }
100
101 void reset(T localRef = NULL) {
102 if (mLocalRef != NULL) {
103 (*mEnv)->DeleteLocalRef(reinterpret_cast<JNIEnv*>(mEnv), mLocalRef);
104 mLocalRef = localRef;
105 }
106 }
107
108 T get() const {
109 return mLocalRef;
110 }
111
112private:
Ian Rogers8288dde2014-11-04 11:42:02 -0800113 C_JNIEnv* const mEnv;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700114 T mLocalRef;
115
Ian Rogers8288dde2014-11-04 11:42:02 -0800116 DISALLOW_COPY_AND_ASSIGN(scoped_local_ref);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700117};
118
119static jclass findClass(C_JNIEnv* env, const char* className) {
120 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
121 return (*env)->FindClass(e, className);
122}
123
124extern "C" int jniRegisterNativeMethods(C_JNIEnv* env, const char* className,
125 const JNINativeMethod* gMethods, int numMethods)
126{
127 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
128
Brian Carlstromdd8af232012-05-13 23:56:07 -0700129 ALOGV("Registering %s's %d native methods...", className, numMethods);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700130
131 scoped_local_ref<jclass> c(env, findClass(env, className));
132 if (c.get() == NULL) {
Andreas Gampe6c5be192015-08-03 10:31:52 -0700133 char* tmp;
134 const char* msg;
135 if (asprintf(&tmp,
136 "Native registration unable to find class '%s'; aborting...",
137 className) == -1) {
138 // Allocation failed, print default warning.
139 msg = "Native registration unable to find class; aborting...";
140 } else {
141 msg = tmp;
142 }
Elliott Hughesa3b57002013-01-22 09:35:09 -0800143 e->FatalError(msg);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700144 }
145
146 if ((*env)->RegisterNatives(e, c.get(), gMethods, numMethods) < 0) {
Andreas Gampe6c5be192015-08-03 10:31:52 -0700147 char* tmp;
148 const char* msg;
149 if (asprintf(&tmp, "RegisterNatives failed for '%s'; aborting...", className) == -1) {
150 // Allocation failed, print default warning.
151 msg = "RegisterNatives failed; aborting...";
152 } else {
153 msg = tmp;
154 }
Elliott Hughesa3b57002013-01-22 09:35:09 -0800155 e->FatalError(msg);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700156 }
157
158 return 0;
159}
160
161/*
162 * Returns a human-readable summary of an exception object. The buffer will
163 * be populated with the "binary" class name and, if present, the
164 * exception message.
165 */
Brian Carlstromdd8af232012-05-13 23:56:07 -0700166static bool getExceptionSummary(C_JNIEnv* env, jthrowable exception, std::string& result) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700167 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
168
169 /* get the name of the exception's class */
170 scoped_local_ref<jclass> exceptionClass(env, (*env)->GetObjectClass(e, exception)); // can't fail
171 scoped_local_ref<jclass> classClass(env,
172 (*env)->GetObjectClass(e, exceptionClass.get())); // java.lang.Class, can't fail
173 jmethodID classGetNameMethod =
174 (*env)->GetMethodID(e, classClass.get(), "getName", "()Ljava/lang/String;");
175 scoped_local_ref<jstring> classNameStr(env,
176 (jstring) (*env)->CallObjectMethod(e, exceptionClass.get(), classGetNameMethod));
177 if (classNameStr.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700178 (*env)->ExceptionClear(e);
179 result = "<error getting class name>";
180 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700181 }
Elliott Hughes87f62a82011-04-22 19:22:54 -0700182 const char* classNameChars = (*env)->GetStringUTFChars(e, classNameStr.get(), NULL);
183 if (classNameChars == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700184 (*env)->ExceptionClear(e);
185 result = "<error getting class name UTF-8>";
186 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700187 }
Brian Carlstromdd8af232012-05-13 23:56:07 -0700188 result += classNameChars;
189 (*env)->ReleaseStringUTFChars(e, classNameStr.get(), classNameChars);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700190
191 /* if the exception has a detail message, get that */
192 jmethodID getMessage =
193 (*env)->GetMethodID(e, exceptionClass.get(), "getMessage", "()Ljava/lang/String;");
194 scoped_local_ref<jstring> messageStr(env,
195 (jstring) (*env)->CallObjectMethod(e, exception, getMessage));
196 if (messageStr.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700197 return true;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700198 }
199
Brian Carlstromdd8af232012-05-13 23:56:07 -0700200 result += ": ";
201
Elliott Hughes87f62a82011-04-22 19:22:54 -0700202 const char* messageChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL);
203 if (messageChars != NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700204 result += messageChars;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700205 (*env)->ReleaseStringUTFChars(e, messageStr.get(), messageChars);
206 } else {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700207 result += "<error getting message>";
Elliott Hughes87f62a82011-04-22 19:22:54 -0700208 (*env)->ExceptionClear(e); // clear OOM
Elliott Hughes87f62a82011-04-22 19:22:54 -0700209 }
210
Brian Carlstromdd8af232012-05-13 23:56:07 -0700211 return true;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700212}
213
214/*
215 * Returns an exception (with stack trace) as a string.
216 */
Brian Carlstromdd8af232012-05-13 23:56:07 -0700217static bool getStackTrace(C_JNIEnv* env, jthrowable exception, std::string& result) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700218 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
219
220 scoped_local_ref<jclass> stringWriterClass(env, findClass(env, "java/io/StringWriter"));
221 if (stringWriterClass.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700222 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700223 }
224
225 jmethodID stringWriterCtor = (*env)->GetMethodID(e, stringWriterClass.get(), "<init>", "()V");
226 jmethodID stringWriterToStringMethod =
227 (*env)->GetMethodID(e, stringWriterClass.get(), "toString", "()Ljava/lang/String;");
228
229 scoped_local_ref<jclass> printWriterClass(env, findClass(env, "java/io/PrintWriter"));
230 if (printWriterClass.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700231 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700232 }
233
234 jmethodID printWriterCtor =
235 (*env)->GetMethodID(e, printWriterClass.get(), "<init>", "(Ljava/io/Writer;)V");
236
237 scoped_local_ref<jobject> stringWriter(env,
238 (*env)->NewObject(e, stringWriterClass.get(), stringWriterCtor));
239 if (stringWriter.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700240 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700241 }
242
Huanqi Chend47c3252014-06-24 13:31:31 +0200243 scoped_local_ref<jobject> printWriter(env,
244 (*env)->NewObject(e, printWriterClass.get(), printWriterCtor, stringWriter.get()));
245 if (printWriter.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700246 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700247 }
248
249 scoped_local_ref<jclass> exceptionClass(env, (*env)->GetObjectClass(e, exception)); // can't fail
250 jmethodID printStackTraceMethod =
251 (*env)->GetMethodID(e, exceptionClass.get(), "printStackTrace", "(Ljava/io/PrintWriter;)V");
Huanqi Chend47c3252014-06-24 13:31:31 +0200252 (*env)->CallVoidMethod(e, exception, printStackTraceMethod, printWriter.get());
Elliott Hughes87f62a82011-04-22 19:22:54 -0700253
254 if ((*env)->ExceptionCheck(e)) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700255 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700256 }
257
258 scoped_local_ref<jstring> messageStr(env,
259 (jstring) (*env)->CallObjectMethod(e, stringWriter.get(), stringWriterToStringMethod));
260 if (messageStr.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700261 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700262 }
263
264 const char* utfChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL);
265 if (utfChars == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700266 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700267 }
268
Brian Carlstromdd8af232012-05-13 23:56:07 -0700269 result = utfChars;
270
Elliott Hughes87f62a82011-04-22 19:22:54 -0700271 (*env)->ReleaseStringUTFChars(e, messageStr.get(), utfChars);
Brian Carlstromdd8af232012-05-13 23:56:07 -0700272 return true;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700273}
274
275extern "C" int jniThrowException(C_JNIEnv* env, const char* className, const char* msg) {
276 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
277
278 if ((*env)->ExceptionCheck(e)) {
279 /* TODO: consider creating the new exception with this as "cause" */
280 scoped_local_ref<jthrowable> exception(env, (*env)->ExceptionOccurred(e));
281 (*env)->ExceptionClear(e);
282
283 if (exception.get() != NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700284 std::string text;
285 getExceptionSummary(env, exception.get(), text);
286 ALOGW("Discarding pending exception (%s) to throw %s", text.c_str(), className);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700287 }
288 }
289
290 scoped_local_ref<jclass> exceptionClass(env, findClass(env, className));
291 if (exceptionClass.get() == NULL) {
Steve Block79a0d9c2012-01-06 19:16:58 +0000292 ALOGE("Unable to find exception class %s", className);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700293 /* ClassNotFoundException now pending */
294 return -1;
295 }
296
297 if ((*env)->ThrowNew(e, exceptionClass.get(), msg) != JNI_OK) {
Steve Block79a0d9c2012-01-06 19:16:58 +0000298 ALOGE("Failed throwing '%s' '%s'", className, msg);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700299 /* an exception, most likely OOM, will now be pending */
300 return -1;
301 }
302
303 return 0;
304}
305
306int jniThrowExceptionFmt(C_JNIEnv* env, const char* className, const char* fmt, va_list args) {
307 char msgBuf[512];
308 vsnprintf(msgBuf, sizeof(msgBuf), fmt, args);
309 return jniThrowException(env, className, msgBuf);
310}
311
312int jniThrowNullPointerException(C_JNIEnv* env, const char* msg) {
313 return jniThrowException(env, "java/lang/NullPointerException", msg);
314}
315
316int jniThrowRuntimeException(C_JNIEnv* env, const char* msg) {
317 return jniThrowException(env, "java/lang/RuntimeException", msg);
318}
319
320int jniThrowIOException(C_JNIEnv* env, int errnum) {
321 char buffer[80];
322 const char* message = jniStrError(errnum, buffer, sizeof(buffer));
323 return jniThrowException(env, "java/io/IOException", message);
324}
325
Elliott Hughesa4e4e742013-09-03 17:47:18 -0700326static std::string jniGetStackTrace(C_JNIEnv* env, jthrowable exception) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700327 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
328
Jeff Browne2b11e72012-04-10 20:34:39 -0700329 scoped_local_ref<jthrowable> currentException(env, (*env)->ExceptionOccurred(e));
Elliott Hughes87f62a82011-04-22 19:22:54 -0700330 if (exception == NULL) {
Jeff Browne2b11e72012-04-10 20:34:39 -0700331 exception = currentException.get();
Elliott Hughes87f62a82011-04-22 19:22:54 -0700332 if (exception == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700333 return "<no pending exception>";
Elliott Hughes87f62a82011-04-22 19:22:54 -0700334 }
Jeff Browne2b11e72012-04-10 20:34:39 -0700335 }
Elliott Hughes87f62a82011-04-22 19:22:54 -0700336
Jeff Browne2b11e72012-04-10 20:34:39 -0700337 if (currentException.get() != NULL) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700338 (*env)->ExceptionClear(e);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700339 }
340
Brian Carlstromdd8af232012-05-13 23:56:07 -0700341 std::string trace;
342 if (!getStackTrace(env, exception, trace)) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700343 (*env)->ExceptionClear(e);
Brian Carlstromdd8af232012-05-13 23:56:07 -0700344 getExceptionSummary(env, exception, trace);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700345 }
346
Elliott Hughes87f62a82011-04-22 19:22:54 -0700347 if (currentException.get() != NULL) {
Jeff Browne2b11e72012-04-10 20:34:39 -0700348 (*env)->Throw(e, currentException.get()); // rethrow
Elliott Hughes87f62a82011-04-22 19:22:54 -0700349 }
Brian Carlstromdd8af232012-05-13 23:56:07 -0700350
351 return trace;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700352}
353
Elliott Hughesa4e4e742013-09-03 17:47:18 -0700354void jniLogException(C_JNIEnv* env, int priority, const char* tag, jthrowable exception) {
355 std::string trace(jniGetStackTrace(env, exception));
356 __android_log_write(priority, tag, trace.c_str());
357}
358
Elliott Hughes87f62a82011-04-22 19:22:54 -0700359const char* jniStrError(int errnum, char* buf, size_t buflen) {
Elliott Hughesd60512c2013-10-08 14:15:18 -0700360#if __GLIBC__
Elliott Hughes87f62a82011-04-22 19:22:54 -0700361 // Note: glibc has a nonstandard strerror_r that returns char* rather than POSIX's int.
362 // char *strerror_r(int errnum, char *buf, size_t n);
Elliott Hughesd60512c2013-10-08 14:15:18 -0700363 return strerror_r(errnum, buf, buflen);
364#else
365 int rc = strerror_r(errnum, buf, buflen);
366 if (rc != 0) {
367 // (POSIX only guarantees a value other than 0. The safest
Elliott Hughes87f62a82011-04-22 19:22:54 -0700368 // way to implement this function is to use C++ and overload on the
Elliott Hughesd60512c2013-10-08 14:15:18 -0700369 // type of strerror_r to accurately distinguish GNU from POSIX.)
Elliott Hughes87f62a82011-04-22 19:22:54 -0700370 snprintf(buf, buflen, "errno %d", errnum);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700371 }
Elliott Hughesd60512c2013-10-08 14:15:18 -0700372 return buf;
373#endif
Elliott Hughes87f62a82011-04-22 19:22:54 -0700374}
375
Elliott Hughes87f62a82011-04-22 19:22:54 -0700376jobject jniCreateFileDescriptor(C_JNIEnv* env, int fd) {
377 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
Logan Chien63e49172016-06-08 11:33:36 +0800378 if (fileDescriptorInitMethod == nullptr) {
379 InitFieldsAndMethods(e);
380 }
381 jobject fileDescriptor = (*env)->NewObject(e, JniConstants::fileDescriptorClass,
382 fileDescriptorInitMethod);
Narayan Kamathe29dbf52013-10-31 17:17:10 +0000383 // NOTE: NewObject ensures that an OutOfMemoryError will be seen by the Java
384 // caller if the alloc fails, so we just return NULL when that happens.
385 if (fileDescriptor != NULL) {
386 jniSetFileDescriptorOfFD(env, fileDescriptor, fd);
387 }
Elliott Hughes87f62a82011-04-22 19:22:54 -0700388 return fileDescriptor;
389}
390
391int jniGetFDFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor) {
392 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
Logan Chien63e49172016-06-08 11:33:36 +0800393 if (fileDescriptorDescriptorField == nullptr) {
394 InitFieldsAndMethods(e);
395 }
Narayan Kamathe29dbf52013-10-31 17:17:10 +0000396 if (fileDescriptor != NULL) {
Logan Chien63e49172016-06-08 11:33:36 +0800397 return (*env)->GetIntField(e, fileDescriptor,
398 fileDescriptorDescriptorField);
Narayan Kamathe29dbf52013-10-31 17:17:10 +0000399 } else {
400 return -1;
401 }
Elliott Hughes87f62a82011-04-22 19:22:54 -0700402}
403
404void jniSetFileDescriptorOfFD(C_JNIEnv* env, jobject fileDescriptor, int value) {
405 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
Logan Chien63e49172016-06-08 11:33:36 +0800406 if (fileDescriptorDescriptorField == nullptr) {
407 InitFieldsAndMethods(e);
408 }
409 (*env)->SetIntField(e, fileDescriptor, fileDescriptorDescriptorField, value);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700410}
411
Jeff Browndc176c52013-04-02 18:09:29 -0700412jobject jniGetReferent(C_JNIEnv* env, jobject ref) {
413 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
Logan Chien63e49172016-06-08 11:33:36 +0800414 if (referenceGetMethod == nullptr) {
415 InitFieldsAndMethods(e);
416 }
417 return (*env)->CallObjectMethod(e, ref, referenceGetMethod);
Jeff Browndc176c52013-04-02 18:09:29 -0700418}
419
Fredrik Roubert2e312802017-07-04 21:53:08 +0200420jstring jniCreateString(C_JNIEnv* env, const jchar* unicodeChars, jsize len) {
421 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
422 return (*env)->NewString(e, unicodeChars, len);
423}