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