20
20
//! ```
21
21
22
22
use 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 } ;
25
25
use once_cell:: sync:: OnceCell ;
26
+ use std:: ffi:: CStr ;
26
27
27
- static GLOBAL : OnceCell < Global > = OnceCell :: new ( ) ;
28
+ static GLOBAL : OnceCell < GlobalStorage > = OnceCell :: new ( ) ;
28
29
29
30
/// A layer to access the Android runtime which is hosting the current
30
31
/// application process.
@@ -37,101 +38,83 @@ pub trait Runtime: Send + Sync {
37
38
/// Returns a reference to the current app's [Context].
38
39
///
39
40
/// [Context]: <https://developer.android.com/reference/android/content/Context>
40
- fn context ( & self ) -> & GlobalRef ;
41
+ fn context ( & self ) -> & Global < JObject < ' static > > ;
41
42
/// 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 > > ;
43
44
}
44
45
45
- enum Global {
46
+ enum GlobalStorage {
46
47
Internal {
47
48
java_vm : JavaVM ,
48
- context : GlobalRef ,
49
- loader : GlobalRef ,
49
+ context : Global < JObject < ' static > > ,
50
+ loader : Global < JClassLoader < ' static > > ,
50
51
} ,
51
52
External ( & ' static dyn Runtime ) ,
52
53
}
53
54
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
+ }
61
61
}
62
62
63
- fn context ( & self ) -> Result < ( GlobalContext , AttachGuard < ' _ > ) , Error > {
64
- let env = self . env ( ) ?;
65
-
63
+ fn context ( & self , env : & mut Env ) -> Result < GlobalContext , Error > {
66
64
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 ( ) ,
69
67
} ;
70
68
71
69
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 ( ) ,
74
72
} ;
75
73
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
+ } )
83
78
}
84
79
}
85
80
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 > > ,
89
85
}
90
86
91
- fn global ( ) -> & ' static Global {
87
+ fn global ( ) -> & ' static GlobalStorage {
92
88
GLOBAL
93
89
. get ( )
94
90
. expect ( "Expect rustls-platform-verifier to be initialized" )
95
91
}
96
92
97
- /// Initialize given a typical Android NDK [`JNIEnv `] and [`JObject`] context.
93
+ /// Initialize given a typical Android NDK [`Env `] and [`JObject`] context.
98
94
///
99
95
/// This method will setup and store an environment locally. This is useful if nothing else in your
100
96
/// 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 > {
102
98
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) ?;
105
100
106
- Ok ( Global :: Internal {
107
- java_vm : env. get_java_vm ( ) ? ,
101
+ Ok ( GlobalStorage :: Internal {
102
+ java_vm : env. get_java_vm ( ) ,
108
103
context : env. new_global_ref ( context) ?,
109
- loader : env. new_global_ref ( JObject :: try_from ( loader) ? ) ?,
104
+ loader : env. new_global_ref ( loader) ?,
110
105
} )
111
106
} ) ?;
112
107
Ok ( ( ) )
113
108
}
114
109
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
-
121
110
/// Initialize with a runtime that can dynamically serve references to
122
111
/// the JVM, context, and class loader.
123
112
///
124
113
/// This is the most flexible option, and is useful for advanced use cases.
125
114
///
126
115
/// This function will never panic.
127
116
pub 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) ) ;
135
118
}
136
119
137
120
/// Initialize with references to the JVM, context, and class loader.
@@ -145,19 +128,25 @@ pub fn init_external(runtime: &'static dyn Runtime) {
145
128
///
146
129
/// ```
147
130
/// 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() };
149
132
/// 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;", &[])?;
151
134
///
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
+ /// });
157
142
/// }
158
143
/// ```
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 {
161
150
java_vm,
162
151
context,
163
152
loader,
@@ -173,45 +162,36 @@ impl From<JNIError> for Error {
173
162
#[ track_caller]
174
163
fn from ( cause : JNIError ) -> Self {
175
164
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
+ } ;
182
174
}
183
175
184
176
Self
185
177
}
186
178
}
187
179
188
180
pub ( 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 ,
192
183
}
193
184
194
185
impl < ' a , ' env > LocalContext < ' a , ' env > {
195
186
/// Load a class from the application class loader
196
187
///
197
188
/// This should be used instead of `JNIEnv::find_class` to ensure all classes
198
189
/// 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)
215
195
}
216
196
}
217
197
@@ -220,51 +200,42 @@ impl<'a, 'env> LocalContext<'a, 'env> {
220
200
/// are cleared.
221
201
pub ( super ) fn with_context < F , T : ' static > ( f : F ) -> Result < T , Error >
222
202
where
223
- F : FnOnce ( & mut LocalContext , & mut JNIEnv ) -> Result < T , Error > ,
203
+ F : FnOnce ( & mut LocalContext ) -> Result < T , Error > ,
224
204
{
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) ?;
236
208
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,
240
211
} ;
241
- f ( & mut context, env )
212
+ f ( & mut context)
242
213
} )
243
214
}
244
215
245
216
/// Loads and caches a class on first use
246
217
pub ( super ) struct CachedClass {
247
- name : & ' static str ,
248
- class : OnceCell < GlobalRef > ,
218
+ name : & ' static CStr ,
219
+ class : OnceCell < Global < JClass < ' static > > > ,
249
220
}
250
221
251
222
impl CachedClass {
252
223
/// 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 {
254
225
Self {
255
226
name,
256
227
class : OnceCell :: new ( ) ,
257
228
}
258
229
}
259
230
260
231
/// 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 > {
262
233
let class = self . class . get_or_try_init ( || -> Result < _ , Error > {
263
234
let class = cx. load_class ( self . name ) ?;
264
235
265
236
Ok ( cx. env . new_global_ref ( class) ?)
266
237
} ) ?;
267
238
268
- Ok ( class. as_obj ( ) . into ( ) )
239
+ Ok ( class)
269
240
}
270
241
}
0 commit comments