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>() {