2020//! ```
2121
2222use jni:: errors:: Error as JNIError ;
23- use jni:: objects:: { GlobalRef , JClass , JObject , JValue } ;
24- use jni:: { AttachGuard , JNIEnv , JavaVM } ;
23+ use jni:: objects:: { Global , JClass , JClassLoader , JObject } ;
24+ use jni:: { Env , JavaVM } ;
2525use once_cell:: sync:: OnceCell ;
26+ use std:: ffi:: CStr ;
2627
27- static GLOBAL : OnceCell < Global > = OnceCell :: new ( ) ;
28+ static GLOBAL : OnceCell < GlobalStorage > = OnceCell :: new ( ) ;
2829
2930/// A layer to access the Android runtime which is hosting the current
3031/// application process.
@@ -37,58 +38,53 @@ pub trait Runtime: Send + Sync {
3738 /// Returns a reference to the current app's [Context].
3839 ///
3940 /// [Context]: <https://developer.android.com/reference/android/content/Context>
40- fn context ( & self ) -> & GlobalRef ;
41+ fn context ( & self ) -> & Global < JObject < ' static > > ;
4142 /// Returns a reference to the class returned by the current JVM's `getClassLoader` call.
42- fn class_loader ( & self ) -> & GlobalRef ;
43+ fn class_loader ( & self ) -> & Global < JClassLoader < ' static > > ;
4344}
4445
45- enum Global {
46+ enum GlobalStorage {
4647 Internal {
4748 java_vm : JavaVM ,
48- context : GlobalRef ,
49- loader : GlobalRef ,
49+ context : Global < JObject < ' static > > ,
50+ loader : Global < JClassLoader < ' static > > ,
5051 } ,
5152 External ( & ' static dyn Runtime ) ,
5253}
5354
54- impl Global {
55- fn env ( & self ) -> Result < AttachGuard < ' _ > , Error > {
56- let vm = match self {
57- Global :: Internal { java_vm, .. } => java_vm,
58- Global :: External ( global) => global. java_vm ( ) ,
59- } ;
60- Ok ( vm. attach_current_thread ( ) ?)
55+ impl GlobalStorage {
56+ fn vm ( & self ) -> & JavaVM {
57+ match self {
58+ GlobalStorage :: Internal { java_vm, .. } => java_vm,
59+ GlobalStorage :: External ( runtime) => runtime. java_vm ( ) ,
60+ }
6161 }
6262
63- fn context ( & self ) -> Result < ( GlobalContext , AttachGuard < ' _ > ) , Error > {
64- let env = self . env ( ) ?;
65-
63+ fn context ( & self , env : & mut Env ) -> Result < GlobalContext , Error > {
6664 let context = match self {
67- Global :: Internal { context, .. } => context,
68- Global :: External ( global) => global. context ( ) ,
65+ Self :: Internal { context, .. } => context,
66+ Self :: External ( global) => global. context ( ) ,
6967 } ;
7068
7169 let loader = match self {
72- Global :: Internal { loader, .. } => loader,
73- Global :: External ( global) => global. class_loader ( ) ,
70+ Self :: Internal { loader, .. } => loader,
71+ Self :: External ( global) => global. class_loader ( ) ,
7472 } ;
7573
76- Ok ( (
77- GlobalContext {
78- context : env. new_global_ref ( context) ?,
79- loader : env. new_global_ref ( loader) ?,
80- } ,
81- env,
82- ) )
74+ Ok ( GlobalContext {
75+ context : env. new_global_ref ( context) ?,
76+ loader : env. new_global_ref ( loader) ?,
77+ } )
8378 }
8479}
8580
86- struct GlobalContext {
87- context : GlobalRef ,
88- loader : GlobalRef ,
81+ pub ( super ) struct GlobalContext {
82+ /// The Android application [Context](https://developer.android.com/reference/android/app/Application).
83+ pub ( super ) context : Global < JObject < ' static > > ,
84+ loader : Global < JClassLoader < ' static > > ,
8985}
9086
91- fn global ( ) -> & ' static Global {
87+ fn global ( ) -> & ' static GlobalStorage {
9288 GLOBAL
9389 . get ( )
9490 . expect ( "Expect rustls-platform-verifier to be initialized" )
@@ -98,40 +94,27 @@ fn global() -> &'static Global {
9894///
9995/// This method will setup and store an environment locally. This is useful if nothing else in your
10096/// application needs to access the Android runtime.
101- pub fn init_with_env ( env : & mut JNIEnv , context : JObject ) -> Result < ( ) , JNIError > {
97+ pub fn init_with_env ( env : & mut Env , context : JObject ) -> Result < ( ) , JNIError > {
10298 GLOBAL . get_or_try_init ( || -> Result < _ , JNIError > {
103- let loader =
104- env. call_method ( & context, "getClassLoader" , "()Ljava/lang/ClassLoader;" , & [ ] ) ?;
99+ let loader = env. get_object_class ( & context) ?. get_class_loader ( env) ?;
105100
106- Ok ( Global :: Internal {
107- java_vm : env. get_java_vm ( ) ? ,
101+ Ok ( GlobalStorage :: Internal {
102+ java_vm : env. get_java_vm ( ) ,
108103 context : env. new_global_ref ( context) ?,
109- loader : env. new_global_ref ( JObject :: try_from ( loader) ? ) ?,
104+ loader : env. new_global_ref ( loader) ?,
110105 } )
111106 } ) ?;
112107 Ok ( ( ) )
113108}
114109
115- /// *Deprecated*: This is the original method name for [`init_with_env`] and is functionally
116- /// identical.
117- pub fn init_hosted ( env : & mut JNIEnv , context : JObject ) -> Result < ( ) , JNIError > {
118- init_with_env ( env, context)
119- }
120-
121110/// Initialize with a runtime that can dynamically serve references to
122111/// the JVM, context, and class loader.
123112///
124113/// This is the most flexible option, and is useful for advanced use cases.
125114///
126115/// This function will never panic.
127116pub fn init_with_runtime ( runtime : & ' static dyn Runtime ) {
128- GLOBAL . get_or_init ( || Global :: External ( runtime) ) ;
129- }
130-
131- /// *Deprecated*: This is the original method name for [`init_with_runtime`] and is functionally
132- /// identical.
133- pub fn init_external ( runtime : & ' static dyn Runtime ) {
134- init_with_runtime ( runtime) ;
117+ GLOBAL . get_or_init ( || GlobalStorage :: External ( runtime) ) ;
135118}
136119
137120/// Initialize with references to the JVM, context, and class loader.
@@ -156,8 +139,12 @@ pub fn init_external(runtime: &'static dyn Runtime) {
156139/// );
157140/// }
158141/// ```
159- pub fn init_with_refs ( java_vm : JavaVM , context : GlobalRef , loader : GlobalRef ) {
160- GLOBAL . get_or_init ( || Global :: Internal {
142+ pub fn init_with_refs (
143+ java_vm : JavaVM ,
144+ context : Global < JObject < ' static > > ,
145+ loader : Global < JClassLoader < ' static > > ,
146+ ) {
147+ GLOBAL . get_or_init ( || GlobalStorage :: Internal {
161148 java_vm,
162149 context,
163150 loader,
@@ -173,45 +160,36 @@ impl From<JNIError> for Error {
173160 #[ track_caller]
174161 fn from ( cause : JNIError ) -> Self {
175162 if let JNIError :: JavaException = cause {
176- if let Ok ( env) = global ( ) . env ( ) {
177- // These should not fail if we are already in a throwing state unless
178- // things are very broken. In that case, there isn't much we can do.
179- let _ = env. exception_describe ( ) ;
180- let _ = env. exception_clear ( ) ;
181- }
163+ // SAFETY: We do not retain the `AttachGuard`, do not have access to the previous guard/env from
164+ // whichever JNI call errored before claling this function, and therefore don't unsafely mutate it.
165+ if let Ok ( mut env) = unsafe { global ( ) . vm ( ) . get_env_attachment ( ) } {
166+ let _ = env. with_env_current_frame ( |env| {
167+ env. exception_describe ( ) ;
168+ env. exception_clear ( ) ;
169+ Ok :: < _ , Error > ( ( ) )
170+ } ) ;
171+ } ;
182172 }
183173
184174 Self
185175 }
186176}
187177
188178pub ( super ) struct LocalContext < ' a , ' env > {
189- env : & ' a mut JNIEnv < ' env > ,
190- context : GlobalRef ,
191- loader : GlobalRef ,
179+ pub ( super ) env : & ' a mut Env < ' env > ,
180+ pub ( super ) global : GlobalContext ,
192181}
193182
194183impl < ' a , ' env > LocalContext < ' a , ' env > {
195184 /// Load a class from the application class loader
196185 ///
197186 /// This should be used instead of `JNIEnv::find_class` to ensure all classes
198187 /// in the application can be found.
199- fn load_class ( & mut self , name : & str ) -> Result < JClass < ' env > , Error > {
200- let name = self . env . new_string ( name) ?;
201- let class = self . env . call_method (
202- & self . loader ,
203- "loadClass" ,
204- "(Ljava/lang/String;)Ljava/lang/Class;" ,
205- & [ JValue :: from ( & name) ] ,
206- ) ?;
207-
208- Ok ( JObject :: try_from ( class) ?. into ( ) )
209- }
210-
211- /// Borrow the `applicationContext` from the Android application
212- /// <https://developer.android.com/reference/android/app/Application>
213- pub ( super ) fn application_context ( & self ) -> & JObject < ' _ > {
214- & self . context
188+ fn load_class ( & mut self , name : & ' static CStr ) -> Result < JClass < ' env > , Error > {
189+ self . global
190+ . loader
191+ . load_class ( name. as_ref ( ) , self . env )
192+ . map_err ( Error :: from)
215193 }
216194}
217195
@@ -220,51 +198,42 @@ impl<'a, 'env> LocalContext<'a, 'env> {
220198/// are cleared.
221199pub ( super ) fn with_context < F , T : ' static > ( f : F ) -> Result < T , Error >
222200where
223- F : FnOnce ( & mut LocalContext , & mut JNIEnv ) -> Result < T , Error > ,
201+ F : FnOnce ( & mut LocalContext ) -> Result < T , Error > ,
224202{
225- let ( global_context, mut binding_env) = global ( ) . context ( ) ?;
226- // SAFETY: Any local references created with this env are always destroyed prior to the parent
227- // frame exiting because we force it to be dropped before the new frame exits and don't allow
228- // the closure to access the env directly. We don't use it anywhere outside that sub-scope either.
229- //
230- // Rust's borrowing rules enforce that any reference that came from this env must be dropped before it is too.
231- let ctx_env = unsafe { binding_env. unsafe_clone ( ) } ;
232-
233- // 16 is the default capacity in the JVM, we can make this configurable if necessary
234- binding_env. with_local_frame ( 16 , |env| {
235- let mut ctx_env = ctx_env;
203+ let global = global ( ) ;
204+ global. vm ( ) . attach_current_thread_for_scope ( |env| {
205+ let global_context = global. context ( env) ?;
236206 let mut context = LocalContext {
237- env : & mut ctx_env,
238- context : global_context. context ,
239- loader : global_context. loader ,
207+ env,
208+ global : global_context,
240209 } ;
241- f ( & mut context, env )
210+ f ( & mut context)
242211 } )
243212}
244213
245214/// Loads and caches a class on first use
246215pub ( super ) struct CachedClass {
247- name : & ' static str ,
248- class : OnceCell < GlobalRef > ,
216+ name : & ' static CStr ,
217+ class : OnceCell < Global < JClass < ' static > > > ,
249218}
250219
251220impl CachedClass {
252221 /// Creates a lazily initialized class reference to the class with `name`.
253- pub ( super ) const fn new ( name : & ' static str ) -> Self {
222+ pub ( super ) const fn new ( name : & ' static CStr ) -> Self {
254223 Self {
255224 name,
256225 class : OnceCell :: new ( ) ,
257226 }
258227 }
259228
260229 /// Gets the cached class reference, loaded on first use
261- pub ( super ) fn get ( & self , cx : & mut LocalContext ) -> Result < & JClass < ' _ > , Error > {
230+ pub ( super ) fn get ( & self , cx : & mut LocalContext ) -> Result < & JClass < ' static > , Error > {
262231 let class = self . class . get_or_try_init ( || -> Result < _ , Error > {
263232 let class = cx. load_class ( self . name ) ?;
264233
265234 Ok ( cx. env . new_global_ref ( class) ?)
266235 } ) ?;
267236
268- Ok ( class. as_obj ( ) . into ( ) )
237+ Ok ( class)
269238 }
270239}
0 commit comments