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_