blob: 0aecd12718f4011041f108ef35a0654c1e29e205 [file] [log] [blame]
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001/*
Andy McFaddend1f4cf72009-10-21 15:36:48 -07002 * Copyright (C) 2006 The Android Open Source Project
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003 *
Andy McFaddend1f4cf72009-10-21 15:36:48 -07004 * 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/*
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080018 * 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 */
32int 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 if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) {
44 LOGE("RegisterNatives failed for '%s'\n", className);
45 return -1;
46 }
47 return 0;
48}
49
50/*
Andy McFaddend1f4cf72009-10-21 15:36:48 -070051 * Get a human-readable summary of an exception object. The buffer will
52 * be populated with the "binary" class name and, if present, the
53 * exception message.
54 */
55static void getExceptionSummary(JNIEnv* env, jthrowable excep, char* buf,
56 size_t bufLen)
57{
58 if (excep == NULL)
59 return;
60
61 /* get the name of the exception's class; none of these should fail */
62 jclass clazz = (*env)->GetObjectClass(env, excep); // exception's class
63 jclass jlc = (*env)->GetObjectClass(env, clazz); // java.lang.Class
64 jmethodID getNameMethod =
65 (*env)->GetMethodID(env, jlc, "getName", "()Ljava/lang/String;");
66 jstring className = (*env)->CallObjectMethod(env, clazz, getNameMethod);
67
68 /* get printable string */
69 const char* nameStr = (*env)->GetStringUTFChars(env, className, NULL);
70 if (nameStr == NULL) {
71 snprintf(buf, bufLen, "%s", "out of memory generating summary");
72 (*env)->ExceptionClear(env); // clear OOM
73 return;
74 }
75
76 /* if the exception has a message string, get that */
77 jmethodID getThrowableMessage =
78 (*env)->GetMethodID(env, clazz, "getMessage", "()Ljava/lang/String;");
79 jstring message = (*env)->CallObjectMethod(env, excep, getThrowableMessage);
80
81 if (message != NULL) {
82 const char* messageStr = (*env)->GetStringUTFChars(env, message, NULL);
83 snprintf(buf, bufLen, "%s: %s", nameStr, messageStr);
84 if (messageStr != NULL)
85 (*env)->ReleaseStringUTFChars(env, message, messageStr);
86 else
87 (*env)->ExceptionClear(env); // clear OOM
88 } else {
89 strncpy(buf, nameStr, bufLen);
90 buf[bufLen-1] = '\0';
91 }
92
93 (*env)->ReleaseStringUTFChars(env, className, nameStr);
94}
95
96/*
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080097 * Throw an exception with the specified class and an optional message.
Andy McFaddend1f4cf72009-10-21 15:36:48 -070098 *
99 * If an exception is currently pending, we log a warning message and
100 * clear it.
101 *
102 * Returns 0 if the specified exception was successfully thrown. (Some
103 * sort of exception will always be pending when this returns.)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800104 */
105int jniThrowException(JNIEnv* env, const char* className, const char* msg)
106{
107 jclass exceptionClass;
108
Andy McFaddend1f4cf72009-10-21 15:36:48 -0700109 if ((*env)->ExceptionCheck(env)) {
110 /* TODO: consider creating the new exception with this as "cause" */
111 char buf[256];
112
113 jthrowable excep = (*env)->ExceptionOccurred(env);
114 (*env)->ExceptionClear(env);
115 getExceptionSummary(env, excep, buf, sizeof(buf));
116 LOGW("Discarding pending exception (%s) to throw %s\n",
117 buf, className);
118 }
119
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800120 exceptionClass = (*env)->FindClass(env, className);
121 if (exceptionClass == NULL) {
122 LOGE("Unable to find exception class %s\n", className);
Andy McFaddend1f4cf72009-10-21 15:36:48 -0700123 /* ClassNotFoundException now pending */
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800124 return -1;
125 }
126
127 if ((*env)->ThrowNew(env, exceptionClass, msg) != JNI_OK) {
128 LOGE("Failed throwing '%s' '%s'\n", className, msg);
Andy McFaddend1f4cf72009-10-21 15:36:48 -0700129 /* an exception, most likely OOM, will now be pending */
130 return -1;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800131 }
132 return 0;
133}
134
135/*
136 * Throw a java.IO.IOException, generating the message from errno.
137 */
138int jniThrowIOException(JNIEnv* env, int errnum)
139{
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800140 char buffer[80];
Elliott Hughes54b596c2009-10-20 16:59:01 -0700141 const char* message = jniStrError(errnum, buffer, sizeof(buffer));
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800142 return jniThrowException(env, "java/io/IOException", message);
143}
144
Elliott Hughes54b596c2009-10-20 16:59:01 -0700145const char* jniStrError(int errnum, char* buf, size_t buflen)
146{
147 // note: glibc has a nonstandard strerror_r that returns char* rather
148 // than POSIX's int.
149 // char *strerror_r(int errnum, char *buf, size_t n);
150 char* ret = (char*) strerror_r(errnum, buf, buflen);
151 if (((int)ret) == 0) {
152 //POSIX strerror_r, success
153 return buf;
154 } else if (((int)ret) == -1) {
155 //POSIX strerror_r, failure
156 // (Strictly, POSIX only guarantees a value other than 0. The safest
157 // way to implement this function is to use C++ and overload on the
158 // type of strerror_r to accurately distinguish GNU from POSIX. But
159 // realistic implementations will always return -1.)
160 snprintf(buf, buflen, "errno %d", errnum);
161 return buf;
162 } else {
163 //glibc strerror_r returning a string
164 return ret;
165 }
166}