• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2023 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "third_party/jni_zero/jni_zero.h"
6 
7 #include <sys/prctl.h>
8 
9 #include "third_party/jni_zero/generate_jni/JniInit_jni.h"
10 #include "third_party/jni_zero/jni_methods.h"
11 #include "third_party/jni_zero/jni_zero_internal.h"
12 #include "third_party/jni_zero/logging.h"
13 
14 #if defined(JNI_ZERO_MULTIPLEXING_ENABLED)
15 extern const int64_t kJniZeroHashWhole;
16 extern const int64_t kJniZeroHashPriority;
17 #endif
18 namespace jni_zero {
19 namespace {
20 // Until we fully migrate base's jni_android, we will maintain a copy of this
21 // global here and will have base set this variable when it sets its own.
22 JavaVM* g_jvm = nullptr;
23 
24 jclass (*g_class_resolver)(JNIEnv*, const char*, const char*) = nullptr;
25 
26 void (*g_exception_handler_callback)(JNIEnv*) = nullptr;
27 
GetClassInternal(JNIEnv * env,const char * class_name,const char * split_name)28 jclass GetClassInternal(JNIEnv* env,
29                         const char* class_name,
30                         const char* split_name) {
31   jclass clazz;
32   if (g_class_resolver != nullptr) {
33     clazz = g_class_resolver(env, class_name, split_name);
34   } else {
35     // Our generated code uses dots instead of slashes for ease of use with
36     // ClassLoader.loadCLass, so convert this.
37     size_t bufsize = strlen(class_name) + 1;
38     char slash_name[bufsize];
39     memmove(slash_name, class_name, bufsize);
40     for (size_t i = 0; i < bufsize; ++i) {
41       if (slash_name[i] == '.') {
42         slash_name[i] = '/';
43       }
44     }
45     clazz = env->FindClass(slash_name);
46   }
47   if (ClearException(env) || !clazz) {
48     JNI_ZERO_FLOG("Failed to find class %s", class_name);
49   }
50   return clazz;
51 }
52 
LazyGetClassInternal(JNIEnv * env,const char * class_name,const char * split_name,std::atomic<jclass> * atomic_class_id)53 jclass LazyGetClassInternal(JNIEnv* env,
54                             const char* class_name,
55                             const char* split_name,
56                             std::atomic<jclass>* atomic_class_id) {
57   jclass ret = nullptr;
58   ScopedJavaGlobalRef<jclass> clazz(
59       env, GetClassInternal(env, class_name, split_name));
60   if (atomic_class_id->compare_exchange_strong(ret, clazz.obj(),
61                                                std::memory_order_acq_rel)) {
62     // We intentionally leak the global ref since we are now storing it as a raw
63     // pointer in |atomic_class_id|.
64     ret = clazz.Release();
65   }
66   return ret;
67 }
68 
GetSystemClassGlobalRef(JNIEnv * env,const char * class_name)69 jclass GetSystemClassGlobalRef(JNIEnv* env, const char* class_name) {
70   return static_cast<jclass>(env->NewGlobalRef(env->FindClass(class_name)));
71 }
72 
73 }  // namespace
74 
75 jclass g_object_class = nullptr;
76 jclass g_string_class = nullptr;
77 
AttachCurrentThread()78 JNIEnv* AttachCurrentThread() {
79   JNI_ZERO_DCHECK(g_jvm);
80   JNIEnv* env = nullptr;
81   jint ret = g_jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_2);
82   if (ret == JNI_EDETACHED || !env) {
83     JavaVMAttachArgs args;
84     args.version = JNI_VERSION_1_2;
85     args.group = nullptr;
86 
87     // 16 is the maximum size for thread names on Android.
88     char thread_name[16];
89     int err = prctl(PR_GET_NAME, thread_name);
90     if (err < 0) {
91       JNI_ZERO_ELOG("prctl(PR_GET_NAME)");
92       args.name = nullptr;
93     } else {
94       args.name = thread_name;
95     }
96 
97 #if defined(JNI_ZERO_IS_ROBOLECTRIC)
98     ret = g_jvm->AttachCurrentThread(reinterpret_cast<void**>(&env), &args);
99 #else
100     ret = g_jvm->AttachCurrentThread(&env, &args);
101 #endif
102     JNI_ZERO_CHECK(ret == JNI_OK);
103   }
104   return env;
105 }
106 
AttachCurrentThreadWithName(const std::string & thread_name)107 JNIEnv* AttachCurrentThreadWithName(const std::string& thread_name) {
108   JNI_ZERO_DCHECK(g_jvm);
109   JavaVMAttachArgs args;
110   args.version = JNI_VERSION_1_2;
111   args.name = const_cast<char*>(thread_name.c_str());
112   args.group = nullptr;
113   JNIEnv* env = nullptr;
114 #if defined(JNI_ZERO_IS_ROBOLECTRIC)
115   jint ret = g_jvm->AttachCurrentThread(reinterpret_cast<void**>(&env), &args);
116 #else
117   jint ret = g_jvm->AttachCurrentThread(&env, &args);
118 #endif
119   JNI_ZERO_CHECK(ret == JNI_OK);
120   return env;
121 }
122 
DetachFromVM()123 void DetachFromVM() {
124   // Ignore the return value, if the thread is not attached, DetachCurrentThread
125   // will fail. But it is ok as the native thread may never be attached.
126   if (g_jvm) {
127     g_jvm->DetachCurrentThread();
128   }
129 }
130 
InitVM(JavaVM * vm)131 void InitVM(JavaVM* vm) {
132   g_jvm = vm;
133   JNIEnv* env = AttachCurrentThread();
134   g_object_class = GetSystemClassGlobalRef(env, "java/lang/Object");
135   g_string_class = GetSystemClassGlobalRef(env, "java/lang/String");
136 #if defined(JNI_ZERO_MULTIPLEXING_ENABLED)
137   Java_JniInit_crashIfMultiplexingMisaligned(env, kJniZeroHashWhole,
138                                              kJniZeroHashPriority);
139 #else
140   // Mark as used when multiplexing not enabled.
141   (void)&Java_JniInit_crashIfMultiplexingMisaligned;
142 #endif
143   CheckException(env);
144 }
145 
DisableJvmForTesting()146 void DisableJvmForTesting() {
147   g_jvm = nullptr;
148 }
149 
IsVMInitialized()150 bool IsVMInitialized() {
151   return g_jvm != nullptr;
152 }
153 
GetVM()154 JavaVM* GetVM() {
155   return g_jvm;
156 }
157 
HasException(JNIEnv * env)158 bool HasException(JNIEnv* env) {
159   return env->ExceptionCheck() != JNI_FALSE;
160 }
161 
ClearException(JNIEnv * env)162 bool ClearException(JNIEnv* env) {
163   if (!HasException(env)) {
164     return false;
165   }
166   env->ExceptionDescribe();
167   env->ExceptionClear();
168   return true;
169 }
170 
SetExceptionHandler(void (* callback)(JNIEnv *))171 void SetExceptionHandler(void (*callback)(JNIEnv*)) {
172   g_exception_handler_callback = callback;
173 }
174 
CheckException(JNIEnv * env)175 void CheckException(JNIEnv* env) {
176   if (!HasException(env)) {
177     return;
178   }
179 
180   if (g_exception_handler_callback) {
181     return g_exception_handler_callback(env);
182   }
183   JNI_ZERO_FLOG("jni_zero crashing due to uncaught Java exception");
184 }
185 
SetClassResolver(jclass (* resolver)(JNIEnv *,const char *,const char *))186 void SetClassResolver(jclass (*resolver)(JNIEnv*, const char*, const char*)) {
187   g_class_resolver = resolver;
188 }
189 
GetClass(JNIEnv * env,const char * class_name,const char * split_name)190 ScopedJavaLocalRef<jclass> GetClass(JNIEnv* env,
191                                     const char* class_name,
192                                     const char* split_name) {
193   return ScopedJavaLocalRef<jclass>(
194       env, GetClassInternal(env, class_name, split_name));
195 }
196 
GetClass(JNIEnv * env,const char * class_name)197 ScopedJavaLocalRef<jclass> GetClass(JNIEnv* env, const char* class_name) {
198   return ScopedJavaLocalRef<jclass>(env, GetClassInternal(env, class_name, ""));
199 }
200 
201 template <MethodID::Type type>
Get(JNIEnv * env,jclass clazz,const char * method_name,const char * jni_signature)202 jmethodID MethodID::Get(JNIEnv* env,
203                         jclass clazz,
204                         const char* method_name,
205                         const char* jni_signature) {
206   auto get_method_ptr = type == MethodID::TYPE_STATIC
207                             ? &JNIEnv::GetStaticMethodID
208                             : &JNIEnv::GetMethodID;
209   jmethodID id = (env->*get_method_ptr)(clazz, method_name, jni_signature);
210   if (ClearException(env) || !id) {
211     JNI_ZERO_FLOG("Failed to find class %smethod %s %s",
212                   (type == TYPE_STATIC ? "static " : ""), method_name,
213                   jni_signature);
214   }
215   return id;
216 }
217 
218 // If |atomic_method_id| set, it'll return immediately. Otherwise, it'll call
219 // into ::Get() above. If there's a race, it's ok since the values are the same
220 // (and the duplicated effort will happen only once).
221 template <MethodID::Type type>
LazyGet(JNIEnv * env,jclass clazz,const char * method_name,const char * jni_signature,std::atomic<jmethodID> * atomic_method_id)222 jmethodID MethodID::LazyGet(JNIEnv* env,
223                             jclass clazz,
224                             const char* method_name,
225                             const char* jni_signature,
226                             std::atomic<jmethodID>* atomic_method_id) {
227   const jmethodID value = atomic_method_id->load(std::memory_order_acquire);
228   if (value) {
229     return value;
230   }
231   jmethodID id = MethodID::Get<type>(env, clazz, method_name, jni_signature);
232   atomic_method_id->store(id, std::memory_order_release);
233   return id;
234 }
235 
236 // Various template instantiations.
237 template jmethodID MethodID::Get<MethodID::TYPE_STATIC>(
238     JNIEnv* env,
239     jclass clazz,
240     const char* method_name,
241     const char* jni_signature);
242 
243 template jmethodID MethodID::Get<MethodID::TYPE_INSTANCE>(
244     JNIEnv* env,
245     jclass clazz,
246     const char* method_name,
247     const char* jni_signature);
248 
249 template jmethodID MethodID::LazyGet<MethodID::TYPE_STATIC>(
250     JNIEnv* env,
251     jclass clazz,
252     const char* method_name,
253     const char* jni_signature,
254     std::atomic<jmethodID>* atomic_method_id);
255 
256 template jmethodID MethodID::LazyGet<MethodID::TYPE_INSTANCE>(
257     JNIEnv* env,
258     jclass clazz,
259     const char* method_name,
260     const char* jni_signature,
261     std::atomic<jmethodID>* atomic_method_id);
262 
263 namespace internal {
LazyGetClass(JNIEnv * env,const char * class_name,const char * split_name,std::atomic<jclass> * atomic_class_id)264 jclass LazyGetClass(JNIEnv* env,
265                     const char* class_name,
266                     const char* split_name,
267                     std::atomic<jclass>* atomic_class_id) {
268   jclass ret = atomic_class_id->load(std::memory_order_acquire);
269   if (ret == nullptr) {
270     ret = LazyGetClassInternal(env, class_name, split_name, atomic_class_id);
271   }
272   return ret;
273 }
274 
LazyGetClass(JNIEnv * env,const char * class_name,std::atomic<jclass> * atomic_class_id)275 jclass LazyGetClass(JNIEnv* env,
276                     const char* class_name,
277                     std::atomic<jclass>* atomic_class_id) {
278   jclass ret = atomic_class_id->load(std::memory_order_acquire);
279   if (ret == nullptr) {
280     ret = LazyGetClassInternal(env, class_name, "", atomic_class_id);
281   }
282   return ret;
283 }
284 
285 }  // namespace internal
286 }  // namespace jni_zero
287