Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 52a7855d authored by Devin Moore's avatar Devin Moore Committed by Gerrit Code Review
Browse files

Merge "Add a libbinder_rs wrapper around the new ARpc APIs" into main

parents bad4c6e2 bbd71b11
Loading
Loading
Loading
Loading
+19 −0
Original line number Diff line number Diff line
@@ -15,6 +15,8 @@ rust_library {
        "libbinder_ndk_sys",
        "libdowncast_rs",
        "liblibc",
        "liblog_rust",
        "libnix",
    ],
    host_supported: true,
    vendor_available: true,
@@ -79,6 +81,9 @@ rust_library {
    shared_libs: [
        "libbinder_ndk",
    ],
    rustlibs: [
        "liblibc",
    ],
    host_supported: true,
    vendor_available: true,
    product_available: true,
@@ -129,9 +134,18 @@ rust_bindgen {
        // rustified
        "libbinder_ndk_bindgen_flags.txt",
    ],
    bindgen_flags: [
        "--blocklist-type",
        "sockaddr",
        "--raw-line",
        "use libc::sockaddr;",
    ],
    shared_libs: [
        "libbinder_ndk",
    ],
    rustlibs: [
        "liblibc",
    ],
    host_supported: true,
    vendor_available: true,
    product_available: true,
@@ -185,6 +199,8 @@ rust_test {
        "libbinder_ndk_sys",
        "libdowncast_rs",
        "liblibc",
        "liblog_rust",
        "libnix",
    ],
}

@@ -196,4 +212,7 @@ rust_test {
    auto_gen_config: true,
    clippy_lints: "none",
    lints: "none",
    rustlibs: [
        "liblibc",
    ],
}
+4 −0
Original line number Diff line number Diff line
@@ -104,6 +104,8 @@ mod proxy;
mod service;
#[cfg(not(trusty))]
mod state;
#[cfg(not(any(android_vendor, android_vndk)))]
mod system_only;

use binder_ndk_sys as sys;

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

/// Binder result containing a [`Status`] on error.
pub type Result<T> = std::result::Result<T, Status>;
+187 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use crate::proxy::SpIBinder;
use crate::sys;

use std::ffi::{c_void, CStr, CString};
use std::os::raw::c_char;

use libc::sockaddr;
use nix::sys::socket::{SockaddrLike, UnixAddr, VsockAddr};
use std::sync::Arc;
use std::{fmt, ptr};

/// Rust wrapper around ABinderRpc_Accessor objects for RPC binder service management.
///
/// Dropping the `Accessor` will drop the underlying object and the binder it owns.
pub struct Accessor {
    accessor: *mut sys::ABinderRpc_Accessor,
}

impl fmt::Debug for Accessor {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "ABinderRpc_Accessor({:p})", self.accessor)
    }
}

/// Socket connection info required for libbinder to connect to a service.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConnectionInfo {
    /// For vsock connection
    Vsock(VsockAddr),
    /// For unix domain socket connection
    Unix(UnixAddr),
}

/// Safety: A `Accessor` is a wrapper around `ABinderRpc_Accessor` which is
/// `Sync` and `Send`. As
/// `ABinderRpc_Accessor` is threadsafe, this structure is too.
/// The Fn owned the Accessor has `Sync` and `Send` properties
unsafe impl Send for Accessor {}

/// Safety: A `Accessor` is a wrapper around `ABinderRpc_Accessor` which is
/// `Sync` and `Send`. As `ABinderRpc_Accessor` is threadsafe, this structure is too.
/// The Fn owned the Accessor has `Sync` and `Send` properties
unsafe impl Sync for Accessor {}

impl Accessor {
    /// Create a new accessor that will call the given callback when its
    /// connection info is required.
    /// The callback object and all objects it captures are owned by the Accessor
    /// and will be deleted some time after the Accessor is Dropped. If the callback
    /// is being called when the Accessor is Dropped, the callback will not be deleted
    /// immediately.
    pub fn new<F>(instance: &str, callback: F) -> Accessor
    where
        F: Fn(&str) -> Option<ConnectionInfo> + Send + Sync + 'static,
    {
        let callback: *mut c_void = Arc::into_raw(Arc::new(callback)) as *mut c_void;
        let inst = CString::new(instance).unwrap();

        // Safety: The function pointer is a valid connection_info callback.
        // This call returns an owned `ABinderRpc_Accessor` pointer which
        // must be destroyed via `ABinderRpc_Accessor_delete` when no longer
        // needed.
        // When the underlying ABinderRpc_Accessor is deleted, it will call
        // the cookie_decr_refcount callback to release its strong ref.
        let accessor = unsafe {
            sys::ABinderRpc_Accessor_new(
                inst.as_ptr(),
                Some(Self::connection_info::<F>),
                callback,
                Some(Self::cookie_decr_refcount::<F>),
            )
        };

        Accessor { accessor }
    }

