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