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

Commit 75e09327 authored by Aritra Sen's avatar Aritra Sen
Browse files

Create a Rust gRPC service for HF Client.

Bug: 262045500
Test: system/gd/cert/run --topshim --clean
Tag: #floss
Change-Id: Ieaf69e3634904644d3a7f9ddc946bfeddd3d9f00
parent c0985510
Loading
Loading
Loading
Loading
+24 −0
Original line number Diff line number Diff line
@@ -111,6 +111,13 @@ service HfpService {
  rpc FetchEvents(FetchEventsRequest) returns (stream FetchEventsResponse) {}
}

service HfClientService {
  rpc StartSlc(StartSlcRequest) returns (StartSlcResponse) {}
  rpc StopSlc(StopSlcRequest) returns (StopSlcResponse) {}
  rpc ConnectAudio(ConnectAudioRequest) returns (ConnectAudioResponse) {}
  rpc DisconnectAudio(DisconnectAudioRequest) returns (DisconnectAudioResponse) {}
}

enum EventType {
  ADAPTER_STATE = 0;
  SSP_REQUEST = 1;
@@ -119,6 +126,7 @@ enum EventType {
  HFP_CONNECTION_STATE = 4;
  ADAPTER_PROPERTY = 5;
}

message FetchEventsRequest {}

message FetchEventsResponse {
@@ -171,20 +179,36 @@ message StartSlcRequest {
  Connection connection = 1;
}

message StartSlcResponse {
  int32 status = 1;
}

message StopSlcRequest {
  Connection connection = 1;
}

message StopSlcResponse {
  int32 status = 1;
}

message ConnectAudioRequest {
  Connection connection = 1;
  bool is_sco_offload_enabled = 2;
  bool force_cvsd = 3;
}

message ConnectAudioResponse {
  int32 status = 1;
}

message DisconnectAudioRequest {
  Connection connection = 1;
}

message DisconnectAudioResponse {
  int32 status = 1;
}

message SetVolumeRequest {
  Connection connection = 1;
  int32 volume = 2;
+143 −0
Original line number Diff line number Diff line
//! HF Client service facade

use bt_topshim::btif::{BluetoothInterface, RawAddress, ToggleableProfile};
use bt_topshim::profiles::hf_client::{BthfClientCallbacksDispatcher, HfClient};
use bt_topshim_facade_protobuf::facade::{
    ConnectAudioRequest, ConnectAudioResponse, DisconnectAudioRequest, DisconnectAudioResponse,
    StartSlcRequest, StartSlcResponse, StopSlcRequest, StopSlcResponse,
};
use bt_topshim_facade_protobuf::facade_grpc::{create_hf_client_service, HfClientService};
use grpcio::*;
use num_traits::cast::ToPrimitive;

use std::str::from_utf8;
use std::sync::{Arc, Mutex};
use tokio::runtime::Runtime;

fn get_hf_client_dispatcher() -> BthfClientCallbacksDispatcher {
    BthfClientCallbacksDispatcher { dispatch: Box::new(move |_cb| {}) }
}

/// Main object for Hf(Hands free) client facade service
#[derive(Clone)]
pub struct HfClientServiceImpl {
    #[allow(dead_code)]
    rt: Arc<Runtime>,
    pub hf_client: Arc<Mutex<HfClient>>,
}

impl HfClientServiceImpl {
    /// Create a new instance of the root facade service
    pub fn create(rt: Arc<Runtime>, btif_intf: Arc<Mutex<BluetoothInterface>>) -> grpcio::Service {
        let hf_client = Arc::new(Mutex::new(HfClient::new(&btif_intf.lock().unwrap())));
        hf_client.lock().unwrap().initialize(get_hf_client_dispatcher());
        hf_client.lock().unwrap().enable();
        create_hf_client_service(Self { rt, hf_client })
    }
}

impl HfClientService for HfClientServiceImpl {
    fn start_slc(
        &mut self,
        ctx: RpcContext<'_>,
        req: StartSlcRequest,
        sink: UnarySink<StartSlcResponse>,
    ) {
        let hf_client = self.hf_client.clone();
        ctx.spawn(async move {
            let addr_bytes = &req.connection.unwrap().cookie;
            let bt_addr = from_utf8(addr_bytes).unwrap();
            if let Some(addr) = RawAddress::from_string(bt_addr) {
                let status = hf_client.lock().unwrap().connect(addr);
                let mut resp = StartSlcResponse::new();
                resp.status = status.to_i32().unwrap();
                sink.success(resp).await.unwrap();
            } else {
                sink.fail(RpcStatus::with_message(
                    RpcStatusCode::INVALID_ARGUMENT,
                    format!("Invalid Request Address: {}", bt_addr),
                ))
                .await
                .unwrap();
            }
        })
    }

    fn stop_slc(
        &mut self,
        ctx: RpcContext<'_>,
        req: StopSlcRequest,
        sink: UnarySink<StopSlcResponse>,
    ) {
        let hf_client = self.hf_client.clone();
        ctx.spawn(async move {
            let addr_bytes = &req.connection.unwrap().cookie;
            let bt_addr = from_utf8(addr_bytes).unwrap();
            if let Some(addr) = RawAddress::from_string(bt_addr) {
                let status = hf_client.lock().unwrap().disconnect(addr);
                let mut resp = StopSlcResponse::new();
                resp.status = status.to_i32().unwrap();
                sink.success(resp).await.unwrap();
            } else {
                sink.fail(RpcStatus::with_message(
                    RpcStatusCode::INVALID_ARGUMENT,
                    format!("Invalid Request Address: {}", bt_addr),
                ))
                .await
                .unwrap();
            }
        })
    }

    fn connect_audio(
        &mut self,
        ctx: RpcContext<'_>,
        req: ConnectAudioRequest,
        sink: UnarySink<ConnectAudioResponse>,
    ) {
        let hf_client = self.hf_client.clone();
        ctx.spawn(async move {
            let addr_bytes = &req.connection.unwrap().cookie;
            let bt_addr = from_utf8(addr_bytes).unwrap();
            if let Some(addr) = RawAddress::from_string(bt_addr) {
                let status = hf_client.lock().unwrap().connect_audio(addr);
                let mut resp = ConnectAudioResponse::new();
                resp.status = status.to_i32().unwrap();
                sink.success(resp).await.unwrap();
            } else {
                sink.fail(RpcStatus::with_message(
                    RpcStatusCode::INVALID_ARGUMENT,
                    format!("Invalid Request Address: {}", bt_addr),
                ))
                .await
                .unwrap();
            }
        })
    }

    fn disconnect_audio(
        &mut self,
        ctx: RpcContext<'_>,
        req: DisconnectAudioRequest,
        sink: UnarySink<DisconnectAudioResponse>,
    ) {
        let hf_client = self.hf_client.clone();
        ctx.spawn(async move {
            let addr_bytes = &req.connection.unwrap().cookie;
            let bt_addr = from_utf8(addr_bytes).unwrap();
            if let Some(addr) = RawAddress::from_string(bt_addr) {
                let status = hf_client.lock().unwrap().disconnect_audio(addr);
                let mut resp = DisconnectAudioResponse::new();
                resp.status = status.to_i32().unwrap();
                sink.success(resp).await.unwrap();
            } else {
                sink.fail(RpcStatus::with_message(
                    RpcStatusCode::INVALID_ARGUMENT,
                    format!("Invalid Request Address: {}", bt_addr),
                ))
                .await
                .unwrap();
            }
        })
    }
}
+5 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ use tokio::runtime::Runtime;

mod adapter_service;
mod gatt_service;
mod hf_client_service;
mod hfp_service;
mod media_service;
mod security_service;
@@ -86,6 +87,9 @@ async fn async_main(rt: Arc<Runtime>, mut sigint: mpsc::UnboundedReceiver<()>) {

    let gatt_service_impl = gatt_service::GattServiceImpl::create(rt.clone(), btif_intf.clone());

    let hf_client_service_impl =
        hf_client_service::HfClientServiceImpl::create(rt.clone(), btif_intf.clone());

    let hfp_service_impl = hfp_service::HfpServiceImpl::create(rt.clone(), btif_intf.clone());

    let media_service_impl = media_service::MediaServiceImpl::create(rt.clone(), btif_intf.clone());
@@ -100,6 +104,7 @@ async fn async_main(rt: Arc<Runtime>, mut sigint: mpsc::UnboundedReceiver<()>) {
        .register_service(adapter_service_impl)
        .register_service(security_service_impl)
        .register_service(gatt_service_impl)
        .register_service(hf_client_service_impl)
        .register_service(hfp_service_impl)
        .register_service(media_service_impl)
        .bind("0.0.0.0", grpc_port)