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,101 +38,83 @@ 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" )
9591}
9692
97- /// Initialize given a typical Android NDK [`JNIEnv `] and [`JObject`] context.
93+ /// Initialize given a typical Android NDK [`Env `] and [`JObject`] context.
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.
@@ -145,19 +128,25 @@ pub fn init_external(runtime: &'static dyn Runtime) {
145128///
146129/// ```
147130/// pub fn android_init(raw_env: *mut c_void, raw_context: *mut c_void) -> Result<(), jni::errors::Error> {
148- /// let mut env = unsafe { jni::JNIEnv ::from_raw(raw_env as *mut jni::sys::JNIEnv).unwrap() };
131+ /// let mut env = unsafe { jni::EnvUnowned ::from_raw(raw_env as *mut jni::sys::JNIEnv).unwrap() };
149132/// let context = unsafe { JObject::from_raw(raw_context as jni::sys::jobject) };
150- /// let loader = env.call_method(&context, "getClassLoader", "()Ljava/lang/ClassLoader;", &[])?;
133+ /// let loader = env.call_method(&context, c "getClassLoader", c "()Ljava/lang/ClassLoader;", &[])?;
151134///
152- /// rustls_platform_verifier::android::init_with_refs(
153- /// env.get_java_vm()?,
154- /// env.new_global_ref(context)?,
155- /// env.new_global_ref(JObject::try_from(loader)?)?,
156- /// );
135+ /// env.with_env(|env| {
136+ /// rustls_platform_verifier::android::init_with_refs(
137+ /// env.get_java_vm(),
138+ /// env.new_global_ref(context)?,
139+ /// env.new_global_ref(JClassLoader::try_from(loader)?)?,
140+ /// );
141+ /// });
157142/// }
158143/// ```
159- pub fn init_with_refs ( java_vm : JavaVM , context : GlobalRef , loader : GlobalRef ) {
160- GLOBAL . get_or_init ( || Global :: Internal {
144+ pub fn init_with_refs (
145+ java_vm : JavaVM ,
146+ context : Global < JObject < ' static > > ,
147+ loader : Global < JClassLoader < ' static > > ,
148+ ) {
149+ GLOBAL . get_or_init ( || GlobalStorage :: Internal {
161150 java_vm,
162151 context,
163152 loader,
@@ -173,45 +162,36 @@ impl From<JNIError> for Error {
173162 #[ track_caller]
174163 fn from ( cause : JNIError ) -> Self {
175164 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- }
165+ // SAFETY: We do not retain the `AttachGuard`, do not have access to the previous guard/env from
166+ // whichever JNI call errored before claling this function, and therefore don't unsafely mutate it.
167+ if let Ok ( mut env) = unsafe { global ( ) . vm ( ) . get_env_attachment ( ) } {
168+ let _ = env. with_env_current_frame ( |env| {
169+ env. exception_describe ( ) ;
170+ env. exception_clear ( ) ;
171+ Ok :: < _ , Error > ( ( ) )
172+ } ) ;
173+ } ;
182174 }
183175
184176 Self
185177 }
186178}
187179
188180pub ( super ) struct LocalContext < ' a , ' env > {
189- env : & ' a mut JNIEnv < ' env > ,
190- context : GlobalRef ,
191- loader : GlobalRef ,
181+ pub ( super ) env : & ' a mut Env < ' env > ,
182+ pub ( super ) global : GlobalContext ,
192183}
193184
194185impl < ' a , ' env > LocalContext < ' a , ' env > {
195186 /// Load a class from the application class loader
196187 ///
197188 /// This should be used instead of `JNIEnv::find_class` to ensure all classes
198189 /// 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
190+ fn load_class ( & mut self , name : & ' static CStr ) -> Result < JClass < ' env > , Error > {
191+ self . global
192+ . loader
193+ . load_class ( name. as_ref ( ) , self . env )
194+ . map_err ( Error :: from)
215195 }
216196}
217197
@@ -220,51 +200,42 @@ impl<'a, 'env> LocalContext<'a, 'env> {
220200/// are cleared.
221201pub ( super ) fn with_context < F , T : ' static > ( f : F ) -> Result < T , Error >
222202where
223- F : FnOnce ( & mut LocalContext , & mut JNIEnv ) -> Result < T , Error > ,
203+ F : FnOnce ( & mut LocalContext ) -> Result < T , Error > ,
224204{
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;
205+ let global = global ( ) ;
206+ global. vm ( ) . attach_current_thread_for_scope ( |env| {
207+ let global_context = global. context ( env) ?;
236208 let mut context = LocalContext {
237- env : & mut ctx_env,
238- context : global_context. context ,
239- loader : global_context. loader ,
209+ env,
210+ global : global_context,
240211 } ;
241- f ( & mut context, env )
212+ f ( & mut context)
242213 } )
243214}
244215
245216/// Loads and caches a class on first use
246217pub ( super ) struct CachedClass {
247- name : & ' static str ,
248- class : OnceCell < GlobalRef > ,
218+ name : & ' static CStr ,
219+ class : OnceCell < Global < JClass < ' static > > > ,
249220}
250221
251222impl CachedClass {
252223 /// Creates a lazily initialized class reference to the class with `name`.
253- pub ( super ) const fn new ( name : & ' static str ) -> Self {
224+ pub ( super ) const fn new ( name : & ' static CStr ) -> Self {
254225 Self {
255226 name,
256227 class : OnceCell :: new ( ) ,
257228 }
258229 }
259230
260231 /// Gets the cached class reference, loaded on first use
261- pub ( super ) fn get ( & self , cx : & mut LocalContext ) -> Result < & JClass < ' _ > , Error > {
232+ pub ( super ) fn get ( & self , cx : & mut LocalContext ) -> Result < & JClass < ' static > , Error > {
262233 let class = self . class . get_or_try_init ( || -> Result < _ , Error > {
263234 let class = cx. load_class ( self . name ) ?;
264235
265236 Ok ( cx. env . new_global_ref ( class) ?)
266237 } ) ?;
267238
268- Ok ( class. as_obj ( ) . into ( ) )
239+ Ok ( class)
269240 }
270241}
0 commit comments