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

Commit 6ca00da4 authored by Rahul Arya's avatar Rahul Arya
Browse files

[Connection Manager] Add FFI to access le_impl from Rust

This uses the API documented in le_manager.rs. This is an
interim solution, since the API does not match what would
be ideal, but it works for now.

Bug: 272572974
Test: unit
Change-Id: Ifc38fcd868c7fef295f89b1c65bcaa8879146890
parent d6707153
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -112,6 +112,7 @@ cc_library_static {
        "libchrome_support_defaults",
    ],
    srcs: [
        "src/connection/ffi/connection_shim.cc",
        "src/core/ffi/module.cc",
        "src/gatt/ffi/gatt_shim.cc",
    ],
@@ -128,6 +129,8 @@ cc_library_static {
    static_libs: [
        "libbt_shim_bridge",
        "libbt_shim_ffi",

        "libflatbuffers-cpp",
    ],
    generated_headers: [
        "cxx-bridge-header",
@@ -167,6 +170,7 @@ cc_library_static {
filegroup {
    name: "libbluetooth_core_rs_ffi",
    srcs: [
        "src/connection/ffi.rs",
        "src/core/ffi.rs",
        "src/gatt/ffi.rs",
    ],
+1 −0
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ cxxbridge_libheader("cxxlibheader") {

static_library("core_rs") {
  sources = [
    "src/connection/ffi/connection_shim.cc",
    "src/core/ffi/module.cc",
    "src/gatt/ffi/gatt_shim.cc",
  ]
+3 −0
Original line number Diff line number Diff line
@@ -17,8 +17,11 @@ use self::le_manager::{
    ErrorCode, InactiveLeAclManager, LeAclManager, LeAclManagerConnectionCallbacks,
};

mod ffi;
pub mod le_manager;

pub use ffi::{LeAclManagerImpl, LeAclManagerShim};

/// Possible errors returned when making a connection attempt
#[derive(Debug)]
pub enum CreateConnectionFailure {
+155 −0
Original line number Diff line number Diff line
//! FFI interfaces for the Connection module.

use std::{fmt::Debug, pin::Pin};

use cxx::UniquePtr;
pub use inner::*;
use tokio::{
    sync::mpsc::{unbounded_channel, UnboundedSender},
    task::spawn_local,
};

use crate::core::address::AddressWithType;

use super::{
    le_manager::{ErrorCode, InactiveLeAclManager, LeAclManager, LeAclManagerConnectionCallbacks},
    LeConnection,
};

unsafe impl Send for LeAclManagerShim {}

#[cxx::bridge]
#[allow(clippy::needless_lifetimes)]
#[allow(clippy::too_many_arguments)]
#[allow(missing_docs)]
mod inner {
    impl UniquePtr<LeAclManagerShim> {}

    #[namespace = "bluetooth::core"]
    extern "C++" {
        type AddressWithType = crate::core::address::AddressWithType;
    }

    #[namespace = "bluetooth::connection"]
    unsafe extern "C++" {
        include!("src/connection/ffi/connection_shim.h");

        /// This lets us send HCI commands, either directly,
        /// or via the address manager
        type LeAclManagerShim;

        /// Add address to direct/background connect list, if not already connected
        /// If connected, then adding to direct list is a no-op, but adding to the
        /// background list will still take place.
        #[cxx_name = "CreateLeConnection"]
        fn create_le_connection(&self, address: AddressWithType, is_direct: bool);

        /// Remove address from both direct + background connect lists
        #[cxx_name = "CancelLeConnect"]
        fn cancel_le_connect(&self, address: AddressWithType);

        /// Register Rust callbacks for connection events
        ///
        /// # Safety
        /// `callbacks` must be Send + Sync, since C++ moves it to a different thread and
        /// invokes it from several others (GD + legacy threads).
        #[cxx_name = "RegisterRustCallbacks"]
        unsafe fn unchecked_register_rust_callbacks(
            self: Pin<&mut Self>,
            callbacks: Box<LeAclManagerCallbackShim>,
        );
    }

    #[namespace = "bluetooth::connection"]
    extern "Rust" {
        type LeAclManagerCallbackShim;
        #[cxx_name = "OnLeConnectSuccess"]
        fn on_le_connect_success(&self, address: AddressWithType);
        #[cxx_name = "OnLeConnectFail"]
        fn on_le_connect_fail(&self, address: AddressWithType, status: u8);
        #[cxx_name = "OnDisconnect"]
        fn on_disconnect(&self, address: AddressWithType);
    }
}

impl LeAclManagerShim {
    fn register_rust_callbacks(
        self: Pin<&mut LeAclManagerShim>,
        callbacks: Box<LeAclManagerCallbackShim>,
    ) where
        Box<LeAclManagerCallbackShim>: Send + Sync,
    {
        // SAFETY: The requirements of this method are enforced
        // by our own trait bounds.
        unsafe {
            self.unchecked_register_rust_callbacks(callbacks);
        }
    }
}

/// Implementation of HciConnectProxy wrapping the corresponding C++ methods
pub struct LeAclManagerImpl(pub UniquePtr<LeAclManagerShim>);

pub struct LeAclManagerCallbackShim(
    UnboundedSender<Box<dyn FnOnce(&dyn LeAclManagerConnectionCallbacks) + Send>>,
);

impl LeAclManagerCallbackShim {
    fn on_le_connect_success(&self, address: AddressWithType) {
        let _ = self.0.send(Box::new(move |callback| {
            callback.on_le_connect_success(LeConnection { remote_address: address })
        }));
    }

    fn on_le_connect_fail(&self, address: AddressWithType, status: u8) {
        let _ = self.0.send(Box::new(move |callback| {
            callback.on_le_connect_fail(address, ErrorCode(status));
        }));
    }

    fn on_disconnect(&self, address: AddressWithType) {
        let _ = self.0.send(Box::new(move |callback| {
            callback.on_disconnect(address);
        }));
    }
}

impl InactiveLeAclManager for LeAclManagerImpl {
    type ActiveManager = Self;

    fn register_callbacks(
        mut self,
        callbacks: impl LeAclManagerConnectionCallbacks + 'static,
    ) -> Self::ActiveManager {
        let (tx, mut rx) = unbounded_channel();

        self.0.pin_mut().register_rust_callbacks(Box::new(LeAclManagerCallbackShim(tx)));

        spawn_local(async move {
            while let Some(f) = rx.recv().await {
                f(&callbacks)
            }
        });
        self
    }
}

impl LeAclManager for LeAclManagerImpl {
    fn add_to_direct_list(&self, address: AddressWithType) {
        self.0.create_le_connection(address, /* is_direct= */ true)
    }

    fn add_to_background_list(&self, address: AddressWithType) {
        self.0.create_le_connection(address, /* is_direct= */ false)
    }

    fn remove_from_all_lists(&self, address: AddressWithType) {
        self.0.cancel_le_connect(address)
    }
}

impl Debug for LeAclManagerImpl {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_tuple("LeAclManagerImpl").finish()
    }
}
+122 −0
Original line number Diff line number Diff line
// Copyright 2023, 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.

#include "connection_shim.h"

#include <algorithm>
#include <cstdint>
#include <iterator>
#include <optional>

#include "hci/acl_manager.h"
#include "hci/address_with_type.h"
#include "hci/hci_packets.h"
#include "main/shim/entry.h"
#include "src/connection/ffi.rs.h"
#include "stack/btm/btm_dev.h"

namespace bluetooth {
namespace connection {

#ifdef TARGET_FLOSS
struct LeAclManagerCallbackShim {
  void OnLeConnectSuccess(core::AddressWithType addr) const {
    LOG_ALWAYS_FATAL("system/rust not available in Floss");
  }
  void OnLeConnectFail(core::AddressWithType addr, uint8_t status) const {
    LOG_ALWAYS_FATAL("system/rust not available in Floss");
  };
  void OnDisconnect(core::AddressWithType addr) const {
    LOG_ALWAYS_FATAL("system/rust not available in Floss");
  };
};

using BoxedLeAclManagerCallbackShim = std::unique_ptr<LeAclManagerCallbackShim>;

#else

using BoxedLeAclManagerCallbackShim = ::rust::Box<LeAclManagerCallbackShim>;

#endif

namespace {
hci::AddressWithType ToCppAddress(core::AddressWithType address) {
  auto hci_address = hci::Address();
  hci_address.FromOctets(address.address.data());
  return hci::AddressWithType(hci_address,
                              (hci::AddressType)address.address_type);
}

core::AddressWithType ToRustAddress(hci::AddressWithType address) {
  return core::AddressWithType{address.GetAddress().address,
                               (core::AddressType)address.GetAddressType()};
}
}  // namespace

struct LeAclManagerShim::impl : hci::acl_manager::LeAcceptlistCallbacks {
 public:
  impl() { acl_manager_ = shim::GetAclManager(); }

  void CreateLeConnection(core::AddressWithType address, bool is_direct) {
    acl_manager_->CreateLeConnection(ToCppAddress(address), is_direct);
  }

  void CancelLeConnect(core::AddressWithType address) {
    acl_manager_->CancelLeConnect(ToCppAddress(address));
  }

#ifndef TARGET_FLOSS
  void RegisterRustCallbacks(BoxedLeAclManagerCallbackShim callbacks) {
    callbacks_ = std::move(callbacks);
    acl_manager_->RegisterLeAcceptlistCallbacks(this);
  }
#endif

  // hci::acl_manager::LeAcceptlistCallbacks
  virtual void OnLeConnectSuccess(hci::AddressWithType address) {
    callbacks_.value()->OnLeConnectSuccess(ToRustAddress(address));
  }

  // hci::acl_manager::LeAcceptlistCallbacks
  virtual void OnResolvingListChange() {}

 private:
  std::optional<BoxedLeAclManagerCallbackShim> callbacks_;
  hci::AclManager* acl_manager_{};
};

LeAclManagerShim::LeAclManagerShim() {
  pimpl_ = std::make_unique<LeAclManagerShim::impl>();
}

LeAclManagerShim::~LeAclManagerShim() = default;

void LeAclManagerShim::CreateLeConnection(core::AddressWithType address,
                                          bool is_direct) const {
  pimpl_->CreateLeConnection(address, is_direct);
}

void LeAclManagerShim::CancelLeConnect(core::AddressWithType address) const {
  pimpl_->CancelLeConnect(address);
}

#ifndef TARGET_FLOSS
void LeAclManagerShim::RegisterRustCallbacks(
    BoxedLeAclManagerCallbackShim callbacks) {
  pimpl_->RegisterRustCallbacks(std::move(callbacks));
}
#endif

}  // namespace connection
}  // namespace bluetooth
Loading