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