diff --git a/rustls-platform-verifier/src/android.rs b/rustls-platform-verifier/src/android.rs
index f50db6c..50db61e 100644
--- a/rustls-platform-verifier/src/android.rs
+++ b/rustls-platform-verifier/src/android.rs
@@ -1,4 +1,23 @@
-//! A small wrapper over interacting with the JNI in a type-safe way.
+//! On Android, initialization must be done before any verification is attempted.
+//!
+//!
+//! Some manual setup is required outside of cargo to use this crate on Android. In order to use
+//! Android’s certificate verifier, the crate needs to call into the JVM. A small Kotlin component
+//! must be included in your app’s build to support rustls-platform-verifier.
+//!
+//! See the [crate's Android section][crate#android] for more details.
+//!
+//!
+//! # Examples
+//!
+//! ```
+//! // A typical entrypoint signature for obtaining the necessary pointers
+//! pub fn android_init(raw_env: *mut c_void, raw_context: *mut c_void) -> Result<(), jni::errors::Error> {
+//! let mut env = unsafe { JNIEnv::from_raw(raw_env as *mut jni::sys::JNIEnv).unwrap() };
+//! let context = unsafe { JObject::from_raw(raw_context as jni::sys::jobject) };
+//! rustls_platform_verifier::android::init_with_env(&mut env, context)?;
+//! }
+//! ```
use jni::errors::Error as JNIError;
use jni::objects::{GlobalRef, JClass, JObject, JValue};
@@ -68,41 +87,76 @@ fn global() -> &'static Global {
.expect("Expect rustls-platform-verifier to be initialized")
}
-/// Initializes and stores the required context for the Android platform.
+/// Initialize given a typical Android NDK [`JNIEnv`] and [`JObject`] context.
///
-/// This method will setup and store an environment locally. This is useful if
-/// nothing else in your application needs access the Android runtime.
-///
-/// Initialization must be done before any verification is attempted.
-pub fn init_hosted(env: &mut JNIEnv, context: JObject) -> Result<(), JNIError> {
+/// This method will setup and store an environment locally. This is useful if nothing else in your
+/// application needs to access the Android runtime.
+pub fn init_with_env(env: &mut JNIEnv, context: JObject) -> Result<(), JNIError> {
GLOBAL.get_or_try_init(|| -> Result<_, JNIError> {
let loader =
env.call_method(&context, "getClassLoader", "()Ljava/lang/ClassLoader;", &[])?;
- let global = Global::Internal {
+
+ Ok(Global::Internal {
java_vm: env.get_java_vm()?,
context: env.new_global_ref(context)?,
loader: env.new_global_ref(JObject::try_from(loader)?)?,
- };
-
- Ok(global)
+ })
})?;
-
Ok(())
}
-/// Initializes and stores the required context for the Android platform.
-///
-/// This method utilizes an existing Android runtime environment and set anything
-/// else up on its own. This is useful if your application already interacts with
-/// the runtime and has pre-existing handles.
+/// *Deprecated*: This is the original method name for [`init_with_env`] and is functionally
+/// identical.
+pub fn init_hosted(env: &mut JNIEnv, context: JObject) -> Result<(), JNIError> {
+ init_with_env(env, context)
+}
+
+/// Initialize with a runtime that can dynamically serve references to
+/// the JVM, context, and class loader.
///
-/// This function will never panic, and is therefore safe to use at FFI boundaries.
+/// This is the most flexible option, and is useful for advanced use cases.
///
-/// Initialization must be done before any verification is attempted.
-pub fn init_external(runtime: &'static dyn Runtime) {
+/// This function will never panic.
+pub fn init_with_runtime(runtime: &'static dyn Runtime) {
GLOBAL.get_or_init(|| Global::External(runtime));
}
+/// *Deprecated*: This is the original method name for [`init_with_runtime`] and is functionally
+/// identical.
+pub fn init_external(runtime: &'static dyn Runtime) {
+ init_with_runtime(runtime);
+}
+
+/// Initialize with references to the JVM, context, and class loader.
+///
+/// This is useful when you're already interacting with `jni-rs` wrapped objects and want to use
+/// global references to objects for efficiency.
+///
+/// This function will never panic.
+///
+/// # Examples
+///
+/// ```
+/// pub fn android_init(raw_env: *mut c_void, raw_context: *mut c_void) -> Result<(), jni::errors::Error> {
+/// let mut env = unsafe { jni::JNIEnv::from_raw(raw_env as *mut jni::sys::JNIEnv).unwrap() };
+/// let context = unsafe { JObject::from_raw(raw_context as jni::sys::jobject) };
+/// let loader = env.call_method(&context, "getClassLoader", "()Ljava/lang/ClassLoader;", &[])?;
+///
+/// rustls_platform_verifier::android::init_with_refs(
+/// env.get_java_vm()?,
+/// env.new_global_ref(context)?,
+/// env.new_global_ref(JObject::try_from(loader)?)?,
+/// );
+/// }
+/// ```
+pub fn init_with_refs(java_vm: JavaVM, context: GlobalRef, loader: GlobalRef) {
+ GLOBAL.get_or_init(|| Global::Internal {
+ java_vm,
+ context,
+ loader,
+ });
+}
+
/// Wrapper for JNI errors that will log and clear exceptions
/// It should generally be preferred to `jni::errors::Error`
#[derive(Debug)]
diff --git a/rustls-platform-verifier/src/tests/ffi.rs b/rustls-platform-verifier/src/tests/ffi.rs
index 9030cc3..70ab573 100644
--- a/rustls-platform-verifier/src/tests/ffi.rs
+++ b/rustls-platform-verifier/src/tests/ffi.rs
@@ -46,7 +46,7 @@ mod android {
.with_max_level(log::Level::Trace.to_level_filter())
.with_filter(log_filter),
);
- crate::android::init_hosted(env, cx).unwrap();
+ crate::android::init_with_env(env, cx).unwrap();
crate::tests::ensure_global_state();
std::panic::set_hook(Box::new(|info| {
let msg = if let Some(msg) = info.payload().downcast_ref::<&'static str>() {