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