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
);
85 // Ignore the return value, if the thread is not attached, DetachCurrentThread
86 // will fail. But it is ok as the native thread may never be attached.
88 g_jvm
->DetachCurrentThread();
91 void InitVM(JavaVM
* vm
) {
96 bool IsVMInitialized() {
100 void InitApplicationContext(JNIEnv
* env
, const JavaRef
<jobject
>& context
) {
101 if (env
->IsSameObject(g_application_context
.Get().obj(), context
.obj())) {
102 // It's safe to set the context more than once if it's the same context.
105 DCHECK(g_application_context
.Get().is_null());
106 g_application_context
.Get().Reset(context
);
109 const jobject
GetApplicationContext() {
110 DCHECK(!g_application_context
.Get().is_null());
111 return g_application_context
.Get().obj();
114 ScopedJavaLocalRef
<jclass
> GetClass(JNIEnv
* env
, const char* class_name
) {
115 jclass clazz
= env
->FindClass(class_name
);
116 CHECK(!ClearException(env
) && clazz
) << "Failed to find class " << class_name
;
117 return ScopedJavaLocalRef
<jclass
>(env
, clazz
);
120 template<MethodID::Type type
>
121 jmethodID
MethodID::Get(JNIEnv
* env
,
123 const char* method_name
,
124 const char* jni_signature
) {
125 jmethodID id
= type
== TYPE_STATIC
?
126 env
->GetStaticMethodID(clazz
, method_name
, jni_signature
) :
127 env
->GetMethodID(clazz
, method_name
, jni_signature
);
128 CHECK(base::android::ClearException(env
) || id
) <<
130 (type
== TYPE_STATIC
? "static " : "") <<
131 "method " << method_name
<< " " << jni_signature
;
135 // If |atomic_method_id| set, it'll return immediately. Otherwise, it'll call
136 // into ::Get() above. If there's a race, it's ok since the values are the same
137 // (and the duplicated effort will happen only once).
138 template<MethodID::Type type
>
139 jmethodID
MethodID::LazyGet(JNIEnv
* env
,
141 const char* method_name
,
142 const char* jni_signature
,
143 base::subtle::AtomicWord
* atomic_method_id
) {
144 COMPILE_ASSERT(sizeof(subtle::AtomicWord
) >= sizeof(jmethodID
),
145 AtomicWord_SmallerThan_jMethodID
);
146 subtle::AtomicWord value
= base::subtle::Acquire_Load(atomic_method_id
);
148 return reinterpret_cast<jmethodID
>(value
);
149 jmethodID id
= MethodID::Get
<type
>(env
, clazz
, method_name
, jni_signature
);
150 base::subtle::Release_Store(
151 atomic_method_id
, reinterpret_cast<subtle::AtomicWord
>(id
));
155 // Various template instantiations.
156 template jmethodID
MethodID::Get
<MethodID::TYPE_STATIC
>(
157 JNIEnv
* env
, jclass clazz
, const char* method_name
,
158 const char* jni_signature
);
160 template jmethodID
MethodID::Get
<MethodID::TYPE_INSTANCE
>(
161 JNIEnv
* env
, jclass clazz
, const char* method_name
,
162 const char* jni_signature
);
164 template jmethodID
MethodID::LazyGet
<MethodID::TYPE_STATIC
>(
165 JNIEnv
* env
, jclass clazz
, const char* method_name
,
166 const char* jni_signature
, base::subtle::AtomicWord
* atomic_method_id
);
168 template jmethodID
MethodID::LazyGet
<MethodID::TYPE_INSTANCE
>(
169 JNIEnv
* env
, jclass clazz
, const char* method_name
,
170 const char* jni_signature
, base::subtle::AtomicWord
* atomic_method_id
);
172 bool HasException(JNIEnv
* env
) {
173 return env
->ExceptionCheck() != JNI_FALSE
;
176 bool ClearException(JNIEnv
* env
) {
177 if (!HasException(env
))
179 env
->ExceptionDescribe();
180 env
->ExceptionClear();
184 void CheckException(JNIEnv
* env
) {
185 if (!HasException(env
)) return;
187 // Exception has been found, might as well tell breakpad about it.
188 jthrowable java_throwable
= env
->ExceptionOccurred();
189 if (!java_throwable
) {
190 // Do nothing but return false.
194 // Clear the pending exception, since a local reference is now held.
195 env
->ExceptionDescribe();
196 env
->ExceptionClear();
198 // Set the exception_string in BuildInfo so that breakpad can read it.
199 // RVO should avoid any extra copies of the exception string.
200 base::android::BuildInfo::GetInstance()->set_java_exception_info(
201 GetJavaExceptionInfo(env
, java_throwable
));
203 // Now, feel good about it and die.
207 } // namespace android