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