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

Add ndk-context. #223

Merged
merged 17 commits into from
Feb 14, 2022
Merged
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
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.
MarijnS95 marked this conversation as resolved.
Show resolved Hide resolved
//!
//! ```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
MarijnS95 marked this conversation as resolved.
Show resolved Hide resolved
/// 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