OSDN Git Service

Move the implementation of JNIHelp.h's FileDescriptor helpers into JNIHelp.c.
[android-x86/dalvik.git] / libnativehelper / JNIHelp.c
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 /*
18  * JNI helper functions.
19  */
20 #define LOG_TAG "JNIHelp"
21 #include "JNIHelp.h"
22 #include "utils/Log.h"
23
24 #include <string.h>
25 #include <assert.h>
26
27 /*
28  * Register native JNI-callable methods.
29  *
30  * "className" looks like "java/lang/String".
31  */
32 int jniRegisterNativeMethods(JNIEnv* env, const char* className,
33     const JNINativeMethod* gMethods, int numMethods)
34 {
35     jclass clazz;
36
37     LOGV("Registering %s natives\n", className);
38     clazz = (*env)->FindClass(env, className);
39     if (clazz == NULL) {
40         LOGE("Native registration unable to find class '%s'\n", className);
41         return -1;
42     }
43
44     int result = 0;
45     if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) {
46         LOGE("RegisterNatives failed for '%s'\n", className);
47         result = -1;
48     }
49
50     (*env)->DeleteLocalRef(env, clazz);
51     return result;
52 }
53
54 /*
55  * Get a human-readable summary of an exception object.  The buffer will
56  * be populated with the "binary" class name and, if present, the
57  * exception message.
58  */
59 static void getExceptionSummary(JNIEnv* env, jthrowable exception, char* buf, size_t bufLen)
60 {
61     int success = 0;
62
63     /* get the name of the exception's class */
64     jclass exceptionClazz = (*env)->GetObjectClass(env, exception); // can't fail
65     jclass classClazz = (*env)->GetObjectClass(env, exceptionClazz); // java.lang.Class, can't fail
66     jmethodID classGetNameMethod = (*env)->GetMethodID(
67             env, classClazz, "getName", "()Ljava/lang/String;");
68     jstring classNameStr = (*env)->CallObjectMethod(env, exceptionClazz, classGetNameMethod);
69     if (classNameStr != NULL) {
70         /* get printable string */
71         const char* classNameChars = (*env)->GetStringUTFChars(env, classNameStr, NULL);
72         if (classNameChars != NULL) {
73             /* if the exception has a message string, get that */
74             jmethodID throwableGetMessageMethod = (*env)->GetMethodID(
75                     env, exceptionClazz, "getMessage", "()Ljava/lang/String;");
76             jstring messageStr = (*env)->CallObjectMethod(
77                     env, exception, throwableGetMessageMethod);
78
79             if (messageStr != NULL) {
80                 const char* messageChars = (*env)->GetStringUTFChars(env, messageStr, NULL);
81                 if (messageChars != NULL) {
82                     snprintf(buf, bufLen, "%s: %s", classNameChars, messageChars);
83                     (*env)->ReleaseStringUTFChars(env, messageStr, messageChars);
84                 } else {
85                     (*env)->ExceptionClear(env); // clear OOM
86                     snprintf(buf, bufLen, "%s: <error getting message>", classNameChars);
87                 }
88                 (*env)->DeleteLocalRef(env, messageStr);
89             } else {
90                 strncpy(buf, classNameChars, bufLen);
91                 buf[bufLen - 1] = '\0';
92             }
93
94             (*env)->ReleaseStringUTFChars(env, classNameStr, classNameChars);
95             success = 1;
96         }
97         (*env)->DeleteLocalRef(env, classNameStr);
98     }
99     (*env)->DeleteLocalRef(env, classClazz);
100     (*env)->DeleteLocalRef(env, exceptionClazz);
101
102     if (! success) {
103         (*env)->ExceptionClear(env);
104         snprintf(buf, bufLen, "%s", "<error getting class name>");
105     }
106 }
107
108 /*
109  * Formats an exception as a string with its stack trace.
110  */
111 static void printStackTrace(JNIEnv* env, jthrowable exception, char* buf, size_t bufLen)
112 {
113     int success = 0;
114
115     jclass stringWriterClazz = (*env)->FindClass(env, "java/io/StringWriter");
116     if (stringWriterClazz != NULL) {
117         jmethodID stringWriterCtor = (*env)->GetMethodID(env, stringWriterClazz,
118                 "<init>", "()V");
119         jmethodID stringWriterToStringMethod = (*env)->GetMethodID(env, stringWriterClazz,
120                 "toString", "()Ljava/lang/String;");
121
122         jclass printWriterClazz = (*env)->FindClass(env, "java/io/PrintWriter");
123         if (printWriterClazz != NULL) {
124             jmethodID printWriterCtor = (*env)->GetMethodID(env, printWriterClazz,
125                     "<init>", "(Ljava/io/Writer;)V");
126
127             jobject stringWriterObj = (*env)->NewObject(env, stringWriterClazz, stringWriterCtor);
128             if (stringWriterObj != NULL) {
129                 jobject printWriterObj = (*env)->NewObject(env, printWriterClazz, printWriterCtor,
130                         stringWriterObj);
131                 if (printWriterObj != NULL) {
132                     jclass exceptionClazz = (*env)->GetObjectClass(env, exception); // can't fail
133                     jmethodID printStackTraceMethod = (*env)->GetMethodID(
134                             env, exceptionClazz, "printStackTrace", "(Ljava/io/PrintWriter;)V");
135
136                     (*env)->CallVoidMethod(
137                             env, exception, printStackTraceMethod, printWriterObj);
138                     if (! (*env)->ExceptionCheck(env)) {
139                         jstring messageStr = (*env)->CallObjectMethod(
140                                 env, stringWriterObj, stringWriterToStringMethod);
141                         if (messageStr != NULL) {
142                             jsize messageStrLength = (*env)->GetStringLength(env, messageStr);
143                             if (messageStrLength >= (jsize) bufLen) {
144                                 messageStrLength = bufLen - 1;
145                             }
146                             (*env)->GetStringUTFRegion(env, messageStr, 0, messageStrLength, buf);
147                             (*env)->DeleteLocalRef(env, messageStr);
148                             buf[messageStrLength] = '\0';
149                             success = 1;
150                         }
151                     }
152                     (*env)->DeleteLocalRef(env, exceptionClazz);
153                     (*env)->DeleteLocalRef(env, printWriterObj);
154                 }
155                 (*env)->DeleteLocalRef(env, stringWriterObj);
156             }
157             (*env)->DeleteLocalRef(env, printWriterClazz);
158         }
159         (*env)->DeleteLocalRef(env, stringWriterClazz);
160     }
161
162     if (! success) {
163         (*env)->ExceptionClear(env);
164         getExceptionSummary(env, exception, buf, bufLen);
165     }
166 }
167
168 /*
169  * Throw an exception with the specified class and an optional message.
170  *
171  * If an exception is currently pending, we log a warning message and
172  * clear it.
173  *
174  * Returns 0 if the specified exception was successfully thrown.  (Some
175  * sort of exception will always be pending when this returns.)
176  */
177 int jniThrowException(JNIEnv* env, const char* className, const char* msg)
178 {
179     jclass exceptionClass;
180
181     if ((*env)->ExceptionCheck(env)) {
182         /* TODO: consider creating the new exception with this as "cause" */
183         char buf[256];
184
185         jthrowable exception = (*env)->ExceptionOccurred(env);
186         (*env)->ExceptionClear(env);
187
188         if (exception != NULL) {
189             getExceptionSummary(env, exception, buf, sizeof(buf));
190             LOGW("Discarding pending exception (%s) to throw %s\n", buf, className);
191             (*env)->DeleteLocalRef(env, exception);
192         }
193     }
194
195     exceptionClass = (*env)->FindClass(env, className);
196     if (exceptionClass == NULL) {
197         LOGE("Unable to find exception class %s\n", className);
198         /* ClassNotFoundException now pending */
199         return -1;
200     }
201
202     int result = 0;
203     if ((*env)->ThrowNew(env, exceptionClass, msg) != JNI_OK) {
204         LOGE("Failed throwing '%s' '%s'\n", className, msg);
205         /* an exception, most likely OOM, will now be pending */
206         result = -1;
207     }
208
209     (*env)->DeleteLocalRef(env, exceptionClass);
210     return result;
211 }
212
213 /*
214  * Throw a java.lang.NullPointerException, with an optional message.
215  */
216 int jniThrowNullPointerException(JNIEnv* env, const char* msg)
217 {
218     return jniThrowException(env, "java/lang/NullPointerException", msg);
219 }
220
221 /*
222  * Throw a java.lang.RuntimeException, with an optional message.
223  */
224 int jniThrowRuntimeException(JNIEnv* env, const char* msg)
225 {
226     return jniThrowException(env, "java/lang/RuntimeException", msg);
227 }
228
229 /*
230  * Throw a java.io.IOException, generating the message from errno.
231  */
232 int jniThrowIOException(JNIEnv* env, int errnum)
233 {
234     char buffer[80];
235     const char* message = jniStrError(errnum, buffer, sizeof(buffer));
236     return jniThrowException(env, "java/io/IOException", message);
237 }
238
239 /*
240  * Log an exception.
241  * If exception is NULL, logs the current exception in the JNI environment, if any.
242  */
243 void jniLogException(JNIEnv* env, int priority, const char* tag, jthrowable exception)
244 {
245     int currentException = 0;
246     if (exception == NULL) {
247         exception = (*env)->ExceptionOccurred(env);
248         if (exception == NULL) {
249             return;
250         }
251
252         (*env)->ExceptionClear(env);
253         currentException = 1;
254     }
255
256     char buffer[1024];
257     printStackTrace(env, exception, buffer, sizeof(buffer));
258     __android_log_write(priority, tag, buffer);
259
260     if (currentException) {
261         (*env)->Throw(env, exception); // rethrow
262         (*env)->DeleteLocalRef(env, exception);
263     }
264 }
265
266 const char* jniStrError(int errnum, char* buf, size_t buflen)
267 {
268     // note: glibc has a nonstandard strerror_r that returns char* rather
269     // than POSIX's int.
270     // char *strerror_r(int errnum, char *buf, size_t n);
271     char* ret = (char*) strerror_r(errnum, buf, buflen);
272     if (((int)ret) == 0) {
273         //POSIX strerror_r, success
274         return buf;
275     } else if (((int)ret) == -1) {
276         //POSIX strerror_r, failure
277         // (Strictly, POSIX only guarantees a value other than 0. The safest
278         // way to implement this function is to use C++ and overload on the
279         // type of strerror_r to accurately distinguish GNU from POSIX. But
280         // realistic implementations will always return -1.)
281         snprintf(buf, buflen, "errno %d", errnum);
282         return buf;
283     } else {
284         //glibc strerror_r returning a string
285         return ret;
286     }
287 }
288
289 static struct CachedFields {
290     jclass fileDescriptorClass;
291     jmethodID fileDescriptorCtor;
292     jfieldID descriptorField;
293 } gCachedFields;
294
295 int registerJniHelp(JNIEnv* env) {
296     gCachedFields.fileDescriptorClass =
297             (*env)->NewGlobalRef(env, (*env)->FindClass(env, "java/io/FileDescriptor"));
298     if (gCachedFields.fileDescriptorClass == NULL) {
299         return -1;
300     }
301
302     gCachedFields.fileDescriptorCtor =
303             (*env)->GetMethodID(env, gCachedFields.fileDescriptorClass, "<init>", "()V");
304     if (gCachedFields.fileDescriptorCtor == NULL) {
305         return -1;
306     }
307
308     gCachedFields.descriptorField =
309             (*env)->GetFieldID(env, gCachedFields.fileDescriptorClass, "descriptor", "I");
310     if (gCachedFields.descriptorField == NULL) {
311         return -1;
312     }
313
314     return 0;
315 }
316
317 /*
318  * Create a java.io.FileDescriptor given an integer fd
319  */
320 jobject jniCreateFileDescriptor(JNIEnv* env, int fd) {
321     jobject fileDescriptor = (*env)->NewObject(env,
322             gCachedFields.fileDescriptorClass, gCachedFields.fileDescriptorCtor);
323     jniSetFileDescriptorOfFD(env, fileDescriptor, fd);
324     return fileDescriptor;
325 }
326
327 /*
328  * Get an int file descriptor from a java.io.FileDescriptor
329  */
330 int jniGetFDFromFileDescriptor(JNIEnv* env, jobject fileDescriptor) {
331     return (*env)->GetIntField(env, fileDescriptor, gCachedFields.descriptorField);
332 }
333
334 /*
335  * Set the descriptor of a java.io.FileDescriptor
336  */
337 void jniSetFileDescriptorOfFD(JNIEnv* env, jobject fileDescriptor, int value) {
338     (*env)->SetIntField(env, fileDescriptor, gCachedFields.descriptorField, value);
339 }