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 "JNIHelp.h"
20 
21 #include <android/log.h>
22 #include "log_compat.h"
23 
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <assert.h>
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 {
34 public:
scoped_local_ref(C_JNIEnv * env,T localRef=NULL)35     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     C_JNIEnv* mEnv;
57     T mLocalRef;
58 
59     // Disallow copy and assignment.
60     scoped_local_ref(const scoped_local_ref&);
61     void operator=(const scoped_local_ref&);
62 };
63 
findClass(C_JNIEnv * env,const char * className)64 static jclass findClass(C_JNIEnv* env, const char* className) {
65     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
66     return (*env)->FindClass(e, className);
67 }
68 
jniRegisterNativeMethods(C_JNIEnv * env,const char * className,const JNINativeMethod * gMethods,int numMethods)69 extern "C" int jniRegisterNativeMethods(C_JNIEnv* env, const char* className,
70     const JNINativeMethod* gMethods, int numMethods)
71 {
72     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
73 
74     ALOGV("Registering %s's %d native methods...", className, numMethods);
75 
76     scoped_local_ref<jclass> c(env, findClass(env, className));
77     if (c.get() == NULL) {
78         char* msg;
79         asprintf(&msg, "Native registration unable to find class '%s'; aborting...", className);
80         e->FatalError(msg);
81     }
82 
83     if ((*env)->RegisterNatives(e, c.get(), gMethods, numMethods) < 0) {
84         char* msg;
85         asprintf(&msg, "RegisterNatives failed for '%s'; aborting...", className);
86         e->FatalError(msg);
87     }
88 
89     return 0;
90 }
91 
jniThrowException(C_JNIEnv * c_env,const char * className,const char * msg)92 extern "C" int jniThrowException(C_JNIEnv* c_env, const char* className, const char* msg) {
93     JNIEnv* env = reinterpret_cast<JNIEnv*>(c_env);
94     jclass exceptionClass = env->FindClass(className);
95 
96     if (exceptionClass == NULL) {
97         ALOGD("Unable to find exception class %s", className);
98         /* ClassNotFoundException now pending */
99         return -1;
100     }
101 
102     if (env->ThrowNew(exceptionClass, msg) != JNI_OK) {
103         ALOGD("Failed throwing '%s' '%s'", className, msg);
104         /* an exception, most likely OOM, will now be pending */
105         return -1;
106     }
107 
108     env->DeleteLocalRef(exceptionClass);
109     return 0;
110 }
111 
jniThrowExceptionFmt(C_JNIEnv * env,const char * className,const char * fmt,va_list args)112 int jniThrowExceptionFmt(C_JNIEnv* env, const char* className, const char* fmt, va_list args) {
113     char msgBuf[512];
114     vsnprintf(msgBuf, sizeof(msgBuf), fmt, args);
115     return jniThrowException(env, className, msgBuf);
116 }
117 
jniThrowNullPointerException(C_JNIEnv * env,const char * msg)118 int jniThrowNullPointerException(C_JNIEnv* env, const char* msg) {
119     return jniThrowException(env, "java/lang/NullPointerException", msg);
120 }
121 
jniThrowRuntimeException(C_JNIEnv * env,const char * msg)122 int jniThrowRuntimeException(C_JNIEnv* env, const char* msg) {
123     return jniThrowException(env, "java/lang/RuntimeException", msg);
124 }
125 
jniThrowIOException(C_JNIEnv * env,int errnum)126 int jniThrowIOException(C_JNIEnv* env, int errnum) {
127     char buffer[80];
128     const char* message = jniStrError(errnum, buffer, sizeof(buffer));
129     return jniThrowException(env, "java/io/IOException", message);
130 }
131 
jniStrError(int errnum,char * buf,size_t buflen)132 const char* jniStrError(int errnum, char* buf, size_t buflen) {
133 #if __GLIBC__
134     // Note: glibc has a nonstandard strerror_r that returns char* rather than POSIX's int.
135     // char *strerror_r(int errnum, char *buf, size_t n);
136     return strerror_r(errnum, buf, buflen);
137 #else
138     int rc = strerror_r(errnum, buf, buflen);
139     if (rc != 0) {
140         // (POSIX only guarantees a value other than 0. The safest
141         // way to implement this function is to use C++ and overload on the
142         // type of strerror_r to accurately distinguish GNU from POSIX.)
143         snprintf(buf, buflen, "errno %d", errnum);
144     }
145     return buf;
146 #endif
147 }
148 
jniGetFDFromFileDescriptor(C_JNIEnv * env,jobject fileDescriptor)149 int jniGetFDFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor) {
150     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
151     scoped_local_ref<jclass> localClass(env, e->FindClass("java/io/FileDescriptor"));
152     static jfieldID fid = e->GetFieldID(localClass.get(), "descriptor", "I");
153     if (fileDescriptor != NULL) {
154         return (*env)->GetIntField(e, fileDescriptor, fid);
155     } else {
156         return -1;
157     }
158 }
159