1 /*
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 #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 /* ...but this code uses asprintf, which is a BSD/GNU extension. */
21 #define _BSD_SOURCE
22 #endif
23
24 #define LOG_TAG "JNIHelp"
25
26 #include <nativehelper/JniConstants.h>
27 #include <nativehelper/JNIHelp.h>
28 #include "ALog-priv.h"
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <assert.h>
34
35 #include <string>
36
37 namespace {
38
39 // java.io.FileDescriptor.descriptor.
40 jfieldID fileDescriptorDescriptorField = nullptr;
41
42 // void java.io.FileDescriptor.<init>().
43 jmethodID fileDescriptorInitMethod = nullptr;
44 // Object java.lang.ref.Reference.get()
45 jmethodID referenceGetMethod = nullptr;
46
FindField(JNIEnv * env,jclass klass,const char * name,const char * desc)47 jfieldID 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
FindMethod(JNIEnv * env,jclass klass,const char * name,const char * signature)56 jmethodID 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
InitFieldsAndMethods(JNIEnv * env)65 void 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
76 namespace android {
77
ClearJNIHelpLocalCache()78 void ClearJNIHelpLocalCache() {
79 fileDescriptorDescriptorField = nullptr;
80 fileDescriptorInitMethod = nullptr;
81 referenceGetMethod = nullptr;
82 }
83
84 }
85
86 /**
87 * Equivalent to ScopedLocalRef, but for C_JNIEnv instead. (And slightly more powerful.)
88 */
89 template<typename T>
90 class scoped_local_ref {
91 public:
scoped_local_ref(C_JNIEnv * env,T localRef=NULL)92 explicit scoped_local_ref(C_JNIEnv* env, T localRef = NULL)
93 : mEnv(env), mLocalRef(localRef)
94 {
95 }
96
~scoped_local_ref()97 ~scoped_local_ref() {
98 reset();
99 }
100
reset(T localRef=NULL)101 void reset(T localRef = NULL) {
102 if (mLocalRef != NULL) {
103 (*mEnv)->DeleteLocalRef(reinterpret_cast<JNIEnv*>(mEnv), mLocalRef);
104 mLocalRef = localRef;
105 }
106 }
107
get() const108 T get() const {
109 return mLocalRef;
110 }
111
112 private:
113 C_JNIEnv* const mEnv;
114 T mLocalRef;
115
116 DISALLOW_COPY_AND_ASSIGN(scoped_local_ref);
117 };
118
findClass(C_JNIEnv * env,const char * className)119 static jclass findClass(C_JNIEnv* env, const char* className) {
120 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
121 return (*env)->FindClass(e, className);
122 }
123
jniRegisterNativeMethods(C_JNIEnv * env,const char * className,const JNINativeMethod * gMethods,int numMethods)124 extern "C" int jniRegisterNativeMethods(C_JNIEnv* env, const char* className,
125 const JNINativeMethod* gMethods, int numMethods)
126 {
127 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
128
129 ALOGV("Registering %s's %d native methods...", className, numMethods);
130
131 scoped_local_ref<jclass> c(env, findClass(env, className));
132 if (c.get() == NULL) {
133 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 }
143 e->FatalError(msg);
144 }
145
146 if ((*env)->RegisterNatives(e, c.get(), gMethods, numMethods) < 0) {
147 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 }
155 e->FatalError(msg);
156 }
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 */
getExceptionSummary(C_JNIEnv * env,jthrowable exception,std::string & result)166 static bool getExceptionSummary(C_JNIEnv* env, jthrowable exception, std::string& result) {
167 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) {
178 (*env)->ExceptionClear(e);
179 result = "<error getting class name>";
180 return false;
181 }
182 const char* classNameChars = (*env)->GetStringUTFChars(e, classNameStr.get(), NULL);
183 if (classNameChars == NULL) {
184 (*env)->ExceptionClear(e);
185 result = "<error getting class name UTF-8>";
186 return false;
187 }
188 result += classNameChars;
189 (*env)->ReleaseStringUTFChars(e, classNameStr.get(), classNameChars);
190
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) {
197 return true;
198 }
199
200 result += ": ";
201
202 const char* messageChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL);
203 if (messageChars != NULL) {
204 result += messageChars;
205 (*env)->ReleaseStringUTFChars(e, messageStr.get(), messageChars);
206 } else {
207 result += "<error getting message>";
208 (*env)->ExceptionClear(e); // clear OOM
209 }
210
211 return true;
212 }
213
214 /*
215 * Returns an exception (with stack trace) as a string.
216 */
getStackTrace(C_JNIEnv * env,jthrowable exception,std::string & result)217 static bool getStackTrace(C_JNIEnv* env, jthrowable exception, std::string& result) {
218 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
219
220 scoped_local_ref<jclass> stringWriterClass(env, findClass(env, "java/io/StringWriter"));
221 if (stringWriterClass.get() == NULL) {
222 return false;
223 }
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) {
231 return false;
232 }
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) {
240 return false;
241 }
242
243 scoped_local_ref<jobject> printWriter(env,
244 (*env)->NewObject(e, printWriterClass.get(), printWriterCtor, stringWriter.get()));
245 if (printWriter.get() == NULL) {
246 return false;
247 }
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");
252 (*env)->CallVoidMethod(e, exception, printStackTraceMethod, printWriter.get());
253
254 if ((*env)->ExceptionCheck(e)) {
255 return false;
256 }
257
258 scoped_local_ref<jstring> messageStr(env,
259 (jstring) (*env)->CallObjectMethod(e, stringWriter.get(), stringWriterToStringMethod));
260 if (messageStr.get() == NULL) {
261 return false;
262 }
263
264 const char* utfChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL);
265 if (utfChars == NULL) {
266 return false;
267 }
268
269 result = utfChars;
270
271 (*env)->ReleaseStringUTFChars(e, messageStr.get(), utfChars);
272 return true;
273 }
274
jniThrowException(C_JNIEnv * env,const char * className,const char * msg)275 extern "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) {
284 std::string text;
285 getExceptionSummary(env, exception.get(), text);
286 ALOGW("Discarding pending exception (%s) to throw %s", text.c_str(), className);
287 }
288 }
289
290 scoped_local_ref<jclass> exceptionClass(env, findClass(env, className));
291 if (exceptionClass.get() == NULL) {
292 ALOGE("Unable to find exception class %s", className);
293 /* ClassNotFoundException now pending */
294 return -1;
295 }
296
297 if ((*env)->ThrowNew(e, exceptionClass.get(), msg) != JNI_OK) {
298 ALOGE("Failed throwing '%s' '%s'", className, msg);
299 /* an exception, most likely OOM, will now be pending */
300 return -1;
301 }
302
303 return 0;
304 }
305
jniThrowExceptionFmt(C_JNIEnv * env,const char * className,const char * fmt,va_list args)306 int 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
jniThrowNullPointerException(C_JNIEnv * env,const char * msg)312 int jniThrowNullPointerException(C_JNIEnv* env, const char* msg) {
313 return jniThrowException(env, "java/lang/NullPointerException", msg);
314 }
315
jniThrowRuntimeException(C_JNIEnv * env,const char * msg)316 int jniThrowRuntimeException(C_JNIEnv* env, const char* msg) {
317 return jniThrowException(env, "java/lang/RuntimeException", msg);
318 }
319
jniThrowIOException(C_JNIEnv * env,int errnum)320 int 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
jniGetStackTrace(C_JNIEnv * env,jthrowable exception)326 static std::string jniGetStackTrace(C_JNIEnv* env, jthrowable exception) {
327 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
328
329 scoped_local_ref<jthrowable> currentException(env, (*env)->ExceptionOccurred(e));
330 if (exception == NULL) {
331 exception = currentException.get();
332 if (exception == NULL) {
333 return "<no pending exception>";
334 }
335 }
336
337 if (currentException.get() != NULL) {
338 (*env)->ExceptionClear(e);
339 }
340
341 std::string trace;
342 if (!getStackTrace(env, exception, trace)) {
343 (*env)->ExceptionClear(e);
344 getExceptionSummary(env, exception, trace);
345 }
346
347 if (currentException.get() != NULL) {
348 (*env)->Throw(e, currentException.get()); // rethrow
349 }
350
351 return trace;
352 }
353
jniLogException(C_JNIEnv * env,int priority,const char * tag,jthrowable exception)354 void 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
jniStrError(int errnum,char * buf,size_t buflen)359 const char* jniStrError(int errnum, char* buf, size_t buflen) {
360 #if __GLIBC__
361 // 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);
363 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
368 // way to implement this function is to use C++ and overload on the
369 // type of strerror_r to accurately distinguish GNU from POSIX.)
370 snprintf(buf, buflen, "errno %d", errnum);
371 }
372 return buf;
373 #endif
374 }
375
jniCreateFileDescriptor(C_JNIEnv * env,int fd)376 jobject jniCreateFileDescriptor(C_JNIEnv* env, int fd) {
377 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
378 if (fileDescriptorInitMethod == nullptr) {
379 InitFieldsAndMethods(e);
380 }
381 jobject fileDescriptor = (*env)->NewObject(e, JniConstants::fileDescriptorClass,
382 fileDescriptorInitMethod);
383 // 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 }
388 return fileDescriptor;
389 }
390
jniGetFDFromFileDescriptor(C_JNIEnv * env,jobject fileDescriptor)391 int jniGetFDFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor) {
392 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
393 if (fileDescriptorDescriptorField == nullptr) {
394 InitFieldsAndMethods(e);
395 }
396 if (fileDescriptor != NULL) {
397 return (*env)->GetIntField(e, fileDescriptor,
398 fileDescriptorDescriptorField);
399 } else {
400 return -1;
401 }
402 }
403
jniSetFileDescriptorOfFD(C_JNIEnv * env,jobject fileDescriptor,int value)404 void jniSetFileDescriptorOfFD(C_JNIEnv* env, jobject fileDescriptor, int value) {
405 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
406 if (fileDescriptorDescriptorField == nullptr) {
407 InitFieldsAndMethods(e);
408 }
409 (*env)->SetIntField(e, fileDescriptor, fileDescriptorDescriptorField, value);
410 }
411
jniGetReferent(C_JNIEnv * env,jobject ref)412 jobject jniGetReferent(C_JNIEnv* env, jobject ref) {
413 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
414 if (referenceGetMethod == nullptr) {
415 InitFieldsAndMethods(e);
416 }
417 return (*env)->CallObjectMethod(e, ref, referenceGetMethod);
418 }
419
jniCreateString(C_JNIEnv * env,const jchar * unicodeChars,jsize len)420 jstring jniCreateString(C_JNIEnv* env, const jchar* unicodeChars, jsize len) {
421 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
422 return (*env)->NewString(e, unicodeChars, len);
423 }
424