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