blob: 56dc5c2ff3fe22c2e4628075beaf1097f60e2f91 [file] [log] [blame]
[email protected]9fc44162012-01-23 22:56:411// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]61c86c62011-08-02 16:11:162// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "base/android/jni_android.h"
6
avib30f2402015-12-24 03:43:287#include <stddef.h>
8
[email protected]96e7ade2011-12-05 14:42:089#include <map>
10
[email protected]1b46a532012-05-23 05:59:4911#include "base/android/build_info.h"
12#include "base/android/jni_string.h"
[email protected]d30dd6e2014-08-21 16:37:3213#include "base/android/jni_utils.h"
dskibaa8c951e2016-10-25 23:39:1114#include "base/debug/debugging_flags.h"
[email protected]96e7ade2011-12-05 14:42:0815#include "base/lazy_instance.h"
[email protected]61c86c62011-08-02 16:11:1616#include "base/logging.h"
dskibaa8c951e2016-10-25 23:39:1117#include "base/threading/thread_local.h"
[email protected]61c86c62011-08-02 16:11:1618
19namespace {
[email protected]1b46a532012-05-23 05:59:4920using base::android::GetClass;
[email protected]c410a2cb2012-10-16 18:35:1021using base::android::MethodID;
[email protected]1b46a532012-05-23 05:59:4922using base::android::ScopedJavaLocalRef;
[email protected]fe0f1ab2012-02-09 21:02:2723
estevenson5b04fba2017-01-31 20:34:5624base::android::JniRegistrationType g_jni_registration_type =
25 base::android::ALL_JNI_REGISTRATION;
torne103bf192015-02-20 23:06:3326
[email protected]995a3ff2012-09-17 06:15:1027JavaVM* g_jvm = NULL;
scottmg5e65e3a2017-03-08 08:48:4628base::LazyInstance<base::android::ScopedJavaGlobalRef<jobject>>::Leaky
[email protected]d30dd6e2014-08-21 16:37:3229 g_class_loader = LAZY_INSTANCE_INITIALIZER;
30jmethodID g_class_loader_load_class_method_id = 0;
[email protected]96e7ade2011-12-05 14:42:0831
dskibaa8c951e2016-10-25 23:39:1132#if BUILDFLAG(ENABLE_PROFILING) && HAVE_TRACE_STACK_FRAME_POINTERS
33base::LazyInstance<base::ThreadLocalPointer<void>>::Leaky
34 g_stack_frame_pointer = LAZY_INSTANCE_INITIALIZER;
35#endif
36
[email protected]1b46a532012-05-23 05:59:4937} // namespace
[email protected]61c86c62011-08-02 16:11:1638
39namespace base {
40namespace android {
41
estevenson5b04fba2017-01-31 20:34:5642JniRegistrationType GetJniRegistrationType() {
43 return g_jni_registration_type;
torne103bf192015-02-20 23:06:3344}
45
estevenson5b04fba2017-01-31 20:34:5646void SetJniRegistrationType(JniRegistrationType jni_registration_type) {
47 g_jni_registration_type = jni_registration_type;
torne103bf192015-02-20 23:06:3348}
49
[email protected]61c86c62011-08-02 16:11:1650JNIEnv* AttachCurrentThread() {
[email protected]7a3b0e42012-10-09 19:43:1051 DCHECK(g_jvm);
[email protected]61c86c62011-08-02 16:11:1652 JNIEnv* env = NULL;
[email protected]691b2002014-01-07 19:51:3753 jint ret = g_jvm->AttachCurrentThread(&env, NULL);
[email protected]7a3b0e42012-10-09 19:43:1054 DCHECK_EQ(JNI_OK, ret);
[email protected]61c86c62011-08-02 16:11:1655 return env;
56}
57
[email protected]f3225bdb2014-06-20 00:38:1958JNIEnv* AttachCurrentThreadWithName(const std::string& thread_name) {
59 DCHECK(g_jvm);
60 JavaVMAttachArgs args;
61 args.version = JNI_VERSION_1_2;
62 args.name = thread_name.c_str();
63 args.group = NULL;
64 JNIEnv* env = NULL;
65 jint ret = g_jvm->AttachCurrentThread(&env, &args);
66 DCHECK_EQ(JNI_OK, ret);
67 return env;
68}
69
[email protected]61c86c62011-08-02 16:11:1670void DetachFromVM() {
71 // Ignore the return value, if the thread is not attached, DetachCurrentThread
72 // will fail. But it is ok as the native thread may never be attached.
[email protected]691b2002014-01-07 19:51:3773 if (g_jvm)
[email protected]61c86c62011-08-02 16:11:1674 g_jvm->DetachCurrentThread();
75}
76
77void InitVM(JavaVM* vm) {
michaelbai25ec25c2015-10-22 19:40:2278 DCHECK(!g_jvm || g_jvm == vm);
[email protected]61c86c62011-08-02 16:11:1679 g_jvm = vm;
80}
81
[email protected]2e944632013-08-21 17:59:4282bool IsVMInitialized() {
83 return g_jvm != NULL;
84}
85
[email protected]d30dd6e2014-08-21 16:37:3286void InitReplacementClassLoader(JNIEnv* env,
87 const JavaRef<jobject>& class_loader) {
88 DCHECK(g_class_loader.Get().is_null());
89 DCHECK(!class_loader.is_null());
90
91 ScopedJavaLocalRef<jclass> class_loader_clazz =
92 GetClass(env, "java/lang/ClassLoader");
93 CHECK(!ClearException(env));
94 g_class_loader_load_class_method_id =
95 env->GetMethodID(class_loader_clazz.obj(),
96 "loadClass",
97 "(Ljava/lang/String;)Ljava/lang/Class;");
98 CHECK(!ClearException(env));
99
100 DCHECK(env->IsInstanceOf(class_loader.obj(), class_loader_clazz.obj()));
101 g_class_loader.Get().Reset(class_loader);
102}
103
[email protected]fe0f1ab2012-02-09 21:02:27104ScopedJavaLocalRef<jclass> GetClass(JNIEnv* env, const char* class_name) {
[email protected]d30dd6e2014-08-21 16:37:32105 jclass clazz;
106 if (!g_class_loader.Get().is_null()) {
tornec16354c2015-03-16 22:53:33107 // ClassLoader.loadClass expects a classname with components separated by
108 // dots instead of the slashes that JNIEnv::FindClass expects. The JNI
109 // generator generates names with slashes, so we have to replace them here.
110 // TODO(torne): move to an approach where we always use ClassLoader except
111 // for the special case of base::android::GetClassLoader(), and change the
112 // JNI generator to generate dot-separated names. http://crbug.com/461773
113 size_t bufsize = strlen(class_name) + 1;
114 char dotted_name[bufsize];
115 memmove(dotted_name, class_name, bufsize);
116 for (size_t i = 0; i < bufsize; ++i) {
117 if (dotted_name[i] == '/') {
118 dotted_name[i] = '.';
119 }
120 }
121
[email protected]d30dd6e2014-08-21 16:37:32122 clazz = static_cast<jclass>(
123 env->CallObjectMethod(g_class_loader.Get().obj(),
124 g_class_loader_load_class_method_id,
tornec16354c2015-03-16 22:53:33125 ConvertUTF8ToJavaString(env, dotted_name).obj()));
[email protected]d30dd6e2014-08-21 16:37:32126 } else {
127 clazz = env->FindClass(class_name);
128 }
yfriedmane524fe72016-05-05 19:51:38129 if (ClearException(env) || !clazz) {
130 LOG(FATAL) << "Failed to find class " << class_name;
131 }
[email protected]c7c2b462013-04-10 02:44:55132 return ScopedJavaLocalRef<jclass>(env, clazz);
[email protected]fe0f1ab2012-02-09 21:02:27133}
134
[email protected]d30dd6e2014-08-21 16:37:32135jclass LazyGetClass(
136 JNIEnv* env,
137 const char* class_name,
138 base::subtle::AtomicWord* atomic_class_id) {
avi4ec0dff2015-11-24 14:26:24139 static_assert(sizeof(subtle::AtomicWord) >= sizeof(jclass),
140 "AtomicWord can't be smaller than jclass");
[email protected]d30dd6e2014-08-21 16:37:32141 subtle::AtomicWord value = base::subtle::Acquire_Load(atomic_class_id);
142 if (value)
143 return reinterpret_cast<jclass>(value);
144 ScopedJavaGlobalRef<jclass> clazz;
145 clazz.Reset(GetClass(env, class_name));
146 subtle::AtomicWord null_aw = reinterpret_cast<subtle::AtomicWord>(NULL);
147 subtle::AtomicWord cas_result = base::subtle::Release_CompareAndSwap(
148 atomic_class_id,
149 null_aw,
150 reinterpret_cast<subtle::AtomicWord>(clazz.obj()));
151 if (cas_result == null_aw) {
152 // We intentionally leak the global ref since we now storing it as a raw
153 // pointer in |atomic_class_id|.
154 return clazz.Release();
155 } else {
156 return reinterpret_cast<jclass>(cas_result);
157 }
158}
159
[email protected]c410a2cb2012-10-16 18:35:10160template<MethodID::Type type>
161jmethodID MethodID::Get(JNIEnv* env,
162 jclass clazz,
163 const char* method_name,
164 const char* jni_signature) {
165 jmethodID id = type == TYPE_STATIC ?
166 env->GetStaticMethodID(clazz, method_name, jni_signature) :
167 env->GetMethodID(clazz, method_name, jni_signature);
yfriedmane524fe72016-05-05 19:51:38168 if (base::android::ClearException(env) || !id) {
169 LOG(FATAL) << "Failed to find " <<
170 (type == TYPE_STATIC ? "static " : "") <<
171 "method " << method_name << " " << jni_signature;
172 }
[email protected]c410a2cb2012-10-16 18:35:10173 return id;
[email protected]fae37d62012-03-08 12:39:13174}
175
[email protected]c410a2cb2012-10-16 18:35:10176// If |atomic_method_id| set, it'll return immediately. Otherwise, it'll call
177// into ::Get() above. If there's a race, it's ok since the values are the same
178// (and the duplicated effort will happen only once).
179template<MethodID::Type type>
180jmethodID MethodID::LazyGet(JNIEnv* env,
[email protected]3764dddb2012-10-02 21:13:55181 jclass clazz,
182 const char* method_name,
[email protected]c410a2cb2012-10-16 18:35:10183 const char* jni_signature,
184 base::subtle::AtomicWord* atomic_method_id) {
avi4ec0dff2015-11-24 14:26:24185 static_assert(sizeof(subtle::AtomicWord) >= sizeof(jmethodID),
186 "AtomicWord can't be smaller than jMethodID");
[email protected]c410a2cb2012-10-16 18:35:10187 subtle::AtomicWord value = base::subtle::Acquire_Load(atomic_method_id);
188 if (value)
189 return reinterpret_cast<jmethodID>(value);
190 jmethodID id = MethodID::Get<type>(env, clazz, method_name, jni_signature);
191 base::subtle::Release_Store(
192 atomic_method_id, reinterpret_cast<subtle::AtomicWord>(id));
193 return id;
[email protected]fe0f1ab2012-02-09 21:02:27194}
195
[email protected]c410a2cb2012-10-16 18:35:10196// Various template instantiations.
197template jmethodID MethodID::Get<MethodID::TYPE_STATIC>(
198 JNIEnv* env, jclass clazz, const char* method_name,
199 const char* jni_signature);
[email protected]fae37d62012-03-08 12:39:13200
[email protected]c410a2cb2012-10-16 18:35:10201template jmethodID MethodID::Get<MethodID::TYPE_INSTANCE>(
202 JNIEnv* env, jclass clazz, const char* method_name,
203 const char* jni_signature);
[email protected]3764dddb2012-10-02 21:13:55204
[email protected]c410a2cb2012-10-16 18:35:10205template jmethodID MethodID::LazyGet<MethodID::TYPE_STATIC>(
206 JNIEnv* env, jclass clazz, const char* method_name,
207 const char* jni_signature, base::subtle::AtomicWord* atomic_method_id);
[email protected]fe0f1ab2012-02-09 21:02:27208
[email protected]c410a2cb2012-10-16 18:35:10209template jmethodID MethodID::LazyGet<MethodID::TYPE_INSTANCE>(
210 JNIEnv* env, jclass clazz, const char* method_name,
211 const char* jni_signature, base::subtle::AtomicWord* atomic_method_id);
[email protected]fe0f1ab2012-02-09 21:02:27212
[email protected]fe0f1ab2012-02-09 21:02:27213bool HasException(JNIEnv* env) {
214 return env->ExceptionCheck() != JNI_FALSE;
[email protected]61c86c62011-08-02 16:11:16215}
216
[email protected]fe0f1ab2012-02-09 21:02:27217bool ClearException(JNIEnv* env) {
218 if (!HasException(env))
[email protected]61c86c62011-08-02 16:11:16219 return false;
[email protected]1b46a532012-05-23 05:59:49220 env->ExceptionDescribe();
[email protected]61c86c62011-08-02 16:11:16221 env->ExceptionClear();
222 return true;
223}
224
[email protected]fe0f1ab2012-02-09 21:02:27225void CheckException(JNIEnv* env) {
[email protected]909193c2014-06-28 01:58:04226 if (!HasException(env))
227 return;
[email protected]1b46a532012-05-23 05:59:49228
[email protected]995a3ff2012-09-17 06:15:10229 // Exception has been found, might as well tell breakpad about it.
[email protected]1b46a532012-05-23 05:59:49230 jthrowable java_throwable = env->ExceptionOccurred();
[email protected]909193c2014-06-28 01:58:04231 if (java_throwable) {
232 // Clear the pending exception, since a local reference is now held.
233 env->ExceptionDescribe();
234 env->ExceptionClear();
235
236 // Set the exception_string in BuildInfo so that breakpad can read it.
237 // RVO should avoid any extra copies of the exception string.
cjhopman060e0072015-05-06 21:37:48238 base::android::BuildInfo::GetInstance()->SetJavaExceptionInfo(
[email protected]909193c2014-06-28 01:58:04239 GetJavaExceptionInfo(env, java_throwable));
[email protected]fe0f1ab2012-02-09 21:02:27240 }
[email protected]1b46a532012-05-23 05:59:49241
[email protected]1b46a532012-05-23 05:59:49242 // Now, feel good about it and die.
yfriedmane524fe72016-05-05 19:51:38243 LOG(FATAL) << "Please include Java exception stack in crash report";
[email protected]fe0f1ab2012-02-09 21:02:27244}
245
cjhopman060e0072015-05-06 21:37:48246std::string GetJavaExceptionInfo(JNIEnv* env, jthrowable java_throwable) {
247 ScopedJavaLocalRef<jclass> throwable_clazz =
248 GetClass(env, "java/lang/Throwable");
249 jmethodID throwable_printstacktrace =
250 MethodID::Get<MethodID::TYPE_INSTANCE>(
251 env, throwable_clazz.obj(), "printStackTrace",
252 "(Ljava/io/PrintStream;)V");
253
254 // Create an instance of ByteArrayOutputStream.
255 ScopedJavaLocalRef<jclass> bytearray_output_stream_clazz =
256 GetClass(env, "java/io/ByteArrayOutputStream");
257 jmethodID bytearray_output_stream_constructor =
258 MethodID::Get<MethodID::TYPE_INSTANCE>(
259 env, bytearray_output_stream_clazz.obj(), "<init>", "()V");
260 jmethodID bytearray_output_stream_tostring =
261 MethodID::Get<MethodID::TYPE_INSTANCE>(
262 env, bytearray_output_stream_clazz.obj(), "toString",
263 "()Ljava/lang/String;");
264 ScopedJavaLocalRef<jobject> bytearray_output_stream(env,
265 env->NewObject(bytearray_output_stream_clazz.obj(),
266 bytearray_output_stream_constructor));
267
268 // Create an instance of PrintStream.
269 ScopedJavaLocalRef<jclass> printstream_clazz =
270 GetClass(env, "java/io/PrintStream");
271 jmethodID printstream_constructor =
272 MethodID::Get<MethodID::TYPE_INSTANCE>(
273 env, printstream_clazz.obj(), "<init>",
274 "(Ljava/io/OutputStream;)V");
275 ScopedJavaLocalRef<jobject> printstream(env,
276 env->NewObject(printstream_clazz.obj(), printstream_constructor,
277 bytearray_output_stream.obj()));
278
279 // Call Throwable.printStackTrace(PrintStream)
280 env->CallVoidMethod(java_throwable, throwable_printstacktrace,
281 printstream.obj());
282
283 // Call ByteArrayOutputStream.toString()
284 ScopedJavaLocalRef<jstring> exception_string(
285 env, static_cast<jstring>(
286 env->CallObjectMethod(bytearray_output_stream.obj(),
287 bytearray_output_stream_tostring)));
288
289 return ConvertJavaStringToUTF8(exception_string);
290}
291
dskibaa8c951e2016-10-25 23:39:11292#if BUILDFLAG(ENABLE_PROFILING) && HAVE_TRACE_STACK_FRAME_POINTERS
293
294JNIStackFrameSaver::JNIStackFrameSaver(void* current_fp) {
295 previous_fp_ = g_stack_frame_pointer.Pointer()->Get();
296 g_stack_frame_pointer.Pointer()->Set(current_fp);
297}
298
299JNIStackFrameSaver::~JNIStackFrameSaver() {
300 g_stack_frame_pointer.Pointer()->Set(previous_fp_);
301}
302
303void* JNIStackFrameSaver::SavedFrame() {
304 return g_stack_frame_pointer.Pointer()->Get();
305}
306
307#endif // ENABLE_PROFILING && HAVE_TRACE_STACK_FRAME_POINTERS
cjhopman060e0072015-05-06 21:37:48308
[email protected]61c86c62011-08-02 16:11:16309} // namespace android
310} // namespace base