    /// Get the underlying binder for this Accessor for when it needs to be either
    /// registered with service manager or sent to another process.
    pub fn as_binder(&self) -> Option<SpIBinder> {
        // Safety: `ABinderRpc_Accessor_asBinder` returns either a null pointer or a
        // valid pointer to an owned `AIBinder`. Either of these values is safe to
        // pass to `SpIBinder::from_raw`.
        unsafe { SpIBinder::from_raw(sys::ABinderRpc_Accessor_asBinder(self.accessor)) }
    }

    /// Callback invoked from C++ when the connection info is needed.
    ///
    /// # Safety
    ///
    /// The `instance` parameter must be a non-null pointer to a valid C string for
    /// CStr::from_ptr. The memory must contain a valid null terminator at the end of
    /// the string within isize::MAX from the pointer. The memory must not be mutated for
    /// the duration of this function  call and must be valid for reads from the pointer
    /// to the null terminator.
    /// The `cookie` parameter must be the cookie for an `Arc<F>` and
    /// the caller must hold a ref-count to it.
    unsafe extern "C" fn connection_info<F>(
        instance: *const c_char,
        cookie: *mut c_void,
    ) -> *mut binder_ndk_sys::ABinderRpc_ConnectionInfo
    where
        F: Fn(&str) -> Option<ConnectionInfo> + Send + Sync + 'static,
    {
        if cookie.is_null() || instance.is_null() {
            log::error!("Cookie({cookie:p}) or instance({instance:p}) is null!");
            return ptr::null_mut();
        }
        // Safety: The caller promises that `cookie` is for an Arc<F>.
        let callback = unsafe { (cookie as *const F).as_ref().unwrap() };

        // Safety: The caller in libbinder_ndk will have already verified this is a valid
        // C string
        let inst = unsafe {
            match CStr::from_ptr(instance).to_str() {
                Ok(s) => s,
                Err(err) => {
                    log::error!("Failed to get a valid C string! {err:?}");
                    return ptr::null_mut();
                }
            }
        };

        let connection = match callback(inst) {
            Some(con) => con,
            None => {
                return ptr::null_mut();
            }
        };

        match connection {
            ConnectionInfo::Vsock(addr) => {
                // Safety: The sockaddr is being copied in the NDK API
                unsafe { sys::ABinderRpc_ConnectionInfo_new(addr.as_ptr(), addr.len()) }
            }
            ConnectionInfo::Unix(addr) => {
                // Safety: The sockaddr is being copied in the NDK API
                // The cast is from sockaddr_un* to sockaddr*.
                unsafe {
                    sys::ABinderRpc_ConnectionInfo_new(addr.as_ptr() as *const sockaddr, addr.len())
                }
            }
        }
    }

    /// Callback that decrements the ref-count.
    /// This is invoked from C++ when a binder is unlinked.
    ///
    /// # Safety
    ///
    /// The `cookie` parameter must be the cookie for an `Arc<F>` and
    /// the owner must give up a ref-count to it.
    unsafe extern "C" fn cookie_decr_refcount<F>(cookie: *mut c_void)
    where
        F: Fn(&str) -> Option<ConnectionInfo> + Send + Sync + 'static,
    {
        // Safety: The caller promises that `cookie` is for an Arc<F>.
        unsafe { Arc::decrement_strong_count(cookie as *const F) };
    }
}

impl Drop for Accessor {
    fn drop(&mut self) {
        // Safety: `self.accessor` is always a valid, owned
        // `ABinderRpc_Accessor` pointer returned by
        // `ABinderRpc_Accessor_new` when `self` was created. This delete
        // method can only be called once when `self` is dropped.
        unsafe {
            sys::ABinderRpc_Accessor_delete(self.accessor);
        }
    }
}
+1 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@
#include <android/binder_parcel.h>
#include <android/binder_parcel_platform.h>
#include <android/binder_process.h>
#include <android/binder_rpc.h>
#include <android/binder_shell.h>
#include <android/binder_stability.h>
#include <android/binder_status.h>
+39 −2
Original line number Diff line number Diff line
@@ -384,8 +384,8 @@ mod tests {
    use std::time::Duration;

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

    struct ToBeDeleted {
        deleted: Arc<AtomicBool>,
    }

    impl Drop for ToBeDeleted {
        fn drop(&mut self) {
            assert!(!self.deleted.load(Ordering::Relaxed));
            self.deleted.store(true, Ordering::Relaxed);
        }
    }

    #[test]
    fn test_accessor_callback_destruction() {
        let deleted: Arc<AtomicBool> = Arc::new(AtomicBool::new(false));
        {
            let accessor: Accessor;
            {
                let helper = ToBeDeleted { deleted: deleted.clone() };
                let get_connection_info = move |_instance: &str| {
                    // Capture this object so we can see it get destructed
                    // after the parent scope
                    let _ = &helper;
                    None
                };
                accessor = Accessor::new("foo.service", get_connection_info);
            }

            match accessor.as_binder() {
                Some(_) => {
                    assert!(!deleted.load(Ordering::Relaxed));
                }
                None => panic!("failed to get that accessor binder"),
            }
        }
        assert!(deleted.load(Ordering::Relaxed));
    }

    #[tokio::test]
    async fn reassociate_rust_binder_async() {
        let service_name = "testing_service";