Skip to content

Commit a7ce644

Browse files
authored
ndk: Add bindings for ASharedMemory (#427)
`ASharedMemory` allows allocating shared memory as a file descriptor that can be `mmap`ed or shared to other processes via parcels or Unix domain sockets. Its Java counterpart is `android.os.SharedMemory` from which (when available as a JNI `jobject`) an owned file descriptor clone can be created to access the memory object in native code. Common Rust traits such as `FromRawFd` are provided so that a `SharedMemory` object can also be created from a received file descriptor.
1 parent 6e36b1b commit a7ce644

File tree

4 files changed

+159
-0
lines changed

4 files changed

+159
-0
lines changed

ndk/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
- **Breaking:** Upgrade to [`ndk-sys 0.5.0`](../ndk-sys/CHANGELOG.md#050-beta0-2023-08-15). (#420)
2727
- **Breaking:** bitmap: Provide detailed implementation for `AndroidBitmapInfoFlags`. (#424)
2828
- native_window: Add `set_buffers_transform()`, `try_allocate_buffers()` and `set_frame_rate*()`. (#425)
29+
- Add bindings for `ASharedMemory`. (#427)
2930
- hardware_buffer: Add `id()` to retrieve a system-wide unique identifier for a `HardwareBuffer`. (#428)
3031
- **Breaking:** bitmap: Strip `Android` prefix from structs and enums, and `Bitmap` from `Result`. (#430)
3132
- **Breaking:** `raw-window-handle 0.5` support is now behind an _optional_ `rwh_05` crate feature and `raw-window-handle` `0.4` and `0.6` support is provided via the new `rwh_04` and (default-enabled) `rwh_06` crate features. (#434)

ndk/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ package = "ndk-sys"
5151
path = "../ndk-sys"
5252
version = "0.5.0-beta.0"
5353

54+
[dev-dependencies]
55+
# Only for use in documentation and doc-tests
56+
libc = "0.2"
57+
5458
[package.metadata.docs.rs]
5559
features = ["jni", "all"]
5660
rustdoc-args = ["--cfg", "docsrs"]

ndk/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ pub mod media;
2626
pub mod media_error;
2727
pub mod native_activity;
2828
pub mod native_window;
29+
pub mod shared_memory;
2930
pub mod surface_texture;
3031
pub mod trace;
3132
mod utils;

ndk/src/shared_memory.rs

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
//! Bindings for [`ASharedMemory`]
2+
//!
3+
//! [`ASharedMemory`]: https://developer.android.com/ndk/reference/group/memory
4+
#![cfg(feature = "api-level-26")]
5+
6+
use std::{
7+
ffi::CStr,
8+
io::{Error, Result},
9+
// TODO: Import from std::os::fd::{} since Rust 1.66
10+
os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd},
11+
ptr,
12+
};
13+
14+
#[cfg(feature = "api-level-27")]
15+
use jni_sys::{jobject, JNIEnv};
16+
17+
/// Enables the creation, mapping, and protection control over anonymous shared memory.
18+
#[derive(Debug)]
19+
#[doc(alias = "ASharedMemory")]
20+
pub struct SharedMemory(OwnedFd);
21+
22+
impl SharedMemory {
23+
/// Create a shared memory region.
24+
///
25+
/// Creates shared memory region and returns a file descriptor. The resulting file descriptor
26+
/// can be `mmap`'ed to process memory space with `PROT_READ | PROT_WRITE | PROT_EXEC`. Access
27+
/// to this shared memory region can be restricted with [`set_prot()`][Self::set_prot()].
28+
///
29+
/// Use [`android.os.ParcelFileDescriptor`] to pass the file descriptor to another process.
30+
/// File descriptors may also be sent to other processes over a Unix domain socket with
31+
/// `sendmsg` and `SCM_RIGHTS`. See `sendmsg(3)` and `cmsg(3)` man pages for more information.
32+
///
33+
/// If you intend to share this file descriptor with a child process after calling `exec(3)`,
34+
/// note that you will need to use `fcntl(2)` with `F_SETFD` to clear the `FD_CLOEXEC` flag for
35+
/// this to work on all versions of Android.
36+
///
37+
/// [`android.os.ParcelFileDescriptor`]: https://developer.android.com/reference/android/os/ParcelFileDescriptor
38+
#[doc(alias = "ASharedMemory_create")]
39+
pub fn create(name: Option<&CStr>, size: usize) -> Result<Self> {
40+
let fd =
41+
unsafe { ffi::ASharedMemory_create(name.map_or(ptr::null(), |p| p.as_ptr()), size) };
42+
if fd < 0 {
43+
Err(Error::last_os_error())
44+
} else {
45+
Ok(unsafe { Self::from_raw_fd(fd) })
46+
}
47+
}
48+
49+
/// Returns a `dup`'d FD from the given Java [`android.os.SharedMemory`] object.
50+
///
51+
/// The returned file descriptor has all the same properties & capabilities as the FD returned
52+
/// from [`create()`][Self::create()], however the protection flags will be the same as those
53+
/// of the [`android.os.SharedMemory`] object.
54+
///
55+
/// [`android.os.SharedMemory`]: https://developer.android.com/reference/android/os/SharedMemory
56+
#[cfg(feature = "api-level-27")]
57+
#[doc(alias = "ASharedMemory_dupFromJava")]
58+
#[allow(clippy::not_unsafe_ptr_arg_deref)]
59+
pub fn dup_from_java(env: *mut JNIEnv, shared_memory: jobject) -> Result<Self> {
60+
let fd = unsafe { ffi::ASharedMemory_dupFromJava(env, shared_memory) };
61+
if fd < 0 {
62+
Err(Error::last_os_error())
63+
} else {
64+
Ok(unsafe { Self::from_raw_fd(fd) })
65+
}
66+
}
67+
68+
/// Get the size of the shared memory region.
69+
#[doc(alias = "ASharedMemory_getSize")]
70+
pub fn size(&self) -> usize {
71+
unsafe { ffi::ASharedMemory_getSize(self.as_raw_fd()) }
72+
}
73+
74+
/// Restrict access of shared memory region.
75+
///
76+
/// This function restricts access of a shared memory region. Access can only be removed. The
77+
/// effect applies globally to all file descriptors in all processes across the system that
78+
/// refer to this shared memory region. Existing memory mapped regions are not affected.
79+
///
80+
/// It is a common use case to create a shared memory region, map it read/write locally to
81+
/// initialize content, and then send the shared memory to another process with read only
82+
/// access. Code example as below:
83+
///
84+
/// ```no_run
85+
/// # use ndk::shared_memory::SharedMemory;
86+
/// # // TODO: Import from std::os::fd::{} since Rust 1.66
87+
/// # use std::os::unix::io::AsRawFd;
88+
/// # use std::ffi::CStr;
89+
/// # unsafe {
90+
/// let mem = SharedMemory::create(Some(CStr::from_bytes_with_nul_unchecked(b"memory\0")), 127).unwrap();
91+
/// // By default it has PROT_READ | PROT_WRITE | PROT_EXEC.
92+
/// let size = mem.size();
93+
/// let buffer = libc::mmap(
94+
/// std::ptr::null_mut(),
95+
/// size,
96+
/// libc::PROT_READ | libc::PROT_WRITE,
97+
/// libc::MAP_SHARED,
98+
/// mem.as_raw_fd(),
99+
/// 0,
100+
/// );
101+
/// let buffer_slice = std::slice::from_raw_parts_mut(buffer.cast(), size);
102+
///
103+
/// // trivially initialize content
104+
/// buffer_slice[..7].copy_from_slice(b"hello!\0");
105+
///
106+
/// // Existing mappings will retain their protection flags (PROT_WRITE here) after set_prod()
107+
/// // unless it is unmapped:
108+
/// libc::munmap(buffer, size);
109+
///
110+
/// // limit access to read only
111+
/// mem.set_prot(libc::PROT_READ);
112+
///
113+
/// // share fd with another process here and the other process can only map with PROT_READ.
114+
/// # }
115+
/// ```
116+
#[doc(alias = "ASharedMemory_setProt")]
117+
pub fn set_prot(&self, prot: i32) -> Result<()> {
118+
let status = unsafe { ffi::ASharedMemory_setProt(self.as_raw_fd(), prot) };
119+
if status < 0 {
120+
Err(Error::last_os_error())
121+
} else {
122+
Ok(())
123+
}
124+
}
125+
}
126+
127+
impl AsFd for SharedMemory {
128+
fn as_fd(&self) -> BorrowedFd<'_> {
129+
self.0.as_fd()
130+
}
131+
}
132+
133+
impl AsRawFd for SharedMemory {
134+
fn as_raw_fd(&self) -> RawFd {
135+
self.0.as_raw_fd()
136+
}
137+
}
138+
139+
impl IntoRawFd for SharedMemory {
140+
fn into_raw_fd(self) -> RawFd {
141+
self.0.into_raw_fd()
142+
}
143+
}
144+
145+
impl FromRawFd for SharedMemory {
146+
/// # Safety
147+
///
148+
/// The resource pointed to by `fd` must be open and suitable for assuming
149+
/// ownership. The resource must not require any cleanup other than `close`.
150+
unsafe fn from_raw_fd(fd: RawFd) -> Self {
151+
Self(OwnedFd::from_raw_fd(fd))
152+
}
153+
}

0 commit comments

Comments
 (0)