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