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