• 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 #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* tmp;
83         const char* msg;
84         if (asprintf(&tmp,
85                      "Native registration unable to find class '%s'; aborting...",
86                      className) == -1) {
87             // Allocation failed, print default warning.
88             msg = "Native registration unable to find class; aborting...";
89         } else {
90             msg = tmp;
91         }
92         e->FatalError(msg);
93     }
94 
95     if ((*env)->RegisterNatives(e, c.get(), gMethods, numMethods) < 0) {
96         char* tmp;
97         const char* msg;
98         if (asprintf(&tmp, "RegisterNatives failed for '%s'; aborting...", className) == -1) {
99             // Allocation failed, print default warning.
100             msg = "RegisterNatives failed; aborting...";
101         } else {
102             msg = tmp;
103         }
104         e->FatalError(msg);
105     }
106 
107     return 0;
108 }
109 
110 /*
111  * Returns a human-readable summary of an exception object.  The buffer will
112  * be populated with the "binary" class name and, if present, the
113  * exception message.
114  */
getExceptionSummary(C_JNIEnv * env,jthrowable exception,std::string & result)115 static bool getExceptionSummary(C_JNIEnv* env, jthrowable exception, std::string& result) {
116     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
117 
118     /* get the name of the exception's class */
119     scoped_local_ref<jclass> exceptionClass(env, (*env)->GetObjectClass(e, exception)); // can't fail
120     scoped_local_ref<jclass> classClass(env,
121             (*env)->GetObjectClass(e, exceptionClass.get())); // java.lang.Class, can't fail
122     jmethodID classGetNameMethod =
123             (*env)->GetMethodID(e, classClass.get(), "getName", "()Ljava/lang/String;");
124     scoped_local_ref<jstring> classNameStr(env,
125             (jstring) (*env)->CallObjectMethod(e, exceptionClass.get(), classGetNameMethod));
126     if (classNameStr.get() == NULL) {
127         (*env)->ExceptionClear(e);
128         result = "<error getting class name>";
129         return false;
130     }
131     const char* classNameChars = (*env)->GetStringUTFChars(e, classNameStr.get(), NULL);
132     if (classNameChars == NULL) {
133         (*env)->ExceptionClear(e);
134         result = "<error getting class name UTF-8>";
135         return false;
136     }
137     result += classNameChars;
138     (*env)->ReleaseStringUTFChars(e, classNameStr.get(), classNameChars);
139 
140     /* if the exception has a detail message, get that */
141     jmethodID getMessage =
142             (*env)->GetMethodID(e, exceptionClass.get(), "getMessage", "()Ljava/lang/String;");
143     scoped_local_ref<jstring> messageStr(env,
144             (jstring) (*env)->CallObjectMethod(e, exception, getMessage));
145     if (messageStr.get() == NULL) {
146         return true;
147     }
148 
149     result += ": ";
150 
151     const char* messageChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL);
152     if (messageChars != NULL) {
153         result += messageChars;
154         (*env)->ReleaseStringUTFChars(e, messageStr.get(), messageChars);
155     } else {
156         result += "<error getting message>";
157         (*env)->ExceptionClear(e); // clear OOM
158     }
159 
160     return true;
161 }
162 
163 /*
164  * Returns an exception (with stack trace) as a string.
165  */
getStackTrace(C_JNIEnv * env,jthrowable exception,std::string & result)166 static bool getStackTrace(C_JNIEnv* env, jthrowable exception, std::string& result) {
167     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
168 
169     scoped_local_ref<jclass> stringWriterClass(env, findClass(env, "java/io/StringWriter"));
170     if (stringWriterClass.get() == NULL) {
171         return false;
172     }
173 
174     jmethodID stringWriterCtor = (*env)->GetMethodID(e, stringWriterClass.get(), "<init>", "()V");
175     jmethodID stringWriterToStringMethod =
176             (*env)->GetMethodID(e, stringWriterClass.get(), "toString", "()Ljava/lang/String;");
177 
178     scoped_local_ref<jclass> printWriterClass(env, findClass(env, "java/io/PrintWriter"));
179     if (printWriterClass.get() == NULL) {
180         return false;
181     }
182 
183     jmethodID printWriterCtor =
184             (*env)->GetMethodID(e, printWriterClass.get(), "<init>", "(Ljava/io/Writer;)V");
185 
186     scoped_local_ref<jobject> stringWriter(env,
187             (*env)->NewObject(e, stringWriterClass.get(), stringWriterCtor));
188     if (stringWriter.get() == NULL) {
189         return false;
190     }
191 
192     scoped_local_ref<jobject> printWriter(env,
193             (*env)->NewObject(e, printWriterClass.get(), printWriterCtor, stringWriter.get()));
194     if (printWriter.get() == NULL) {
195         return false;
196     }
197 
198     scoped_local_ref<jclass> exceptionClass(env, (*env)->GetObjectClass(e, exception)); // can't fail
199     jmethodID printStackTraceMethod =
200             (*env)->GetMethodID(e, exceptionClass.get(), "printStackTrace", "(Ljava/io/PrintWriter;)V");
201     (*env)->CallVoidMethod(e, exception, printStackTraceMethod, printWriter.get());
202 
203     if ((*env)->ExceptionCheck(e)) {
204         return false;
205     }
206 
207     scoped_local_ref<jstring> messageStr(env,
208             (jstring) (*env)->CallObjectMethod(e, stringWriter.get(), stringWriterToStringMethod));
209     if (messageStr.get() == NULL) {
210         return false;
211     }
212 
213     const char* utfChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL);
214     if (utfChars == NULL) {
215         return false;
216     }
217 
218     result = utfChars;
219 
220     (*env)->ReleaseStringUTFChars(e, messageStr.get(), utfChars);
221     return true;
222 }
223 
jniThrowException(C_JNIEnv * env,const char * className,const char * msg)224 extern "C" int jniThrowException(C_JNIEnv* env, const char* className, const char* msg) {
225     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
226 
227     if ((*env)->ExceptionCheck(e)) {
228         /* TODO: consider creating the new exception with this as "cause" */
229         scoped_local_ref<jthrowable> exception(env, (*env)->ExceptionOccurred(e));
230         (*env)->ExceptionClear(e);
231 
232         if (exception.get() != NULL) {
233             std::string text;
234             getExceptionSummary(env, exception.get(), text);
235             ALOGW("Discarding pending exception (%s) to throw %s", text.c_str(), className);
236         }
237     }
238 
239     scoped_local_ref<jclass> exceptionClass(env, findClass(env, className));
240     if (exceptionClass.get() == NULL) {
241         ALOGE("Unable to find exception class %s", className);
242         /* ClassNotFoundException now pending */
243         return -1;
244     }
245 
246     if ((*env)->ThrowNew(e, exceptionClass.get(), msg) != JNI_OK) {
247         ALOGE("Failed throwing '%s' '%s'", className, msg);
248         /* an exception, most likely OOM, will now be pending */
249         return -1;
250     }
251 
252     return 0;
253 }
254 
jniThrowExceptionFmt(C_JNIEnv * env,const char * className,const char * fmt,va_list args)255 int jniThrowExceptionFmt(C_JNIEnv* env, const char* className, const char* fmt, va_list args) {
256     char msgBuf[512];
257     vsnprintf(msgBuf, sizeof(msgBuf), fmt, args);
258     return jniThrowException(env, className, msgBuf);
259 }
260 
jniThrowNullPointerException(C_JNIEnv * env,const char * msg)261 int jniThrowNullPointerException(C_JNIEnv* env, const char* msg) {
262     return jniThrowException(env, "java/lang/NullPointerException", msg);
263 }
264 
jniThrowRuntimeException(C_JNIEnv * env,const char * msg)265 int jniThrowRuntimeException(C_JNIEnv* env, const char* msg) {
266     return jniThrowException(env, "java/lang/RuntimeException", msg);
267 }
268 
jniThrowIOException(C_JNIEnv * env,int errnum)269 int jniThrowIOException(C_JNIEnv* env, int errnum) {
270     char buffer[80];
271     const char* message = jniStrError(errnum, buffer, sizeof(buffer));
272     return jniThrowException(env, "java/io/IOException", message);
273 }
274 
jniGetStackTrace(C_JNIEnv * env,jthrowable exception)275 static std::string jniGetStackTrace(C_JNIEnv* env, jthrowable exception) {
276     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
277 
278     scoped_local_ref<jthrowable> currentException(env, (*env)->ExceptionOccurred(e));
279     if (exception == NULL) {
280         exception = currentException.get();
281         if (exception == NULL) {
282           return "<no pending exception>";
283         }
284     }
285 
286     if (currentException.get() != NULL) {
287         (*env)->ExceptionClear(e);
288     }
289 
290     std::string trace;
291     if (!getStackTrace(env, exception, trace)) {
292         (*env)->ExceptionClear(e);
293         getExceptionSummary(env, exception, trace);
294     }
295 
296     if (currentException.get() != NULL) {
297         (*env)->Throw(e, currentException.get()); // rethrow
298     }
299 
300     return trace;
301 }
302 
jniLogException(C_JNIEnv * env,int priority,const char * tag,jthrowable exception)303 void jniLogException(C_JNIEnv* env, int priority, const char* tag, jthrowable exception) {
304     std::string trace(jniGetStackTrace(env, exception));
305     __android_log_write(priority, tag, trace.c_str());
306 }
307 
jniStrError(int errnum,char * buf,size_t buflen)308 const char* jniStrError(int errnum, char* buf, size_t buflen) {
309 #if __GLIBC__
310     // Note: glibc has a nonstandard strerror_r that returns char* rather than POSIX's int.
311     // char *strerror_r(int errnum, char *buf, size_t n);
312     return strerror_r(errnum, buf, buflen);
313 #else
314     int rc = strerror_r(errnum, buf, buflen);
315     if (rc != 0) {
316         // (POSIX only guarantees a value other than 0. The safest
317         // way to implement this function is to use C++ and overload on the
318         // type of strerror_r to accurately distinguish GNU from POSIX.)
319         snprintf(buf, buflen, "errno %d", errnum);
320     }
321     return buf;
322 #endif
323 }
324 
jniCreateFileDescriptor(C_JNIEnv * env,int fd)325 jobject jniCreateFileDescriptor(C_JNIEnv* env, int fd) {
326     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
327     JniConstants::init(e);
328     static jmethodID ctor = e->GetMethodID(JniConstants::fileDescriptorClass, "<init>", "()V");
329     jobject fileDescriptor = (*env)->NewObject(e, JniConstants::fileDescriptorClass, ctor);
330     // NOTE: NewObject ensures that an OutOfMemoryError will be seen by the Java
331     // caller if the alloc fails, so we just return NULL when that happens.
332     if (fileDescriptor != NULL)  {
333         jniSetFileDescriptorOfFD(env, fileDescriptor, fd);
334     }
335     return fileDescriptor;
336 }
337 
jniGetFDFromFileDescriptor(C_JNIEnv * env,jobject fileDescriptor)338 int jniGetFDFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor) {
339     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
340     JniConstants::init(e);
341     static jfieldID fid = e->GetFieldID(JniConstants::fileDescriptorClass, "descriptor", "I");
342     if (fileDescriptor != NULL) {
343         return (*env)->GetIntField(e, fileDescriptor, fid);
344     } else {
345         return -1;
346     }
347 }
348 
jniSetFileDescriptorOfFD(C_JNIEnv * env,jobject fileDescriptor,int value)349 void jniSetFileDescriptorOfFD(C_JNIEnv* env, jobject fileDescriptor, int value) {
350     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
351     JniConstants::init(e);
352     static jfieldID fid = e->GetFieldID(JniConstants::fileDescriptorClass, "descriptor", "I");
353     (*env)->SetIntField(e, fileDescriptor, fid, value);
354 }
355 
jniGetReferent(C_JNIEnv * env,jobject ref)356 jobject jniGetReferent(C_JNIEnv* env, jobject ref) {
357     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
358     JniConstants::init(e);
359     static jmethodID get = e->GetMethodID(JniConstants::referenceClass, "get", "()Ljava/lang/Object;");
360     return (*env)->CallObjectMethod(e, ref, get);
361 }
362 
363