Re-land: content: Refactor GPU memory buffer framework.
[chromium-blink-merge.git] / base / android / jni_android.cc
blobe09c2d5d150587aedfbb9d57fc294b57dcc931ad
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 JavaVM* g_jvm = NULL;
21 // Leak the global app context, as it is used from a non-joinable worker thread
22 // that may still be running at shutdown. There is no harm in doing this.
23 base::LazyInstance<base::android::ScopedJavaGlobalRef<jobject> >::Leaky
24 g_application_context = LAZY_INSTANCE_INITIALIZER;
25 base::LazyInstance<base::android::ScopedJavaGlobalRef<jobject> >::Leaky
26 g_class_loader = LAZY_INSTANCE_INITIALIZER;
27 jmethodID g_class_loader_load_class_method_id = 0;
29 std::string GetJavaExceptionInfo(JNIEnv* env, jthrowable java_throwable) {
30 ScopedJavaLocalRef<jclass> throwable_clazz =
31 GetClass(env, "java/lang/Throwable");
32 jmethodID throwable_printstacktrace =
33 MethodID::Get<MethodID::TYPE_INSTANCE>(
34 env, throwable_clazz.obj(), "printStackTrace",
35 "(Ljava/io/PrintStream;)V");
37 // Create an instance of ByteArrayOutputStream.
38 ScopedJavaLocalRef<jclass> bytearray_output_stream_clazz =
39 GetClass(env, "java/io/ByteArrayOutputStream");
40 jmethodID bytearray_output_stream_constructor =
41 MethodID::Get<MethodID::TYPE_INSTANCE>(
42 env, bytearray_output_stream_clazz.obj(), "<init>", "()V");
43 jmethodID bytearray_output_stream_tostring =
44 MethodID::Get<MethodID::TYPE_INSTANCE>(
45 env, bytearray_output_stream_clazz.obj(), "toString",
46 "()Ljava/lang/String;");
47 ScopedJavaLocalRef<jobject> bytearray_output_stream(env,
48 env->NewObject(bytearray_output_stream_clazz.obj(),
49 bytearray_output_stream_constructor));
51 // Create an instance of PrintStream.
52 ScopedJavaLocalRef<jclass> printstream_clazz =
53 GetClass(env, "java/io/PrintStream");
54 jmethodID printstream_constructor =
55 MethodID::Get<MethodID::TYPE_INSTANCE>(
56 env, printstream_clazz.obj(), "<init>",
57 "(Ljava/io/OutputStream;)V");
58 ScopedJavaLocalRef<jobject> printstream(env,
59 env->NewObject(printstream_clazz.obj(), printstream_constructor,
60 bytearray_output_stream.obj()));
62 // Call Throwable.printStackTrace(PrintStream)
63 env->CallVoidMethod(java_throwable, throwable_printstacktrace,
64 printstream.obj());
66 // Call ByteArrayOutputStream.toString()
67 ScopedJavaLocalRef<jstring> exception_string(
68 env, static_cast<jstring>(
69 env->CallObjectMethod(bytearray_output_stream.obj(),
70 bytearray_output_stream_tostring)));
72 return ConvertJavaStringToUTF8(exception_string);
75 } // namespace
77 namespace base {
78 namespace android {
80 JNIEnv* AttachCurrentThread() {
81 DCHECK(g_jvm);
82 JNIEnv* env = NULL;
83 jint ret = g_jvm->AttachCurrentThread(&env, NULL);
84 DCHECK_EQ(JNI_OK, ret);
85 return env;
88 JNIEnv* AttachCurrentThreadWithName(const std::string& thread_name) {
89 DCHECK(g_jvm);
90 JavaVMAttachArgs args;
91 args.version = JNI_VERSION_1_2;
92 args.name = thread_name.c_str();
93 args.group = NULL;
94 JNIEnv* env = NULL;
95 jint ret = g_jvm->AttachCurrentThread(&env, &args);
96 DCHECK_EQ(JNI_OK, ret);
97 return env;
100 void DetachFromVM() {
101 // Ignore the return value, if the thread is not attached, DetachCurrentThread
102 // will fail. But it is ok as the native thread may never be attached.
103 if (g_jvm)
104 g_jvm->DetachCurrentThread();
107 void InitVM(JavaVM* vm) {
108 DCHECK(!g_jvm);
109 g_jvm = vm;
112 bool IsVMInitialized() {
113 return g_jvm != NULL;
116 void InitApplicationContext(JNIEnv* env, const JavaRef<jobject>& context) {
117 if (env->IsSameObject(g_application_context.Get().obj(), context.obj())) {
118 // It's safe to set the context more than once if it's the same context.
119 return;
121 DCHECK(g_application_context.Get().is_null());
122 g_application_context.Get().Reset(context);
125 void InitReplacementClassLoader(JNIEnv* env,
126 const JavaRef<jobject>& class_loader) {
127 DCHECK(g_class_loader.Get().is_null());
128 DCHECK(!class_loader.is_null());
130 ScopedJavaLocalRef<jclass> class_loader_clazz =
131 GetClass(env, "java/lang/ClassLoader");
132 CHECK(!ClearException(env));
133 g_class_loader_load_class_method_id =
134 env->GetMethodID(class_loader_clazz.obj(),
135 "loadClass",
136 "(Ljava/lang/String;)Ljava/lang/Class;");
137 CHECK(!ClearException(env));
139 DCHECK(env->IsInstanceOf(class_loader.obj(), class_loader_clazz.obj()));
140 g_class_loader.Get().Reset(class_loader);
143 const jobject GetApplicationContext() {
144 DCHECK(!g_application_context.Get().is_null());
145 return g_application_context.Get().obj();
148 ScopedJavaLocalRef<jclass> GetClass(JNIEnv* env, const char* class_name) {
149 jclass clazz;
150 if (!g_class_loader.Get().is_null()) {
151 clazz = static_cast<jclass>(
152 env->CallObjectMethod(g_class_loader.Get().obj(),
153 g_class_loader_load_class_method_id,
154 ConvertUTF8ToJavaString(env, class_name).obj()));
155 } else {
156 clazz = env->FindClass(class_name);
158 CHECK(!ClearException(env) && clazz) << "Failed to find class " << class_name;
159 return ScopedJavaLocalRef<jclass>(env, clazz);
162 jclass LazyGetClass(
163 JNIEnv* env,
164 const char* class_name,
165 base::subtle::AtomicWord* atomic_class_id) {
166 COMPILE_ASSERT(sizeof(subtle::AtomicWord) >= sizeof(jclass),
167 AtomicWord_SmallerThan_jMethodID);
168 subtle::AtomicWord value = base::subtle::Acquire_Load(atomic_class_id);
169 if (value)
170 return reinterpret_cast<jclass>(value);
171 ScopedJavaGlobalRef<jclass> clazz;
172 clazz.Reset(GetClass(env, class_name));
173 subtle::AtomicWord null_aw = reinterpret_cast<subtle::AtomicWord>(NULL);
174 subtle::AtomicWord cas_result = base::subtle::Release_CompareAndSwap(
175 atomic_class_id,
176 null_aw,
177 reinterpret_cast<subtle::AtomicWord>(clazz.obj()));
178 if (cas_result == null_aw) {
179 // We intentionally leak the global ref since we now storing it as a raw
180 // pointer in |atomic_class_id|.
181 return clazz.Release();
182 } else {
183 return reinterpret_cast<jclass>(cas_result);
187 template<MethodID::Type type>
188 jmethodID MethodID::Get(JNIEnv* env,
189 jclass clazz,
190 const char* method_name,
191 const char* jni_signature) {
192 jmethodID id = type == TYPE_STATIC ?
193 env->GetStaticMethodID(clazz, method_name, jni_signature) :
194 env->GetMethodID(clazz, method_name, jni_signature);
195 CHECK(base::android::ClearException(env) || id) <<
196 "Failed to find " <<
197 (type == TYPE_STATIC ? "static " : "") <<
198 "method " << method_name << " " << jni_signature;
199 return id;
202 // If |atomic_method_id| set, it'll return immediately. Otherwise, it'll call
203 // into ::Get() above. If there's a race, it's ok since the values are the same
204 // (and the duplicated effort will happen only once).
205 template<MethodID::Type type>
206 jmethodID MethodID::LazyGet(JNIEnv* env,
207 jclass clazz,
208 const char* method_name,
209 const char* jni_signature,
210 base::subtle::AtomicWord* atomic_method_id) {
211 COMPILE_ASSERT(sizeof(subtle::AtomicWord) >= sizeof(jmethodID),
212 AtomicWord_SmallerThan_jMethodID);
213 subtle::AtomicWord value = base::subtle::Acquire_Load(atomic_method_id);
214 if (value)
215 return reinterpret_cast<jmethodID>(value);
216 jmethodID id = MethodID::Get<type>(env, clazz, method_name, jni_signature);
217 base::subtle::Release_Store(
218 atomic_method_id, reinterpret_cast<subtle::AtomicWord>(id));
219 return id;
222 // Various template instantiations.
223 template jmethodID MethodID::Get<MethodID::TYPE_STATIC>(
224 JNIEnv* env, jclass clazz, const char* method_name,
225 const char* jni_signature);
227 template jmethodID MethodID::Get<MethodID::TYPE_INSTANCE>(
228 JNIEnv* env, jclass clazz, const char* method_name,
229 const char* jni_signature);
231 template jmethodID MethodID::LazyGet<MethodID::TYPE_STATIC>(
232 JNIEnv* env, jclass clazz, const char* method_name,
233 const char* jni_signature, base::subtle::AtomicWord* atomic_method_id);
235 template jmethodID MethodID::LazyGet<MethodID::TYPE_INSTANCE>(
236 JNIEnv* env, jclass clazz, const char* method_name,
237 const char* jni_signature, base::subtle::AtomicWord* atomic_method_id);
239 bool HasException(JNIEnv* env) {
240 return env->ExceptionCheck() != JNI_FALSE;
243 bool ClearException(JNIEnv* env) {
244 if (!HasException(env))
245 return false;
246 env->ExceptionDescribe();
247 env->ExceptionClear();
248 return true;
251 void CheckException(JNIEnv* env) {
252 if (!HasException(env))
253 return;
255 // Exception has been found, might as well tell breakpad about it.
256 jthrowable java_throwable = env->ExceptionOccurred();
257 if (java_throwable) {
258 // Clear the pending exception, since a local reference is now held.
259 env->ExceptionDescribe();
260 env->ExceptionClear();
262 // Set the exception_string in BuildInfo so that breakpad can read it.
263 // RVO should avoid any extra copies of the exception string.
264 base::android::BuildInfo::GetInstance()->set_java_exception_info(
265 GetJavaExceptionInfo(env, java_throwable));
268 // Now, feel good about it and die.
269 CHECK(false) << "Please include Java exception stack in crash report";
272 } // namespace android
273 } // namespace base