Home | History | Annotate | Download | only in include
      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:
     33     JniUtil() {}
     34     ~JniUtil() {}
     35 
     36 public:
     37     /**
     38      * Obtains the current thread's JNIEnv
     39      */
     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_