• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 #ifndef CONSCRYPT_JNIUTIL_H_
18 #define CONSCRYPT_JNIUTIL_H_
19 
20 #include <jni.h>
21 #include <cstdlib>
22 #include "ScopedLocalRef.h"
23 #include "compat.h"
24 #include "macros.h"
25 
26 namespace conscrypt {
27 
28 /**
29  * Utility methods for working with JNI.
30  */
31 class JniUtil {
32 private:
JniUtil()33     JniUtil() {}
~JniUtil()34     ~JniUtil() {}
35 
36 public:
37     /**
38      * Obtains the current thread's JNIEnv
39      */
getJNIEnv(JavaVM * gJavaVM)40     static inline JNIEnv* getJNIEnv(JavaVM* gJavaVM) {
41         JNIEnv* env;
42 #ifdef ANDROID
43         if (gJavaVM->AttachCurrentThread(&env, nullptr) < 0) {
44 #else
45         if (gJavaVM->AttachCurrentThread(reinterpret_cast<void**>(&env), nullptr) < 0) {
46 #endif
47             ALOGE("Could not attach JavaVM to find current JNIEnv");
48             return nullptr;
49         }
50         return env;
51     }
52 
53     static inline jclass getGlobalRefToClass(JNIEnv* env, const char* className) {
54         ScopedLocalRef<jclass> localClass(env, env->FindClass(className));
55         jclass globalRef = reinterpret_cast<jclass>(env->NewGlobalRef(localClass.get()));
56         if (globalRef == nullptr) {
57             ALOGE("failed to find class %s", className);
58             abort();
59         }
60         return globalRef;
61     }
62 
63     static inline jmethodID getMethodRef(JNIEnv* env, jclass clazz, const char* name,
64                                          const char* sig) {
65         jmethodID localMethod = env->GetMethodID(clazz, name, sig);
66         if (localMethod == nullptr) {
67             ALOGE("could not find method %s", name);
68             abort();
69         }
70         return localMethod;
71     }
72 
73     static inline jfieldID getFieldRef(JNIEnv* env, jclass clazz, const char* name,
74                                        const char* sig) {
75         jfieldID localField = env->GetFieldID(clazz, name, sig);
76         if (localField == nullptr) {
77             ALOGE("could not find field %s", name);
78             abort();
79         }
80         return localField;
81     }
82 
83     static inline jclass findClass(JNIEnv* env, const char* name) {
84         ScopedLocalRef<jclass> localClass(env, env->FindClass(name));
85         jclass result = reinterpret_cast<jclass>(env->NewGlobalRef(localClass.get()));
86         if (result == nullptr) {
87             ALOGE("failed to find class '%s'", name);
88             abort();
89         }
90         return result;
91     }
92 
93     /**
94      * Register one or more native methods with a particular class.
95      * "className" looks like "java/lang/String". Aborts on failure.
96      */
97     static void jniRegisterNativeMethods(JNIEnv* env, const char* className,
98                                         const JNINativeMethod* gMethods, int numMethods) {
99         ALOGV("Registering %s's %d native methods...", className, numMethods);
100 
101         ScopedLocalRef<jclass> c(env, env->FindClass(className));
102         if (c.get() == nullptr) {
103             char* msg;
104             (void)asprintf(&msg, "Native registration unable to find class '%s'; aborting...",
105                            className);
106             env->FatalError(msg);
107         }
108 
109         if (env->RegisterNatives(c.get(), gMethods, numMethods) < 0) {
110             char* msg;
111             (void)asprintf(&msg, "RegisterNatives failed for '%s'; aborting...", className);
112             env->FatalError(msg);
113         }
114     }
115 
116     /**
117      * Returns the int fd from a java.io.FileDescriptor.
118      */
119     static inline int jniGetFDFromFileDescriptor(JNIEnv* env, jobject fileDescriptor) {
120         ScopedLocalRef<jclass> localClass(env, env->FindClass("java/io/FileDescriptor"));
121 #if defined(ANDROID) && !defined(CONSCRYPT_OPENJDK)
122         static jfieldID fid = env->GetFieldID(localClass.get(), "descriptor", "I");
123 #else /* !ANDROID || CONSCRYPT_OPENJDK */
124         static jfieldID fid = env->GetFieldID(localClass.get(), "fd", "I");
125 #endif
126         if (fileDescriptor != nullptr) {
127             return env->GetIntField(fileDescriptor, fid);
128         } else {
129             return -1;
130         }
131     }
132 
133     /**
134      * Returns true if the VM's JNI GetByteArrayElements method is likely to create a copy when
135      * invoked on an array of the provided size.
136      */
137     static inline bool isGetByteArrayElementsLikelyToReturnACopy(size_t size) {
138 #if defined(ANDROID) && !defined(CONSCRYPT_OPENJDK)
139         // ART's GetByteArrayElements creates copies only for arrays smaller than 12 kB.
140         return size <= 12 * 1024;
141 #else
142         (void)size;
143         // On OpenJDK based VMs GetByteArrayElements appears to always create a copy.
144         return true;
145 #endif
146     }
147 };
148 
149 }  // namespace conscrypt
150 
151 #endif  // CONSCRYPT_JNIUTIL_H_