1 /*
2 * Copyright (C) 2024 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 #include "JniConstants.h"
18
19 #include <pthread.h>
20 #include <stdbool.h>
21 #include <stddef.h>
22 #include <string.h>
23
24 #define LOG_TAG "JniConstants"
25 #include <log/log.h>
26
27 // jclass constants list:
28 // <class, signature, androidOnly>
29 #define JCLASS_CONSTANTS_LIST(V) \
30 V(FileDescriptor, "java/io/FileDescriptor", false) \
31 V(NioBuffer, "java/nio/Buffer", false) \
32 V(NioByteBuffer, "java/nio/ByteBuffer", false) \
33 V(NioShortBuffer, "java/nio/ShortBuffer", false) \
34 V(NioCharBuffer, "java/nio/CharBuffer", false) \
35 V(NioIntBuffer, "java/nio/IntBuffer", false) \
36 V(NioFloatBuffer, "java/nio/FloatBuffer", false) \
37 V(NioLongBuffer, "java/nio/LongBuffer", false) \
38 V(NioDoubleBuffer, "java/nio/DoubleBuffer", false)
39
40 // jmethodID's of public methods constants list:
41 // <Class, method, method-string, signature, is_static>
42 #define JMETHODID_CONSTANTS_LIST(V) \
43 V(FileDescriptor, init, "<init>", "()V", false) \
44 V(NioBuffer, array, "array", "()Ljava/lang/Object;", false) \
45 V(NioBuffer, hasArray, "hasArray", "()Z", false) \
46 V(NioBuffer, isDirect, "isDirect", "()Z", false) \
47 V(NioBuffer, arrayOffset, "arrayOffset", "()I", false)
48
49 // jfieldID constants list:
50 // <Class, field, signature, is_static>
51 #define JFIELDID_CONSTANTS_LIST(V) \
52 V(FileDescriptor, fd, "I", false) \
53 V(NioBuffer, address, "J", false) \
54 V(NioBuffer, limit, "I", false) \
55 V(NioBuffer, position, "I", false)
56
57 #define CLASS_NAME(cls) g_ ## cls
58 #define METHOD_NAME(cls, method) g_ ## cls ## _ ## method
59 #define FIELD_NAME(cls, field) g_ ## cls ## _ ## field
60
61 //
62 // Declare storage for cached classes, methods and fields.
63 //
64
65 #define JCLASS_DECLARE_STORAGE(cls, ...) \
66 static jclass CLASS_NAME(cls) = NULL;
67 JCLASS_CONSTANTS_LIST(JCLASS_DECLARE_STORAGE)
68 #undef JCLASS_DECLARE_STORAGE
69
70 #define JMETHODID_DECLARE_STORAGE(cls, method, ...) \
71 static jmethodID METHOD_NAME(cls, method) = NULL;
JMETHODID_CONSTANTS_LIST(JMETHODID_DECLARE_STORAGE)72 JMETHODID_CONSTANTS_LIST(JMETHODID_DECLARE_STORAGE)
73 #undef JMETHODID_DECLARE_STORAGE
74
75 #define JFIELDID_DECLARE_STORAGE(cls, field, ...) \
76 static jfieldID FIELD_NAME(cls, field) = NULL;
77 JFIELDID_CONSTANTS_LIST(JFIELDID_DECLARE_STORAGE)
78 #undef JFIELDID_DECLARE_STORAGE
79
80 //
81 // Helper methods
82 //
83
84 static jclass FindClass(JNIEnv* env, const char* signature, bool androidOnly) {
85 jclass cls = (*env)->FindClass(env, signature);
86 if (cls == NULL) {
87 LOG_ALWAYS_FATAL_IF(!androidOnly, "Class not found: %s", signature);
88 return NULL;
89 }
90 return (*env)->NewGlobalRef(env, cls);
91 }
92
FindMethod(JNIEnv * env,jclass cls,const char * name,const char * signature,bool isStatic)93 static jmethodID FindMethod(JNIEnv* env, jclass cls,
94 const char* name, const char* signature, bool isStatic) {
95 jmethodID method;
96 if (isStatic) {
97 method = (*env)->GetStaticMethodID(env, cls, name, signature);
98 } else {
99 method = (*env)->GetMethodID(env, cls, name, signature);
100 }
101 LOG_ALWAYS_FATAL_IF(method == NULL, "Method not found: %s:%s", name, signature);
102 return method;
103 }
104
FindField(JNIEnv * env,jclass cls,const char * name,const char * signature,bool isStatic)105 static jfieldID FindField(JNIEnv* env, jclass cls,
106 const char* name, const char* signature, bool isStatic) {
107 jfieldID field;
108 if (isStatic) {
109 field = (*env)->GetStaticFieldID(env, cls, name, signature);
110 } else {
111 field = (*env)->GetFieldID(env, cls, name, signature);
112 }
113 LOG_ALWAYS_FATAL_IF(field == NULL, "Field not found: %s:%s", name, signature);
114 return field;
115 }
116
117 static pthread_once_t g_initialized = PTHREAD_ONCE_INIT;
118 static JNIEnv* g_init_env;
119
InitializeConstants()120 static void InitializeConstants() {
121 // Initialize cached classes.
122 #define JCLASS_INITIALIZE(cls, signature, androidOnly) \
123 CLASS_NAME(cls) = FindClass(g_init_env, signature, androidOnly);
124 JCLASS_CONSTANTS_LIST(JCLASS_INITIALIZE)
125 #undef JCLASS_INITIALIZE
126
127 // Initialize cached methods.
128 #define JMETHODID_INITIALIZE(cls, method, name, signature, isStatic) \
129 METHOD_NAME(cls, method) = \
130 FindMethod(g_init_env, CLASS_NAME(cls), name, signature, isStatic);
131 JMETHODID_CONSTANTS_LIST(JMETHODID_INITIALIZE)
132 #undef JMETHODID_INITIALIZE
133
134 // Initialize cached fields.
135 #define JFIELDID_INITIALIZE(cls, field, signature, isStatic) \
136 FIELD_NAME(cls, field) = \
137 FindField(g_init_env, CLASS_NAME(cls), #field, signature, isStatic);
138 JFIELDID_CONSTANTS_LIST(JFIELDID_INITIALIZE)
139 #undef JFIELDID_INITIALIZE
140 }
141
EnsureInitialized(JNIEnv * env)142 void EnsureInitialized(JNIEnv* env) {
143 // This method has to be called in every cache accesses because library can be built
144 // 2 different ways and existing usage for compat version doesn't have a good hook for
145 // initialization and is widely used.
146 g_init_env = env;
147 pthread_once(&g_initialized, InitializeConstants);
148 }
149
150 // API exported by libnativehelper_api.h.
151
jniUninitializeConstants()152 void jniUninitializeConstants() {
153 // Uninitialize cached classes, methods and fields.
154 //
155 // NB we assume the runtime is stopped at this point and do not delete global
156 // references.
157 #define JCLASS_INVALIDATE(cls, ...) CLASS_NAME(cls) = NULL;
158 JCLASS_CONSTANTS_LIST(JCLASS_INVALIDATE);
159 #undef JCLASS_INVALIDATE
160
161 #define JMETHODID_INVALIDATE(cls, method, ...) METHOD_NAME(cls, method) = NULL;
162 JMETHODID_CONSTANTS_LIST(JMETHODID_INVALIDATE);
163 #undef JMETHODID_INVALIDATE
164
165 #define JFIELDID_INVALIDATE(cls, field, ...) FIELD_NAME(cls, field) = NULL;
166 JFIELDID_CONSTANTS_LIST(JFIELDID_INVALIDATE);
167 #undef JFIELDID_INVALIDATE
168
169 // If jniConstantsUninitialize is called, runtime has shutdown. Reset
170 // state as some tests re-start the runtime.
171 pthread_once_t o = PTHREAD_ONCE_INIT;
172 memcpy(&g_initialized, &o, sizeof(o));
173 }
174
175 //
176 // Accessors
177 //
178
179 #define JCLASS_ACCESSOR_IMPL(cls, ...) \
180 jclass JniConstants_ ## cls ## Class(JNIEnv* env) { \
181 EnsureInitialized(env); \
182 return CLASS_NAME(cls); \
183 }
184 JCLASS_CONSTANTS_LIST(JCLASS_ACCESSOR_IMPL)
185 #undef JCLASS_ACCESSOR_IMPL
186
187 #define JMETHODID_ACCESSOR_IMPL(cls, method, ...) \
188 jmethodID JniConstants_ ## cls ## _ ## method(JNIEnv* env) { \
189 EnsureInitialized(env); \
190 return METHOD_NAME(cls, method); \
191 }
192 JMETHODID_CONSTANTS_LIST(JMETHODID_ACCESSOR_IMPL)
193
194 #define JFIELDID_ACCESSOR_IMPL(cls, field, ...) \
195 jfieldID JniConstants_ ## cls ## _ ## field(JNIEnv* env) { \
196 EnsureInitialized(env); \
197 return FIELD_NAME(cls, field); \
198 }
199 JFIELDID_CONSTANTS_LIST(JFIELDID_ACCESSOR_IMPL)
200