• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
GetJavaExceptionInfo(JNIEnv * env,jthrowable java_throwable)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 
AttachCurrentThread()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 
DetachFromVM()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 
InitVM(JavaVM * vm)91 void InitVM(JavaVM* vm) {
92   DCHECK(!g_jvm);
93   g_jvm = vm;
94 }
95 
IsVMInitialized()96 bool IsVMInitialized() {
97   return g_jvm != NULL;
98 }
99 
InitApplicationContext(JNIEnv * env,const JavaRef<jobject> & context)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 
GetApplicationContext()109 const jobject GetApplicationContext() {
110   DCHECK(!g_application_context.Get().is_null());
111   return g_application_context.Get().obj();
112 }
113 
GetClass(JNIEnv * env,const char * class_name)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>
Get(JNIEnv * env,jclass clazz,const char * method_name,const char * jni_signature)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>
LazyGet(JNIEnv * env,jclass clazz,const char * method_name,const char * jni_signature,base::subtle::AtomicWord * atomic_method_id)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 
HasException(JNIEnv * env)172 bool HasException(JNIEnv* env) {
173   return env->ExceptionCheck() != JNI_FALSE;
174 }
175 
ClearException(JNIEnv * env)176 bool ClearException(JNIEnv* env) {
177   if (!HasException(env))
178     return false;
179   env->ExceptionDescribe();
180   env->ExceptionClear();
181   return true;
182 }
183 
CheckException(JNIEnv * env)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