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,58 +38,53 @@ 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" )
@@ -98,40 +94,27 @@ fn global() -> &'static Global {
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.
@@ -156,8 +139,12 @@ pub fn init_external(runtime: &'static dyn Runtime) {
156
139
/// );
157
140
/// }
158
141
/// ```
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 {
161
148
java_vm,
162
149
context,
163
150
loader,
@@ -173,45 +160,36 @@ impl From<JNIError> for Error {
173
160
#[ track_caller]
174
161
fn from ( cause : JNIError ) -> Self {
175
162
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
+ } ;
182
172
}
183
173
184
174
Self
185
175
}
186
176
}
187
177
188
178
pub ( 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 ,
192
181
}
193
182
194
183
impl < ' a , ' env > LocalContext < ' a , ' env > {
195
184
/// Load a class from the application class loader
196
185
///
197
186
/// This should be used instead of `JNIEnv::find_class` to ensure all classes
198
187
/// 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)
215
193
}
216
194
}
217
195
@@ -220,51 +198,42 @@ impl<'a, 'env> LocalContext<'a, 'env> {
220
198
/// are cleared.
221
199
pub ( super ) fn with_context < F , T : ' static > ( f : F ) -> Result < T , Error >
222
200
where
223
- F : FnOnce ( & mut LocalContext , & mut JNIEnv ) -> Result < T , Error > ,
201
+ F : FnOnce ( & mut LocalContext ) -> Result < T , Error > ,
224
202
{
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) ?;
236
206
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,
240
209
} ;
241
- f ( & mut context, env )
210
+ f ( & mut context)
242
211
} )
243
212
}
244
213
245
214
/// Loads and caches a class on first use
246
215
pub ( super ) struct CachedClass {
247
- name : & ' static str ,
248
- class : OnceCell < GlobalRef > ,
216
+ name : & ' static CStr ,
217
+ class : OnceCell < Global < JClass < ' static > > > ,
249
218
}
250
219
251
220
impl CachedClass {
252
221
/// 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 {
254
223
Self {
255
224
name,
256
225
class : OnceCell :: new ( ) ,
257
226
}
258
227
}
259
228
260
229
/// 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 > {
262
231
let class = self . class . get_or_try_init ( || -> Result < _ , Error > {
263
232
let class = cx. load_class ( self . name ) ?;
264
233
265
234
Ok ( cx. env . new_global_ref ( class) ?)
266
235
} ) ?;
267
236
268
- Ok ( class. as_obj ( ) . into ( ) )
237
+ Ok ( class)
269
238
}
270
239
}
0 commit comments