ndk_context/lib.rs
1//! Provides a stable api to rust crates for interfacing with the Android platform. It is
2//! initialized by the runtime, usually [__ndk-glue__](https://crates.io/crates/ndk-glue),
3//! but could also be initialized by Java or Kotlin code when embedding in an existing Android
4//! project.
5//!
6//! ```no_run
7//! let ctx = ndk_context::android_context();
8//! let vm = unsafe { jni::JavaVM::from_raw(ctx.vm().cast()) }?;
9//! let env = vm.attach_current_thread();
10//! let class_ctx = env.find_class("android/content/Context")?;
11//! let audio_service = env.get_static_field(class_ctx, "AUDIO_SERVICE", "Ljava/lang/String;")?;
12//! let audio_manager = env
13//! .call_method(
14//! ctx.context() as jni::sys::jobject,
15//! "getSystemService",
16//! "(Ljava/lang/String;)Ljava/lang/Object;",
17//! &[audio_service],
18//! )?
19//! .l()?;
20//! ```
21use std::ffi::c_void;
22
23static mut ANDROID_CONTEXT: Option<AndroidContext> = None;
24
25/// [`AndroidContext`] provides the pointers required to interface with the jni on Android
26/// platforms.
27#[derive(Clone, Copy, Debug)]
28pub struct AndroidContext {
29 java_vm: *mut c_void,
30 context_jobject: *mut c_void,
31}
32
33impl AndroidContext {
34 /// A handle to the `JavaVM` object.
35 ///
36 /// Usage with [__jni__](https://crates.io/crates/jni) crate:
37 /// ```no_run
38 /// let ctx = ndk_context::android_context();
39 /// let vm = unsafe { jni::JavaVM::from_raw(ctx.vm().cast()) }?;
40 /// let env = vm.attach_current_thread();
41 /// ```
42 pub fn vm(self) -> *mut c_void {
43 self.java_vm
44 }
45
46 /// A handle to an [android.content.Context](https://developer.android.com/reference/android/content/Context).
47 /// In most cases this will be a ptr to an `Activity`, but this isn't guaranteed.
48 ///
49 /// Usage with [__jni__](https://crates.io/crates/jni) crate:
50 /// ```no_run
51 /// let ctx = ndk_context::android_context();
52 /// let vm = unsafe { jni::JavaVM::from_raw(ctx.vm().cast()) }?;
53 /// let env = vm.attach_current_thread();
54 /// let class_ctx = env.find_class("android/content/Context")?;
55 /// let audio_service = env.get_static_field(class_ctx, "AUDIO_SERVICE", "Ljava/lang/String;")?;
56 /// let audio_manager = env
57 /// .call_method(
58 /// ctx.context() as jni::sys::jobject,
59 /// "getSystemService",
60 /// "(Ljava/lang/String;)Ljava/lang/Object;",
61 /// &[audio_service],
62 /// )?
63 /// .l()?;
64 /// ```
65 pub fn context(self) -> *mut c_void {
66 self.context_jobject
67 }
68}
69
70/// Main entry point to this crate. Returns an [`AndroidContext`].
71pub fn android_context() -> AndroidContext {
72 unsafe { ANDROID_CONTEXT.expect("android context was not initialized") }
73}
74
75/// Initializes the [`AndroidContext`]. [`AndroidContext`] is initialized by [__ndk-glue__](https://crates.io/crates/ndk-glue)
76/// before `main` is called.
77///
78/// # Safety
79///
80/// The pointers must be valid and this function must be called exactly once before `main` is
81/// called.
82pub unsafe fn initialize_android_context(java_vm: *mut c_void, context_jobject: *mut c_void) {
83 let previous = ANDROID_CONTEXT.replace(AndroidContext {
84 java_vm,
85 context_jobject,
86 });
87 assert!(previous.is_none());
88}
89
90/// Removes the [`AndroidContext`]. It is released by [__ndk-glue__](https://crates.io/crates/ndk-glue)
91/// when the activity is finished and destroyed.
92///
93/// # Safety
94///
95/// This function must only be called after [`initialize_android_context()`],
96/// when the activity is subsequently destroyed according to Android.
97pub unsafe fn release_android_context() {
98 let previous = ANDROID_CONTEXT.take();
99 assert!(previous.is_some());
100}