1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/android/jni_android.h"
9 #include "base/android/build_info.h"
10 #include "base/android/jni_string.h"
11 #include "base/lazy_instance.h"
12 #include "base/logging.h"
15 using base::android::GetClass
;
16 using base::android::MethodID
;
17 using base::android::ScopedJavaLocalRef
;
20 // Leak the global app context, as it is used from a non-joinable worker thread
21 // that may still be running at shutdown. There is no harm in doing this.
22 base::LazyInstance
<base::android::ScopedJavaGlobalRef
<jobject
> >::Leaky
23 g_application_context
= LAZY_INSTANCE_INITIALIZER
;
25 std::string
GetJavaExceptionInfo(JNIEnv
* env
, jthrowable java_throwable
) {
26 ScopedJavaLocalRef
<jclass
> throwable_clazz
=
27 GetClass(env
, "java/lang/Throwable");
28 jmethodID throwable_printstacktrace
=
29 MethodID::Get
<MethodID::TYPE_INSTANCE
>(
30 env
, throwable_clazz
.obj(), "printStackTrace",
31 "(Ljava/io/PrintStream;)V");
33 // Create an instance of ByteArrayOutputStream.
34 ScopedJavaLocalRef
<jclass
> bytearray_output_stream_clazz
=
35 GetClass(env
, "java/io/ByteArrayOutputStream");
36 jmethodID bytearray_output_stream_constructor
=
37 MethodID::Get
<MethodID::TYPE_INSTANCE
>(
38 env
, bytearray_output_stream_clazz
.obj(), "<init>", "()V");
39 jmethodID bytearray_output_stream_tostring
=
40 MethodID::Get
<MethodID::TYPE_INSTANCE
>(
41 env
, bytearray_output_stream_clazz
.obj(), "toString",
42 "()Ljava/lang/String;");
43 ScopedJavaLocalRef
<jobject
> bytearray_output_stream(env
,
44 env
->NewObject(bytearray_output_stream_clazz
.obj(),
45 bytearray_output_stream_constructor
));
47 // Create an instance of PrintStream.
48 ScopedJavaLocalRef
<jclass
> printstream_clazz
=
49 GetClass(env
, "java/io/PrintStream");
50 jmethodID printstream_constructor
=
51 MethodID::Get
<MethodID::TYPE_INSTANCE
>(
52 env
, printstream_clazz
.obj(), "<init>",
53 "(Ljava/io/OutputStream;)V");
54 ScopedJavaLocalRef
<jobject
> printstream(env
,
55 env
->NewObject(printstream_clazz
.obj(), printstream_constructor
,
56 bytearray_output_stream
.obj()));
58 // Call Throwable.printStackTrace(PrintStream)
59 env
->CallVoidMethod(java_throwable
, throwable_printstacktrace
,
62 // Call ByteArrayOutputStream.toString()
63 ScopedJavaLocalRef
<jstring
> exception_string(
64 env
, static_cast<jstring
>(
65 env
->CallObjectMethod(bytearray_output_stream
.obj(),
66 bytearray_output_stream_tostring
)));
68 return ConvertJavaStringToUTF8(exception_string
);
76 JNIEnv
* AttachCurrentThread() {
79 jint ret
= g_jvm
->AttachCurrentThread(&env
, NULL
);
80 DCHECK_EQ(JNI_OK
, ret
);
84 JNIEnv
* AttachCurrentThreadWithName(const std::string
& thread_name
) {
86 JavaVMAttachArgs args
;
87 args
.version
= JNI_VERSION_1_2
;
88 args
.name
= thread_name
.c_str();
91 jint ret
= g_jvm
->AttachCurrentThread(&env
, &args
);
92 DCHECK_EQ(JNI_OK
, ret
);
97 // Ignore the return value, if the thread is not attached, DetachCurrentThread
98 // will fail. But it is ok as the native thread may never be attached.
100 g_jvm
->DetachCurrentThread();
103 void InitVM(JavaVM
* vm
) {
108 bool IsVMInitialized() {
109 return g_jvm
!= NULL
;
112 void InitApplicationContext(JNIEnv
* env
, const JavaRef
<jobject
>& context
) {
113 if (env
->IsSameObject(g_application_context
.Get().obj(), context
.obj())) {
114 // It's safe to set the context more than once if it's the same context.
117 DCHECK(g_application_context
.Get().is_null());
118 g_application_context
.Get().Reset(context
);
121 const jobject
GetApplicationContext() {
122 DCHECK(!g_application_context
.Get().is_null());
123 return g_application_context
.Get().obj();
126 ScopedJavaLocalRef
<jclass
> GetClass(JNIEnv
* env
, const char* class_name
) {
127 jclass clazz
= env
->FindClass(class_name
);
128 CHECK(!ClearException(env
) && clazz
) << "Failed to find class " << class_name
;
129 return ScopedJavaLocalRef
<jclass
>(env
, clazz
);
132 template<MethodID::Type type
>
133 jmethodID
MethodID::Get(JNIEnv
* env
,
135 const char* method_name
,
136 const char* jni_signature
) {
137 jmethodID id
= type
== TYPE_STATIC
?
138 env
->GetStaticMethodID(clazz
, method_name
, jni_signature
) :
139 env
->GetMethodID(clazz
, method_name
, jni_signature
);
140 CHECK(base::android::ClearException(env
) || id
) <<
142 (type
== TYPE_STATIC
? "static " : "") <<
143 "method " << method_name
<< " " << jni_signature
;
147 // If |atomic_method_id| set, it'll return immediately. Otherwise, it'll call
148 // into ::Get() above. If there's a race, it's ok since the values are the same
149 // (and the duplicated effort will happen only once).
150 template<MethodID::Type type
>
151 jmethodID
MethodID::LazyGet(JNIEnv
* env
,
153 const char* method_name
,
154 const char* jni_signature
,
155 base::subtle::AtomicWord
* atomic_method_id
) {
156 COMPILE_ASSERT(sizeof(subtle::AtomicWord
) >= sizeof(jmethodID
),
157 AtomicWord_SmallerThan_jMethodID
);
158 subtle::AtomicWord value
= base::subtle::Acquire_Load(atomic_method_id
);
160 return reinterpret_cast<jmethodID
>(value
);
161 jmethodID id
= MethodID::Get
<type
>(env
, clazz
, method_name
, jni_signature
);
162 base::subtle::Release_Store(
163 atomic_method_id
, reinterpret_cast<subtle::AtomicWord
>(id
));
167 // Various template instantiations.
168 template jmethodID
MethodID::Get
<MethodID::TYPE_STATIC
>(
169 JNIEnv
* env
, jclass clazz
, const char* method_name
,
170 const char* jni_signature
);
172 template jmethodID
MethodID::Get
<MethodID::TYPE_INSTANCE
>(
173 JNIEnv
* env
, jclass clazz
, const char* method_name
,
174 const char* jni_signature
);
176 template jmethodID
MethodID::LazyGet
<MethodID::TYPE_STATIC
>(
177 JNIEnv
* env
, jclass clazz
, const char* method_name
,
178 const char* jni_signature
, base::subtle::AtomicWord
* atomic_method_id
);
180 template jmethodID
MethodID::LazyGet
<MethodID::TYPE_INSTANCE
>(
181 JNIEnv
* env
, jclass clazz
, const char* method_name
,
182 const char* jni_signature
, base::subtle::AtomicWord
* atomic_method_id
);
184 bool HasException(JNIEnv
* env
) {
185 return env
->ExceptionCheck() != JNI_FALSE
;
188 bool ClearException(JNIEnv
* env
) {
189 if (!HasException(env
))
191 env
->ExceptionDescribe();
192 env
->ExceptionClear();
196 void CheckException(JNIEnv
* env
) {
197 if (!HasException(env
))
200 // Exception has been found, might as well tell breakpad about it.
201 jthrowable java_throwable
= env
->ExceptionOccurred();
202 if (java_throwable
) {
203 // Clear the pending exception, since a local reference is now held.
204 env
->ExceptionDescribe();
205 env
->ExceptionClear();
207 // Set the exception_string in BuildInfo so that breakpad can read it.
208 // RVO should avoid any extra copies of the exception string.
209 base::android::BuildInfo::GetInstance()->set_java_exception_info(
210 GetJavaExceptionInfo(env
, java_throwable
));
213 // Now, feel good about it and die.
214 CHECK(false) << "Please include Java exception stack in crash report";
217 } // namespace android