| /* |
| * Copyright (C) 2010 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #define LOG_TAG "JniConstants" |
| #include "ALog-priv.h" |
| |
| #include "JniConstants.h" |
| |
| #include <atomic> |
| #include <mutex> |
| #include <string> |
| |
| #include "nativehelper/ScopedLocalRef.h" |
| |
| namespace { |
| |
| // Mutex protecting the initialization of cached class references. |
| std::mutex g_class_refs_mutex; |
| |
| // Atomic boolean flag for double locked checking that class references are |
| // initialized before use. |
| std::atomic<bool> g_class_refs_initialized(false); |
| |
| // Cached global references to class instances. |
| // |
| // These are GC heap references that are initialized under the protection of |
| // |g_class_refs_mutex| as they should only be initialized once to avoid losing a |
| // global reference. Initialization happens lazily when an accessor tries to |
| // retrieve one of these classes. |
| |
| jclass g_file_descriptor_class = nullptr; // java.io.FileDescriptor |
| jclass g_nio_access_class = nullptr; // java.nio.Access |
| jclass g_nio_buffer_class = nullptr; // java.nio.Buffer |
| jclass g_reference_class = nullptr; // java.lang.ref.Reference |
| jclass g_string_class = nullptr; // java.lang.String |
| |
| // Cached field and method ids. |
| // |
| // These are non-GC heap values. They are initialized lazily and racily. We |
| // avoid holding a mutex here because the JNI API supports concurrent calls to |
| // Get{Field,Method}ID and also because finding an id may recursively call into |
| // Get{Field,Method}ID. |
| // |
| // The recursion issue occurs here for the fields in the FileDescriptor class |
| // since retrieving a field id requires the class to be initialized. Class |
| // initialization leads to the initialization of static fields. The |
| // FileDescriptor class has static fields that are FileDescriptor instances. The |
| // initialization of these static FileDescriptor fields follows a convoluted |
| // path that that leads to a call to jniGetFDFromFileDescriptor() which then |
| // needs to call GetFieldID() which is in the call stack. If thread-safety were |
| // desirable here, a recursive mutex would be required. |
| // |
| // These field and method ids have default values of nullptr. They are reset |
| // back to nullptr in JniConstants::Uninitialize(), along with the class |
| // references, when a new runtime instance is created via JNI_CreateJavaVM(). The |
| // reset happens before the new runtime instance is returned to the caller and |
| // under the protection of the |g_class_refs_mutex|. |
| |
| jfieldID g_file_descriptor_descriptor_field = nullptr; // java.io.FileDescriptor.descriptor |
| jfieldID g_file_descriptor_owner_id_field = nullptr; // java.io.FileDescriptor.ownerId |
| jmethodID g_file_descriptor_init_method = nullptr; // void java.io.FileDescriptor.<init>() |
| jmethodID g_nio_access_get_base_array_method = nullptr; // Object java.nio.NIOAccess.getBaseArray() |
| jmethodID g_nio_access_get_base_array_offset_method = nullptr; // Object java.nio.NIOAccess.getBaseArray() |
| jfieldID g_nio_buffer_address_field = nullptr; // long java.nio.Buffer.address |
| jfieldID g_nio_buffer_element_size_shift_field = nullptr; // int java.nio.Buffer._elementSizeShift |
| jfieldID g_nio_buffer_limit_field = nullptr; // int java.nio.Buffer.limit |
| jfieldID g_nio_buffer_position_field = nullptr; // int java.nio.Buffer.position |
| jmethodID g_nio_buffer_array_method = nullptr; // Object java.nio.Buffer.array() |
| jmethodID g_nio_buffer_array_offset_method = nullptr; // int java.nio.Buffer.arrayOffset() |
| jmethodID g_reference_get_method = nullptr; // Object java.lang.ref.Reference.get() |
| |
| jclass FindClass(JNIEnv* env, const char* name) { |
| ScopedLocalRef<jclass> klass(env, env->FindClass(name)); |
| ALOG_ALWAYS_FATAL_IF(klass.get() == nullptr, "failed to find class '%s'", name); |
| return reinterpret_cast<jclass>(env->NewGlobalRef(klass.get())); |
| } |
| |
| jfieldID FindField(JNIEnv* env, jclass klass, const char* name, const char* desc) { |
| jfieldID result = env->GetFieldID(klass, name, desc); |
| ALOG_ALWAYS_FATAL_IF(result == nullptr, "failed to find field '%s:%s'", name, desc); |
| return result; |
| } |
| |
| jmethodID FindMethod(JNIEnv* env, jclass klass, const char* name, const char* signature) { |
| jmethodID result = env->GetMethodID(klass, name, signature); |
| ALOG_ALWAYS_FATAL_IF(result == nullptr, "failed to find method '%s%s'", name, signature); |
| return result; |
| } |
| |
| jmethodID FindStaticMethod(JNIEnv* env, jclass klass, const char* name, const char* signature) { |
| jmethodID result = env->GetStaticMethodID(klass, name, signature); |
| ALOG_ALWAYS_FATAL_IF(result == nullptr, "failed to find static method '%s%s'", name, signature); |
| return result; |
| } |
| |
| } // namespace |
| |
| jclass JniConstants::GetFileDescriptorClass(JNIEnv* env) { |
| EnsureClassReferencesInitialized(env); |
| return g_file_descriptor_class; |
| } |
| |
| jclass JniConstants::GetNioAccessClass(JNIEnv* env) { |
| EnsureClassReferencesInitialized(env); |
| return g_nio_access_class; |
| } |
| |
| jclass JniConstants::GetNioBufferClass(JNIEnv* env) { |
| EnsureClassReferencesInitialized(env); |
| return g_nio_buffer_class; |
| } |
| |
| jclass JniConstants::GetReferenceClass(JNIEnv* env) { |
| EnsureClassReferencesInitialized(env); |
| return g_reference_class; |
| } |
| |
| jclass JniConstants::GetStringClass(JNIEnv* env) { |
| EnsureClassReferencesInitialized(env); |
| return g_string_class; |
| } |
| |
| jfieldID JniConstants::GetFileDescriptorDescriptorField(JNIEnv* env) { |
| if (g_file_descriptor_descriptor_field == nullptr) { |
| jclass klass = GetFileDescriptorClass(env); |
| g_file_descriptor_descriptor_field = FindField(env, klass, "descriptor", "I"); |
| } |
| return g_file_descriptor_descriptor_field; |
| } |
| |
| jfieldID JniConstants::GetFileDescriptorOwnerIdField(JNIEnv* env) { |
| if (g_file_descriptor_owner_id_field == nullptr) { |
| jclass klass = GetFileDescriptorClass(env); |
| g_file_descriptor_owner_id_field = FindField(env, klass, "ownerId", "J"); |
| } |
| return g_file_descriptor_owner_id_field; |
| } |
| |
| jmethodID JniConstants::GetFileDescriptorInitMethod(JNIEnv* env) { |
| if (g_file_descriptor_init_method == nullptr) { |
| jclass klass = GetFileDescriptorClass(env); |
| g_file_descriptor_init_method = FindMethod(env, klass, "<init>", "()V"); |
| } |
| return g_file_descriptor_init_method; |
| } |
| |
| jmethodID JniConstants::GetNioAccessGetBaseArrayMethod(JNIEnv* env) { |
| if (g_nio_access_get_base_array_method == nullptr) { |
| jclass klass = GetNioAccessClass(env); |
| g_nio_access_get_base_array_method = |
| FindStaticMethod(env, klass, "getBaseArray", |
| "(Ljava/nio/Buffer;)Ljava/lang/Object;"); |
| } |
| return g_nio_access_get_base_array_method; |
| } |
| |
| jmethodID JniConstants::GetNioAccessGetBaseArrayOffsetMethod(JNIEnv* env) { |
| if (g_nio_access_get_base_array_offset_method == nullptr) { |
| jclass klass = GetNioAccessClass(env); |
| g_nio_access_get_base_array_offset_method = |
| FindStaticMethod(env, klass, "getBaseArrayOffset", "(Ljava/nio/Buffer;)I"); |
| } |
| return g_nio_access_get_base_array_offset_method; |
| } |
| |
| jfieldID JniConstants::GetNioBufferAddressField(JNIEnv* env) { |
| if (g_nio_buffer_address_field == nullptr) { |
| jclass klass = GetNioBufferClass(env); |
| g_nio_buffer_address_field = FindField(env, klass, "address", "J"); |
| } |
| return g_nio_buffer_address_field; |
| } |
| |
| jfieldID JniConstants::GetNioBufferElementSizeShiftField(JNIEnv* env) { |
| if (g_nio_buffer_element_size_shift_field == nullptr) { |
| jclass klass = GetNioBufferClass(env); |
| g_nio_buffer_element_size_shift_field = FindField(env, klass, "_elementSizeShift", "I"); |
| } |
| return g_nio_buffer_element_size_shift_field; |
| } |
| |
| jfieldID JniConstants::GetNioBufferLimitField(JNIEnv* env) { |
| if (g_nio_buffer_limit_field == nullptr) { |
| jclass klass = GetNioBufferClass(env); |
| g_nio_buffer_limit_field = FindField(env, klass, "limit", "I"); |
| } |
| return g_nio_buffer_limit_field; |
| } |
| |
| jfieldID JniConstants::GetNioBufferPositionField(JNIEnv* env) { |
| if (g_nio_buffer_position_field == nullptr) { |
| jclass klass = GetNioBufferClass(env); |
| g_nio_buffer_position_field = FindField(env, klass, "position", "I"); |
| } |
| return g_nio_buffer_position_field; |
| } |
| |
| jmethodID JniConstants::GetNioBufferArrayMethod(JNIEnv* env) { |
| if (g_nio_buffer_array_method == nullptr) { |
| jclass klass = GetNioBufferClass(env); |
| g_nio_buffer_array_method = FindMethod(env, klass, "array", "()Ljava/lang/Object;"); |
| } |
| return g_nio_buffer_array_method; |
| } |
| |
| jmethodID JniConstants::GetNioBufferArrayOffsetMethod(JNIEnv* env) { |
| if (g_nio_buffer_array_offset_method == nullptr) { |
| jclass klass = GetNioBufferClass(env); |
| g_nio_buffer_array_offset_method = FindMethod(env, klass, "arrayOffset", "()I"); |
| } |
| return g_nio_buffer_array_offset_method; |
| } |
| |
| jmethodID JniConstants::GetReferenceGetMethod(JNIEnv* env) { |
| if (g_reference_get_method == nullptr) { |
| jclass klass = GetReferenceClass(env); |
| g_reference_get_method = FindMethod(env, klass, "get", "()Ljava/lang/Object;"); |
| } |
| return g_reference_get_method; |
| } |
| |
| void JniConstants::EnsureClassReferencesInitialized(JNIEnv* env) { |
| // Fast check if class references are initialized. |
| if (g_class_refs_initialized.load(std::memory_order_acquire)) { |
| return; |
| } |
| |
| // Slower check with initialization if necessary. |
| std::lock_guard<std::mutex> guard(g_class_refs_mutex); |
| if (g_class_refs_initialized.load(std::memory_order_relaxed)) { |
| return; |
| } |
| |
| // Class constants should be initialized only once because they global |
| // references. Field ids and Method ids can be initialized later since they |
| // are not references and races only have trivial performance |
| // consequences. |
| g_file_descriptor_class = FindClass(env, "java/io/FileDescriptor"); |
| g_nio_access_class = FindClass(env, "java/nio/NIOAccess"); |
| g_nio_buffer_class = FindClass(env, "java/nio/Buffer"); |
| g_reference_class = FindClass(env, "java/lang/ref/Reference"); |
| g_string_class = FindClass(env, "java/lang/String"); |
| g_class_refs_initialized.store(true, std::memory_order_release); |
| } |
| |
| void JniConstants::Uninitialize() { |
| // This method is called when a new runtime instance is created. There is no |
| // notification of a runtime instance being destroyed in the JNI interface |
| // so we piggyback on creation. Since only one runtime is supported at a |
| // time, we know the constants are invalid when JNI_CreateJavaVM() is |
| // called. |
| // |
| // Clean shutdown would require calling DeleteGlobalRef() for each of the |
| // class references. |
| std::lock_guard<std::mutex> guard(g_class_refs_mutex); |
| g_file_descriptor_class = nullptr; |
| g_file_descriptor_descriptor_field = nullptr; |
| g_file_descriptor_owner_id_field = nullptr; |
| g_file_descriptor_init_method = nullptr; |
| g_nio_access_class = nullptr; |
| g_nio_access_get_base_array_method = nullptr; |
| g_nio_access_get_base_array_offset_method = nullptr; |
| g_nio_buffer_class = nullptr; |
| g_nio_buffer_address_field = nullptr; |
| g_nio_buffer_element_size_shift_field = nullptr; |
| g_nio_buffer_limit_field = nullptr; |
| g_nio_buffer_position_field = nullptr; |
| g_nio_buffer_array_method = nullptr; |
| g_nio_buffer_array_offset_method = nullptr; |
| g_reference_class = nullptr; |
| g_reference_get_method = nullptr; |
| g_string_class = nullptr; |
| g_class_refs_initialized.store(false, std::memory_order_release); |
| } |