blob: 91e3f7a9b7f1cb709a7696ed2b3bd319b289927e [file] [log] [blame]
// Copyright 2022, 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.
//! This module provides a thin adapter of UwbService and UwbServiceCallback that encodes the
//! arguments to protobuf.
use log::{debug, error};
use crate::error::{Error, Result};
use crate::params::{AppConfigParams, DeviceState, ReasonCode, SessionId, SessionState};
use crate::proto::bindings::{
AndroidGetPowerStatsResponse, AndroidSetCountryCodeRequest, AndroidSetCountryCodeResponse,
DeinitSessionRequest, DeinitSessionResponse, DisableResponse, EnableResponse,
InitSessionRequest, InitSessionResponse, RangeDataReceivedSignal, ReconfigureRequest,
ReconfigureResponse, SendVendorCmdRequest, SendVendorCmdResponse, ServiceResetSignal,
SessionParamsRequest, SessionParamsResponse, SessionStateChangedSignal, SetLoggerModeRequest,
SetLoggerModeResponse, StartRangingRequest, StartRangingResponse, Status as ProtoStatus,
StopRangingRequest, StopRangingResponse, UciDeviceStatusChangedSignal,
UpdateControllerMulticastListRequest, UpdateControllerMulticastListResponse,
VendorNotificationReceivedSignal,
};
use crate::proto::utils::{parse_from_bytes, write_to_bytes};
use crate::service::uwb_service::{UwbService, UwbServiceCallback};
use crate::uci::notification::SessionRangeData;
/// A thin adapter of UwbService. The argument and the response of each method are protobuf-encoded
/// buffer. The definition of the protobuf is at protos/uwb_core_protos.proto.
///
/// For the naming of the protobuf struct, the argument of a method `do_something()` will be called
/// `DoSomethingRequest`, and the result will be called `DoSomethingResponse`.
pub struct ProtoUwbService {
service: UwbService,
}
impl ProtoUwbService {
/// Create a ProtoUwbService.
pub fn new(service: UwbService) -> Self {
Self { service }
}
/// Set UCI log mode.
pub fn set_logger_mode(&self, request: &[u8]) -> Result<Vec<u8>> {
let request = parse_from_bytes::<SetLoggerModeRequest>(request)?;
let mut resp = SetLoggerModeResponse::new();
resp.set_status(self.service.set_logger_mode(request.logger_mode.into()).into());
write_to_bytes(&resp)
}
/// Enable the UWB service.
pub fn enable(&self) -> Result<Vec<u8>> {
let mut resp = EnableResponse::new();
resp.set_status(self.service.enable().into());
write_to_bytes(&resp)
}
/// Disable the UWB service.
pub fn disable(&self) -> Result<Vec<u8>> {
let mut resp = DisableResponse::new();
resp.set_status(self.service.disable().into());
write_to_bytes(&resp)
}
/// Initialize a new ranging session with the given parameters.
///
/// Note: Currently the protobuf only support Fira parameters, but not support CCC parameters.
pub fn init_session(&self, request: &[u8]) -> Result<Vec<u8>> {
let mut request = parse_from_bytes::<InitSessionRequest>(request)?;
let params = request
.params
.take()
.ok_or_else(|| {
error!("InitSessionRequest.params is empty");
Error::BadParameters
})?
.try_into()
.map_err(|e| {
error!("Failed to convert to AppConfigParams: {}", e);
Error::BadParameters
})?;
let mut resp = InitSessionResponse::new();
resp.set_status(
self.service
.init_session(request.session_id, request.session_type.into(), params)
.into(),
);
write_to_bytes(&resp)
}
/// Destroy the session.
pub fn deinit_session(&self, request: &[u8]) -> Result<Vec<u8>> {
let request = parse_from_bytes::<DeinitSessionRequest>(request)?;
let mut resp = DeinitSessionResponse::new();
resp.set_status(self.service.deinit_session(request.session_id).into());
write_to_bytes(&resp)
}
/// Start ranging of the session.
pub fn start_ranging(&self, request: &[u8]) -> Result<Vec<u8>> {
let request = parse_from_bytes::<StartRangingRequest>(request)?;
// Currently we only support FiRa session, not CCC. For FiRa session, the returned
// AppConfigParams is the same as the configured one before start_ranging(). Therefore, we
// don't reply the AppConfigParams received from uwb_core.
let mut resp = StartRangingResponse::new();
resp.set_status(self.service.start_ranging(request.session_id).into());
write_to_bytes(&resp)
}
/// Stop ranging.
pub fn stop_ranging(&self, request: &[u8]) -> Result<Vec<u8>> {
let request = parse_from_bytes::<StopRangingRequest>(request)?;
let mut resp = StopRangingResponse::new();
resp.set_status(self.service.stop_ranging(request.session_id).into());
write_to_bytes(&resp)
}
/// Reconfigure the parameters of the session.
pub fn reconfigure(&self, request: &[u8]) -> Result<Vec<u8>> {
let mut request = parse_from_bytes::<ReconfigureRequest>(request)?;
let params = request
.params
.take()
.ok_or_else(|| {
error!("ReconfigureRequest.params is empty");
Error::BadParameters
})?
.try_into()
.map_err(|e| {
error!("Failed to convert to AppConfigParams: {}", e);
Error::BadParameters
})?;
let mut resp = ReconfigureResponse::new();
resp.set_status(self.service.reconfigure(request.session_id, params).into());
write_to_bytes(&resp)
}
/// Update the list of the controlees to the ongoing session.
pub fn update_controller_multicast_list(&self, request: &[u8]) -> Result<Vec<u8>> {
let request = parse_from_bytes::<UpdateControllerMulticastListRequest>(request)?;
let mut controlees = vec![];
for controlee in request.controlees.into_iter() {
let controlee = controlee.try_into().map_err(|e| {
error!("Failed to convert Controlee: {:?}", e);
Error::BadParameters
})?;
controlees.push(controlee);
}
let mut resp = UpdateControllerMulticastListResponse::new();
resp.set_status(
self.service
.update_controller_multicast_list(
request.session_id,
request.action.into(),
controlees,
)
.into(),
);
write_to_bytes(&resp)
}
/// Set the country code. Android-specific method.
pub fn android_set_country_code(&self, request: &[u8]) -> Result<Vec<u8>> {
let request = parse_from_bytes::<AndroidSetCountryCodeRequest>(request)?;
let country_code = request.country_code.try_into()?;
let mut resp = AndroidSetCountryCodeResponse::new();
resp.set_status(self.service.android_set_country_code(country_code).into());
write_to_bytes(&resp)
}
/// Get the power statistics. Android-specific method.
pub fn android_get_power_stats(&self) -> Result<Vec<u8>> {
let mut resp = AndroidGetPowerStatsResponse::new();
match self.service.android_get_power_stats() {
Ok(power_stats) => {
resp.set_status(Ok(()).into());
resp.set_power_stats(power_stats.into());
}
Err(e) => {
resp.set_status(From::<Result<()>>::from(Err(e)));
}
}
write_to_bytes(&resp)
}
/// Send a raw UCI message.
pub fn raw_uci_cmd(&self, request: &[u8]) -> Result<Vec<u8>> {
let request = parse_from_bytes::<SendVendorCmdRequest>(request)?;
let mut resp = SendVendorCmdResponse::new();
match self.service.raw_uci_cmd(request.gid, request.oid, request.payload) {
Ok(msg) => {
resp.set_status(Ok(()).into());
resp.set_gid(msg.gid);
resp.set_oid(msg.oid);
resp.set_payload(msg.payload);
}
Err(e) => {
resp.set_status(From::<Result<()>>::from(Err(e)));
}
}
write_to_bytes(&resp)
}
/// Get app config params for the given session id
pub fn session_params(&self, request: &[u8]) -> Result<Vec<u8>> {
let request = parse_from_bytes::<SessionParamsRequest>(request)?;
let mut resp = SessionParamsResponse::new();
match self.service.session_params(request.session_id) {
Ok(AppConfigParams::Fira(params)) => {
resp.set_status(Ok(()).into());
resp.set_params(params.into());
}
Ok(params) => {
error!("Received non-Fira session parameters: {:?}", params);
resp.set_status(ProtoStatus::UNKNOWN);
}
Err(e) => {
resp.set_status(From::<Result<()>>::from(Err(e)));
}
}
write_to_bytes(&resp)
}
}
/// The trait that provides the same callbacks of UwbServiceCallback. It has the blanket
/// implementation of UwbServiceCallback trait that converts the arguments to one protobuf-encoded
/// payload.
///
/// For the naming of the protobuf struct, the payload of a callback `on_something_happened()`
/// will be called `SomethingHappenedSignal`.
pub trait ProtoUwbServiceCallback: 'static {
/// Notify the UWB service has been reset due to internal error. All the sessions are closed.
fn on_service_reset(&mut self, payload: Vec<u8>);
/// Notify the status of the UCI device.
fn on_uci_device_status_changed(&mut self, payload: Vec<u8>);
/// Notify the state of the session is changed.
fn on_session_state_changed(&mut self, payload: Vec<u8>);
/// Notify the ranging data of the session is received.
fn on_range_data_received(&mut self, payload: Vec<u8>);
/// Notify the vendor notification is received.
fn on_vendor_notification_received(&mut self, payload: Vec<u8>);
}
impl<C: ProtoUwbServiceCallback> UwbServiceCallback for C {
fn on_service_reset(&mut self, success: bool) {
debug!("UwbService is reset, success: {}", success);
let mut msg = ServiceResetSignal::new();
msg.set_success(success);
if let Ok(payload) = write_to_bytes(&msg) {
ProtoUwbServiceCallback::on_service_reset(self, payload);
} else {
error!("Failed to call on_service_reset()");
}
}
fn on_uci_device_status_changed(&mut self, state: DeviceState) {
debug!("UCI device status is changed: {}", state);
let mut msg = UciDeviceStatusChangedSignal::new();
msg.set_state(state.into());
if let Ok(payload) = write_to_bytes(&msg) {
ProtoUwbServiceCallback::on_uci_device_status_changed(self, payload);
} else {
error!("Failed to call on_uci_device_status_changed()");
}
}
fn on_session_state_changed(
&mut self,
session_id: SessionId,
session_state: SessionState,
reason_code: ReasonCode,
) {
debug!(
"Session {:?}'s state is changed to {:?}, reason: {:?}",
session_id, session_state, reason_code
);
let mut msg = SessionStateChangedSignal::new();
msg.set_session_id(session_id);
msg.set_session_state(session_state.into());
msg.set_reason_code(reason_code.into());
if let Ok(payload) = write_to_bytes(&msg) {
ProtoUwbServiceCallback::on_session_state_changed(self, payload);
} else {
error!("Failed to call on_session_state_changed()");
}
}
fn on_range_data_received(&mut self, session_id: SessionId, range_data: SessionRangeData) {
debug!("Received range data {:?} from Session {:?}", range_data, session_id);
let mut msg = RangeDataReceivedSignal::new();
msg.set_session_id(session_id);
msg.set_range_data(range_data.into());
if let Ok(payload) = write_to_bytes(&msg) {
ProtoUwbServiceCallback::on_range_data_received(self, payload);
} else {
error!("Failed to call on_range_data_received()");
}
}
fn on_vendor_notification_received(&mut self, gid: u32, oid: u32, payload: Vec<u8>) {
debug!("Received vendor notification: gid={}, oid={}, payload={:?}", gid, oid, payload);
let mut msg = VendorNotificationReceivedSignal::new();
msg.set_gid(gid);
msg.set_oid(oid);
msg.set_payload(payload);
if let Ok(payload) = write_to_bytes(&msg) {
ProtoUwbServiceCallback::on_vendor_notification_received(self, payload);
} else {
error!("Failed to call on_vendor_notification_received()");
}
}
}