jni_android.cc revision 6d86b77056ed63eb6871182f42a9fd5f07550f90
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. 4 5#include "base/android/jni_android.h" 6 7#include <map> 8 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 14namespace { 15using base::android::GetClass; 16using base::android::MethodID; 17using base::android::ScopedJavaLocalRef; 18 19JavaVM* g_jvm = NULL; 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. 22base::LazyInstance<base::android::ScopedJavaGlobalRef<jobject> >::Leaky 23 g_application_context = LAZY_INSTANCE_INITIALIZER; 24 25std::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"); 32 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)); 46 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())); 57 58 // Call Throwable.printStackTrace(PrintStream) 59 env->CallVoidMethod(java_throwable, throwable_printstacktrace, 60 printstream.obj()); 61 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))); 67 68 return ConvertJavaStringToUTF8(exception_string); 69} 70 71} // namespace 72 73namespace base { 74namespace android { 75 76JNIEnv* AttachCurrentThread() { 77 DCHECK(g_jvm); 78 JNIEnv* env = NULL; 79 jint ret = g_jvm->AttachCurrentThread(&env, NULL); 80 DCHECK_EQ(JNI_OK, ret); 81 return env; 82} 83 84JNIEnv* AttachCurrentThreadWithName(const std::string& thread_name) { 85 DCHECK(g_jvm); 86 JavaVMAttachArgs args; 87 args.version = JNI_VERSION_1_2; 88 args.name = thread_name.c_str(); 89 args.group = NULL; 90 JNIEnv* env = NULL; 91 jint ret = g_jvm->AttachCurrentThread(&env, &args); 92 DCHECK_EQ(JNI_OK, ret); 93 return env; 94} 95 96void DetachFromVM() { 97 // Ignore the return value, if the thread is not attached, DetachCurrentThread 98 // will fail. But it is ok as the native thread may never be attached. 99 if (g_jvm) 100 g_jvm->DetachCurrentThread(); 101} 102 103void InitVM(JavaVM* vm) { 104 DCHECK(!g_jvm); 105 g_jvm = vm; 106} 107 108bool IsVMInitialized() { 109 return g_jvm != NULL; 110} 111 112void InitApplicationContext(JNIEnv* env, const JavaRef<jobject>& context) { 113 if (env->IsSameObject(g_application_context.Get().obj(), context.obj())) { 114 // It's safe to set the context more than once if it's the same context. 115 return; 116 } 117 DCHECK(g_application_context.Get().is_null()); 118 g_application_context.Get().Reset(context); 119} 120 121const jobject GetApplicationContext() { 122 DCHECK(!g_application_context.Get().is_null()); 123 return g_application_context.Get().obj(); 124} 125 126ScopedJavaLocalRef<jclass> GetClass(JNIEnv* env, const char* class_name) { 127 jclass clazz = env->FindClass(class_name); 128 CHECK(!ClearException(env) && clazz) << "Failed to find class " << class_name; 129 return ScopedJavaLocalRef<jclass>(env, clazz); 130} 131 132template<MethodID::Type type> 133jmethodID MethodID::Get(JNIEnv* env, 134 jclass clazz, 135 const char* method_name, 136 const char* jni_signature) { 137 jmethodID id = type == TYPE_STATIC ? 138 env->GetStaticMethodID(clazz, method_name, jni_signature) : 139 env->GetMethodID(clazz, method_name, jni_signature); 140 CHECK(base::android::ClearException(env) || id) << 141 "Failed to find " << 142 (type == TYPE_STATIC ? "static " : "") << 143 "method " << method_name << " " << jni_signature; 144 return id; 145} 146 147// If |atomic_method_id| set, it'll return immediately. Otherwise, it'll call 148// into ::Get() above. If there's a race, it's ok since the values are the same 149// (and the duplicated effort will happen only once). 150template<MethodID::Type type> 151jmethodID MethodID::LazyGet(JNIEnv* env, 152 jclass clazz, 153 const char* method_name, 154 const char* jni_signature, 155 base::subtle::AtomicWord* atomic_method_id) { 156 COMPILE_ASSERT(sizeof(subtle::AtomicWord) >= sizeof(jmethodID), 157 AtomicWord_SmallerThan_jMethodID); 158 subtle::AtomicWord value = base::subtle::Acquire_Load(atomic_method_id); 159 if (value) 160 return reinterpret_cast<jmethodID>(value); 161 jmethodID id = MethodID::Get<type>(env, clazz, method_name, jni_signature); 162 base::subtle::Release_Store( 163 atomic_method_id, reinterpret_cast<subtle::AtomicWord>(id)); 164 return id; 165} 166 167// Various template instantiations. 168template jmethodID MethodID::Get<MethodID::TYPE_STATIC>( 169 JNIEnv* env, jclass clazz, const char* method_name, 170 const char* jni_signature); 171 172template jmethodID MethodID::Get<MethodID::TYPE_INSTANCE>( 173 JNIEnv* env, jclass clazz, const char* method_name, 174 const char* jni_signature); 175 176template jmethodID MethodID::LazyGet<MethodID::TYPE_STATIC>( 177 JNIEnv* env, jclass clazz, const char* method_name, 178 const char* jni_signature, base::subtle::AtomicWord* atomic_method_id); 179 180template jmethodID MethodID::LazyGet<MethodID::TYPE_INSTANCE>( 181 JNIEnv* env, jclass clazz, const char* method_name, 182 const char* jni_signature, base::subtle::AtomicWord* atomic_method_id); 183 184bool HasException(JNIEnv* env) { 185 return env->ExceptionCheck() != JNI_FALSE; 186} 187 188bool ClearException(JNIEnv* env) { 189 if (!HasException(env)) 190 return false; 191 env->ExceptionDescribe(); 192 env->ExceptionClear(); 193 return true; 194} 195 196void CheckException(JNIEnv* env) { 197 if (!HasException(env)) return; 198 199 // Exception has been found, might as well tell breakpad about it. 200 jthrowable java_throwable = env->ExceptionOccurred(); 201 if (!java_throwable) { 202 // Do nothing but return false. 203 CHECK(false); 204 } 205 206 // Clear the pending exception, since a local reference is now held. 207 env->ExceptionDescribe(); 208 env->ExceptionClear(); 209 210 // Set the exception_string in BuildInfo so that breakpad can read it. 211 // RVO should avoid any extra copies of the exception string. 212 base::android::BuildInfo::GetInstance()->set_java_exception_info( 213 GetJavaExceptionInfo(env, java_throwable)); 214 215 // Now, feel good about it and die. 216 CHECK(false); 217} 218 219} // namespace android 220} // namespace base 221