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