| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "base/android/jni_android.h" | 5 #include "base/android/jni_android.h" |
| 6 | 6 |
| 7 #include <map> | 7 #include <map> |
| 8 | 8 |
| 9 #include "base/android/build_info.h" | 9 #include "base/android/build_info.h" |
| 10 #include "base/android/jni_string.h" | 10 #include "base/android/jni_string.h" |
| 11 #include "base/atomicops.h" | |
| 12 #include "base/lazy_instance.h" | 11 #include "base/lazy_instance.h" |
| 13 #include "base/logging.h" | 12 #include "base/logging.h" |
| 14 #include "base/threading/platform_thread.h" | 13 #include "base/threading/platform_thread.h" |
| 15 | 14 |
| 16 namespace { | 15 namespace { |
| 17 using base::android::GetClass; | 16 using base::android::GetClass; |
| 18 using base::android::GetMethodID; | 17 using base::android::MethodID; |
| 19 using base::android::ScopedJavaLocalRef; | 18 using base::android::ScopedJavaLocalRef; |
| 20 | 19 |
| 21 struct MethodIdentifier { | 20 struct MethodIdentifier { |
| 22 const char* class_name; | 21 const char* class_name; |
| 23 const char* method; | 22 const char* method; |
| 24 const char* jni_signature; | 23 const char* jni_signature; |
| 25 | 24 |
| 26 bool operator<(const MethodIdentifier& other) const { | 25 bool operator<(const MethodIdentifier& other) const { |
| 27 int r = strcmp(class_name, other.class_name); | 26 int r = strcmp(class_name, other.class_name); |
| 28 if (r < 0) { | 27 if (r < 0) { |
| (...skipping 22 matching lines...) Expand all Loading... |
| 51 // Leak the global app context, as it is used from a non-joinable worker thread | 50 // Leak the global app context, as it is used from a non-joinable worker thread |
| 52 // that may still be running at shutdown. There is no harm in doing this. | 51 // that may still be running at shutdown. There is no harm in doing this. |
| 53 base::LazyInstance<base::android::ScopedJavaGlobalRef<jobject> >::Leaky | 52 base::LazyInstance<base::android::ScopedJavaGlobalRef<jobject> >::Leaky |
| 54 g_application_context = LAZY_INSTANCE_INITIALIZER; | 53 g_application_context = LAZY_INSTANCE_INITIALIZER; |
| 55 base::LazyInstance<MethodIDMap> g_method_id_map = LAZY_INSTANCE_INITIALIZER; | 54 base::LazyInstance<MethodIDMap> g_method_id_map = LAZY_INSTANCE_INITIALIZER; |
| 56 | 55 |
| 57 std::string GetJavaExceptionInfo(JNIEnv* env, jthrowable java_throwable) { | 56 std::string GetJavaExceptionInfo(JNIEnv* env, jthrowable java_throwable) { |
| 58 ScopedJavaLocalRef<jclass> throwable_clazz = | 57 ScopedJavaLocalRef<jclass> throwable_clazz = |
| 59 GetClass(env, "java/lang/Throwable"); | 58 GetClass(env, "java/lang/Throwable"); |
| 60 jmethodID throwable_printstacktrace = | 59 jmethodID throwable_printstacktrace = |
| 61 GetMethodID(env, throwable_clazz, "printStackTrace", | 60 MethodID::Get<MethodID::TYPE_INSTANCE>( |
| 62 "(Ljava/io/PrintStream;)V"); | 61 env, throwable_clazz.obj(), "printStackTrace", |
| 62 "(Ljava/io/PrintStream;)V"); |
| 63 | 63 |
| 64 // Create an instance of ByteArrayOutputStream. | 64 // Create an instance of ByteArrayOutputStream. |
| 65 ScopedJavaLocalRef<jclass> bytearray_output_stream_clazz = | 65 ScopedJavaLocalRef<jclass> bytearray_output_stream_clazz = |
| 66 GetClass(env, "java/io/ByteArrayOutputStream"); | 66 GetClass(env, "java/io/ByteArrayOutputStream"); |
| 67 jmethodID bytearray_output_stream_constructor = | 67 jmethodID bytearray_output_stream_constructor = |
| 68 GetMethodID(env, bytearray_output_stream_clazz, "<init>", "()V"); | 68 MethodID::Get<MethodID::TYPE_INSTANCE>( |
| 69 env, bytearray_output_stream_clazz.obj(), "<init>", "()V"); |
| 69 jmethodID bytearray_output_stream_tostring = | 70 jmethodID bytearray_output_stream_tostring = |
| 70 GetMethodID(env, bytearray_output_stream_clazz, "toString", | 71 MethodID::Get<MethodID::TYPE_INSTANCE>( |
| 71 "()Ljava/lang/String;"); | 72 env, bytearray_output_stream_clazz.obj(), "toString", |
| 73 "()Ljava/lang/String;"); |
| 72 ScopedJavaLocalRef<jobject> bytearray_output_stream(env, | 74 ScopedJavaLocalRef<jobject> bytearray_output_stream(env, |
| 73 env->NewObject(bytearray_output_stream_clazz.obj(), | 75 env->NewObject(bytearray_output_stream_clazz.obj(), |
| 74 bytearray_output_stream_constructor)); | 76 bytearray_output_stream_constructor)); |
| 75 | 77 |
| 76 // Create an instance of PrintStream. | 78 // Create an instance of PrintStream. |
| 77 ScopedJavaLocalRef<jclass> printstream_clazz = | 79 ScopedJavaLocalRef<jclass> printstream_clazz = |
| 78 GetClass(env, "java/io/PrintStream"); | 80 GetClass(env, "java/io/PrintStream"); |
| 79 jmethodID printstream_constructor = | 81 jmethodID printstream_constructor = |
| 80 GetMethodID(env, printstream_clazz, "<init>", | 82 MethodID::Get<MethodID::TYPE_INSTANCE>( |
| 81 "(Ljava/io/OutputStream;)V"); | 83 env, printstream_clazz.obj(), "<init>", |
| 84 "(Ljava/io/OutputStream;)V"); |
| 82 ScopedJavaLocalRef<jobject> printstream(env, | 85 ScopedJavaLocalRef<jobject> printstream(env, |
| 83 env->NewObject(printstream_clazz.obj(), printstream_constructor, | 86 env->NewObject(printstream_clazz.obj(), printstream_constructor, |
| 84 bytearray_output_stream.obj())); | 87 bytearray_output_stream.obj())); |
| 85 | 88 |
| 86 // Call Throwable.printStackTrace(PrintStream) | 89 // Call Throwable.printStackTrace(PrintStream) |
| 87 env->CallVoidMethod(java_throwable, throwable_printstacktrace, | 90 env->CallVoidMethod(java_throwable, throwable_printstacktrace, |
| 88 printstream.obj()); | 91 printstream.obj()); |
| 89 | 92 |
| 90 // Call ByteArrayOutputStream.toString() | 93 // Call ByteArrayOutputStream.toString() |
| 91 ScopedJavaLocalRef<jstring> exception_string( | 94 ScopedJavaLocalRef<jstring> exception_string( |
| 92 env, static_cast<jstring>( | 95 env, static_cast<jstring>( |
| 93 env->CallObjectMethod(bytearray_output_stream.obj(), | 96 env->CallObjectMethod(bytearray_output_stream.obj(), |
| 94 bytearray_output_stream_tostring))); | 97 bytearray_output_stream_tostring))); |
| 95 | 98 |
| 96 return ConvertJavaStringToUTF8(exception_string); | 99 return ConvertJavaStringToUTF8(exception_string); |
| 97 } | 100 } |
| 98 | 101 |
| 99 enum MethodType { | |
| 100 METHODTYPE_STATIC, | |
| 101 METHODTYPE_NORMAL, | |
| 102 }; | |
| 103 | |
| 104 enum ExceptionCheck { | |
| 105 EXCEPTIONCHECK_YES, | |
| 106 EXCEPTIONCHECK_NO, | |
| 107 }; | |
| 108 | |
| 109 template<MethodType method_type, ExceptionCheck exception_check> | |
| 110 jmethodID GetMethodIDInternal(JNIEnv* env, | |
| 111 jclass clazz, | |
| 112 const char* method_name, | |
| 113 const char* jni_signature) { | |
| 114 jmethodID method_id = method_type == METHODTYPE_STATIC ? | |
| 115 env->GetStaticMethodID(clazz, method_name, jni_signature) : | |
| 116 env->GetMethodID(clazz, method_name, jni_signature); | |
| 117 if (exception_check == EXCEPTIONCHECK_YES) { | |
| 118 CHECK(!base::android::ClearException(env) && method_id) << | |
| 119 "Failed to find " << | |
| 120 (method_type == METHODTYPE_STATIC ? "static " : "") << | |
| 121 "method " << method_name << " " << jni_signature; | |
| 122 } else if (base::android::HasException(env)) { | |
| 123 env->ExceptionClear(); | |
| 124 } | |
| 125 return method_id; | |
| 126 } | |
| 127 | |
| 128 } // namespace | 102 } // namespace |
| 129 | 103 |
| 130 namespace base { | 104 namespace base { |
| 131 namespace android { | 105 namespace android { |
| 132 | 106 |
| 133 JNIEnv* AttachCurrentThread() { | 107 JNIEnv* AttachCurrentThread() { |
| 134 DCHECK(g_jvm); | 108 DCHECK(g_jvm); |
| 135 JNIEnv* env = NULL; | 109 JNIEnv* env = NULL; |
| 136 jint ret = g_jvm->AttachCurrentThread(&env, NULL); | 110 jint ret = g_jvm->AttachCurrentThread(&env, NULL); |
| 137 DCHECK_EQ(JNI_OK, ret); | 111 DCHECK_EQ(JNI_OK, ret); |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 174 ScopedJavaLocalRef<jclass> clazz(env, env->FindClass(class_name)); | 148 ScopedJavaLocalRef<jclass> clazz(env, env->FindClass(class_name)); |
| 175 if (!clazz.obj()) { | 149 if (!clazz.obj()) { |
| 176 ClearException(env); | 150 ClearException(env); |
| 177 return false; | 151 return false; |
| 178 } | 152 } |
| 179 bool error = ClearException(env); | 153 bool error = ClearException(env); |
| 180 DCHECK(!error); | 154 DCHECK(!error); |
| 181 return true; | 155 return true; |
| 182 } | 156 } |
| 183 | 157 |
| 184 jmethodID GetMethodID(JNIEnv* env, | 158 template<MethodID::Type type> |
| 185 const JavaRef<jclass>& clazz, | 159 jmethodID MethodID::Get(JNIEnv* env, |
| 186 const char* method_name, | 160 jclass clazz, |
| 187 const char* jni_signature) { | 161 const char* method_name, |
| 188 // clazz.env() can not be used as that may be from a different thread. | 162 const char* jni_signature) { |
| 189 return GetMethodID(env, clazz.obj(), method_name, 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; |
| 190 } | 171 } |
| 191 | 172 |
| 192 jmethodID GetMethodID(JNIEnv* env, | 173 // If |atomic_method_id| set, it'll return immediately. Otherwise, it'll call |
| 193 jclass clazz, | 174 // into ::Get() above. If there's a race, it's ok since the values are the same |
| 194 const char* method_name, | 175 // (and the duplicated effort will happen only once). |
| 195 const char* jni_signature) { | 176 template<MethodID::Type type> |
| 196 return GetMethodIDInternal<METHODTYPE_NORMAL, EXCEPTIONCHECK_YES>( | 177 jmethodID MethodID::LazyGet(JNIEnv* env, |
| 197 env, clazz, method_name, jni_signature); | 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; |
| 198 } | 191 } |
| 199 | 192 |
| 200 jmethodID GetMethodIDOrNull(JNIEnv* env, | 193 // Various template instantiations. |
| 201 jclass clazz, | 194 template jmethodID MethodID::Get<MethodID::TYPE_STATIC>( |
| 202 const char* method_name, | 195 JNIEnv* env, jclass clazz, const char* method_name, |
| 203 const char* jni_signature) { | 196 const char* jni_signature); |
| 204 return GetMethodIDInternal<METHODTYPE_NORMAL, EXCEPTIONCHECK_NO>( | |
| 205 env, clazz, method_name, jni_signature); | |
| 206 } | |
| 207 | 197 |
| 208 jmethodID GetStaticMethodID(JNIEnv* env, | 198 template jmethodID MethodID::Get<MethodID::TYPE_INSTANCE>( |
| 209 const JavaRef<jclass>& clazz, | 199 JNIEnv* env, jclass clazz, const char* method_name, |
| 210 const char* method_name, | 200 const char* jni_signature); |
| 211 const char* jni_signature) { | |
| 212 return GetStaticMethodID(env, clazz.obj(), method_name, | |
| 213 jni_signature); | |
| 214 } | |
| 215 | 201 |
| 216 jmethodID GetStaticMethodID(JNIEnv* env, | 202 template jmethodID MethodID::LazyGet<MethodID::TYPE_STATIC>( |
| 217 jclass clazz, | 203 JNIEnv* env, jclass clazz, const char* method_name, |
| 218 const char* method_name, | 204 const char* jni_signature, base::subtle::AtomicWord* atomic_method_id); |
| 219 const char* jni_signature) { | |
| 220 return GetMethodIDInternal<METHODTYPE_STATIC, EXCEPTIONCHECK_YES>( | |
| 221 env, clazz, method_name, jni_signature); | |
| 222 } | |
| 223 | 205 |
| 224 jmethodID GetStaticMethodIDOrNull(JNIEnv* env, | 206 template jmethodID MethodID::LazyGet<MethodID::TYPE_INSTANCE>( |
| 225 jclass clazz, | 207 JNIEnv* env, jclass clazz, const char* method_name, |
| 226 const char* method_name, | 208 const char* jni_signature, base::subtle::AtomicWord* atomic_method_id); |
| 227 const char* jni_signature) { | |
| 228 return GetMethodIDInternal<METHODTYPE_STATIC, EXCEPTIONCHECK_NO>( | |
| 229 env, clazz, method_name, jni_signature); | |
| 230 } | |
| 231 | |
| 232 bool HasMethod(JNIEnv* env, | |
| 233 const JavaRef<jclass>& clazz, | |
| 234 const char* method_name, | |
| 235 const char* jni_signature) { | |
| 236 jmethodID method_id = | |
| 237 env->GetMethodID(clazz.obj(), method_name, jni_signature); | |
| 238 if (!method_id) { | |
| 239 ClearException(env); | |
| 240 return false; | |
| 241 } | |
| 242 bool error = ClearException(env); | |
| 243 DCHECK(!error); | |
| 244 return true; | |
| 245 } | |
| 246 | 209 |
| 247 jfieldID GetFieldID(JNIEnv* env, | 210 jfieldID GetFieldID(JNIEnv* env, |
| 248 const JavaRef<jclass>& clazz, | 211 const JavaRef<jclass>& clazz, |
| 249 const char* field_name, | 212 const char* field_name, |
| 250 const char* jni_signature) { | 213 const char* jni_signature) { |
| 251 jfieldID field_id = env->GetFieldID(clazz.obj(), field_name, jni_signature); | 214 jfieldID field_id = env->GetFieldID(clazz.obj(), field_name, jni_signature); |
| 252 CHECK(!ClearException(env) && field_id) << "Failed to find field " << | 215 CHECK(!ClearException(env) && field_id) << "Failed to find field " << |
| 253 field_name << " " << jni_signature; | 216 field_name << " " << jni_signature; |
| 254 return field_id; | 217 return field_id; |
| 255 } | 218 } |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 301 found = true; | 264 found = true; |
| 302 } | 265 } |
| 303 base::subtle::Release_Store(&g_method_id_map_lock, kUnlocked); | 266 base::subtle::Release_Store(&g_method_id_map_lock, kUnlocked); |
| 304 | 267 |
| 305 // Addition to the map does not invalidate this iterator. | 268 // Addition to the map does not invalidate this iterator. |
| 306 if (found) { | 269 if (found) { |
| 307 return iter->second; | 270 return iter->second; |
| 308 } | 271 } |
| 309 | 272 |
| 310 ScopedJavaLocalRef<jclass> clazz(env, env->FindClass(class_name)); | 273 ScopedJavaLocalRef<jclass> clazz(env, env->FindClass(class_name)); |
| 311 jmethodID id = GetMethodID(env, clazz, method, jni_signature); | 274 jmethodID id = MethodID::Get<MethodID::TYPE_INSTANCE>( |
| 275 env, clazz.obj(), method, jni_signature); |
| 312 | 276 |
| 313 while (base::subtle::Acquire_CompareAndSwap(&g_method_id_map_lock, | 277 while (base::subtle::Acquire_CompareAndSwap(&g_method_id_map_lock, |
| 314 kUnlocked, | 278 kUnlocked, |
| 315 kLocked) != kUnlocked) { | 279 kLocked) != kUnlocked) { |
| 316 base::PlatformThread::YieldCurrentThread(); | 280 base::PlatformThread::YieldCurrentThread(); |
| 317 } | 281 } |
| 318 // Another thread may have populated the map already. | 282 // Another thread may have populated the map already. |
| 319 std::pair<MethodIDMap::const_iterator, bool> result = | 283 std::pair<MethodIDMap::const_iterator, bool> result = |
| 320 map->insert(std::make_pair(key, id)); | 284 map->insert(std::make_pair(key, id)); |
| 321 DCHECK_EQ(id, result.first->second); | 285 DCHECK_EQ(id, result.first->second); |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 354 // RVO should avoid any extra copies of the exception string. | 318 // RVO should avoid any extra copies of the exception string. |
| 355 base::android::BuildInfo::GetInstance()->set_java_exception_info( | 319 base::android::BuildInfo::GetInstance()->set_java_exception_info( |
| 356 GetJavaExceptionInfo(env, java_throwable)); | 320 GetJavaExceptionInfo(env, java_throwable)); |
| 357 | 321 |
| 358 // Now, feel good about it and die. | 322 // Now, feel good about it and die. |
| 359 CHECK(false); | 323 CHECK(false); |
| 360 } | 324 } |
| 361 | 325 |
| 362 } // namespace android | 326 } // namespace android |
| 363 } // namespace base | 327 } // namespace base |
| OLD | NEW |