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 */
jniRegisterNativeMethods(JNIEnv * env,const char * className,const JNINativeMethod * gMethods,int numMethods)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 */
getExceptionSummary(JNIEnv * env,jthrowable exception,char * buf,size_t bufLen)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 */
printStackTrace(JNIEnv * env,jthrowable exception,char * buf,size_t bufLen)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 */
jniThrowException(JNIEnv * env,const char * className,const char * msg)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 */
jniThrowNullPointerException(JNIEnv * env,const char * msg)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 */
jniThrowRuntimeException(JNIEnv * env,const char * msg)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 */
jniThrowIOException(JNIEnv * env,int errnum)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 */
jniLogException(JNIEnv * env,int priority,const char * tag,jthrowable exception)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
jniStrError(int errnum,char * buf,size_t buflen)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
registerJniHelp(JNIEnv * env)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 */
jniCreateFileDescriptor(JNIEnv * env,int fd)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 */
jniGetFDFromFileDescriptor(JNIEnv * env,jobject fileDescriptor)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 */
jniSetFileDescriptorOfFD(JNIEnv * env,jobject fileDescriptor,int value)337 void jniSetFileDescriptorOfFD(JNIEnv* env, jobject fileDescriptor, int value) {
338 (*env)->SetIntField(env, fileDescriptor, gCachedFields.descriptorField, value);
339 }
340