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 
FindClass(JNIEnv * env,const char * name)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 
FindField(JNIEnv * env,jclass klass,const char * name,const char * desc)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 
FindMethod(JNIEnv * env,jclass klass,const char * name,const char * signature)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 
FindStaticMethod(JNIEnv * env,jclass klass,const char * name,const char * signature)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 
GetFileDescriptorClass(JNIEnv * env)111 jclass JniConstants::GetFileDescriptorClass(JNIEnv* env) {
112     EnsureClassReferencesInitialized(env);
113     return g_file_descriptor_class;
114 }
115 
GetNioAccessClass(JNIEnv * env)116 jclass JniConstants::GetNioAccessClass(JNIEnv* env) {
117     EnsureClassReferencesInitialized(env);
118     return g_nio_access_class;
119 }
120 
GetNioBufferClass(JNIEnv * env)121 jclass JniConstants::GetNioBufferClass(JNIEnv* env) {
122     EnsureClassReferencesInitialized(env);
123     return g_nio_buffer_class;
124 }
125 
GetReferenceClass(JNIEnv * env)126 jclass JniConstants::GetReferenceClass(JNIEnv* env) {
127     EnsureClassReferencesInitialized(env);
128     return g_reference_class;
129 }
130 
GetStringClass(JNIEnv * env)131 jclass JniConstants::GetStringClass(JNIEnv* env) {
132     EnsureClassReferencesInitialized(env);
133     return g_string_class;
134 }
135 
GetFileDescriptorDescriptorField(JNIEnv * env)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 
GetFileDescriptorOwnerIdField(JNIEnv * env)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 
GetFileDescriptorInitMethod(JNIEnv * env)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 
GetNioAccessGetBaseArrayMethod(JNIEnv * env)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 
GetNioAccessGetBaseArrayOffsetMethod(JNIEnv * env)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 
GetNioBufferAddressField(JNIEnv * env)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 
GetNioBufferElementSizeShiftField(JNIEnv * env)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 
GetNioBufferLimitField(JNIEnv * env)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 
GetNioBufferPositionField(JNIEnv * env)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 
GetNioBufferArrayMethod(JNIEnv * env)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 
GetNioBufferArrayOffsetMethod(JNIEnv * env)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 
GetReferenceGetMethod(JNIEnv * env)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 
EnsureClassReferencesInitialized(JNIEnv * env)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 
Uninitialize()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