Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(557)

Side by Side Diff: base/android/jni_android.cc

Issue 1647803004: Move base to DEPS (Closed) Base URL: git@github.com:domokit/mojo.git@master
Patch Set: Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « base/android/jni_android.h ('k') | base/android/jni_android_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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/android/jni_utils.h"
12 #include "base/lazy_instance.h"
13 #include "base/logging.h"
14
15 namespace {
16 using base::android::GetClass;
17 using base::android::MethodID;
18 using base::android::ScopedJavaLocalRef;
19
20 bool g_disable_manual_jni_registration = false;
21
22 JavaVM* g_jvm = NULL;
23 // Leak the global app context, as it is used from a non-joinable worker thread
24 // that may still be running at shutdown. There is no harm in doing this.
25 base::LazyInstance<base::android::ScopedJavaGlobalRef<jobject> >::Leaky
26 g_application_context = LAZY_INSTANCE_INITIALIZER;
27 base::LazyInstance<base::android::ScopedJavaGlobalRef<jobject> >::Leaky
28 g_class_loader = LAZY_INSTANCE_INITIALIZER;
29 jmethodID g_class_loader_load_class_method_id = 0;
30
31 } // namespace
32
33 namespace base {
34 namespace android {
35
36 bool IsManualJniRegistrationDisabled() {
37 return g_disable_manual_jni_registration;
38 }
39
40 void DisableManualJniRegistration() {
41 DCHECK(!g_disable_manual_jni_registration);
42 g_disable_manual_jni_registration = true;
43 }
44
45 JNIEnv* AttachCurrentThread() {
46 DCHECK(g_jvm);
47 JNIEnv* env = NULL;
48 jint ret = g_jvm->AttachCurrentThread(&env, NULL);
49 DCHECK_EQ(JNI_OK, ret);
50 return env;
51 }
52
53 JNIEnv* AttachCurrentThreadWithName(const std::string& thread_name) {
54 DCHECK(g_jvm);
55 JavaVMAttachArgs args;
56 args.version = JNI_VERSION_1_2;
57 args.name = thread_name.c_str();
58 args.group = NULL;
59 JNIEnv* env = NULL;
60 jint ret = g_jvm->AttachCurrentThread(&env, &args);
61 DCHECK_EQ(JNI_OK, ret);
62 return env;
63 }
64
65 void DetachFromVM() {
66 // Ignore the return value, if the thread is not attached, DetachCurrentThread
67 // will fail. But it is ok as the native thread may never be attached.
68 if (g_jvm)
69 g_jvm->DetachCurrentThread();
70 }
71
72 void InitVM(JavaVM* vm) {
73 DCHECK(!g_jvm);
74 g_jvm = vm;
75 }
76
77 bool IsVMInitialized() {
78 return g_jvm != NULL;
79 }
80
81 void InitApplicationContext(JNIEnv* env, const JavaRef<jobject>& context) {
82 if (env->IsSameObject(g_application_context.Get().obj(), context.obj())) {
83 // It's safe to set the context more than once if it's the same context.
84 return;
85 }
86 DCHECK(g_application_context.Get().is_null());
87 g_application_context.Get().Reset(context);
88 }
89
90 void InitReplacementClassLoader(JNIEnv* env,
91 const JavaRef<jobject>& class_loader) {
92 DCHECK(g_class_loader.Get().is_null());
93 DCHECK(!class_loader.is_null());
94
95 ScopedJavaLocalRef<jclass> class_loader_clazz =
96 GetClass(env, "java/lang/ClassLoader");
97 CHECK(!ClearException(env));
98 g_class_loader_load_class_method_id =
99 env->GetMethodID(class_loader_clazz.obj(),
100 "loadClass",
101 "(Ljava/lang/String;)Ljava/lang/Class;");
102 CHECK(!ClearException(env));
103
104 DCHECK(env->IsInstanceOf(class_loader.obj(), class_loader_clazz.obj()));
105 g_class_loader.Get().Reset(class_loader);
106 }
107
108 const jobject GetApplicationContext() {
109 DCHECK(!g_application_context.Get().is_null());
110 return g_application_context.Get().obj();
111 }
112
113 ScopedJavaLocalRef<jclass> GetClass(JNIEnv* env, const char* class_name) {
114 jclass clazz;
115 if (!g_class_loader.Get().is_null()) {
116 // ClassLoader.loadClass expects a classname with components separated by
117 // dots instead of the slashes that JNIEnv::FindClass expects. The JNI
118 // generator generates names with slashes, so we have to replace them here.
119 // TODO(torne): move to an approach where we always use ClassLoader except
120 // for the special case of base::android::GetClassLoader(), and change the
121 // JNI generator to generate dot-separated names. http://crbug.com/461773
122 size_t bufsize = strlen(class_name) + 1;
123 char dotted_name[bufsize];
124 memmove(dotted_name, class_name, bufsize);
125 for (size_t i = 0; i < bufsize; ++i) {
126 if (dotted_name[i] == '/') {
127 dotted_name[i] = '.';
128 }
129 }
130
131 clazz = static_cast<jclass>(
132 env->CallObjectMethod(g_class_loader.Get().obj(),
133 g_class_loader_load_class_method_id,
134 ConvertUTF8ToJavaString(env, dotted_name).obj()));
135 } else {
136 clazz = env->FindClass(class_name);
137 }
138 CHECK(!ClearException(env) && clazz) << "Failed to find class " << class_name;
139 return ScopedJavaLocalRef<jclass>(env, clazz);
140 }
141
142 jclass LazyGetClass(
143 JNIEnv* env,
144 const char* class_name,
145 base::subtle::AtomicWord* atomic_class_id) {
146 COMPILE_ASSERT(sizeof(subtle::AtomicWord) >= sizeof(jclass),
147 AtomicWord_SmallerThan_jMethodID);
148 subtle::AtomicWord value = base::subtle::Acquire_Load(atomic_class_id);
149 if (value)
150 return reinterpret_cast<jclass>(value);
151 ScopedJavaGlobalRef<jclass> clazz;
152 clazz.Reset(GetClass(env, class_name));
153 subtle::AtomicWord null_aw = reinterpret_cast<subtle::AtomicWord>(NULL);
154 subtle::AtomicWord cas_result = base::subtle::Release_CompareAndSwap(
155 atomic_class_id,
156 null_aw,
157 reinterpret_cast<subtle::AtomicWord>(clazz.obj()));
158 if (cas_result == null_aw) {
159 // We intentionally leak the global ref since we now storing it as a raw
160 // pointer in |atomic_class_id|.
161 return clazz.Release();
162 } else {
163 return reinterpret_cast<jclass>(cas_result);
164 }
165 }
166
167 template<MethodID::Type type>
168 jmethodID MethodID::Get(JNIEnv* env,
169 jclass clazz,
170 const char* method_name,
171 const char* jni_signature) {
172 jmethodID id = type == TYPE_STATIC ?
173 env->GetStaticMethodID(clazz, method_name, jni_signature) :
174 env->GetMethodID(clazz, method_name, jni_signature);
175 CHECK(base::android::ClearException(env) || id) <<
176 "Failed to find " <<
177 (type == TYPE_STATIC ? "static " : "") <<
178 "method " << method_name << " " << jni_signature;
179 return id;
180 }
181
182 // If |atomic_method_id| set, it'll return immediately. Otherwise, it'll call
183 // into ::Get() above. If there's a race, it's ok since the values are the same
184 // (and the duplicated effort will happen only once).
185 template<MethodID::Type type>
186 jmethodID MethodID::LazyGet(JNIEnv* env,
187 jclass clazz,
188 const char* method_name,
189 const char* jni_signature,
190 base::subtle::AtomicWord* atomic_method_id) {
191 COMPILE_ASSERT(sizeof(subtle::AtomicWord) >= sizeof(jmethodID),
192 AtomicWord_SmallerThan_jMethodID);
193 subtle::AtomicWord value = base::subtle::Acquire_Load(atomic_method_id);
194 if (value)
195 return reinterpret_cast<jmethodID>(value);
196 jmethodID id = MethodID::Get<type>(env, clazz, method_name, jni_signature);
197 base::subtle::Release_Store(
198 atomic_method_id, reinterpret_cast<subtle::AtomicWord>(id));
199 return id;
200 }
201
202 // Various template instantiations.
203 template jmethodID MethodID::Get<MethodID::TYPE_STATIC>(
204 JNIEnv* env, jclass clazz, const char* method_name,
205 const char* jni_signature);
206
207 template jmethodID MethodID::Get<MethodID::TYPE_INSTANCE>(
208 JNIEnv* env, jclass clazz, const char* method_name,
209 const char* jni_signature);
210
211 template jmethodID MethodID::LazyGet<MethodID::TYPE_STATIC>(
212 JNIEnv* env, jclass clazz, const char* method_name,
213 const char* jni_signature, base::subtle::AtomicWord* atomic_method_id);
214
215 template jmethodID MethodID::LazyGet<MethodID::TYPE_INSTANCE>(
216 JNIEnv* env, jclass clazz, const char* method_name,
217 const char* jni_signature, base::subtle::AtomicWord* atomic_method_id);
218
219 bool HasException(JNIEnv* env) {
220 return env->ExceptionCheck() != JNI_FALSE;
221 }
222
223 bool ClearException(JNIEnv* env) {
224 if (!HasException(env))
225 return false;
226 env->ExceptionDescribe();
227 env->ExceptionClear();
228 return true;
229 }
230
231 void CheckException(JNIEnv* env) {
232 if (!HasException(env))
233 return;
234
235 // Exception has been found, might as well tell breakpad about it.
236 jthrowable java_throwable = env->ExceptionOccurred();
237 if (java_throwable) {
238 // Clear the pending exception, since a local reference is now held.
239 env->ExceptionDescribe();
240 env->ExceptionClear();
241
242 // Set the exception_string in BuildInfo so that breakpad can read it.
243 // RVO should avoid any extra copies of the exception string.
244 base::android::BuildInfo::GetInstance()->SetJavaExceptionInfo(
245 GetJavaExceptionInfo(env, java_throwable));
246 }
247
248 // Now, feel good about it and die.
249 CHECK(false) << "Please include Java exception stack in crash report";
250 }
251
252 std::string GetJavaExceptionInfo(JNIEnv* env, jthrowable java_throwable) {
253 ScopedJavaLocalRef<jclass> throwable_clazz =
254 GetClass(env, "java/lang/Throwable");
255 jmethodID throwable_printstacktrace =
256 MethodID::Get<MethodID::TYPE_INSTANCE>(
257 env, throwable_clazz.obj(), "printStackTrace",
258 "(Ljava/io/PrintStream;)V");
259
260 // Create an instance of ByteArrayOutputStream.
261 ScopedJavaLocalRef<jclass> bytearray_output_stream_clazz =
262 GetClass(env, "java/io/ByteArrayOutputStream");
263 jmethodID bytearray_output_stream_constructor =
264 MethodID::Get<MethodID::TYPE_INSTANCE>(
265 env, bytearray_output_stream_clazz.obj(), "<init>", "()V");
266 jmethodID bytearray_output_stream_tostring =
267 MethodID::Get<MethodID::TYPE_INSTANCE>(
268 env, bytearray_output_stream_clazz.obj(), "toString",
269 "()Ljava/lang/String;");
270 ScopedJavaLocalRef<jobject> bytearray_output_stream(env,
271 env->NewObject(bytearray_output_stream_clazz.obj(),
272 bytearray_output_stream_constructor));
273
274 // Create an instance of PrintStream.
275 ScopedJavaLocalRef<jclass> printstream_clazz =
276 GetClass(env, "java/io/PrintStream");
277 jmethodID printstream_constructor =
278 MethodID::Get<MethodID::TYPE_INSTANCE>(
279 env, printstream_clazz.obj(), "<init>",
280 "(Ljava/io/OutputStream;)V");
281 ScopedJavaLocalRef<jobject> printstream(env,
282 env->NewObject(printstream_clazz.obj(), printstream_constructor,
283 bytearray_output_stream.obj()));
284
285 // Call Throwable.printStackTrace(PrintStream)
286 env->CallVoidMethod(java_throwable, throwable_printstacktrace,
287 printstream.obj());
288
289 // Call ByteArrayOutputStream.toString()
290 ScopedJavaLocalRef<jstring> exception_string(
291 env, static_cast<jstring>(
292 env->CallObjectMethod(bytearray_output_stream.obj(),
293 bytearray_output_stream_tostring)));
294
295 return ConvertJavaStringToUTF8(exception_string);
296 }
297
298
299 } // namespace android
300 } // namespace base
OLDNEW
« no previous file with comments | « base/android/jni_android.h ('k') | base/android/jni_android_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698