Loading system/blueberry/facade/topshim/facade.proto +38 −0 Original line number Diff line number Diff line Loading @@ -100,6 +100,14 @@ service GattService { rpc ServerReadPhy(google.protobuf.Empty) returns (google.protobuf.Empty) {} } service HfpService { rpc StartSlc(StartSlcRequest) returns (google.protobuf.Empty) {} rpc StopSlc(StopSlcRequest) returns (google.protobuf.Empty) {} rpc ConnectAudio(ConnectAudioRequest) returns (google.protobuf.Empty) {} rpc DisconnectAudio(DisconnectAudioRequest) returns (google.protobuf.Empty) {} rpc SetVolume(SetVolumeRequest) returns (google.protobuf.Empty) {} } enum EventType { ADAPTER_STATE = 0; SSP_REQUEST = 1; Loading Loading @@ -152,3 +160,33 @@ message A2dpSourceSetActiveDevicetResponse {} message RemoveBondRequest { string address = 1; } message StartSlcRequest { Connection connection = 1; } message StopSlcRequest { Connection connection = 1; } message ConnectAudioRequest { Connection connection = 1; bool is_sco_offload_enabled = 2; bool force_cvsd = 3; } message DisconnectAudioRequest { Connection connection = 1; } message SetVolumeRequest { Connection connection = 1; int32 volume = 2; } // A Token representing an ACL connection. // It's acquired via a Connect on the Host service (Bluetooth Core stack in our case). message Connection { // For our HFP APIs this would store the bluetooth address but staying consistent with Pandora naming. bytes cookie = 1; } system/gd/rust/topshim/facade/src/hfp_service.rs 0 → 100644 +142 −0 Original line number Diff line number Diff line //! HFP service facade use bt_topshim::btif::{BluetoothInterface, RawAddress}; use bt_topshim::profiles::hfp::{Hfp, HfpCallbacksDispatcher}; use bt_topshim_facade_protobuf::empty::Empty; use bt_topshim_facade_protobuf::facade::{ ConnectAudioRequest, DisconnectAudioRequest, SetVolumeRequest, StartSlcRequest, StopSlcRequest, }; use bt_topshim_facade_protobuf::facade_grpc::{create_hfp_service, HfpService}; use grpcio::*; use std::str::from_utf8; use std::sync::{Arc, Mutex}; use tokio::runtime::Runtime; fn get_hfp_dispatcher() -> HfpCallbacksDispatcher { HfpCallbacksDispatcher { dispatch: Box::new(move |_cb| {}) } } /// Main object for Hfp facade service #[derive(Clone)] pub struct HfpServiceImpl { #[allow(dead_code)] rt: Arc<Runtime>, pub btif_hfp: Arc<Mutex<Hfp>>, } impl HfpServiceImpl { /// Create a new instance of the root facade service pub fn create(rt: Arc<Runtime>, btif_intf: Arc<Mutex<BluetoothInterface>>) -> grpcio::Service { let mut btif_hfp = Hfp::new(&btif_intf.lock().unwrap()); btif_hfp.initialize(get_hfp_dispatcher()); create_hfp_service(Self { rt, btif_hfp: Arc::new(Mutex::new(btif_hfp)) }) } } impl HfpService for HfpServiceImpl { fn start_slc(&mut self, ctx: RpcContext<'_>, req: StartSlcRequest, sink: UnarySink<Empty>) { let hfp = self.btif_hfp.clone(); ctx.spawn(async move { let bt_addr = &req.connection.unwrap().cookie; if let Some(addr) = RawAddress::from_bytes(bt_addr) { hfp.lock().unwrap().connect(addr); sink.success(Empty::default()).await.unwrap(); } else { sink.fail(RpcStatus::with_message( RpcStatusCode::INVALID_ARGUMENT, format!("Invalid Request Address: {}", from_utf8(bt_addr).unwrap()), )) .await .unwrap(); } }) } fn stop_slc(&mut self, ctx: RpcContext<'_>, req: StopSlcRequest, sink: UnarySink<Empty>) { let hfp = self.btif_hfp.clone(); ctx.spawn(async move { let bt_addr = &req.connection.unwrap().cookie; if let Some(addr) = RawAddress::from_bytes(bt_addr) { hfp.lock().unwrap().disconnect(addr); sink.success(Empty::default()).await.unwrap(); } else { sink.fail(RpcStatus::with_message( RpcStatusCode::INVALID_ARGUMENT, format!("Invalid Request Address: {}", from_utf8(bt_addr).unwrap()), )) .await .unwrap(); } }) } fn connect_audio( &mut self, ctx: RpcContext<'_>, req: ConnectAudioRequest, sink: UnarySink<Empty>, ) { let hfp = self.btif_hfp.clone(); ctx.spawn(async move { let bt_addr = &req.connection.unwrap().cookie; if let Some(addr) = RawAddress::from_bytes(bt_addr) { hfp.lock().unwrap().connect_audio(addr, req.is_sco_offload_enabled, req.force_cvsd); hfp.lock().unwrap().set_active_device(addr); sink.success(Empty::default()).await.unwrap(); } else { sink.fail(RpcStatus::with_message( RpcStatusCode::INVALID_ARGUMENT, format!("Invalid Request Address: {}", from_utf8(bt_addr).unwrap()), )) .await .unwrap(); } }) } fn disconnect_audio( &mut self, ctx: RpcContext<'_>, req: DisconnectAudioRequest, sink: UnarySink<Empty>, ) { let hfp = self.btif_hfp.clone(); ctx.spawn(async move { let bt_addr = &req.connection.unwrap().cookie; if let Some(addr) = RawAddress::from_bytes(bt_addr) { hfp.lock().unwrap().disconnect_audio(addr); sink.success(Empty::default()).await.unwrap(); } else { sink.fail(RpcStatus::with_message( RpcStatusCode::INVALID_ARGUMENT, format!("Invalid Request Address: {}", from_utf8(bt_addr).unwrap()), )) .await .unwrap(); } }) } fn set_volume(&mut self, ctx: RpcContext<'_>, req: SetVolumeRequest, sink: UnarySink<Empty>) { let hfp = self.btif_hfp.clone(); ctx.spawn(async move { let bt_addr = &req.connection.unwrap().cookie; if let Some(addr) = RawAddress::from_bytes(bt_addr) { // TODO(aritrasen): Consider using TryFrom and cap the maximum volume here // since `as` silently deals with data overflow, which might not be preferred. hfp.lock().unwrap().set_volume(req.volume as i8, addr); sink.success(Empty::default()).await.unwrap(); } else { sink.fail(RpcStatus::with_message( RpcStatusCode::INVALID_ARGUMENT, format!("Invalid Request Address: {}", from_utf8(bt_addr).unwrap()), )) .await .unwrap(); } }) } } system/gd/rust/topshim/facade/src/main.rs +4 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ use tokio::runtime::Runtime; mod adapter_service; mod gatt_service; mod hfp_service; mod media_service; mod security_service; Loading Loading @@ -85,6 +86,8 @@ 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 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()); let start_stack_now = value_t!(matches, "start-stack-now", bool).unwrap(); Loading @@ -97,6 +100,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(hfp_service_impl) .register_service(media_service_impl) .bind("0.0.0.0", grpc_port) .build() Loading Loading
system/blueberry/facade/topshim/facade.proto +38 −0 Original line number Diff line number Diff line Loading @@ -100,6 +100,14 @@ service GattService { rpc ServerReadPhy(google.protobuf.Empty) returns (google.protobuf.Empty) {} } service HfpService { rpc StartSlc(StartSlcRequest) returns (google.protobuf.Empty) {} rpc StopSlc(StopSlcRequest) returns (google.protobuf.Empty) {} rpc ConnectAudio(ConnectAudioRequest) returns (google.protobuf.Empty) {} rpc DisconnectAudio(DisconnectAudioRequest) returns (google.protobuf.Empty) {} rpc SetVolume(SetVolumeRequest) returns (google.protobuf.Empty) {} } enum EventType { ADAPTER_STATE = 0; SSP_REQUEST = 1; Loading Loading @@ -152,3 +160,33 @@ message A2dpSourceSetActiveDevicetResponse {} message RemoveBondRequest { string address = 1; } message StartSlcRequest { Connection connection = 1; } message StopSlcRequest { Connection connection = 1; } message ConnectAudioRequest { Connection connection = 1; bool is_sco_offload_enabled = 2; bool force_cvsd = 3; } message DisconnectAudioRequest { Connection connection = 1; } message SetVolumeRequest { Connection connection = 1; int32 volume = 2; } // A Token representing an ACL connection. // It's acquired via a Connect on the Host service (Bluetooth Core stack in our case). message Connection { // For our HFP APIs this would store the bluetooth address but staying consistent with Pandora naming. bytes cookie = 1; }
system/gd/rust/topshim/facade/src/hfp_service.rs 0 → 100644 +142 −0 Original line number Diff line number Diff line //! HFP service facade use bt_topshim::btif::{BluetoothInterface, RawAddress}; use bt_topshim::profiles::hfp::{Hfp, HfpCallbacksDispatcher}; use bt_topshim_facade_protobuf::empty::Empty; use bt_topshim_facade_protobuf::facade::{ ConnectAudioRequest, DisconnectAudioRequest, SetVolumeRequest, StartSlcRequest, StopSlcRequest, }; use bt_topshim_facade_protobuf::facade_grpc::{create_hfp_service, HfpService}; use grpcio::*; use std::str::from_utf8; use std::sync::{Arc, Mutex}; use tokio::runtime::Runtime; fn get_hfp_dispatcher() -> HfpCallbacksDispatcher { HfpCallbacksDispatcher { dispatch: Box::new(move |_cb| {}) } } /// Main object for Hfp facade service #[derive(Clone)] pub struct HfpServiceImpl { #[allow(dead_code)] rt: Arc<Runtime>, pub btif_hfp: Arc<Mutex<Hfp>>, } impl HfpServiceImpl { /// Create a new instance of the root facade service pub fn create(rt: Arc<Runtime>, btif_intf: Arc<Mutex<BluetoothInterface>>) -> grpcio::Service { let mut btif_hfp = Hfp::new(&btif_intf.lock().unwrap()); btif_hfp.initialize(get_hfp_dispatcher()); create_hfp_service(Self { rt, btif_hfp: Arc::new(Mutex::new(btif_hfp)) }) } } impl HfpService for HfpServiceImpl { fn start_slc(&mut self, ctx: RpcContext<'_>, req: StartSlcRequest, sink: UnarySink<Empty>) { let hfp = self.btif_hfp.clone(); ctx.spawn(async move { let bt_addr = &req.connection.unwrap().cookie; if let Some(addr) = RawAddress::from_bytes(bt_addr) { hfp.lock().unwrap().connect(addr); sink.success(Empty::default()).await.unwrap(); } else { sink.fail(RpcStatus::with_message( RpcStatusCode::INVALID_ARGUMENT, format!("Invalid Request Address: {}", from_utf8(bt_addr).unwrap()), )) .await .unwrap(); } }) } fn stop_slc(&mut self, ctx: RpcContext<'_>, req: StopSlcRequest, sink: UnarySink<Empty>) { let hfp = self.btif_hfp.clone(); ctx.spawn(async move { let bt_addr = &req.connection.unwrap().cookie; if let Some(addr) = RawAddress::from_bytes(bt_addr) { hfp.lock().unwrap().disconnect(addr); sink.success(Empty::default()).await.unwrap(); } else { sink.fail(RpcStatus::with_message( RpcStatusCode::INVALID_ARGUMENT, format!("Invalid Request Address: {}", from_utf8(bt_addr).unwrap()), )) .await .unwrap(); } }) } fn connect_audio( &mut self, ctx: RpcContext<'_>, req: ConnectAudioRequest, sink: UnarySink<Empty>, ) { let hfp = self.btif_hfp.clone(); ctx.spawn(async move { let bt_addr = &req.connection.unwrap().cookie; if let Some(addr) = RawAddress::from_bytes(bt_addr) { hfp.lock().unwrap().connect_audio(addr, req.is_sco_offload_enabled, req.force_cvsd); hfp.lock().unwrap().set_active_device(addr); sink.success(Empty::default()).await.unwrap(); } else { sink.fail(RpcStatus::with_message( RpcStatusCode::INVALID_ARGUMENT, format!("Invalid Request Address: {}", from_utf8(bt_addr).unwrap()), )) .await .unwrap(); } }) } fn disconnect_audio( &mut self, ctx: RpcContext<'_>, req: DisconnectAudioRequest, sink: UnarySink<Empty>, ) { let hfp = self.btif_hfp.clone(); ctx.spawn(async move { let bt_addr = &req.connection.unwrap().cookie; if let Some(addr) = RawAddress::from_bytes(bt_addr) { hfp.lock().unwrap().disconnect_audio(addr); sink.success(Empty::default()).await.unwrap(); } else { sink.fail(RpcStatus::with_message( RpcStatusCode::INVALID_ARGUMENT, format!("Invalid Request Address: {}", from_utf8(bt_addr).unwrap()), )) .await .unwrap(); } }) } fn set_volume(&mut self, ctx: RpcContext<'_>, req: SetVolumeRequest, sink: UnarySink<Empty>) { let hfp = self.btif_hfp.clone(); ctx.spawn(async move { let bt_addr = &req.connection.unwrap().cookie; if let Some(addr) = RawAddress::from_bytes(bt_addr) { // TODO(aritrasen): Consider using TryFrom and cap the maximum volume here // since `as` silently deals with data overflow, which might not be preferred. hfp.lock().unwrap().set_volume(req.volume as i8, addr); sink.success(Empty::default()).await.unwrap(); } else { sink.fail(RpcStatus::with_message( RpcStatusCode::INVALID_ARGUMENT, format!("Invalid Request Address: {}", from_utf8(bt_addr).unwrap()), )) .await .unwrap(); } }) } }
system/gd/rust/topshim/facade/src/main.rs +4 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ use tokio::runtime::Runtime; mod adapter_service; mod gatt_service; mod hfp_service; mod media_service; mod security_service; Loading Loading @@ -85,6 +86,8 @@ 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 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()); let start_stack_now = value_t!(matches, "start-stack-now", bool).unwrap(); Loading @@ -97,6 +100,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(hfp_service_impl) .register_service(media_service_impl) .bind("0.0.0.0", grpc_port) .build() Loading