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(const JavaRef
<jobject
>& context
) {
101 DCHECK(g_application_context
.Get().is_null());
102 g_application_context
.Get().Reset(context
);
105 const jobject
GetApplicationContext() {
106 DCHECK(!g_application_context
.Get().is_null());
107 return g_application_context
.Get().obj();
110 ScopedJavaLocalRef
<jclass
> GetClass(JNIEnv
* env
, const char* class_name
) {
111 jclass clazz
= env
->FindClass(class_name
);
112 CHECK(!ClearException(env
) && clazz
) << "Failed to find class " << class_name
;
113 return ScopedJavaLocalRef
<jclass
>(env
, clazz
);
116 template<MethodID::Type type
>
117 jmethodID
MethodID::Get(JNIEnv
* env
,
119 const char* method_name
,
120 const char* jni_signature
) {
121 jmethodID id
= type
== TYPE_STATIC
?
122 env
->GetStaticMethodID(clazz
, method_name
, jni_signature
) :
123 env
->GetMethodID(clazz
, method_name
, jni_signature
);
124 CHECK(base::android::ClearException(env
) || id
) <<
126 (type
== TYPE_STATIC
? "static " : "") <<
127 "method " << method_name
<< " " << jni_signature
;
131 // If |atomic_method_id| set, it'll return immediately. Otherwise, it'll call
132 // into ::Get() above. If there's a race, it's ok since the values are the same
133 // (and the duplicated effort will happen only once).
134 template<MethodID::Type type
>
135 jmethodID
MethodID::LazyGet(JNIEnv
* env
,
137 const char* method_name
,
138 const char* jni_signature
,
139 base::subtle::AtomicWord
* atomic_method_id
) {
140 COMPILE_ASSERT(sizeof(subtle::AtomicWord
) >= sizeof(jmethodID
),
141 AtomicWord_SmallerThan_jMethodID
);
142 subtle::AtomicWord value
= base::subtle::Acquire_Load(atomic_method_id
);
144 return reinterpret_cast<jmethodID
>(value
);
145 jmethodID id
= MethodID::Get
<type
>(env
, clazz
, method_name
, jni_signature
);
146 base::subtle::Release_Store(
147 atomic_method_id
, reinterpret_cast<subtle::AtomicWord
>(id
));
151 // Various template instantiations.
152 template jmethodID
MethodID::Get
<MethodID::TYPE_STATIC
>(
153 JNIEnv
* env
, jclass clazz
, const char* method_name
,
154 const char* jni_signature
);
156 template jmethodID
MethodID::Get
<MethodID::TYPE_INSTANCE
>(
157 JNIEnv
* env
, jclass clazz
, const char* method_name
,
158 const char* jni_signature
);
160 template jmethodID
MethodID::LazyGet
<MethodID::TYPE_STATIC
>(
161 JNIEnv
* env
, jclass clazz
, const char* method_name
,
162 const char* jni_signature
, base::subtle::AtomicWord
* atomic_method_id
);
164 template jmethodID
MethodID::LazyGet
<MethodID::TYPE_INSTANCE
>(
165 JNIEnv
* env
, jclass clazz
, const char* method_name
,
166 const char* jni_signature
, base::subtle::AtomicWord
* atomic_method_id
);
168 bool HasException(JNIEnv
* env
) {
169 return env
->ExceptionCheck() != JNI_FALSE
;
172 bool ClearException(JNIEnv
* env
) {
173 if (!HasException(env
))
175 env
->ExceptionDescribe();
176 env
->ExceptionClear();
180 void CheckException(JNIEnv
* env
) {
181 if (!HasException(env
)) return;
183 // Exception has been found, might as well tell breakpad about it.
184 jthrowable java_throwable
= env
->ExceptionOccurred();
185 if (!java_throwable
) {
186 // Do nothing but return false.
190 // Clear the pending exception, since a local reference is now held.
191 env
->ExceptionDescribe();
192 env
->ExceptionClear();
194 // Set the exception_string in BuildInfo so that breakpad can read it.
195 // RVO should avoid any extra copies of the exception string.
196 base::android::BuildInfo::GetInstance()->set_java_exception_info(
197 GetJavaExceptionInfo(env
, java_throwable
));
199 // Now, feel good about it and die.
203 } // namespace android