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 #if defined(__ANDROID__)
18 /* libnativehelper is built by NDK 19 in one variant, which doesn't yet have the GNU strerror_r. */
19 #undef _GNU_SOURCE
20 #endif
21
22 #define LOG_TAG "JNIHelp"
23
24 #include "JniConstants.h"
25 #include "JNIHelp.h"
26 #include "ALog-priv.h"
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <assert.h>
32
33 #include <string>
34
35 /**
36 * Equivalent to ScopedLocalRef, but for C_JNIEnv instead. (And slightly more powerful.)
37 */
38 template<typename T>
39 class scoped_local_ref {
40 public:
scoped_local_ref(C_JNIEnv * env,T localRef=NULL)41 scoped_local_ref(C_JNIEnv* env, T localRef = NULL)
42 : mEnv(env), mLocalRef(localRef)
43 {
44 }
45
~scoped_local_ref()46 ~scoped_local_ref() {
47 reset();
48 }
49
reset(T localRef=NULL)50 void reset(T localRef = NULL) {
51 if (mLocalRef != NULL) {
52 (*mEnv)->DeleteLocalRef(reinterpret_cast<JNIEnv*>(mEnv), mLocalRef);
53 mLocalRef = localRef;
54 }
55 }
56
get() const57 T get() const {
58 return mLocalRef;
59 }
60
61 private:
62 C_JNIEnv* const mEnv;
63 T mLocalRef;
64
65 DISALLOW_COPY_AND_ASSIGN(scoped_local_ref);
66 };
67
findClass(C_JNIEnv * env,const char * className)68 static jclass findClass(C_JNIEnv* env, const char* className) {
69 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
70 return (*env)->FindClass(e, className);
71 }
72
jniRegisterNativeMethods(C_JNIEnv * env,const char * className,const JNINativeMethod * gMethods,int numMethods)73 extern "C" int jniRegisterNativeMethods(C_JNIEnv* env, const char* className,
74 const JNINativeMethod* gMethods, int numMethods)
75 {
76 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
77
78 ALOGV("Registering %s's %d native methods...", className, numMethods);
79
80 scoped_local_ref<jclass> c(env, findClass(env, className));
81 if (c.get() == NULL) {
82 char* msg;
83 asprintf(&msg, "Native registration unable to find class '%s'; aborting...", className);
84 e->FatalError(msg);
85 }
86
87 if ((*env)->RegisterNatives(e, c.get(), gMethods, numMethods) < 0) {
88 char* msg;
89 asprintf(&msg, "RegisterNatives failed for '%s'; aborting...", className);
90 e->FatalError(msg);
91 }
92
93 return 0;
94 }
95
96 /*
97 * Returns a human-readable summary of an exception object. The buffer will
98 * be populated with the "binary" class name and, if present, the
99 * exception message.
100 */
getExceptionSummary(C_JNIEnv * env,jthrowable exception,std::string & result)101 static bool getExceptionSummary(C_JNIEnv* env, jthrowable exception, std::string& result) {
102 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
103
104 /* get the name of the exception's class */
105 scoped_local_ref<jclass> exceptionClass(env, (*env)->GetObjectClass(e, exception)); // can't fail
106 scoped_local_ref<jclass> classClass(env,
107 (*env)->GetObjectClass(e, exceptionClass.get())); // java.lang.Class, can't fail
108 jmethodID classGetNameMethod =
109 (*env)->GetMethodID(e, classClass.get(), "getName", "()Ljava/lang/String;");
110 scoped_local_ref<jstring> classNameStr(env,
111 (jstring) (*env)->CallObjectMethod(e, exceptionClass.get(), classGetNameMethod));
112 if (classNameStr.get() == NULL) {
113 (*env)->ExceptionClear(e);
114 result = "<error getting class name>";
115 return false;
116 }
117 const char* classNameChars = (*env)->GetStringUTFChars(e, classNameStr.get(), NULL);
118 if (classNameChars == NULL) {
119 (*env)->ExceptionClear(e);
120 result = "<error getting class name UTF-8>";
121 return false;
122 }
123 result += classNameChars;
124 (*env)->ReleaseStringUTFChars(e, classNameStr.get(), classNameChars);
125
126 /* if the exception has a detail message, get that */
127 jmethodID getMessage =
128 (*env)->GetMethodID(e, exceptionClass.get(), "getMessage", "()Ljava/lang/String;");
129 scoped_local_ref<jstring> messageStr(env,
130 (jstring) (*env)->CallObjectMethod(e, exception, getMessage));
131 if (messageStr.get() == NULL) {
132 return true;
133 }
134
135 result += ": ";
136
137 const char* messageChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL);
138 if (messageChars != NULL) {
139 result += messageChars;
140 (*env)->ReleaseStringUTFChars(e, messageStr.get(), messageChars);
141 } else {
142 result += "<error getting message>";
143 (*env)->ExceptionClear(e); // clear OOM
144 }
145
146 return true;
147 }
148
149 /*
150 * Returns an exception (with stack trace) as a string.
151 */
getStackTrace(C_JNIEnv * env,jthrowable exception,std::string & result)152 static bool getStackTrace(C_JNIEnv* env, jthrowable exception, std::string& result) {
153 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
154
155 scoped_local_ref<jclass> stringWriterClass(env, findClass(env, "java/io/StringWriter"));
156 if (stringWriterClass.get() == NULL) {
157 return false;
158 }
159
160 jmethodID stringWriterCtor = (*env)->GetMethodID(e, stringWriterClass.get(), "<init>", "()V");
161 jmethodID stringWriterToStringMethod =
162 (*env)->GetMethodID(e, stringWriterClass.get(), "toString", "()Ljava/lang/String;");
163
164 scoped_local_ref<jclass> printWriterClass(env, findClass(env, "java/io/PrintWriter"));
165 if (printWriterClass.get() == NULL) {
166 return false;
167 }
168
169 jmethodID printWriterCtor =
170 (*env)->GetMethodID(e, printWriterClass.get(), "<init>", "(Ljava/io/Writer;)V");
171
172 scoped_local_ref<jobject> stringWriter(env,
173 (*env)->NewObject(e, stringWriterClass.get(), stringWriterCtor));
174 if (stringWriter.get() == NULL) {
175 return false;
176 }
177
178 scoped_local_ref<jobject> printWriter(env,
179 (*env)->NewObject(e, printWriterClass.get(), printWriterCtor, stringWriter.get()));
180 if (printWriter.get() == NULL) {
181 return false;
182 }
183
184 scoped_local_ref<jclass> exceptionClass(env, (*env)->GetObjectClass(e, exception)); // can't fail
185 jmethodID printStackTraceMethod =
186 (*env)->GetMethodID(e, exceptionClass.get(), "printStackTrace", "(Ljava/io/PrintWriter;)V");
187 (*env)->CallVoidMethod(e, exception, printStackTraceMethod, printWriter.get());
188
189 if ((*env)->ExceptionCheck(e)) {
190 return false;
191 }
192
193 scoped_local_ref<jstring> messageStr(env,
194 (jstring) (*env)->CallObjectMethod(e, stringWriter.get(), stringWriterToStringMethod));
195 if (messageStr.get() == NULL) {
196 return false;
197 }
198
199 const char* utfChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL);
200 if (utfChars == NULL) {
201 return false;
202 }
203
204 result = utfChars;
205
206 (*env)->ReleaseStringUTFChars(e, messageStr.get(), utfChars);
207 return true;
208 }
209
jniThrowException(C_JNIEnv * env,const char * className,const char * msg)210 extern "C" int jniThrowException(C_JNIEnv* env, const char* className, const char* msg) {
211 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
212
213 if ((*env)->ExceptionCheck(e)) {
214 /* TODO: consider creating the new exception with this as "cause" */
215 scoped_local_ref<jthrowable> exception(env, (*env)->ExceptionOccurred(e));
216 (*env)->ExceptionClear(e);
217
218 if (exception.get() != NULL) {
219 std::string text;
220 getExceptionSummary(env, exception.get(), text);
221 ALOGW("Discarding pending exception (%s) to throw %s", text.c_str(), className);
222 }
223 }
224
225 scoped_local_ref<jclass> exceptionClass(env, findClass(env, className));
226 if (exceptionClass.get() == NULL) {
227 ALOGE("Unable to find exception class %s", className);
228 /* ClassNotFoundException now pending */
229 return -1;
230 }
231
232 if ((*env)->ThrowNew(e, exceptionClass.get(), msg) != JNI_OK) {
233 ALOGE("Failed throwing '%s' '%s'", className, msg);
234 /* an exception, most likely OOM, will now be pending */
235 return -1;
236 }
237
238 return 0;
239 }
240
jniThrowExceptionFmt(C_JNIEnv * env,const char * className,const char * fmt,va_list args)241 int jniThrowExceptionFmt(C_JNIEnv* env, const char* className, const char* fmt, va_list args) {
242 char msgBuf[512];
243 vsnprintf(msgBuf, sizeof(msgBuf), fmt, args);
244 return jniThrowException(env, className, msgBuf);
245 }
246
jniThrowNullPointerException(C_JNIEnv * env,const char * msg)247 int jniThrowNullPointerException(C_JNIEnv* env, const char* msg) {
248 return jniThrowException(env, "java/lang/NullPointerException", msg);
249 }
250
jniThrowRuntimeException(C_JNIEnv * env,const char * msg)251 int jniThrowRuntimeException(C_JNIEnv* env, const char* msg) {
252 return jniThrowException(env, "java/lang/RuntimeException", msg);
253 }
254
jniThrowIOException(C_JNIEnv * env,int errnum)255 int jniThrowIOException(C_JNIEnv* env, int errnum) {
256 char buffer[80];
257 const char* message = jniStrError(errnum, buffer, sizeof(buffer));
258 return jniThrowException(env, "java/io/IOException", message);
259 }
260
jniGetStackTrace(C_JNIEnv * env,jthrowable exception)261 static std::string jniGetStackTrace(C_JNIEnv* env, jthrowable exception) {
262 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
263
264 scoped_local_ref<jthrowable> currentException(env, (*env)->ExceptionOccurred(e));
265 if (exception == NULL) {
266 exception = currentException.get();
267 if (exception == NULL) {
268 return "<no pending exception>";
269 }
270 }
271
272 if (currentException.get() != NULL) {
273 (*env)->ExceptionClear(e);
274 }
275
276 std::string trace;
277 if (!getStackTrace(env, exception, trace)) {
278 (*env)->ExceptionClear(e);
279 getExceptionSummary(env, exception, trace);
280 }
281
282 if (currentException.get() != NULL) {
283 (*env)->Throw(e, currentException.get()); // rethrow
284 }
285
286 return trace;
287 }
288
jniLogException(C_JNIEnv * env,int priority,const char * tag,jthrowable exception)289 void jniLogException(C_JNIEnv* env, int priority, const char* tag, jthrowable exception) {
290 std::string trace(jniGetStackTrace(env, exception));
291 __android_log_write(priority, tag, trace.c_str());
292 }
293
jniStrError(int errnum,char * buf,size_t buflen)294 const char* jniStrError(int errnum, char* buf, size_t buflen) {
295 #if __GLIBC__
296 // Note: glibc has a nonstandard strerror_r that returns char* rather than POSIX's int.
297 // char *strerror_r(int errnum, char *buf, size_t n);
298 return strerror_r(errnum, buf, buflen);
299 #else
300 int rc = strerror_r(errnum, buf, buflen);
301 if (rc != 0) {
302 // (POSIX only guarantees a value other than 0. The safest
303 // way to implement this function is to use C++ and overload on the
304 // type of strerror_r to accurately distinguish GNU from POSIX.)
305 snprintf(buf, buflen, "errno %d", errnum);
306 }
307 return buf;
308 #endif
309 }
310
jniCreateFileDescriptor(C_JNIEnv * env,int fd)311 jobject jniCreateFileDescriptor(C_JNIEnv* env, int fd) {
312 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
313 static jmethodID ctor = e->GetMethodID(JniConstants::fileDescriptorClass, "<init>", "()V");
314 jobject fileDescriptor = (*env)->NewObject(e, JniConstants::fileDescriptorClass, ctor);
315 // NOTE: NewObject ensures that an OutOfMemoryError will be seen by the Java
316 // caller if the alloc fails, so we just return NULL when that happens.
317 if (fileDescriptor != NULL) {
318 jniSetFileDescriptorOfFD(env, fileDescriptor, fd);
319 }
320 return fileDescriptor;
321 }
322
jniGetFDFromFileDescriptor(C_JNIEnv * env,jobject fileDescriptor)323 int jniGetFDFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor) {
324 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
325 static jfieldID fid = e->GetFieldID(JniConstants::fileDescriptorClass, "descriptor", "I");
326 if (fileDescriptor != NULL) {
327 return (*env)->GetIntField(e, fileDescriptor, fid);
328 } else {
329 return -1;
330 }
331 }
332
jniSetFileDescriptorOfFD(C_JNIEnv * env,jobject fileDescriptor,int value)333 void jniSetFileDescriptorOfFD(C_JNIEnv* env, jobject fileDescriptor, int value) {
334 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
335 static jfieldID fid = e->GetFieldID(JniConstants::fileDescriptorClass, "descriptor", "I");
336 (*env)->SetIntField(e, fileDescriptor, fid, value);
337 }
338
jniGetReferent(C_JNIEnv * env,jobject ref)339 jobject jniGetReferent(C_JNIEnv* env, jobject ref) {
340 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
341 static jmethodID get = e->GetMethodID(JniConstants::referenceClass, "get", "()Ljava/lang/Object;");
342 return (*env)->CallObjectMethod(e, ref, get);
343 }
344
345