• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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