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 #define LOG_TAG "JNIHelp"
18
19 #include "JNIHelp.h"
20 #include "cutils/log.h"
21
22 #include <stdlib.h>
23 #include <string.h>
24 #include <assert.h>
25
26 /**
27 * Equivalent to ScopedLocalRef, but for C_JNIEnv instead. (And slightly more powerful.)
28 */
29 template<typename T>
30 class scoped_local_ref {
31 public:
scoped_local_ref(C_JNIEnv * env,T localRef=NULL)32 scoped_local_ref(C_JNIEnv* env, T localRef = NULL)
33 : mEnv(env), mLocalRef(localRef)
34 {
35 }
36
~scoped_local_ref()37 ~scoped_local_ref() {
38 reset();
39 }
40
reset(T localRef=NULL)41 void reset(T localRef = NULL) {
42 if (mLocalRef != NULL) {
43 (*mEnv)->DeleteLocalRef(reinterpret_cast<JNIEnv*>(mEnv), mLocalRef);
44 mLocalRef = localRef;
45 }
46 }
47
get() const48 T get() const {
49 return mLocalRef;
50 }
51
52 private:
53 C_JNIEnv* mEnv;
54 T mLocalRef;
55
56 // Disallow copy and assignment.
57 scoped_local_ref(const scoped_local_ref&);
58 void operator=(const scoped_local_ref&);
59 };
60
findClass(C_JNIEnv * env,const char * className)61 static jclass findClass(C_JNIEnv* env, const char* className) {
62 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
63 return (*env)->FindClass(e, className);
64 }
65
jniRegisterNativeMethods(C_JNIEnv * env,const char * className,const JNINativeMethod * gMethods,int numMethods)66 extern "C" int jniRegisterNativeMethods(C_JNIEnv* env, const char* className,
67 const JNINativeMethod* gMethods, int numMethods)
68 {
69 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
70
71 ALOGV("Registering %s natives", className);
72
73 scoped_local_ref<jclass> c(env, findClass(env, className));
74 if (c.get() == NULL) {
75 char* msg;
76 asprintf(&msg, "Native registration unable to find class '%s', aborting", className);
77 e->FatalError(msg);
78 }
79
80 if ((*env)->RegisterNatives(e, c.get(), gMethods, numMethods) < 0) {
81 char* msg;
82 asprintf(&msg, "RegisterNatives failed for '%s', aborting", className);
83 e->FatalError(msg);
84 }
85
86 return 0;
87 }
88
89 /*
90 * Returns a human-readable summary of an exception object. The buffer will
91 * be populated with the "binary" class name and, if present, the
92 * exception message.
93 */
getExceptionSummary0(C_JNIEnv * env,jthrowable exception)94 static char* getExceptionSummary0(C_JNIEnv* env, jthrowable exception) {
95 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
96
97 /* get the name of the exception's class */
98 scoped_local_ref<jclass> exceptionClass(env, (*env)->GetObjectClass(e, exception)); // can't fail
99 scoped_local_ref<jclass> classClass(env,
100 (*env)->GetObjectClass(e, exceptionClass.get())); // java.lang.Class, can't fail
101 jmethodID classGetNameMethod =
102 (*env)->GetMethodID(e, classClass.get(), "getName", "()Ljava/lang/String;");
103 scoped_local_ref<jstring> classNameStr(env,
104 (jstring) (*env)->CallObjectMethod(e, exceptionClass.get(), classGetNameMethod));
105 if (classNameStr.get() == NULL) {
106 return NULL;
107 }
108
109 /* get printable string */
110 const char* classNameChars = (*env)->GetStringUTFChars(e, classNameStr.get(), NULL);
111 if (classNameChars == NULL) {
112 return NULL;
113 }
114
115 /* if the exception has a detail message, get that */
116 jmethodID getMessage =
117 (*env)->GetMethodID(e, exceptionClass.get(), "getMessage", "()Ljava/lang/String;");
118 scoped_local_ref<jstring> messageStr(env,
119 (jstring) (*env)->CallObjectMethod(e, exception, getMessage));
120 if (messageStr.get() == NULL) {
121 return strdup(classNameChars);
122 }
123
124 char* result = NULL;
125 const char* messageChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL);
126 if (messageChars != NULL) {
127 asprintf(&result, "%s: %s", classNameChars, messageChars);
128 (*env)->ReleaseStringUTFChars(e, messageStr.get(), messageChars);
129 } else {
130 (*env)->ExceptionClear(e); // clear OOM
131 asprintf(&result, "%s: <error getting message>", classNameChars);
132 }
133
134 (*env)->ReleaseStringUTFChars(e, classNameStr.get(), classNameChars);
135 return result;
136 }
137
getExceptionSummary(C_JNIEnv * env,jthrowable exception)138 static char* getExceptionSummary(C_JNIEnv* env, jthrowable exception) {
139 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
140 char* result = getExceptionSummary0(env, exception);
141 if (result == NULL) {
142 (*env)->ExceptionClear(e);
143 result = strdup("<error getting class name>");
144 }
145 return result;
146 }
147
148 /*
149 * Returns an exception (with stack trace) as a string.
150 */
getStackTrace(C_JNIEnv * env,jthrowable exception)151 static char* getStackTrace(C_JNIEnv* env, jthrowable exception) {
152 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
153
154 scoped_local_ref<jclass> stringWriterClass(env, findClass(env, "java/io/StringWriter"));
155 if (stringWriterClass.get() == NULL) {
156 return NULL;
157 }
158
159 jmethodID stringWriterCtor = (*env)->GetMethodID(e, stringWriterClass.get(), "<init>", "()V");
160 jmethodID stringWriterToStringMethod =
161 (*env)->GetMethodID(e, stringWriterClass.get(), "toString", "()Ljava/lang/String;");
162
163 scoped_local_ref<jclass> printWriterClass(env, findClass(env, "java/io/PrintWriter"));
164 if (printWriterClass.get() == NULL) {
165 return NULL;
166 }
167
168 jmethodID printWriterCtor =
169 (*env)->GetMethodID(e, printWriterClass.get(), "<init>", "(Ljava/io/Writer;)V");
170
171 scoped_local_ref<jobject> stringWriter(env,
172 (*env)->NewObject(e, stringWriterClass.get(), stringWriterCtor));
173 if (stringWriter.get() == NULL) {
174 return NULL;
175 }
176
177 jobject printWriter =
178 (*env)->NewObject(e, printWriterClass.get(), printWriterCtor, stringWriter.get());
179 if (printWriter == NULL) {
180 return NULL;
181 }
182
183 scoped_local_ref<jclass> exceptionClass(env, (*env)->GetObjectClass(e, exception)); // can't fail
184 jmethodID printStackTraceMethod =
185 (*env)->GetMethodID(e, exceptionClass.get(), "printStackTrace", "(Ljava/io/PrintWriter;)V");
186 (*env)->CallVoidMethod(e, exception, printStackTraceMethod, printWriter);
187
188 if ((*env)->ExceptionCheck(e)) {
189 return NULL;
190 }
191
192 scoped_local_ref<jstring> messageStr(env,
193 (jstring) (*env)->CallObjectMethod(e, stringWriter.get(), stringWriterToStringMethod));
194 if (messageStr.get() == NULL) {
195 return NULL;
196 }
197
198 const char* utfChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL);
199 if (utfChars == NULL) {
200 return NULL;
201 }
202
203 char* result = strdup(utfChars);
204 (*env)->ReleaseStringUTFChars(e, messageStr.get(), utfChars);
205 return result;
206 }
207
jniThrowException(C_JNIEnv * env,const char * className,const char * msg)208 extern "C" int jniThrowException(C_JNIEnv* env, const char* className, const char* msg) {
209 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
210
211 if ((*env)->ExceptionCheck(e)) {
212 /* TODO: consider creating the new exception with this as "cause" */
213 scoped_local_ref<jthrowable> exception(env, (*env)->ExceptionOccurred(e));
214 (*env)->ExceptionClear(e);
215
216 if (exception.get() != NULL) {
217 char* text = getExceptionSummary(env, exception.get());
218 ALOGW("Discarding pending exception (%s) to throw %s", text, className);
219 free(text);
220 }
221 }
222
223 scoped_local_ref<jclass> exceptionClass(env, findClass(env, className));
224 if (exceptionClass.get() == NULL) {
225 ALOGE("Unable to find exception class %s", className);
226 /* ClassNotFoundException now pending */
227 return -1;
228 }
229
230 if ((*env)->ThrowNew(e, exceptionClass.get(), msg) != JNI_OK) {
231 ALOGE("Failed throwing '%s' '%s'", className, msg);
232 /* an exception, most likely OOM, will now be pending */
233 return -1;
234 }
235
236 return 0;
237 }
238
jniThrowExceptionFmt(C_JNIEnv * env,const char * className,const char * fmt,va_list args)239 int jniThrowExceptionFmt(C_JNIEnv* env, const char* className, const char* fmt, va_list args) {
240 char msgBuf[512];
241 vsnprintf(msgBuf, sizeof(msgBuf), fmt, args);
242 return jniThrowException(env, className, msgBuf);
243 }
244
jniThrowNullPointerException(C_JNIEnv * env,const char * msg)245 int jniThrowNullPointerException(C_JNIEnv* env, const char* msg) {
246 return jniThrowException(env, "java/lang/NullPointerException", msg);
247 }
248
jniThrowRuntimeException(C_JNIEnv * env,const char * msg)249 int jniThrowRuntimeException(C_JNIEnv* env, const char* msg) {
250 return jniThrowException(env, "java/lang/RuntimeException", msg);
251 }
252
jniThrowIOException(C_JNIEnv * env,int errnum)253 int jniThrowIOException(C_JNIEnv* env, int errnum) {
254 char buffer[80];
255 const char* message = jniStrError(errnum, buffer, sizeof(buffer));
256 return jniThrowException(env, "java/io/IOException", message);
257 }
258
jniLogException(C_JNIEnv * env,int priority,const char * tag,jthrowable exception)259 void jniLogException(C_JNIEnv* env, int priority, const char* tag, jthrowable exception) {
260 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
261
262 scoped_local_ref<jthrowable> currentException(env, (*env)->ExceptionOccurred(e));
263 if (exception == NULL) {
264 exception = currentException.get();
265 if (exception == NULL) {
266 return;
267 }
268 }
269
270 if (currentException.get() != NULL) {
271 (*env)->ExceptionClear(e);
272 }
273
274 char* buffer = getStackTrace(env, exception);
275 if (buffer == NULL) {
276 (*env)->ExceptionClear(e);
277 buffer = getExceptionSummary(env, exception);
278 }
279
280 __android_log_write(priority, tag, buffer);
281 free(buffer);
282
283 if (currentException.get() != NULL) {
284 (*env)->Throw(e, currentException.get()); // rethrow
285 }
286 }
287
jniStrError(int errnum,char * buf,size_t buflen)288 const char* jniStrError(int errnum, char* buf, size_t buflen) {
289 // Note: glibc has a nonstandard strerror_r that returns char* rather than POSIX's int.
290 // char *strerror_r(int errnum, char *buf, size_t n);
291 char* ret = (char*) strerror_r(errnum, buf, buflen);
292 if (((int)ret) == 0) {
293 // POSIX strerror_r, success
294 return buf;
295 } else if (((int)ret) == -1) {
296 // POSIX strerror_r, failure
297 // (Strictly, POSIX only guarantees a value other than 0. The safest
298 // way to implement this function is to use C++ and overload on the
299 // type of strerror_r to accurately distinguish GNU from POSIX. But
300 // realistic implementations will always return -1.)
301 snprintf(buf, buflen, "errno %d", errnum);
302 return buf;
303 } else {
304 // glibc strerror_r returning a string
305 return ret;
306 }
307 }
308
309 static struct {
310 jclass clazz;
311 jmethodID ctor;
312 jfieldID descriptor;
313 } gFileDescriptorClassInfo;
314
315 static struct {
316 jmethodID get;
317 } gReferenceClassInfo;
318
JNI_OnLoad(JavaVM * vm,void *)319 jint JNI_OnLoad(JavaVM* vm, void*) {
320 JNIEnv* env;
321 if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
322 ALOGE("JavaVM::GetEnv() failed");
323 abort();
324 }
325
326 gFileDescriptorClassInfo.clazz =
327 reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass("java/io/FileDescriptor")));
328 if (gFileDescriptorClassInfo.clazz == NULL) {
329 abort();
330 }
331
332 gFileDescriptorClassInfo.ctor =
333 env->GetMethodID(gFileDescriptorClassInfo.clazz, "<init>", "()V");
334 if (gFileDescriptorClassInfo.ctor == NULL) {
335 abort();
336 }
337
338 gFileDescriptorClassInfo.descriptor =
339 env->GetFieldID(gFileDescriptorClassInfo.clazz, "descriptor", "I");
340 if (gFileDescriptorClassInfo.descriptor == NULL) {
341 abort();
342 }
343
344 jclass clazz = reinterpret_cast<jclass>(env->FindClass("java/lang/ref/Reference"));
345 if (clazz == NULL) {
346 abort();
347 }
348
349 gReferenceClassInfo.get = env->GetMethodID(clazz, "get", "()Ljava/lang/Object;");
350 if (gReferenceClassInfo.get == NULL) {
351 abort();
352 }
353
354 return JNI_VERSION_1_6;
355 }
356
jniCreateFileDescriptor(C_JNIEnv * env,int fd)357 jobject jniCreateFileDescriptor(C_JNIEnv* env, int fd) {
358 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
359 jobject fileDescriptor = (*env)->NewObject(e,
360 gFileDescriptorClassInfo.clazz, gFileDescriptorClassInfo.ctor);
361 jniSetFileDescriptorOfFD(env, fileDescriptor, fd);
362 return fileDescriptor;
363 }
364
jniGetFDFromFileDescriptor(C_JNIEnv * env,jobject fileDescriptor)365 int jniGetFDFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor) {
366 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
367 return (*env)->GetIntField(e, fileDescriptor, gFileDescriptorClassInfo.descriptor);
368 }
369
jniSetFileDescriptorOfFD(C_JNIEnv * env,jobject fileDescriptor,int value)370 void jniSetFileDescriptorOfFD(C_JNIEnv* env, jobject fileDescriptor, int value) {
371 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
372 (*env)->SetIntField(e, fileDescriptor, gFileDescriptorClassInfo.descriptor, value);
373 }
374
jniGetReferent(C_JNIEnv * env,jobject ref)375 jobject jniGetReferent(C_JNIEnv* env, jobject ref) {
376 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
377 return (*env)->CallObjectMethod(e, ref, gReferenceClassInfo.get);
378 }
379
380 /*
381 * DO NOT USE THIS FUNCTION
382 *
383 * Get a pointer to the elements of a non-movable array.
384 *
385 * The semantics are similar to GetDirectBufferAddress. Specifically, the VM
386 * guarantees that the array will not move, and the caller must ensure that
387 * it does not continue to use the pointer after the object is collected.
388 *
389 * We currently use an illegal sequence that trips up CheckJNI when
390 * the "forcecopy" mode is enabled. We pass in a magic value to work
391 * around the problem.
392 *
393 * Returns NULL if the array is movable.
394 */
395 #define kNoCopyMagic 0xd5aab57f /* also in CheckJni.c */
jniGetNonMovableArrayElements(C_JNIEnv * env,jarray arrayObj)396 extern "C" jbyte* jniGetNonMovableArrayElements(C_JNIEnv* env, jarray arrayObj) {
397 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
398
399 jbyteArray byteArray = reinterpret_cast<jbyteArray>(arrayObj);
400
401 /*
402 * Normally the "isCopy" parameter is for a return value only, so the
403 * non-CheckJNI VM will ignore whatever we pass in.
404 */
405 uint32_t noCopy = kNoCopyMagic;
406 jbyte* result = (*env)->GetByteArrayElements(e, byteArray, reinterpret_cast<jboolean*>(&noCopy));
407
408 /*
409 * The non-CheckJNI implementation only cares about the array object,
410 * so we can replace the element pointer with the magic value.
411 */
412 (*env)->ReleaseByteArrayElements(e, byteArray, reinterpret_cast<jbyte*>(kNoCopyMagic), 0);
413 return result;
414 }
415