From dc9ca832c55a4db93bef13221bdb89778887a55d Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Mon, 13 Feb 2023 02:38:49 +0000 Subject: [PATCH] Add AndroidApp::vm_as_ptr() and ::activity_as_ptr() APIs This enables applications to make JNI calls without needing the `ndk-context` crate - which we would like to deprecate. Fixes: #60 --- android-activity/CHANGELOG.md | 3 ++ android-activity/src/game_activity/mod.rs | 11 ++++++ android-activity/src/lib.rs | 44 +++++++++++++++++++++ android-activity/src/native_activity/mod.rs | 10 +++++ 4 files changed, 68 insertions(+) diff --git a/android-activity/CHANGELOG.md b/android-activity/CHANGELOG.md index d524f7e..b2dbebd 100644 --- a/android-activity/CHANGELOG.md +++ b/android-activity/CHANGELOG.md @@ -3,6 +3,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Added +- Added `AndroidApp::vm_as_ptr()` to expose JNI `JavaVM` pointer ([#60](https://github.com/rust-mobile/android-activity/issues/60)) +- Added `AndroidApp::activity_as_ptr()` to expose Android `Activity` JNI reference as pointer ([#60](https://github.com/rust-mobile/android-activity/issues/60)) ## [0.4] - 2022-11-10 ### Changed diff --git a/android-activity/src/game_activity/mod.rs b/android-activity/src/game_activity/mod.rs index 0813fa3..842865f 100644 --- a/android-activity/src/game_activity/mod.rs +++ b/android-activity/src/game_activity/mod.rs @@ -12,6 +12,7 @@ use std::sync::{Arc, RwLock}; use std::time::Duration; use std::{ptr, thread}; +use libc::c_void; use log::{error, trace, Level}; use jni_sys::*; @@ -155,6 +156,16 @@ pub struct AndroidAppInner { } impl AndroidAppInner { + pub fn vm_as_ptr(&self) -> *mut c_void { + let app_ptr = self.native_app.as_ptr(); + unsafe { (*(*app_ptr).activity).vm as _ } + } + + pub fn activity_as_ptr(&self) -> *mut c_void { + let app_ptr = self.native_app.as_ptr(); + unsafe { (*(*app_ptr).activity).javaGameActivity as _ } + } + pub fn native_window(&self) -> Option { self.native_window.read().unwrap().clone() } diff --git a/android-activity/src/lib.rs b/android-activity/src/lib.rs index 594dd59..6ae6b0c 100644 --- a/android-activity/src/lib.rs +++ b/android-activity/src/lib.rs @@ -62,6 +62,7 @@ use std::sync::Arc; use std::sync::RwLock; use std::time::Duration; +use libc::c_void; use ndk::asset::AssetManager; use ndk::native_window::NativeWindow; @@ -472,6 +473,49 @@ impl AndroidApp { self.inner.read().unwrap().native_window() } + /// Returns a pointer to the Java Virtual Machine, for making JNI calls + /// + /// This returns a pointer to the Java Virtual Machine which can be used + /// with the [`jni`] crate (or similar crates) to make JNI calls that bridge + /// between native Rust code and Java/Kotlin code running within the JVM. + /// + /// If you use the [`jni`] crate you can wrap this as a [`JavaVM`] via: + /// ```ignore + /// # use jni::JavaVM; + /// # let app: AndroidApp = todo!(); + /// let vm = unsafe { JavaVM::from_raw(app.vm_as_ptr()) }; + /// ``` + /// + /// [`jni`]: https://crates.io/crates/jni + /// [`JavaVM`]: https://docs.rs/jni/latest/jni/struct.JavaVM.html + pub fn vm_as_ptr(&self) -> *mut c_void { + self.inner.read().unwrap().vm_as_ptr() + } + + /// Returns a JNI object reference for this application's JVM `Activity` as a pointer + /// + /// If you use the [`jni`] crate you can wrap this as an object reference via: + /// ```ignore + /// # use jni::objects::JObject; + /// # let app: AndroidApp = todo!(); + /// let activity = unsafe { JObject::from_raw(app.activity_as_ptr()) }; + /// ``` + /// + /// # JNI Safety + /// + /// Note that the object reference will be a JNI global reference, not a + /// local reference and it should not be deleted. Don't wrap the reference + /// in an [`AutoLocal`] which would try to explicitly delete the reference + /// when dropped. Similarly, don't wrap the reference as a [`GlobalRef`] + /// which would also try to explicitly delete the reference when dropped. + /// + /// [`jni`]: https://crates.io/crates/jni + /// [`AutoLocal`]: https://docs.rs/jni/latest/jni/objects/struct.AutoLocal.html + /// [`GlobalRef`]: https://docs.rs/jni/latest/jni/objects/struct.GlobalRef.html + pub fn activity_as_ptr(&self) -> *mut c_void { + self.inner.read().unwrap().activity_as_ptr() + } + /// Polls for any events associated with this [AndroidApp] and processes those events /// (such as lifecycle events) via the given `callback`. /// diff --git a/android-activity/src/native_activity/mod.rs b/android-activity/src/native_activity/mod.rs index f8676e6..a67f56a 100644 --- a/android-activity/src/native_activity/mod.rs +++ b/android-activity/src/native_activity/mod.rs @@ -5,6 +5,7 @@ use std::ptr::NonNull; use std::sync::{Arc, RwLock}; use std::time::Duration; +use libc::c_void; use log::{error, trace}; use ndk_sys::ALooper_wake; @@ -130,6 +131,15 @@ pub(crate) struct AndroidAppInner { } impl AndroidAppInner { + pub(crate) fn vm_as_ptr(&self) -> *mut c_void { + unsafe { (*self.native_activity.activity).vm as _ } + } + + pub(crate) fn activity_as_ptr(&self) -> *mut c_void { + // "clazz" is a completely bogus name; this is the _instance_ not class pointer + unsafe { (*self.native_activity.activity).clazz as _ } + } + pub(crate) fn native_activity(&self) -> *const ndk_sys::ANativeActivity { self.native_activity.activity }