1 /* 2 * Copyright (C) 2010 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 "JniConstants" 18 #include "ALog-priv.h" 19 20 #include "JniConstants.h" 21 22 #include <atomic> 23 #include <mutex> 24 #include <string> 25 26 #include "nativehelper/ScopedLocalRef.h" 27 28 namespace { 29 30 // Mutex protecting the initialization of cached class references. 31 std::mutex g_class_refs_mutex; 32 33 // Atomic boolean flag for double locked checking that class references are 34 // initialized before use. 35 std::atomic<bool> g_class_refs_initialized(false); 36 37 // Cached global references to class instances. 38 // 39 // These are GC heap references that are initialized under the protection of 40 // |g_class_refs_mutex| as they should only be initialized once to avoid losing a 41 // global reference. Initialization happens lazily when an accessor tries to 42 // retrieve one of these classes. 43 44 jclass g_file_descriptor_class = nullptr; // java.io.FileDescriptor 45 jclass g_nio_access_class = nullptr; // java.nio.Access 46 jclass g_nio_buffer_class = nullptr; // java.nio.Buffer 47 jclass g_reference_class = nullptr; // java.lang.ref.Reference 48 jclass g_string_class = nullptr; // java.lang.String 49 50 // Cached field and method ids. 51 // 52 // These are non-GC heap values. They are initialized lazily and racily. We 53 // avoid holding a mutex here because the JNI API supports concurrent calls to 54 // Get{Field,Method}ID and also because finding an id may recursively call into 55 // Get{Field,Method}ID. 56 // 57 // The recursion issue occurs here for the fields in the FileDescriptor class 58 // since retrieving a field id requires the class to be initialized. Class 59 // initialization leads to the initialization of static fields. The 60 // FileDescriptor class has static fields that are FileDescriptor instances. The 61 // initialization of these static FileDescriptor fields follows a convoluted 62 // path that that leads to a call to jniGetFDFromFileDescriptor() which then 63 // needs to call GetFieldID() which is in the call stack. If thread-safety were 64 // desirable here, a recursive mutex would be required. 65 // 66 // These field and method ids have default values of nullptr. They are reset 67 // back to nullptr in JniConstants::Uninitialize(), along with the class 68 // references, when a new runtime instance is created via JNI_CreateJavaVM(). The 69 // reset happens before the new runtime instance is returned to the caller and 70 // under the protection of the |g_class_refs_mutex|. 71 72 jfieldID g_file_descriptor_descriptor_field = nullptr; // java.io.FileDescriptor.descriptor 73 jfieldID g_file_descriptor_owner_id_field = nullptr; // java.io.FileDescriptor.ownerId 74 jmethodID g_file_descriptor_init_method = nullptr; // void java.io.FileDescriptor.<init>() 75 jmethodID g_nio_access_get_base_array_method = nullptr; // Object java.nio.NIOAccess.getBaseArray() 76 jmethodID g_nio_access_get_base_array_offset_method = nullptr; // Object java.nio.NIOAccess.getBaseArray() 77 jfieldID g_nio_buffer_address_field = nullptr; // long java.nio.Buffer.address 78 jfieldID g_nio_buffer_element_size_shift_field = nullptr; // int java.nio.Buffer._elementSizeShift 79 jfieldID g_nio_buffer_limit_field = nullptr; // int java.nio.Buffer.limit 80 jfieldID g_nio_buffer_position_field = nullptr; // int java.nio.Buffer.position 81 jmethodID g_nio_buffer_array_method = nullptr; // Object java.nio.Buffer.array() 82 jmethodID g_nio_buffer_array_offset_method = nullptr; // int java.nio.Buffer.arrayOffset() 83 jmethodID g_reference_get_method = nullptr; // Object java.lang.ref.Reference.get() 84 85 jclass FindClass(JNIEnv* env, const char* name) { 86 ScopedLocalRef<jclass> klass(env, env->FindClass(name)); 87 ALOG_ALWAYS_FATAL_IF(klass.get() == nullptr, "failed to find class '%s'", name); 88 return reinterpret_cast<jclass>(env->NewGlobalRef(klass.get())); 89 } 90 91 jfieldID FindField(JNIEnv* env, jclass klass, const char* name, const char* desc) { 92 jfieldID result = env->GetFieldID(klass, name, desc); 93 ALOG_ALWAYS_FATAL_IF(result == nullptr, "failed to find field '%s:%s'", name, desc); 94 return result; 95 } 96 97 jmethodID FindMethod(JNIEnv* env, jclass klass, const char* name, const char* signature) { 98 jmethodID result = env->GetMethodID(klass, name, signature); 99 ALOG_ALWAYS_FATAL_IF(result == nullptr, "failed to find method '%s%s'", name, signature); 100 return result; 101 } 102 103 jmethodID FindStaticMethod(JNIEnv* env, jclass klass, const char* name, const char* signature) { 104 jmethodID result = env->GetStaticMethodID(klass, name, signature); 105 ALOG_ALWAYS_FATAL_IF(result == nullptr, "failed to find static method '%s%s'", name, signature); 106 return result; 107 } 108 109 } // namespace 110 111 jclass JniConstants::GetFileDescriptorClass(JNIEnv* env) { 112 EnsureClassReferencesInitialized(env); 113 return g_file_descriptor_class; 114 } 115 116 jclass JniConstants::GetNioAccessClass(JNIEnv* env) { 117 EnsureClassReferencesInitialized(env); 118 return g_nio_access_class; 119 } 120 121 jclass JniConstants::GetNioBufferClass(JNIEnv* env) { 122 EnsureClassReferencesInitialized(env); 123 return g_nio_buffer_class; 124 } 125 126 jclass JniConstants::GetReferenceClass(JNIEnv* env) { 127 EnsureClassReferencesInitialized(env); 128 return g_reference_class; 129 } 130 131 jclass JniConstants::GetStringClass(JNIEnv* env) { 132 EnsureClassReferencesInitialized(env); 133 return g_string_class; 134 } 135 136 jfieldID JniConstants::GetFileDescriptorDescriptorField(JNIEnv* env) { 137 if (g_file_descriptor_descriptor_field == nullptr) { 138 jclass klass = GetFileDescriptorClass(env); 139 g_file_descriptor_descriptor_field = FindField(env, klass, "descriptor", "I"); 140 } 141 return g_file_descriptor_descriptor_field; 142 } 143 144 jfieldID JniConstants::GetFileDescriptorOwnerIdField(JNIEnv* env) { 145 if (g_file_descriptor_owner_id_field == nullptr) { 146 jclass klass = GetFileDescriptorClass(env); 147 g_file_descriptor_owner_id_field = FindField(env, klass, "ownerId", "J"); 148 } 149 return g_file_descriptor_owner_id_field; 150 } 151 152 jmethodID JniConstants::GetFileDescriptorInitMethod(JNIEnv* env) { 153 if (g_file_descriptor_init_method == nullptr) { 154 jclass klass = GetFileDescriptorClass(env); 155 g_file_descriptor_init_method = FindMethod(env, klass, "<init>", "()V"); 156 } 157 return g_file_descriptor_init_method; 158 } 159 160 jmethodID JniConstants::GetNioAccessGetBaseArrayMethod(JNIEnv* env) { 161 if (g_nio_access_get_base_array_method == nullptr) { 162 jclass klass = GetNioAccessClass(env); 163 g_nio_access_get_base_array_method = 164 FindStaticMethod(env, klass, "getBaseArray", 165 "(Ljava/nio/Buffer;)Ljava/lang/Object;"); 166 } 167 return g_nio_access_get_base_array_method; 168 } 169 170 jmethodID JniConstants::GetNioAccessGetBaseArrayOffsetMethod(JNIEnv* env) { 171 if (g_nio_access_get_base_array_offset_method == nullptr) { 172 jclass klass = GetNioAccessClass(env); 173 g_nio_access_get_base_array_offset_method = 174 FindStaticMethod(env, klass, "getBaseArrayOffset", "(Ljava/nio/Buffer;)I"); 175 } 176 return g_nio_access_get_base_array_offset_method; 177 } 178 179 jfieldID JniConstants::GetNioBufferAddressField(JNIEnv* env) { 180 if (g_nio_buffer_address_field == nullptr) { 181 jclass klass = GetNioBufferClass(env); 182 g_nio_buffer_address_field = FindField(env, klass, "address", "J"); 183 } 184 return g_nio_buffer_address_field; 185 } 186 187 jfieldID JniConstants::GetNioBufferElementSizeShiftField(JNIEnv* env) { 188 if (g_nio_buffer_element_size_shift_field == nullptr) { 189 jclass klass = GetNioBufferClass(env); 190 g_nio_buffer_element_size_shift_field = FindField(env, klass, "_elementSizeShift", "I"); 191 } 192 return g_nio_buffer_element_size_shift_field; 193 } 194 195 jfieldID JniConstants::GetNioBufferLimitField(JNIEnv* env) { 196 if (g_nio_buffer_limit_field == nullptr) { 197 jclass klass = GetNioBufferClass(env); 198 g_nio_buffer_limit_field = FindField(env, klass, "limit", "I"); 199 } 200 return g_nio_buffer_limit_field; 201 } 202 203 jfieldID JniConstants::GetNioBufferPositionField(JNIEnv* env) { 204 if (g_nio_buffer_position_field == nullptr) { 205 jclass klass = GetNioBufferClass(env); 206 g_nio_buffer_position_field = FindField(env, klass, "position", "I"); 207 } 208 return g_nio_buffer_position_field; 209 } 210 211 jmethodID JniConstants::GetNioBufferArrayMethod(JNIEnv* env) { 212 if (g_nio_buffer_array_method == nullptr) { 213 jclass klass = GetNioBufferClass(env); 214 g_nio_buffer_array_method = FindMethod(env, klass, "array", "()Ljava/lang/Object;"); 215 } 216 return g_nio_buffer_array_method; 217 } 218 219 jmethodID JniConstants::GetNioBufferArrayOffsetMethod(JNIEnv* env) { 220 if (g_nio_buffer_array_offset_method == nullptr) { 221 jclass klass = GetNioBufferClass(env); 222 g_nio_buffer_array_offset_method = FindMethod(env, klass, "arrayOffset", "()I"); 223 } 224 return g_nio_buffer_array_offset_method; 225 } 226 227 jmethodID JniConstants::GetReferenceGetMethod(JNIEnv* env) { 228 if (g_reference_get_method == nullptr) { 229 jclass klass = GetReferenceClass(env); 230 g_reference_get_method = FindMethod(env, klass, "get", "()Ljava/lang/Object;"); 231 } 232 return g_reference_get_method; 233 } 234 235 void JniConstants::EnsureClassReferencesInitialized(JNIEnv* env) { 236 // Fast check if class references are initialized. 237 if (g_class_refs_initialized.load(std::memory_order_acquire)) { 238 return; 239 } 240 241 // Slower check with initialization if necessary. 242 std::lock_guard<std::mutex> guard(g_class_refs_mutex); 243 if (g_class_refs_initialized.load(std::memory_order_relaxed)) { 244 return; 245 } 246 247 // Class constants should be initialized only once because they global 248 // references. Field ids and Method ids can be initialized later since they 249 // are not references and races only have trivial performance 250 // consequences. 251 g_file_descriptor_class = FindClass(env, "java/io/FileDescriptor"); 252 g_nio_access_class = FindClass(env, "java/nio/NIOAccess"); 253 g_nio_buffer_class = FindClass(env, "java/nio/Buffer"); 254 g_reference_class = FindClass(env, "java/lang/ref/Reference"); 255 g_string_class = FindClass(env, "java/lang/String"); 256 g_class_refs_initialized.store(true, std::memory_order_release); 257 } 258 259 void JniConstants::Uninitialize() { 260 // This method is called when a new runtime instance is created. There is no 261 // notification of a runtime instance being destroyed in the JNI interface 262 // so we piggyback on creation. Since only one runtime is supported at a 263 // time, we know the constants are invalid when JNI_CreateJavaVM() is 264 // called. 265 // 266 // Clean shutdown would require calling DeleteGlobalRef() for each of the 267 // class references. 268 std::lock_guard<std::mutex> guard(g_class_refs_mutex); 269 g_file_descriptor_class = nullptr; 270 g_file_descriptor_descriptor_field = nullptr; 271 g_file_descriptor_owner_id_field = nullptr; 272 g_file_descriptor_init_method = nullptr; 273 g_nio_access_class = nullptr; 274 g_nio_access_get_base_array_method = nullptr; 275 g_nio_access_get_base_array_offset_method = nullptr; 276 g_nio_buffer_class = nullptr; 277 g_nio_buffer_address_field = nullptr; 278 g_nio_buffer_element_size_shift_field = nullptr; 279 g_nio_buffer_limit_field = nullptr; 280 g_nio_buffer_position_field = nullptr; 281 g_nio_buffer_array_method = nullptr; 282 g_nio_buffer_array_offset_method = nullptr; 283 g_reference_class = nullptr; 284 g_reference_get_method = nullptr; 285 g_string_class = nullptr; 286 g_class_refs_initialized.store(false, std::memory_order_release); 287 } 288