Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor Android init methods for more flexibility #159

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 74 additions & 20 deletions rustls-platform-verifier/src/android.rs
Original file line number Diff line number Diff line change
@@ -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.
//!
//! <div class="warning">
//! 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.
//! </div>
//!
//! # 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};
Expand Down Expand Up @@ -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)]
Expand Down
2 changes: 1 addition & 1 deletion rustls-platform-verifier/src/tests/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>() {
Expand Down