Skip to content

Commit bbd71b1

Browse files
Add a libbinder_rs wrapper around the new ARpc APIs
This allows rust clients to set up client connections to binder RPC services underneath the libbinder_rs service manager APIs. Test: atest binderRpcTest vm_accessor_test Bug: 358427181 Change-Id: I05f1c5ff4e3447262eaf9df8250c9d99e3414559
1 parent 02401d9 commit bbd71b1

5 files changed

Lines changed: 250 additions & 2 deletions

File tree

libs/binder/rust/Android.bp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ rust_library {
1515
"libbinder_ndk_sys",
1616
"libdowncast_rs",
1717
"liblibc",
18+
"liblog_rust",
19+
"libnix",
1820
],
1921
host_supported: true,
2022
vendor_available: true,
@@ -79,6 +81,9 @@ rust_library {
7981
shared_libs: [
8082
"libbinder_ndk",
8183
],
84+
rustlibs: [
85+
"liblibc",
86+
],
8287
host_supported: true,
8388
vendor_available: true,
8489
product_available: true,
@@ -129,9 +134,18 @@ rust_bindgen {
129134
// rustified
130135
"libbinder_ndk_bindgen_flags.txt",
131136
],
137+
bindgen_flags: [
138+
"--blocklist-type",
139+
"sockaddr",
140+
"--raw-line",
141+
"use libc::sockaddr;",
142+
],
132143
shared_libs: [
133144
"libbinder_ndk",
134145
],
146+
rustlibs: [
147+
"liblibc",
148+
],
135149
host_supported: true,
136150
vendor_available: true,
137151
product_available: true,
@@ -185,6 +199,8 @@ rust_test {
185199
"libbinder_ndk_sys",
186200
"libdowncast_rs",
187201
"liblibc",
202+
"liblog_rust",
203+
"libnix",
188204
],
189205
}
190206

@@ -196,4 +212,7 @@ rust_test {
196212
auto_gen_config: true,
197213
clippy_lints: "none",
198214
lints: "none",
215+
rustlibs: [
216+
"liblibc",
217+
],
199218
}

libs/binder/rust/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ mod proxy;
104104
mod service;
105105
#[cfg(not(trusty))]
106106
mod state;
107+
#[cfg(not(any(android_vendor, android_vndk)))]
108+
mod system_only;
107109

108110
use binder_ndk_sys as sys;
109111

@@ -120,6 +122,8 @@ pub use service::{
120122
};
121123
#[cfg(not(trusty))]
122124
pub use state::{ProcessState, ThreadState};
125+
#[cfg(not(any(android_vendor, android_vndk)))]
126+
pub use system_only::{Accessor, ConnectionInfo};
123127

