Skip to content

Commit

Permalink
Add ndk-context. (#223)
Browse files Browse the repository at this point in the history
* Add ndk-context.

* Add doc comment for `initialize_android_context`.

* Add links to README and some metadata.

* Fix ndk-build doc link.

* Deprecate `native_activity`.

* Revert "Deprecate `native_activity`."

This reverts commit bb117f0.

* Address review comments.

* Panic if initialized more than once.

* Update ndk-examples/examples/jni_audio.rs

Co-authored-by: Marijn Suijten <[email protected]>

* Update ndk-examples/examples/jni_audio.rs

Co-authored-by: Marijn Suijten <[email protected]>

* Use cast() function

* fmt

* Update ndk-context/src/lib.rs

Co-authored-by: Marijn Suijten <[email protected]>

* Address review comments.

* Update ndk-context/src/lib.rs

Co-authored-by: Marijn Suijten <[email protected]>

* Add deprecation warning.

* Prepare release.

Co-authored-by: Marijn Suijten <[email protected]>
  • Loading branch information
dvc94ch and MarijnS95 authored Feb 14, 2022
1 parent c1fb076 commit 21e78af
Show file tree
Hide file tree
Showing 13 changed files with 130 additions and 9 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[workspace]
members = [
"ndk",
"ndk-context",
"ndk-macro",
"ndk-build",
"ndk-examples",
Expand Down
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Name | Description | Badges
--- | --- | ---
[`ndk-sys`](./ndk-sys) | Raw FFI bindings to the NDK | [![crates.io](https://img.shields.io/crates/v/ndk-sys.svg)](https://crates.io/crates/ndk-sys) [![crates.io](https://docs.rs/ndk-sys/badge.svg)](https://docs.rs/ndk-sys)
[`ndk`](./ndk) | Safe abstraction of the bindings | [![crates.io](https://img.shields.io/crates/v/ndk.svg)](https://crates.io/crates/ndk) [![crates.io](https://docs.rs/ndk/badge.svg)](https://docs.rs/ndk)
[`ndk-context`](./ndk-context) | Android handles | [![crates.io](https://img.shields.io/crates/v/ndk-context.svg)](https://crates.io/crates/ndk-context)
[`ndk-glue`](./ndk-glue) | Startup code | [![crates.io](https://img.shields.io/crates/v/ndk-glue.svg)](https://crates.io/crates/ndk-glue) [![crates.io](https://docs.rs/ndk-glue/badge.svg)](https://docs.rs/ndk-glue)
[`ndk-build`](./ndk-build) | Everything for building apk's | [![crates.io](https://img.shields.io/crates/v/ndk-build.svg)](https://crates.io/crates/ndk-build) [![crates.io](https://docs.rs/ndk-build/badge.svg)](https://docs.rs/ndk-build)
[`cargo-apk`](./cargo-apk) | Build tool | [![crates.io](https://img.shields.io/crates/v/cargo-apk.svg)](https://crates.io/crates/cargo-apk) [![crates.io](https://docs.rs/cargo-apk/badge.svg)](https://docs.rs/cargo-apk)
Expand Down Expand Up @@ -105,7 +106,8 @@ fn main() {}
```

## JNI
Java Native Interface (JNI) allows executing Java code in a VM from native applications.
`ndk-examples` contains an `jni_audio` example which will print out all output audio devices in the log.
Java Native Interface (JNI) allows executing Java code in a VM from native applications. To access
the JNI use the `AndroidContext` from the `ndk-context` crate. `ndk-examples` contains a `jni_audio`
example which will print out all output audio devices in the log.

- [`jni`](https://crates.io/crates/jni), JNI bindings for Rust
2 changes: 1 addition & 1 deletion ndk-build/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ edition = "2018"
description = "Utilities for building Android binaries"
license = "MIT OR Apache-2.0"
keywords = ["android", "ndk", "apk"]
documentation = "https://docs.rs/android-build-tools"
documentation = "https://docs.rs/ndk-build"
homepage = "https://github.com/rust-windowing/android-ndk-rs"
repository = "https://github.com/rust-windowing/android-ndk-rs"

Expand Down
5 changes: 5 additions & 0 deletions ndk-context/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Unreleased

# 0.1.0 (2022-02-14)

- Initial release! 🎉
11 changes: 11 additions & 0 deletions ndk-context/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "ndk-context"
version = "0.1.0"
authors = ["The Rust Windowing contributors"]
edition = "2021"
description = "Handles for accessing Android APIs"
license = "MIT OR Apache-2.0"
keywords = ["android", "ndk", "apk", "jni"]
documentation = "https://docs.rs/ndk-context"
homepage = "https://github.com/rust-windowing/android-ndk-rs"
repository = "https://github.com/rust-windowing/android-ndk-rs"
6 changes: 6 additions & 0 deletions ndk-context/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# ndk-context

Provides a stable api to rust crates for interfacing with the android platform. It is
initialized by the runtime, usually [__ndk-glue__](https://crates.io/crates/ndk-glue),
but could also be initialized by java or kotlin code when embedding in an existing android
project.
88 changes: 88 additions & 0 deletions ndk-context/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
//! Provides a stable api to rust crates for interfacing with the android platform. It is
//! initialized by the runtime, usually [__ndk-glue__](https://crates.io/crates/ndk-glue),
//! but could also be initialized by java or kotlin code when embedding in an existing android
//! project.
//!
//! ```no_run
//! let ctx = ndk_context::android_context();
//! let vm = unsafe { jni::JavaVM::from_raw(ctx.vm().cast()) }?;
//! let env = vm.attach_current_thread();
//! let class_ctx = env.find_class("android/content/Context")?;
//! let audio_service = env.get_static_field(class_ctx, "AUDIO_SERVICE", "Ljava/lang/String;")?;
//! let audio_manager = env
//! .call_method(
//! ctx.context() as jni::sys::jobject,
//! "getSystemService",
//! "(Ljava/lang/String;)Ljava/lang/Object;",
//! &[audio_service],
//! )?
//! .l()?;
//! ```
use std::ffi::c_void;

static mut ANDROID_CONTEXT: Option<AndroidContext> = None;

/// [`AndroidContext`] provides the pointers required to interface with the jni on android
/// platforms.
#[derive(Clone, Copy, Debug)]
pub struct AndroidContext {
java_vm: *mut c_void,
context_jobject: *mut c_void,
}

impl AndroidContext {
/// A handle to the `JavaVM` object.
///
/// Usage with [__jni__](https://crates.io/crates/jni) crate:
/// ```no_run
/// let ctx = ndk_context::android_context();
/// let vm = unsafe { jni::JavaVM::from_raw(ctx.vm().cast()) }?;
/// let env = vm.attach_current_thread();
/// ```
pub fn vm(self) -> *mut c_void {
self.java_vm
}

/// A handle to an [android.content.Context](https://developer.android.com/reference/android/content/Context).
/// In most cases this will be a ptr to an `Activity`, but this isn't guaranteed.
///
/// Usage with [__jni__](https://crates.io/crates/jni) crate:
/// ```no_run
/// let ctx = ndk_context::android_context();
/// let vm = unsafe { jni::JavaVM::from_raw(ctx.vm().cast()) }?;
/// let env = vm.attach_current_thread();
/// let class_ctx = env.find_class("android/content/Context")?;
/// let audio_service = env.get_static_field(class_ctx, "AUDIO_SERVICE", "Ljava/lang/String;")?;
/// let audio_manager = env
/// .call_method(
/// ctx.context() as jni::sys::jobject,
/// "getSystemService",
/// "(Ljava/lang/String;)Ljava/lang/Object;",
/// &[audio_service],
/// )?
/// .l()?;
/// ```
pub fn context(self) -> *mut c_void {
self.context_jobject
}
}

/// Main entry point to this crate. Returns an [`AndroidContext`].
pub fn android_context() -> AndroidContext {
unsafe { ANDROID_CONTEXT.expect("android context was not initialized") }
}

/// Initializes the [`AndroidContext`]. [`AndroidContext`] is initialized by [__ndk-glue__](https://crates.io/crates/ndk-glue)
/// before `main` is called.
///
/// # Safety
///
/// The pointers must be valid and this function must be called exactly once before `main` is
/// called.
pub unsafe fn initialize_android_context(java_vm: *mut c_void, context_jobject: *mut c_void) {
let previous = ANDROID_CONTEXT.replace(AndroidContext {
java_vm,
context_jobject,
});
assert!(previous.is_none());
}
1 change: 1 addition & 0 deletions ndk-examples/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ jni = "0.18.0"
libc = "0.2"
log = "0.4.14"
ndk = { path = "../ndk", features = ["trace"] }
ndk-context = { path = "../ndk-context" }
ndk-glue = { path = "../ndk-glue", features = ["logger"] }

[[example]]
Expand Down
7 changes: 3 additions & 4 deletions ndk-examples/examples/jni_audio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@ const GET_DEVICES_OUTPUTS: jni::sys::jint = 2;

fn enumerate_audio_devices() -> Result<(), Box<dyn std::error::Error>> {
// Create a VM for executing Java calls
let native_activity = ndk_glue::native_activity();
let vm_ptr = native_activity.vm();
let vm = unsafe { jni::JavaVM::from_raw(vm_ptr) }?;
let ctx = ndk_context::android_context();
let vm = unsafe { jni::JavaVM::from_raw(ctx.vm().cast()) }?;
let env = vm.attach_current_thread()?;

// Query the global Audio Service
Expand All @@ -18,7 +17,7 @@ fn enumerate_audio_devices() -> Result<(), Box<dyn std::error::Error>> {

let audio_manager = env
.call_method(
native_activity.activity(),
ctx.context().cast(),
"getSystemService",
// JNI type signature needs to be derived from the Java API
// (ArgTys)ResultTy
Expand Down
1 change: 1 addition & 0 deletions ndk-examples/examples/looper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,5 +154,6 @@ fn main() {
}

// Stop the activity
#[allow(deprecated)]
ndk_glue::native_activity().finish()
}
4 changes: 4 additions & 0 deletions ndk-glue/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Unreleased

# 0.6.1 (2022-02-14)

- Initializes `ndk-context`.

# 0.6.0 (2022-01-05)

- **Breaking:** Update to `ndk-sys 0.3.0` and `ndk 0.6.0`.
Expand Down
5 changes: 3 additions & 2 deletions ndk-glue/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "ndk-glue"
version = "0.6.0"
version = "0.6.1"
authors = ["The Rust Windowing contributors"]
edition = "2018"
description = "Startup code for android binaries"
Expand All @@ -13,8 +13,9 @@ repository = "https://github.com/rust-windowing/android-ndk-rs"

[dependencies]
ndk = { path = "../ndk", version = "0.6.0" }
ndk-sys = { path = "../ndk-sys", version = "0.3.0" }
ndk-context = { path = "../ndk-context", version = "0.1.0" }
ndk-macro = { path = "../ndk-macro", version = "0.3.0" }
ndk-sys = { path = "../ndk-sys", version = "0.3.0" }
lazy_static = "1.4.0"
libc = "0.2.84"
log = "0.4.14"
Expand Down
2 changes: 2 additions & 0 deletions ndk-glue/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ lazy_static! {

static mut NATIVE_ACTIVITY: Option<NativeActivity> = None;

#[deprecated = "Use `ndk_context::android_context().vm()` instead."]
pub fn native_activity() -> &'static NativeActivity {
unsafe { NATIVE_ACTIVITY.as_ref().unwrap() }
}
Expand Down Expand Up @@ -165,6 +166,7 @@ pub unsafe fn init(
callbacks.onLowMemory = Some(on_low_memory);

let activity = NativeActivity::from_ptr(activity);
ndk_context::initialize_android_context(activity.vm().cast(), activity.activity().cast());
NATIVE_ACTIVITY = Some(activity);

let mut logpipe: [RawFd; 2] = Default::default();
Expand Down

0 comments on commit 21e78af

Please sign in to comment.