Speculative fix for the white/stale tab issue on Windows.
[chromium-blink-merge.git] / base / android / jni_android.cc
blobfdc217049166dce27e7efc8d4ec65e7f34309efb
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/lazy_instance.h"
12 #include "base/logging.h"
13 #include "base/threading/platform_thread.h"
15 namespace {
16 using base::android::GetClass;
17 using base::android::MethodID;
18 using base::android::ScopedJavaLocalRef;
20 struct MethodIdentifier {
21 const char* class_name;
22 const char* method;
23 const char* jni_signature;
25 bool operator<(const MethodIdentifier& other) const {
26 int r = strcmp(class_name, other.class_name);
27 if (r < 0) {
28 return true;
29 } else if (r > 0) {
30 return false;
33 r = strcmp(method, other.method);
34 if (r < 0) {
35 return true;
36 } else if (r > 0) {
37 return false;
40 return strcmp(jni_signature, other.jni_signature) < 0;
44 typedef std::map<MethodIdentifier, jmethodID> MethodIDMap;
46 const base::subtle::AtomicWord kUnlocked = 0;
47 const base::subtle::AtomicWord kLocked = 1;
48 base::subtle::AtomicWord g_method_id_map_lock = kUnlocked;
49 JavaVM* g_jvm = NULL;
50 // Leak the global app context, as it is used from a non-joinable worker thread
51 // that may still be running at shutdown. There is no harm in doing this.
52 base::LazyInstance<base::android::ScopedJavaGlobalRef<jobject> >::Leaky
53 g_application_context = LAZY_INSTANCE_INITIALIZER;
54 base::LazyInstance<MethodIDMap> g_method_id_map = LAZY_INSTANCE_INITIALIZER;
56 std::string GetJavaExceptionInfo(JNIEnv* env, jthrowable java_throwable) {
57 ScopedJavaLocalRef<jclass> throwable_clazz =
58 GetClass(env, "java/lang/Throwable");
59 jmethodID throwable_printstacktrace =
60 MethodID::Get<MethodID::TYPE_INSTANCE>(
61 env, throwable_clazz.obj(), "printStackTrace",
62 "(Ljava/io/PrintStream;)V");
64 // Create an instance of ByteArrayOutputStream.
65 ScopedJavaLocalRef<jclass> bytearray_output_stream_clazz =
66 GetClass(env, "java/io/ByteArrayOutputStream");
67 jmethodID bytearray_output_stream_constructor =
68 MethodID::Get<MethodID::TYPE_INSTANCE>(
69 env, bytearray_output_stream_clazz.obj(), "<init>", "()V");
70 jmethodID bytearray_output_stream_tostring =
71 MethodID::Get<MethodID::TYPE_INSTANCE>(
72 env, bytearray_output_stream_clazz.obj(), "toString",
73 "()Ljava/lang/String;");
74 ScopedJavaLocalRef<jobject> bytearray_output_stream(env,
75 env->NewObject(bytearray_output_stream_clazz.obj(),
76 bytearray_output_stream_constructor));
78 // Create an instance of PrintStream.
79 ScopedJavaLocalRef<jclass> printstream_clazz =
80 GetClass(env, "java/io/PrintStream");
81 jmethodID printstream_constructor =
82 MethodID::Get<MethodID::TYPE_INSTANCE>(
83 env, printstream_clazz.obj(), "<init>",
84 "(Ljava/io/OutputStream;)V");
85 ScopedJavaLocalRef<jobject> printstream(env,
86 env->NewObject(printstream_clazz.obj(), printstream_constructor,
87 bytearray_output_stream.obj()));
89 // Call Throwable.printStackTrace(PrintStream)
90 env->CallVoidMethod(java_throwable, throwable_printstacktrace,
91 printstream.obj());
93 // Call ByteArrayOutputStream.toString()
94 ScopedJavaLocalRef<jstring> exception_string(
95 env, static_cast<jstring>(
96 env->CallObjectMethod(bytearray_output_stream.obj(),
97 bytearray_output_stream_tostring)));
99 return ConvertJavaStringToUTF8(exception_string);
102 } // namespace
104 namespace base {
105 namespace android {
107 JNIEnv* AttachCurrentThread() {
108 DCHECK(g_jvm);
109 JNIEnv* env = NULL;
110 jint ret = g_jvm->AttachCurrentThread(&env, NULL);
111 DCHECK_EQ(JNI_OK, ret);
112 return env;
115 void DetachFromVM() {
116 // Ignore the return value, if the thread is not attached, DetachCurrentThread
117 // will fail. But it is ok as the native thread may never be attached.
118 if (g_jvm)
119 g_jvm->DetachCurrentThread();
122 void InitVM(JavaVM* vm) {
123 DCHECK(!g_jvm);
124 g_jvm = vm;
127 void InitApplicationContext(const JavaRef<jobject>& context) {
128 DCHECK(g_application_context.Get().is_null());
129 g_application_context.Get().Reset(context);
132 const jobject GetApplicationContext() {
133 DCHECK(!g_application_context.Get().is_null());
134 return g_application_context.Get().obj();
137 ScopedJavaLocalRef<jclass> GetClass(JNIEnv* env, const char* class_name) {
138 return ScopedJavaLocalRef<jclass>(env, GetUnscopedClass(env, class_name));
141 jclass GetUnscopedClass(JNIEnv* env, const char* class_name) {
142 jclass clazz = env->FindClass(class_name);
143 CHECK(!ClearException(env) && clazz) << "Failed to find class " << class_name;
144 return clazz;
147 bool HasClass(JNIEnv* env, const char* class_name) {
148 ScopedJavaLocalRef<jclass> clazz(env, env->FindClass(class_name));
149 if (!clazz.obj()) {
150 ClearException(env);
151 return false;
153 bool error = ClearException(env);
154 DCHECK(!error);
155 return true;
158 template<MethodID::Type type>
159 jmethodID MethodID::Get(JNIEnv* env,
160 jclass clazz,
161 const char* method_name,
162 const char* jni_signature) {
163 jmethodID id = type == TYPE_STATIC ?
164 env->GetStaticMethodID(clazz, method_name, jni_signature) :
165 env->GetMethodID(clazz, method_name, jni_signature);
166 CHECK(base::android::ClearException(env) || id) <<
167 "Failed to find " <<
168 (type == TYPE_STATIC ? "static " : "") <<
169 "method " << method_name << " " << jni_signature;
170 return id;
173 // If |atomic_method_id| set, it'll return immediately. Otherwise, it'll call
174 // into ::Get() above. If there's a race, it's ok since the values are the same
175 // (and the duplicated effort will happen only once).
176 template<MethodID::Type type>
177 jmethodID MethodID::LazyGet(JNIEnv* env,
178 jclass clazz,
179 const char* method_name,
180 const char* jni_signature,
181 base::subtle::AtomicWord* atomic_method_id) {
182 COMPILE_ASSERT(sizeof(subtle::AtomicWord) >= sizeof(jmethodID),
183 AtomicWord_SmallerThan_jMethodID);
184 subtle::AtomicWord value = base::subtle::Acquire_Load(atomic_method_id);
185 if (value)
186 return reinterpret_cast<jmethodID>(value);
187 jmethodID id = MethodID::Get<type>(env, clazz, method_name, jni_signature);
188 base::subtle::Release_Store(
189 atomic_method_id, reinterpret_cast<subtle::AtomicWord>(id));
190 return id;
193 // Various template instantiations.
194 template jmethodID MethodID::Get<MethodID::TYPE_STATIC>(
195 JNIEnv* env, jclass clazz, const char* method_name,
196 const char* jni_signature);
198 template jmethodID MethodID::Get<MethodID::TYPE_INSTANCE>(
199 JNIEnv* env, jclass clazz, const char* method_name,
200 const char* jni_signature);
202 template jmethodID MethodID::LazyGet<MethodID::TYPE_STATIC>(
203 JNIEnv* env, jclass clazz, const char* method_name,
204 const char* jni_signature, base::subtle::AtomicWord* atomic_method_id);
206 template jmethodID MethodID::LazyGet<MethodID::TYPE_INSTANCE>(
207 JNIEnv* env, jclass clazz, const char* method_name,
208 const char* jni_signature, base::subtle::AtomicWord* atomic_method_id);
210 jfieldID GetFieldID(JNIEnv* env,
211 const JavaRef<jclass>& clazz,
212 const char* field_name,
213 const char* jni_signature) {
214 jfieldID field_id = env->GetFieldID(clazz.obj(), field_name, jni_signature);
215 CHECK(!ClearException(env) && field_id) << "Failed to find field " <<
216 field_name << " " << jni_signature;
217 return field_id;
220 bool HasField(JNIEnv* env,
221 const JavaRef<jclass>& clazz,
222 const char* field_name,
223 const char* jni_signature) {
224 jfieldID field_id = env->GetFieldID(clazz.obj(), field_name, jni_signature);
225 if (!field_id) {
226 ClearException(env);
227 return false;
229 bool error = ClearException(env);
230 DCHECK(!error);
231 return true;
234 jfieldID GetStaticFieldID(JNIEnv* env,
235 const JavaRef<jclass>& clazz,
236 const char* field_name,
237 const char* jni_signature) {
238 jfieldID field_id =
239 env->GetStaticFieldID(clazz.obj(), field_name, jni_signature);
240 CHECK(!ClearException(env) && field_id) << "Failed to find static field " <<
241 field_name << " " << jni_signature;
242 return field_id;
245 jmethodID GetMethodIDFromClassName(JNIEnv* env,
246 const char* class_name,
247 const char* method,
248 const char* jni_signature) {
249 MethodIdentifier key;
250 key.class_name = class_name;
251 key.method = method;
252 key.jni_signature = jni_signature;
254 MethodIDMap* map = g_method_id_map.Pointer();
255 bool found = false;
257 while (base::subtle::Acquire_CompareAndSwap(&g_method_id_map_lock,
258 kUnlocked,
259 kLocked) != kUnlocked) {
260 base::PlatformThread::YieldCurrentThread();
262 MethodIDMap::const_iterator iter = map->find(key);
263 if (iter != map->end()) {
264 found = true;
266 base::subtle::Release_Store(&g_method_id_map_lock, kUnlocked);
268 // Addition to the map does not invalidate this iterator.
269 if (found) {
270 return iter->second;
273 ScopedJavaLocalRef<jclass> clazz(env, env->FindClass(class_name));
274 jmethodID id = MethodID::Get<MethodID::TYPE_INSTANCE>(
275 env, clazz.obj(), method, jni_signature);
277 while (base::subtle::Acquire_CompareAndSwap(&g_method_id_map_lock,
278 kUnlocked,
279 kLocked) != kUnlocked) {
280 base::PlatformThread::YieldCurrentThread();
282 // Another thread may have populated the map already.
283 std::pair<MethodIDMap::const_iterator, bool> result =
284 map->insert(std::make_pair(key, id));
285 DCHECK_EQ(id, result.first->second);
286 base::subtle::Release_Store(&g_method_id_map_lock, kUnlocked);
288 return id;
291 bool HasException(JNIEnv* env) {
292 return env->ExceptionCheck() != JNI_FALSE;
295 bool ClearException(JNIEnv* env) {
296 if (!HasException(env))
297 return false;
298 env->ExceptionDescribe();
299 env->ExceptionClear();
300 return true;
303 void CheckException(JNIEnv* env) {
304 if (!HasException(env)) return;
306 // Exception has been found, might as well tell breakpad about it.
307 jthrowable java_throwable = env->ExceptionOccurred();
308 if (!java_throwable) {
309 // Do nothing but return false.
310 CHECK(false);
313 // Clear the pending exception, since a local reference is now held.
314 env->ExceptionDescribe();
315 env->ExceptionClear();
317 // Set the exception_string in BuildInfo so that breakpad can read it.
318 // RVO should avoid any extra copies of the exception string.
319 base::android::BuildInfo::GetInstance()->set_java_exception_info(
320 GetJavaExceptionInfo(env, java_throwable));
322 // Now, feel good about it and die.
323 CHECK(false);
326 } // namespace android
327 } // namespace base