124128
/// Binder result containing a [`Status`] on error.
125129
pub type Result<T> = std::result::Result<T, Status>;
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
/*
2+
* Copyright (C) 2024 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
use crate::proxy::SpIBinder;
18+
use crate::sys;
19+
20+
use std::ffi::{c_void, CStr, CString};
21+
use std::os::raw::c_char;
22+
23+
use libc::sockaddr;
24+
use nix::sys::socket::{SockaddrLike, UnixAddr, VsockAddr};
25+
use std::sync::Arc;
26+
use std::{fmt, ptr};
27+
28+
/// Rust wrapper around ABinderRpc_Accessor objects for RPC binder service management.
29+
///
30+
/// Dropping the `Accessor` will drop the underlying object and the binder it owns.
31+
pub struct Accessor {
32+
accessor: *mut sys::ABinderRpc_Accessor,
33+
}
34+
35+
impl fmt::Debug for Accessor {
36+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37+
write!(f, "ABinderRpc_Accessor({:p})", self.accessor)
38+
}
39+
}
40+
41+
/// Socket connection info required for libbinder to connect to a service.
42+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
43+
pub enum ConnectionInfo {
44+
/// For vsock connection
45+
Vsock(VsockAddr),
46+
/// For unix domain socket connection
47+
Unix(UnixAddr),
48+
}
49+
50+
/// Safety: A `Accessor` is a wrapper around `ABinderRpc_Accessor` which is
51+
/// `Sync` and `Send`. As
52+
/// `ABinderRpc_Accessor` is threadsafe, this structure is too.
53+
/// The Fn owned the Accessor has `Sync` and `Send` properties
54+
unsafe impl Send for Accessor {}
55+
56+
/// Safety: A `Accessor` is a wrapper around `ABinderRpc_Accessor` which is
57+
/// `Sync` and `Send`. As `ABinderRpc_Accessor` is threadsafe, this structure is too.
58+
/// The Fn owned the Accessor has `Sync` and `Send` properties
59+
unsafe impl Sync for Accessor {}
60+
61+
impl Accessor {
62+
/// Create a new accessor that will call the given callback when its
63+
/// connection info is required.
64+
/// The callback object and all objects it captures are owned by the Accessor
65+
/// and will be deleted some time after the Accessor is Dropped. If the callback
66+
/// is being called when the Accessor is Dropped, the callback will not be deleted
67+
/// immediately.
68+
pub fn new<F>(instance: &str, callback: F) -> Accessor
69+
where
70+
F: Fn(&str) -> Option<ConnectionInfo> + Send + Sync + 'static,
71+
{
72+
let callback: *mut c_void = Arc::into_raw(Arc::new(callback)) as *mut c_void;
73+
let inst = CString::new(instance).unwrap();
74+
75+
// Safety: The function pointer is a valid connection_info callback.
76+
// This call returns an owned `ABinderRpc_Accessor` pointer which
77+
// must be destroyed via `ABinderRpc_Accessor_delete` when no longer
78+
// needed.
79+
// When the underlying ABinderRpc_Accessor is deleted, it will call
80+
// the cookie_decr_refcount callback to release its strong ref.
81+
let accessor = unsafe {
82+
sys::ABinderRpc_Accessor_new(
83+
inst.as_ptr(),
84+
Some(Self::connection_info::<F>),
85+
callback,
86+
Some(Self::cookie_decr_refcount::<F>),
87+
)
88+
};
89+
90+
Accessor { accessor }
91+
}
92+
93+
/// Get the underlying binder for this Accessor for when it needs to be either
94+
/// registered with service manager or sent to another process.
95+
pub fn as_binder(&self) -> Option<SpIBinder> {
96+
// Safety: `ABinderRpc_Accessor_asBinder` returns either a null pointer or a
97+
// valid pointer to an owned `AIBinder`. Either of these values is safe to
98+
// pass to `SpIBinder::from_raw`.
99+
unsafe { SpIBinder::from_raw(sys::ABinderRpc_Accessor_asBinder(self.accessor)) }
100+
}
101+
102+
/// Callback invoked from C++ when the connection info is needed.
103+
///
104+
/// # Safety
105+
///
106+
/// The `instance` parameter must be a non-null pointer to a valid C string for
107+
/// CStr::from_ptr. The memory must contain a valid null terminator at the end of
108+
/// the string within isize::MAX from the pointer. The memory must not be mutated for
109+
/// the duration of this function call and must be valid for reads from the pointer
110+
/// to the null terminator.
111+
/// The `cookie` parameter must be the cookie for an `Arc<F>` and
112+
/// the caller must hold a ref-count to it.
113+
unsafe extern "C" fn connection_info<F>(
114+
instance: *const c_char,
115+
cookie: *mut c_void,
116+
) -> *mut binder_ndk_sys::ABinderRpc_ConnectionInfo
117+
where
118+
F: Fn(&str) -> Option<ConnectionInfo> + Send + Sync + 'static,
119+
{
120+
if cookie.is_null() || instance.is_null() {
121+
log::error!("Cookie({cookie:p}) or instance({instance:p}) is null!");
122+
return ptr::null_mut();
123+
}
124+
// Safety: The caller promises that `cookie` is for an Arc<F>.
125+
let callback = unsafe { (cookie as *const F).as_ref().unwrap() };
126+
127+
// Safety: The caller in libbinder_ndk will have already verified this is a valid
128+
// C string
129+
let inst = unsafe {
130+
match CStr::from_ptr(instance).to_str() {
131+
Ok(s) => s,
132+
Err(err) => {
133+
log::error!("Failed to get a valid C string! {err:?}");
134+
return ptr::null_mut();
135+
}
136+
}
137+
};
138+
139+
let connection = match callback(inst) {
140+
Some(con) => con,
141+
None => {
142+
return ptr::null_mut();
143+
}
144+
};
145+
146+
match connection {
147+
ConnectionInfo::Vsock(addr) => {
148+
// Safety: The sockaddr is being copied in the NDK API
149+
unsafe { sys::ABinderRpc_ConnectionInfo_new(addr.as_ptr(), addr.len()) }
150+
}
151+
ConnectionInfo::Unix(addr) => {
152+
// Safety: The sockaddr is being copied in the NDK API
153+
// The cast is from sockaddr_un* to sockaddr*.
154+
unsafe {
155+
sys::ABinderRpc_ConnectionInfo_new(addr.as_ptr() as *const sockaddr, addr.len())
156+
}
157+
}
158+
}
159+
}
160+
161+
/// Callback that decrements the ref-count.
162+
/// This is invoked from C++ when a binder is unlinked.
163+
///
164+
/// # Safety
165+
///
166+
/// The `cookie` parameter must be the cookie for an `Arc<F>` and
167+
/// the owner must give up a ref-count to it.
168+
unsafe extern "C" fn cookie_decr_refcount<F>(cookie: *mut c_void)
169+
where
170+
F: Fn(&str) -> Option<ConnectionInfo> + Send + Sync + 'static,
171+
{
172+
// Safety: The caller promises that `cookie` is for an Arc<F>.
173+
unsafe { Arc::decrement_strong_count(cookie as *const F) };
174+
}
175+
}
176+
177+
impl Drop for Accessor {
178+
fn drop(&mut self) {
179+
// Safety: `self.accessor` is always a valid, owned
180+
// `ABinderRpc_Accessor` pointer returned by
181+
// `ABinderRpc_Accessor_new` when `self` was created. This delete
182+
// method can only be called once when `self` is dropped.
183+
unsafe {
184+
sys::ABinderRpc_Accessor_delete(self.accessor);
185+
}
186+
}
187+
}

libs/binder/rust/sys/BinderBindings.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <android/binder_parcel.h>
2121
#include <android/binder_parcel_platform.h>
2222
#include <android/binder_process.h>
23+
#include <android/binder_rpc.h>
2324
#include <android/binder_shell.h>
2425
#include <android/binder_stability.h>
2526
#include <android/binder_status.h>

libs/binder/rust/tests/integration.rs

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -384,8 +384,8 @@ mod tests {
384384
use std::time::Duration;
385385

386386
use binder::{
387-
BinderFeatures, DeathRecipient, FromIBinder, IBinder, Interface, SpIBinder, StatusCode,
388-
Strong,
387+
Accessor, BinderFeatures, DeathRecipient, FromIBinder, IBinder, Interface, SpIBinder,
388+
StatusCode, Strong,
389389
};
390390
// Import from impl API for testing only, should not be necessary as long as
391391
// you are using AIDL.
@@ -908,6 +908,43 @@ mod tests {
908908
assert_eq!(service.test().unwrap(), service_name);
909909
}
910910

911+
struct ToBeDeleted {
912+
deleted: Arc<AtomicBool>,
913+
}
914+
915+
impl Drop for ToBeDeleted {
916+
fn drop(&mut self) {
917+
assert!(!self.deleted.load(Ordering::Relaxed));
918+
self.deleted.store(true, Ordering::Relaxed);
919+
}
920+
}
921+
922+
#[test]
923+
fn test_accessor_callback_destruction() {
924+
let deleted: Arc<AtomicBool> = Arc::new(AtomicBool::new(false));
925+
{
926+
let accessor: Accessor;
927+
{
928+
let helper = ToBeDeleted { deleted: deleted.clone() };
929+
let get_connection_info = move |_instance: &str| {
930+
// Capture this object so we can see it get destructed
931+
// after the parent scope
932+
let _ = &helper;
933+
None
934+
};
935+
accessor = Accessor::new("foo.service", get_connection_info);
936+
}
937+
938+
match accessor.as_binder() {
939+
Some(_) => {
940+
assert!(!deleted.load(Ordering::Relaxed));
941+
}
942+
None => panic!("failed to get that accessor binder"),
943+
}
944+
}
945+
assert!(deleted.load(Ordering::Relaxed));
946+
}
947+
911948
#[tokio::test]
912949
async fn reassociate_rust_binder_async() {
913950
let service_name = "testing_service";

0 commit comments

Comments
 (0)