@@ -13,6 +13,8 @@ use std::slice;
1313use std:: sync:: atomic:: { AtomicUsize , AtomicBool , Ordering } ;
1414use std:: io:: Write ;
1515
16+ macro_rules! cstr { ( $s: expr) => ( concat!( $s, "\0 " ) as * const _ as * const :: std:: os:: raw:: c_char) }
17+
1618pub type pthread_t = c_long ;
1719pub type pthread_mutexattr_t = c_long ;
1820pub type pthread_attr_t = c_void ; // FIXME: wrong
@@ -64,11 +66,39 @@ pub unsafe extern fn cargo_apk_injected_glue_load_asset(ptr: *const (), len: usi
6466 Box :: into_raw ( Box :: new ( data) ) as * mut _
6567}
6668
69+ #[ no_mangle]
70+ pub unsafe extern fn cargo_apk_injected_glue_jni_attach_thread ( ) -> * mut c_void {
71+ let mut jni_env : * mut ffi:: JNIEnv = ptr:: null_mut ( ) ;
72+ let android_app = get_app ( ) ;
73+ let jvm = ( * android_app. activity ) . vm ;
74+ ( ( * ( * jvm) . functions ) . AttachCurrentThread ) ( jvm, & mut jni_env, ptr:: null_mut ( ) ) ;
75+ jni_env as * mut c_void
76+ }
77+
78+ #[ no_mangle]
79+ pub unsafe extern fn cargo_apk_injected_glue_jni_detach_thread ( ) {
80+ let android_app = get_app ( ) ;
81+ let jvm = ( * android_app. activity ) . vm ;
82+ ( ( * ( * jvm) . functions ) . DetachCurrentThread ) ( jvm) ;
83+ }
84+
85+ #[ no_mangle]
86+ pub unsafe extern fn cargo_apk_injected_glue_jni_class_loader ( ) -> * mut c_void {
87+ CLASS_LOADER as * mut c_void
88+ }
89+
90+ #[ no_mangle]
91+ pub unsafe extern fn cargo_apk_injected_glue_jni_activity ( ) -> * mut c_void {
92+ ACTIVITY as * mut c_void
93+ }
94+
6795/// This static variable will store the android_app* on creation, and set it back to 0 at
6896/// destruction.
6997/// Apart from this, the static is never written, so there is no risk of race condition.
7098#[ no_mangle]
7199pub static mut ANDROID_APP : * mut ffi:: android_app = 0 as * mut ffi:: android_app ;
100+ pub static mut CLASS_LOADER : ffi:: jobject = 0 as ffi:: jobject ;
101+ pub static mut ACTIVITY : ffi:: jobject = 0 as ffi:: jobject ;
72102
73103/// This is the structure that serves as user data in the android_app*
74104#[ doc( hidden) ]
@@ -170,7 +200,46 @@ pub fn android_main2<F>(app: *mut ffi::android_app, main_function: F)
170200
171201 unsafe { ANDROID_APP = app; } ;
172202 let app: & mut ffi:: android_app = unsafe { & mut * app } ;
173-
203+
204+ // The thread spawned below needs access to the Android class loader
205+ // associated with the main application thread. If not, it would not
206+ // have access to the full set of Android classes.
207+ // See https://developer.android.com/training/articles/perf-jni.html#faq_FindClass
208+ let mut jni_env : * mut ffi:: JNIEnv = ptr:: null_mut ( ) ;
209+ let mut class_class : ffi:: jclass = ptr:: null_mut ( ) ;
210+ let mut jni_functions : & ffi:: JNINativeInterface ;
211+ unsafe {
212+ let jvm = ( * app. activity ) . vm ;
213+ ( ( * ( * jvm) . functions ) . GetEnv ) ( jvm, & mut jni_env, ffi:: JNI_VERSION_1_6 ) ;
214+ ( ( * ( * jvm) . functions ) . AttachCurrentThread ) ( jvm, & mut jni_env, ptr:: null_mut ( ) ) ;
215+ if jni_env. is_null ( ) { panic ! ( "Failed to acquire JNI environment." ) ; }
216+
217+ jni_functions = & * ( * jni_env) . functions ;
218+ class_class = ( jni_functions. FindClass ) ( jni_env, cstr ! ( "java/lang/Class" ) ) ;
219+ if class_class. is_null ( ) { panic ! ( "Could not find java.lang.Class" ) ; }
220+ let get_classloader_method = ( jni_functions. GetMethodID ) ( jni_env, class_class,
221+ cstr ! ( "getClassLoader" ) ,
222+ cstr ! ( "()Ljava/lang/ClassLoader;" ) ) ;
223+ if get_classloader_method. is_null ( ) { panic ! ( "Could not find java.lang.Class.GetClassLoader()" ) ; }
224+
225+ let local_class_loader = ( jni_functions. CallObjectMethod ) ( jni_env, class_class, get_classloader_method) ;
226+ // jobjects accessed from another thread need to be global, for details see
227+ // http://android-developers.blogspot.be/2011/11/jni-local-reference-changes-in-ics.html
228+ CLASS_LOADER = ( jni_functions. NewGlobalRef ) ( jni_env, local_class_loader) ;
229+ if CLASS_LOADER . is_null ( ) { panic ! ( "Could not obtain classloader instance." ) ; }
230+ // The activity "clazz" field is actually not a class, but the activity instance.
231+ // The documentation lies. This is also why we don't use the clazz field as a class
232+ // to get the classloader above.
233+ ACTIVITY = ( jni_functions. NewGlobalRef ) ( jni_env, ( * app. activity ) . clazz ) ;
234+ if ACTIVITY . is_null ( ) { panic ! ( "Could not obtain activity instance." ) ; }
235+
236+ ( jni_functions. DeleteLocalRef ) ( jni_env, class_class) ;
237+ ( jni_functions. DeleteLocalRef ) ( jni_env, local_class_loader) ;
238+ write_log ( format ! ( "ClassLoader -> {:X}" , CLASS_LOADER as u64 ) . as_str ( ) ) ;
239+ write_log ( format ! ( "Activity -> {:X}" , ACTIVITY as u64 ) . as_str ( ) ) ;
240+ if ( jni_functions. ExceptionCheck ) ( jni_env) != 0 { panic ! ( "Exception occurred while looking up ClassLoader and Activity instance." ) ; }
241+ }
242+
174243 // Creating the context that will be passed to the callback
175244 let context = Context {
176245 senders : Mutex :: new ( Vec :: new ( ) ) ,
@@ -342,6 +411,14 @@ pub fn android_main2<F>(app: *mut ffi::android_app, main_function: F)
342411 }
343412 }
344413 }
414+
415+ // TODO: detach JNI thread? Or is this handled elsewhere by default?
416+ unsafe {
417+ ( jni_functions. DeleteGlobalRef ) ( jni_env, CLASS_LOADER ) ;
418+ ( jni_functions. DeleteGlobalRef ) ( jni_env, ACTIVITY ) ;
419+ CLASS_LOADER = 0 as ffi:: jobject ;
420+ }
421+
345422
346423 // Terminating the application. This kills the thread the Rust main thread.
347424 // TODO: consider waiting on thread?
0 commit comments