blob: 3742b0d79a64c0f22a43e876c032fef85b6f48b8 [file] [log] [blame]
//! Definition of UwbClientCallback
use crate::error::UwbErr;
use crate::uci::uci_hrcv;
use crate::uci::HalCallback;
use android_hardware_uwb::aidl::android::hardware::uwb::{
IUwb::{BnUwb, IUwb},
IUwbChip::{BnUwbChip, IUwbChip},
IUwbClientCallback::{BnUwbClientCallback, IUwbClientCallback},
UwbEvent::UwbEvent,
UwbStatus::UwbStatus,
};
use android_hardware_uwb::binder::{
self, BinderFeatures, Interface, Result as BinderResult, Strong,
};
use log::{error, info, warn};
use std::result::Result;
use tokio::sync::mpsc;
use uwb_uci_packets::{UciPacketChild, UciPacketPacket};
pub struct UwbClientCallback {
rsp_sender: mpsc::UnboundedSender<HalCallback>,
}
impl UwbClientCallback {
fn new(rsp_sender: mpsc::UnboundedSender<HalCallback>) -> Self {
UwbClientCallback { rsp_sender }
}
}
impl Interface for UwbClientCallback {}
impl IUwbClientCallback for UwbClientCallback {
fn onHalEvent(&self, event: UwbEvent, event_status: UwbStatus) -> BinderResult<()> {
self.rsp_sender
.send(HalCallback::Event { event, event_status })
.unwrap_or_else(|e| error!("Error sending evt callback: {:?}", e));
Ok(())
}
fn onUciMessage(&self, data: &[u8]) -> BinderResult<()> {
match UciPacketPacket::parse(data) {
Ok(evt) => {
let packetMsg = uci_hrcv::uci_message(evt);
match packetMsg {
Ok(uci_hrcv::UciMessage::Response(evt)) => self
.rsp_sender
.send(HalCallback::UciRsp(evt))
.unwrap_or_else(|e| error!("Error sending uci response: {:?}", e)),
Ok(uci_hrcv::UciMessage::Notification(evt)) => self
.rsp_sender
.send(HalCallback::UciNtf(evt))
.unwrap_or_else(|e| error!("Error sending uci notification: {:?}", e)),
_ => error!("UCI message which is neither a UCI RSP or NTF: {:?}", data),
}
}
_ => error!("Failed to parse packet: {:?}", data),
}
Ok(())
}
}
fn get_hal_service() -> Option<Strong<dyn IUwbChip>> {
let service_name: &str = "android.hardware.uwb.IUwb/default";
let i_uwb: Strong<dyn IUwb> = match binder::get_interface(service_name) {
Ok(chip) => chip,
Err(e) => {
warn!("Failed to connect to the AIDL HAL service.");
return None;
}
};
let chip_names = match i_uwb.getChips() {
Ok(names) => names,
Err(e) => {
warn!("Failed to retrieve the HAL chip names.");
return None;
}
};
let i_uwb_chip = match i_uwb.getChip(&chip_names[0]) {
Ok(chip) => chip,
Err(e) => {
warn!("Failed to retrieve the HAL chip.");
return None;
}
};
Some(i_uwb_chip)
}
pub trait UwbAdaptation {
fn finalize(&mut self, exit_status: bool);
fn hal_open(&self);
fn hal_close(&self);
fn core_initialization(&self) -> Result<(), UwbErr>;
fn session_initialization(&self, session_id: i32) -> Result<(), UwbErr>;
fn send_uci_message(&self, data: &[u8]) -> Result<(), UwbErr>;
}
#[derive(Clone)]
pub struct UwbAdaptationImpl {
hal: Strong<dyn IUwbChip>,
rsp_sender: mpsc::UnboundedSender<HalCallback>,
}
impl UwbAdaptationImpl {
pub fn new(rsp_sender: mpsc::UnboundedSender<HalCallback>) -> Result<Self, UwbErr> {
let hal = get_hal_service().ok_or(UwbErr::HalUnavailable)?;
Ok(UwbAdaptationImpl { hal, rsp_sender })
}
fn get_supported_android_uci_version(&self) -> Result<i32, UwbErr> {
Ok(self.hal.getSupportedAndroidUciVersion()?)
}
fn get_supported_android_capabilities(&self) -> Result<i64, UwbErr> {
Ok(self.hal.getSupportedAndroidCapabilities()?)
}
}
impl UwbAdaptation for UwbAdaptationImpl {
fn finalize(&mut self, exit_status: bool) {}
fn hal_open(&self) {
let m_cback = BnUwbClientCallback::new_binder(
UwbClientCallback { rsp_sender: self.rsp_sender.clone() },
BinderFeatures::default(),
);
self.hal.open(&m_cback);
}
fn hal_close(&self) {
self.hal.close();
}
fn core_initialization(&self) -> Result<(), UwbErr> {
Ok(self.hal.coreInit()?)
}
fn session_initialization(&self, session_id: i32) -> Result<(), UwbErr> {
Ok(self.hal.sessionInit(session_id)?)
}
fn send_uci_message(&self, data: &[u8]) -> Result<(), UwbErr> {
self.hal.sendUciMessage(data)?;
// TODO should we be validating the returned number?
Ok(())
}
}
#[cfg(test)]
pub struct MockUwbAdaptation {
rsp_sender: mpsc::UnboundedSender<HalCallback>,
}
#[cfg(test)]
impl MockUwbAdaptation {
pub fn new(rsp_sender: mpsc::UnboundedSender<HalCallback>) -> Self {
Self { rsp_sender }
}
}
#[cfg(test)]
impl UwbAdaptation for MockUwbAdaptation {
fn finalize(&mut self, exit_status: bool) {}
fn hal_open(&self) {}
fn hal_close(&self) {}
fn core_initialization(&self) -> Result<(), UwbErr> {
let uwb_event_test = UwbEvent::POST_INIT_CPLT;
let uwb_status_test = UwbStatus::OK;
let uwb_client_callback_test = UwbClientCallback::new(self.rsp_sender.clone());
let result = uwb_client_callback_test.onHalEvent(uwb_event_test, uwb_status_test);
Ok(())
}
fn session_initialization(&self, session_id: i32) -> Result<(), UwbErr> {
Ok(())
}
fn send_uci_message(&self, data: &[u8]) -> Result<(), UwbErr> {
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_onHalEvent() {
let uwb_event_test = UwbEvent(0);
let uwb_status_test = UwbStatus(1);
let (rsp_sender, _) = mpsc::unbounded_channel::<HalCallback>();
let uwb_client_callback_test = UwbClientCallback::new(rsp_sender);
let result = uwb_client_callback_test.onHalEvent(uwb_event_test, uwb_status_test);
assert_eq!(result, Ok(()));
}
#[tokio::test]
async fn test_onUciMessage_good() {
let data = [
0x40, 0x02, 0x00, 0x0b, 0x01, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x01,
0x0a,
];
let (rsp_sender, mut rsp_receiver) = mpsc::unbounded_channel::<HalCallback>();
let uwb_client_callback_test = UwbClientCallback::new(rsp_sender);
let result = uwb_client_callback_test.onUciMessage(&data);
assert_eq!(result, Ok(()));
let response = rsp_receiver.recv().await;
assert!(matches!(
response,
Some(HalCallback::UciRsp(uci_hrcv::UciResponse::GetDeviceInfoRsp(_)))
));
}
#[test]
fn test_onUciMessage_bad() {
let data = [
0x42, 0x02, 0x00, 0x0b, 0x01, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x01,
0x0a,
];
let (rsp_sender, mut rsp_receiver) = mpsc::unbounded_channel::<HalCallback>();
let uwb_client_callback_test = UwbClientCallback::new(rsp_sender);
let result = uwb_client_callback_test.onUciMessage(&data);
assert_eq!(result, Ok(()));
let response = rsp_receiver.try_recv();
assert!(response.is_err());
}
}