Roll src/third_party/WebKit d9c6159:8139f33 (svn 201974:201975)
[chromium-blink-merge.git] / base / android / jni_android.cc
blob5416be32c2b1f430ed6cd531ce46d8cc438063a7
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"
7 #include <map>
9 #include "base/android/build_info.h"
10 #include "base/android/jni_string.h"
11 #include "base/android/jni_utils.h"
12 #include "base/lazy_instance.h"
13 #include "base/logging.h"
15 namespace {
16 using base::android::GetClass;
17 using base::android::MethodID;
18 using base::android::ScopedJavaLocalRef;
20 bool g_disable_manual_jni_registration = false;
22 JavaVM* g_jvm = NULL;
23 // Leak the global app context, as it is used from a non-joinable worker thread
24 // that may still be running at shutdown. There is no harm in doing this.
25 base::LazyInstance<base::android::ScopedJavaGlobalRef<jobject> >::Leaky
26 g_application_context = LAZY_INSTANCE_INITIALIZER;
27 base::LazyInstance<base::android::ScopedJavaGlobalRef<jobject> >::Leaky
28 g_class_loader = LAZY_INSTANCE_INITIALIZER;
29 jmethodID g_class_loader_load_class_method_id = 0;
31 } // namespace
33 namespace base {
34 namespace android {
36 bool IsManualJniRegistrationDisabled() {
37 return g_disable_manual_jni_registration;
40 void DisableManualJniRegistration() {
41 DCHECK(!g_disable_manual_jni_registration);
42 g_disable_manual_jni_registration = true;
45 JNIEnv* AttachCurrentThread() {
46 DCHECK(g_jvm);
47 JNIEnv* env = NULL;
48 jint ret = g_jvm->AttachCurrentThread(&env, NULL);
49 DCHECK_EQ(JNI_OK, ret);
50 return env;
53 JNIEnv* AttachCurrentThreadWithName(const std::string& thread_name) {
54 DCHECK(g_jvm);
55 JavaVMAttachArgs args;
56 args.version = JNI_VERSION_1_2;
57 args.name = thread_name.c_str();
58 args.group = NULL;
59 JNIEnv* env = NULL;
60 jint ret = g_jvm->AttachCurrentThread(&env, &args);
61 DCHECK_EQ(JNI_OK, ret);
62 return env;
65 void DetachFromVM() {
66 // Ignore the return value, if the thread is not attached, DetachCurrentThread
67 // will fail. But it is ok as the native thread may never be attached.
68 if (g_jvm)
69 g_jvm->DetachCurrentThread();
72 void InitVM(JavaVM* vm) {
73 DCHECK(!g_jvm);
74 g_jvm = vm;
77 bool IsVMInitialized() {
78 return g_jvm != NULL;
81 void InitApplicationContext(JNIEnv* env, const JavaRef<jobject>& context) {
82 if (env->IsSameObject(g_application_context.Get().obj(), context.obj())) {
83 // It's safe to set the context more than once if it's the same context.
84 return;
86 DCHECK(g_application_context.Get().is_null());
87 g_application_context.Get().Reset(context);
90 void InitReplacementClassLoader(JNIEnv* env,
91 const JavaRef<jobject>& class_loader) {
92 DCHECK(g_class_loader.Get().is_null());
93 DCHECK(!class_loader.is_null());
95 ScopedJavaLocalRef<jclass> class_loader_clazz =
96 GetClass(env, "java/lang/ClassLoader");
97 CHECK(!ClearException(env));
98 g_class_loader_load_class_method_id =
99 env->GetMethodID(class_loader_clazz.obj(),
100 "loadClass",
101 "(Ljava/lang/String;)Ljava/lang/Class;");
102 CHECK(!ClearException(env));
104 DCHECK(env->IsInstanceOf(class_loader.obj(), class_loader_clazz.obj()));
105 g_class_loader.Get().Reset(class_loader);
108 const jobject GetApplicationContext() {
109 DCHECK(!g_application_context.Get().is_null());
110 return g_application_context.Get().obj();
113 ScopedJavaLocalRef<jclass> GetClass(JNIEnv* env, const char* class_name) {
114 jclass clazz;
115 if (!g_class_loader.Get().is_null()) {
116 // ClassLoader.loadClass expects a classname with components separated by
117 // dots instead of the slashes that JNIEnv::FindClass expects. The JNI
118 // generator generates names with slashes, so we have to replace them here.
119 // TODO(torne): move to an approach where we always use ClassLoader except
120 // for the special case of base::android::GetClassLoader(), and change the
121 // JNI generator to generate dot-separated names. http://crbug.com/461773
122 size_t bufsize = strlen(class_name) + 1;
123 char dotted_name[bufsize];
124 memmove(dotted_name, class_name, bufsize);
125 for (size_t i = 0; i < bufsize; ++i) {
126 if (dotted_name[i] == '/') {
127 dotted_name[i] = '.';
131 clazz = static_cast<jclass>(
132 env->CallObjectMethod(g_class_loader.Get().obj(),
133 g_class_loader_load_class_method_id,
134 ConvertUTF8ToJavaString(env, dotted_name).obj()));
135 } else {
136 clazz = env->FindClass(class_name);
138 CHECK(!ClearException(env) && clazz) << "Failed to find class " << class_name;
139 return ScopedJavaLocalRef<jclass>(env, clazz);
142 jclass LazyGetClass(
143 JNIEnv* env,
144 const char* class_name,
145 base::subtle::AtomicWord* atomic_class_id) {
146 COMPILE_ASSERT(sizeof(subtle::AtomicWord) >= sizeof(jclass),
147 AtomicWord_SmallerThan_jMethodID);
148 subtle::AtomicWord value = base::subtle::Acquire_Load(atomic_class_id);
149 if (value)
150 return reinterpret_cast<jclass>(value);
151 ScopedJavaGlobalRef<jclass> clazz;
152 clazz.Reset(GetClass(env, class_name));
153 subtle::AtomicWord null_aw = reinterpret_cast<subtle::AtomicWord>(NULL);
154 subtle::AtomicWord cas_result = base::subtle::Release_CompareAndSwap(
155 atomic_class_id,
156 null_aw,
157 reinterpret_cast<subtle::AtomicWord>(clazz.obj()));
158 if (cas_result == null_aw) {
159 // We intentionally leak the global ref since we now storing it as a raw
160 // pointer in |atomic_class_id|.
161 return clazz.Release();
162 } else {
163 return reinterpret_cast<jclass>(cas_result);
167 template<MethodID::Type type>
168 jmethodID MethodID::Get(JNIEnv* env,
169 jclass clazz,
170 const char* method_name,
171 const char* jni_signature) {
172 jmethodID id = type == TYPE_STATIC ?
173 env->GetStaticMethodID(clazz, method_name, jni_signature) :
174 env->GetMethodID(clazz, method_name, jni_signature);
175 CHECK(base::android::ClearException(env) || id) <<
176 "Failed to find " <<
177 (type == TYPE_STATIC ? "static " : "") <<
178 "method " << method_name << " " << jni_signature;
179 return id;
182 // If |atomic_method_id| set, it'll return immediately. Otherwise, it'll call
183 // into ::Get() above. If there's a race, it's ok since the values are the same
184 // (and the duplicated effort will happen only once).
185 template<MethodID::Type type>
186 jmethodID MethodID::LazyGet(JNIEnv* env,
187 jclass clazz,
188 const char* method_name,
189 const char* jni_signature,
190 base::subtle::AtomicWord* atomic_method_id) {
191 COMPILE_ASSERT(sizeof(subtle::AtomicWord) >= sizeof(jmethodID),
192 AtomicWord_SmallerThan_jMethodID);
193 subtle::AtomicWord value = base::subtle::Acquire_Load(atomic_method_id);
194 if (value)
195 return reinterpret_cast<jmethodID>(value);
196 jmethodID id = MethodID::Get<type>(env, clazz, method_name, jni_signature);
197 base::subtle::Release_Store(
198 atomic_method_id, reinterpret_cast<subtle::AtomicWord>(id));
199 return id;
202 // Various template instantiations.
203 template jmethodID MethodID::Get<MethodID::TYPE_STATIC>(
204 JNIEnv* env, jclass clazz, const char* method_name,
205 const char* jni_signature);
207 template jmethodID MethodID::Get<MethodID::TYPE_INSTANCE>(
208 JNIEnv* env, jclass clazz, const char* method_name,
209 const char* jni_signature);
211 template jmethodID MethodID::LazyGet<MethodID::TYPE_STATIC>(
212 JNIEnv* env, jclass clazz, const char* method_name,
213 const char* jni_signature, base::subtle::AtomicWord* atomic_method_id);
215 template jmethodID MethodID::LazyGet<MethodID::TYPE_INSTANCE>(
216 JNIEnv* env, jclass clazz, const char* method_name,
217 const char* jni_signature, base::subtle::AtomicWord* atomic_method_id);
219 bool HasException(JNIEnv* env) {
220 return env->ExceptionCheck() != JNI_FALSE;
223 bool ClearException(JNIEnv* env) {
224 if (!HasException(env))
225 return false;
226 env->ExceptionDescribe();
227 env->ExceptionClear();
228 return true;
231 void CheckException(JNIEnv* env) {
232 if (!HasException(env))
233 return;
235 // Exception has been found, might as well tell breakpad about it.
236 jthrowable java_throwable = env->ExceptionOccurred();
237 if (java_throwable) {
238 // Clear the pending exception, since a local reference is now held.
239 env->ExceptionDescribe();
240 env->ExceptionClear();
242 // Set the exception_string in BuildInfo so that breakpad can read it.
243 // RVO should avoid any extra copies of the exception string.
244 base::android::BuildInfo::GetInstance()->SetJavaExceptionInfo(
245 GetJavaExceptionInfo(env, java_throwable));
248 // Now, feel good about it and die.
249 CHECK(false) << "Please include Java exception stack in crash report";
252 std::string GetJavaExceptionInfo(JNIEnv* env, jthrowable java_throwable) {
253 ScopedJavaLocalRef<jclass> throwable_clazz =
254 GetClass(env, "java/lang/Throwable");
255 jmethodID throwable_printstacktrace =
256 MethodID::Get<MethodID::TYPE_INSTANCE>(
257 env, throwable_clazz.obj(), "printStackTrace",
258 "(Ljava/io/PrintStream;)V");
260 // Create an instance of ByteArrayOutputStream.
261 ScopedJavaLocalRef<jclass> bytearray_output_stream_clazz =
262 GetClass(env, "java/io/ByteArrayOutputStream");
263 jmethodID bytearray_output_stream_constructor =
264 MethodID::Get<MethodID::TYPE_INSTANCE>(
265 env, bytearray_output_stream_clazz.obj(), "<init>", "()V");
266 jmethodID bytearray_output_stream_tostring =
267 MethodID::Get<MethodID::TYPE_INSTANCE>(
268 env, bytearray_output_stream_clazz.obj(), "toString",
269 "()Ljava/lang/String;");
270 ScopedJavaLocalRef<jobject> bytearray_output_stream(env,
271 env->NewObject(bytearray_output_stream_clazz.obj(),
272 bytearray_output_stream_constructor));
274 // Create an instance of PrintStream.
275 ScopedJavaLocalRef<jclass> printstream_clazz =
276 GetClass(env, "java/io/PrintStream");
277 jmethodID printstream_constructor =
278 MethodID::Get<MethodID::TYPE_INSTANCE>(
279 env, printstream_clazz.obj(), "<init>",
280 "(Ljava/io/OutputStream;)V");
281 ScopedJavaLocalRef<jobject> printstream(env,
282 env->NewObject(printstream_clazz.obj(), printstream_constructor,
283 bytearray_output_stream.obj()));
285 // Call Throwable.printStackTrace(PrintStream)
286 env->CallVoidMethod(java_throwable, throwable_printstacktrace,
287 printstream.obj());
289 // Call ByteArrayOutputStream.toString()
290 ScopedJavaLocalRef<jstring> exception_string(
291 env, static_cast<jstring>(
292 env->CallObjectMethod(bytearray_output_stream.obj(),
293 bytearray_output_stream_tostring)));
295 return ConvertJavaStringToUTF8(exception_string);
299 } // namespace android
300 } // namespace base