Skip to content

Commit 269f877

Browse files
committed
Initial attempt to port internals to JNI 0.22
1 parent e9cf369 commit 269f877

File tree

4 files changed

+186
-210
lines changed

4 files changed

+186
-210
lines changed

rustls-platform-verifier/src/android.rs

Lines changed: 69 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,12 @@
2020
//! ```
2121
2222
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};
2525
use 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.
127116
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));
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

188178
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,
192181
}
193182

194183
impl<'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.
221199
pub(super) fn with_context<F, T: 'static>(f: F) -> Result<T, Error>
222200
where
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
246215
pub(super) struct CachedClass {
247-
name: &'static str,
248-
class: OnceCell<GlobalRef>,
216+
name: &'static CStr,
217+
class: OnceCell<Global<JClass<'static>>>,
249218
}
250219

251220
impl 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

Comments
 (0)