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

Commit 355e96d4 authored by Jeremy Wu's avatar Jeremy Wu
Browse files

Floss: implement vc_shim

This CL implements the topshim layer to bridge APIs between the native
stack and the Rust interface.

Bug: 317682584
Test: m Bluetooth
Flag: EXEMPT floss only changes
Change-Id: I25428532cdbbc0fab93cc841c958df0fa1a33fe8
parent 155fb1bd
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -53,6 +53,7 @@ cc_library_static {
        "gatt/gatt_shim.cc",
        "hfp/hfp_shim.cc",
        "le_audio/le_audio_shim.cc",
        "vc/vc_shim.cc",
    ],
    generated_headers: [
        "BluetoothGeneratedDumpsysDataSchema_h",
@@ -89,6 +90,7 @@ gensrcs {
        "src/profiles/gatt.rs",
        "src/profiles/hfp.rs",
        "src/profiles/le_audio.rs",
        "src/profiles/vc.rs",
    ],
    output_extension: "rs.h",
    export_include_dirs: ["."],
@@ -106,6 +108,7 @@ gensrcs {
        "src/profiles/gatt.rs",
        "src/profiles/hfp.rs",
        "src/profiles/le_audio.rs",
        "src/profiles/vc.rs",
    ],
    output_extension: "cc",
    export_include_dirs: ["."],
+3 −0
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ cxxbridge_header("btif_bridge_header") {
    "src/profiles/gatt.rs",
    "src/profiles/hfp.rs",
    "src/profiles/le_audio.rs",
    "src/profiles/vc.rs",
  ]
  all_dependent_configs = [ ":rust_topshim_config" ]
  deps = [ ":cxxlibheader" ]
@@ -45,6 +46,7 @@ cxxbridge_cc("btif_bridge_code") {
    "src/profiles/gatt.rs",
    "src/profiles/hfp.rs",
    "src/profiles/le_audio.rs",
    "src/profiles/vc.rs",
  ]
  deps = [
    ":btif_bridge_header",
@@ -67,6 +69,7 @@ source_set("btif_cxx_bridge_code") {
    "gatt/gatt_shim.cc",
    "hfp/hfp_shim.cc",
    "le_audio/le_audio_shim.cc",
    "vc/vc_shim.cc",
    "metrics/metrics_shim.cc",
  ]

+1 −0
Original line number Diff line number Diff line
@@ -35,3 +35,4 @@ pub mod hid_host;
pub mod le_audio;
pub mod sdp;
pub mod socket;
pub mod vc;
+311 −0
Original line number Diff line number Diff line
use crate::btif::{BluetoothInterface, RawAddress, ToggleableProfile};
use crate::topstack::get_dispatchers;

use std::sync::{Arc, Mutex};
use topshim_macros::{cb_variant, profile_enabled_or};

use log::warn;

#[cxx::bridge(namespace = bluetooth::topshim::rust)]
pub mod ffi {
    unsafe extern "C++" {
        include!("gd/rust/topshim/common/type_alias.h");
        type RawAddress = crate::btif::RawAddress;
    }

    #[derive(Debug, Copy, Clone)]
    pub enum BtVcConnectionState {
        Disconnected = 0,
        Connecting,
        Connected,
        Disconnecting,
    }

    unsafe extern "C++" {
        include!("vc/vc_shim.h");

        type VolumeControlIntf;

        unsafe fn GetVolumeControlProfile(btif: *const u8) -> UniquePtr<VolumeControlIntf>;

        fn init(self: Pin<&mut VolumeControlIntf>);
        fn cleanup(self: Pin<&mut VolumeControlIntf>);
        fn connect(self: Pin<&mut VolumeControlIntf>, addr: RawAddress);
        fn disconnect(self: Pin<&mut VolumeControlIntf>, addr: RawAddress);
        fn remove_device(self: Pin<&mut VolumeControlIntf>, addr: RawAddress);
        fn set_volume(self: Pin<&mut VolumeControlIntf>, group_id: i32, volume: u8);
        fn mute(self: Pin<&mut VolumeControlIntf>, addr: RawAddress);
        fn unmute(self: Pin<&mut VolumeControlIntf>, addr: RawAddress);
        fn get_ext_audio_out_volume_offset(
            self: Pin<&mut VolumeControlIntf>,
            addr: RawAddress,
            ext_output_id: u8,
        );
        fn set_ext_audio_out_volume_offset(
            self: Pin<&mut VolumeControlIntf>,
            addr: RawAddress,
            ext_output_id: u8,
            offset_val: i16,
        );
        fn get_ext_audio_out_location(
            self: Pin<&mut VolumeControlIntf>,
            addr: RawAddress,
            ext_output_id: u8,
        );
        fn set_ext_audio_out_location(
            self: Pin<&mut VolumeControlIntf>,
            addr: RawAddress,
            ext_output_id: u8,
            location: u32,
        );
        fn get_ext_audio_out_description(
            self: Pin<&mut VolumeControlIntf>,
            addr: RawAddress,
            ext_output_id: u8,
        );
        unsafe fn set_ext_audio_out_description(
            self: Pin<&mut VolumeControlIntf>,
            addr: RawAddress,
            ext_output_id: u8,
            descr: *const c_char,
        );
    }

    extern "Rust" {
        fn vc_connection_state_callback(state: BtVcConnectionState, addr: RawAddress);
        fn vc_volume_state_callback(
            address: RawAddress,
            volume: u8,
            mute: bool,
            is_autonomous: bool,
        );
        fn vc_group_volume_state_callback(
            group_id: i32,
            volume: u8,
            mute: bool,
            is_autonomous: bool,
        );
        fn vc_device_available_callback(address: RawAddress, num_offset: u8);
        fn vc_ext_audio_out_volume_offset_callback(
            address: RawAddress,
            ext_output_id: u8,
            offset: i16,
        );
        fn vc_ext_audio_out_location_callback(
            address: RawAddress,
            ext_output_id: u8,
            location: u32,
        );
        fn vc_ext_audio_out_description_callback(
            address: RawAddress,
            ext_output_id: u8,
            descr: String,
        );
    }
}

pub type BtVcConnectionState = ffi::BtVcConnectionState;

#[derive(Debug)]
pub enum VolumeControlCallbacks {
    ConnectionState(BtVcConnectionState, RawAddress),
    VolumeState(RawAddress, u8, bool, bool),
    GroupVolumeState(i32, u8, bool, bool),
    DeviceAvailable(RawAddress, u8),
    ExtAudioOutVolume(RawAddress, u8, i16),
    ExtAudioOutLocation(RawAddress, u8, u32),
    ExtAudioOutDescription(RawAddress, u8, String),
}

pub struct VolumeControlCallbacksDispatcher {
    pub dispatch: Box<dyn Fn(VolumeControlCallbacks) + Send>,
}

type VolumeControlCb = Arc<Mutex<VolumeControlCallbacksDispatcher>>;

cb_variant!(VolumeControlCb,
            vc_connection_state_callback -> VolumeControlCallbacks::ConnectionState,
            BtVcConnectionState, RawAddress);

cb_variant!(VolumeControlCb,
            vc_volume_state_callback -> VolumeControlCallbacks::VolumeState,
            RawAddress, u8, bool, bool);

cb_variant!(VolumeControlCb,
            vc_group_volume_state_callback -> VolumeControlCallbacks::GroupVolumeState,
            i32, u8, bool, bool);

cb_variant!(VolumeControlCb,
            vc_device_available_callback -> VolumeControlCallbacks::DeviceAvailable,
            RawAddress, u8);

cb_variant!(VolumeControlCb,
            vc_ext_audio_out_volume_offset_callback -> VolumeControlCallbacks::ExtAudioOutVolume,
            RawAddress, u8, i16);

cb_variant!(VolumeControlCb,
            vc_ext_audio_out_location_callback -> VolumeControlCallbacks::ExtAudioOutLocation,
            RawAddress, u8, u32);

cb_variant!(VolumeControlCb,
            vc_ext_audio_out_description_callback -> VolumeControlCallbacks::ExtAudioOutDescription,
            RawAddress, u8, String);

pub struct VolumeControl {
    internal: cxx::UniquePtr<ffi::VolumeControlIntf>,
    is_init: bool,
    is_enabled: bool,
}

// For *const u8 opaque btif
// SAFETY: `VolumeControlIntf` is thread-safe to make calls from.
unsafe impl Send for VolumeControl {}

impl ToggleableProfile for VolumeControl {
    fn is_enabled(&self) -> bool {
        self.is_enabled
    }

    fn enable(&mut self) -> bool {
        if self.is_enabled {
            warn!("VolumeControl is already enabled.");
            return false;
        }

        self.internal.pin_mut().init();
        self.is_enabled = true;
        true
    }

    #[profile_enabled_or(false)]
    fn disable(&mut self) -> bool {
        if !self.is_enabled {
            warn!("VolumeControl is already disabled.");
            return false;
        }

        self.internal.pin_mut().cleanup();
        self.is_enabled = false;
        true
    }
}

impl VolumeControl {
    pub fn new(intf: &BluetoothInterface) -> VolumeControl {
        let vc_if: cxx::UniquePtr<ffi::VolumeControlIntf>;

        // SAFETY: `intf.as_raw_ptr()` is a valid pointer to a `BluetoothInterface`
        vc_if = unsafe { ffi::GetVolumeControlProfile(intf.as_raw_ptr()) };

        VolumeControl { internal: vc_if, is_init: false, is_enabled: false }
    }

    pub fn is_initialized(&self) -> bool {
        self.is_init
    }

    // `internal.init` is invoked during `ToggleableProfile::enable`
    pub fn initialize(&mut self, callbacks: VolumeControlCallbacksDispatcher) -> bool {
        if self.is_init {
            warn!("VolumeControl has already been initialized");
            return false;
        }

        if get_dispatchers().lock().unwrap().set::<VolumeControlCb>(Arc::new(Mutex::new(callbacks)))
        {
            panic!("Tried to set dispatcher for VolumeControl callbacks while it already exists");
        }

        self.is_init = true;

        true
    }

    #[profile_enabled_or]
    pub fn cleanup(&mut self) {
        self.internal.pin_mut().cleanup();
    }

    #[profile_enabled_or]
    pub fn connect(&mut self, addr: RawAddress) {
        self.internal.pin_mut().connect(addr);
    }

    #[profile_enabled_or]
    pub fn disconnect(&mut self, addr: RawAddress) {
        self.internal.pin_mut().disconnect(addr);
    }

    #[profile_enabled_or]
    pub fn remove_device(&mut self, addr: RawAddress) {
        self.internal.pin_mut().remove_device(addr);
    }

    #[profile_enabled_or]
    pub fn set_volume(&mut self, group_id: i32, volume: u8) {
        self.internal.pin_mut().set_volume(group_id, volume);
    }

    #[profile_enabled_or]
    pub fn mute(&mut self, addr: RawAddress) {
        self.internal.pin_mut().mute(addr);
    }

    #[profile_enabled_or]
    pub fn unmute(&mut self, addr: RawAddress) {
        self.internal.pin_mut().unmute(addr);
    }

    #[profile_enabled_or]
    pub fn get_ext_audio_out_volume_offset(&mut self, addr: RawAddress, ext_output_id: u8) {
        self.internal.pin_mut().get_ext_audio_out_volume_offset(addr, ext_output_id);
    }

    #[profile_enabled_or]
    pub fn set_ext_audio_out_volume_offset(
        &mut self,
        addr: RawAddress,
        ext_output_id: u8,
        offset_val: i16,
    ) {
        self.internal.pin_mut().set_ext_audio_out_volume_offset(addr, ext_output_id, offset_val);
    }

    #[profile_enabled_or]
    pub fn get_ext_audio_out_location(&mut self, addr: RawAddress, ext_output_id: u8) {
        self.internal.pin_mut().get_ext_audio_out_location(addr, ext_output_id);
    }

    #[profile_enabled_or]
    pub fn set_ext_audio_out_location(
        &mut self,
        addr: RawAddress,
        ext_output_id: u8,
        location: u32,
    ) {
        self.internal.pin_mut().set_ext_audio_out_location(addr, ext_output_id, location);
    }

    #[profile_enabled_or]
    pub fn get_ext_audio_out_description(&mut self, addr: RawAddress, ext_output_id: u8) {
        self.internal.pin_mut().get_ext_audio_out_description(addr, ext_output_id);
    }

    #[profile_enabled_or]
    pub fn set_ext_audio_out_description(
        &mut self,
        addr: RawAddress,
        ext_output_id: u8,
        descr: String,
    ) {
        let c_descr = std::ffi::CString::new(descr).unwrap();
        unsafe {
            // SAFETY: calling an FFI where the pointer is const, no modification.
            self.internal.pin_mut().set_ext_audio_out_description(
                addr,
                ext_output_id,
                c_descr.as_ptr(),
            );
        }
    }
}
+229 −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.
 */

#include "gd/rust/topshim/vc/vc_shim.h"

#include <bluetooth/log.h>
#include <hardware/bluetooth.h>

#include <string>

#include "os/log.h"
#include "src/profiles/vc.rs.h"
#include "types/raw_address.h"

namespace rusty = ::bluetooth::topshim::rust;

namespace bluetooth {
namespace topshim {
namespace rust {
namespace internal {

static VolumeControlIntf* g_vc_if;

static BtVcConnectionState to_rust_btvc_connection_state(vc::ConnectionState state) {
  switch (state) {
    case vc::ConnectionState::DISCONNECTED:
      return BtVcConnectionState::Disconnected;
    case vc::ConnectionState::CONNECTING:
      return BtVcConnectionState::Connecting;
    case vc::ConnectionState::CONNECTED:
      return BtVcConnectionState::Connected;
    case vc::ConnectionState::DISCONNECTING:
      return BtVcConnectionState::Disconnecting;
    default:
      log::assert_that(false, "Unhandled enum value from C++");
  }
  return BtVcConnectionState{};
}

static void connection_state_cb(vc::ConnectionState state, const RawAddress& address) {
  vc_connection_state_callback(to_rust_btvc_connection_state(state), address);
}

static void volume_state_cb(
    const RawAddress& address, uint8_t volume, bool mute, bool is_autonomous) {
  vc_volume_state_callback(address, volume, mute, is_autonomous);
}

static void group_volume_state_cb(int group_id, uint8_t volume, bool mute, bool is_autonomous) {
  vc_group_volume_state_callback(group_id, volume, mute, is_autonomous);
}

static void device_available_cb(const RawAddress& address, uint8_t num_offset) {
  vc_device_available_callback(address, num_offset);
}

static void ext_audio_out_volume_offset_cb(
    const RawAddress& address, uint8_t ext_output_id, int16_t offset) {
  vc_ext_audio_out_volume_offset_callback(address, ext_output_id, offset);
}

static void ext_audio_out_location_cb(
    const RawAddress& address, uint8_t ext_output_id, uint32_t location) {
  vc_ext_audio_out_location_callback(address, ext_output_id, location);
}

static void ext_audio_out_description_cb(
    const RawAddress& address, uint8_t ext_output_id, std::string descr) {
  vc_ext_audio_out_description_callback(address, ext_output_id, descr);
}

}  // namespace internal

class DBusVolumeControlCallbacks : public vc::VolumeControlCallbacks {
 public:
  static vc::VolumeControlCallbacks* GetInstance() {
    static auto instance = new DBusVolumeControlCallbacks();
    return instance;
  }

  DBusVolumeControlCallbacks(){};

  void OnConnectionState(vc::ConnectionState state, const RawAddress& address) override {
    log::info("state={}, address={}", static_cast<int>(state), ADDRESS_TO_LOGGABLE_CSTR(address));
    topshim::rust::internal::connection_state_cb(state, address);
  }

  void OnVolumeStateChanged(
      const RawAddress& address, uint8_t volume, bool mute, bool is_autonomous) override {
    log::info(
        "address={}, volume={}, mute={}, is_autonomous={}",
        ADDRESS_TO_LOGGABLE_CSTR(address),
        volume,
        mute,
        is_autonomous);
    topshim::rust::internal::volume_state_cb(address, volume, mute, is_autonomous);
  }

  void OnGroupVolumeStateChanged(
      int group_id, uint8_t volume, bool mute, bool is_autonomous) override {
    log::info(
        "group_id={}, volume={}, mute={}, is_autonomous={}", group_id, volume, mute, is_autonomous);
    topshim::rust::internal::group_volume_state_cb(group_id, volume, mute, is_autonomous);
  }

  void OnDeviceAvailable(const RawAddress& address, uint8_t num_offset) override {
    log::info("address={}, num_offset={}", ADDRESS_TO_LOGGABLE_CSTR(address), num_offset);
    topshim::rust::internal::device_available_cb(address, num_offset);
  }

  void OnExtAudioOutVolumeOffsetChanged(
      const RawAddress& address, uint8_t ext_output_id, int16_t offset) override {
    log::info(
        "address={}, ext_output_id={}, offset={}",
        ADDRESS_TO_LOGGABLE_CSTR(address),
        ext_output_id,
        offset);
    topshim::rust::internal::ext_audio_out_volume_offset_cb(address, ext_output_id, offset);
  }

  void OnExtAudioOutLocationChanged(
      const RawAddress& address, uint8_t ext_output_id, uint32_t location) override {
    log::info(
        "address={}, ext_output_id, location={}",
        ADDRESS_TO_LOGGABLE_CSTR(address),
        ext_output_id,
        location);
    topshim::rust::internal::ext_audio_out_location_cb(address, ext_output_id, location);
  }

  void OnExtAudioOutDescriptionChanged(
      const RawAddress& address, uint8_t ext_output_id, std::string descr) override {
    log::info(
        "address={}, ext_output_id={}, descr={}",
        ADDRESS_TO_LOGGABLE_CSTR(address),
        ext_output_id,
        descr.c_str());
    topshim::rust::internal::ext_audio_out_description_cb(address, ext_output_id, descr);
  }
};

std::unique_ptr<VolumeControlIntf> GetVolumeControlProfile(const unsigned char* btif) {
  if (internal::g_vc_if) std::abort();

  const bt_interface_t* btif_ = reinterpret_cast<const bt_interface_t*>(btif);

  auto vc_if = std::make_unique<VolumeControlIntf>(
      const_cast<vc::VolumeControlInterface*>(reinterpret_cast<const vc::VolumeControlInterface*>(
          btif_->get_profile_interface("volume_control"))));

  internal::g_vc_if = vc_if.get();

  return vc_if;
}

void VolumeControlIntf::init(/*VolumeControlCallbacks* callbacks*/) {
  return intf_->Init(DBusVolumeControlCallbacks::GetInstance());
}

void VolumeControlIntf::cleanup() {
  return intf_->Cleanup();
}

void VolumeControlIntf::connect(RawAddress addr) {
  return intf_->Connect(addr);
}

void VolumeControlIntf::disconnect(RawAddress addr) {
  return intf_->Disconnect(addr);
}

void VolumeControlIntf::remove_device(RawAddress addr) {
  return intf_->RemoveDevice(addr);
}

void VolumeControlIntf::set_volume(int group_id, uint8_t volume) {
  return intf_->SetVolume(group_id, volume);
}

void VolumeControlIntf::mute(RawAddress addr) {
  return intf_->Mute(addr);
}

void VolumeControlIntf::unmute(RawAddress addr) {
  return intf_->Unmute(addr);
}

void VolumeControlIntf::get_ext_audio_out_volume_offset(RawAddress addr, uint8_t ext_output_id) {
  return intf_->GetExtAudioOutVolumeOffset(addr, ext_output_id);
}

void VolumeControlIntf::set_ext_audio_out_volume_offset(
    RawAddress addr, uint8_t ext_output_id, int16_t offset_val) {
  return intf_->SetExtAudioOutVolumeOffset(addr, ext_output_id, offset_val);
}

void VolumeControlIntf::get_ext_audio_out_location(RawAddress addr, uint8_t ext_output_id) {
  return intf_->GetExtAudioOutLocation(addr, ext_output_id);
}

void VolumeControlIntf::set_ext_audio_out_location(
    RawAddress addr, uint8_t ext_output_id, uint32_t location) {
  return intf_->SetExtAudioOutLocation(addr, ext_output_id, location);
}

void VolumeControlIntf::get_ext_audio_out_description(RawAddress addr, uint8_t ext_output_id) {
  return intf_->GetExtAudioOutDescription(addr, ext_output_id);
}

void VolumeControlIntf::set_ext_audio_out_description(
    RawAddress addr, uint8_t ext_output_id, const char* descr) {
  return intf_->SetExtAudioOutDescription(addr, ext_output_id, std::string(descr));
}
}  // namespace rust
}  // namespace topshim
}  // namespace bluetooth
Loading