1 // Copyright 2022, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 //! This module defines the parameters or responses of the UciManager's methods. Most of them are
16 //! re-exported from the uwb_uci_packets crate.
17
18 use std::collections::{hash_map::RandomState, HashMap};
19 use std::iter::FromIterator;
20
21 // Re-export enums and structs from uwb_uci_packets.
22 pub use uwb_uci_packets::{
23 AppConfigStatus, AppConfigTlv as RawAppConfigTlv, AppConfigTlvType, CapTlv, CapTlvType,
24 Controlee, ControleeStatus, Controlees, CreditAvailability, DataRcvStatusCode,
25 DataTransferNtfStatusCode, DeviceConfigId, DeviceConfigStatus, DeviceConfigTlv, DeviceState,
26 ExtendedAddressDlTdoaRangingMeasurement, ExtendedAddressOwrAoaRangingMeasurement,
27 ExtendedAddressTwoWayRangingMeasurement, FiraComponent, GroupId, MessageType,
28 MulticastUpdateStatusCode, PowerStats, RangingMeasurementType, ReasonCode, ResetConfig,
29 SessionState, SessionType, ShortAddressDlTdoaRangingMeasurement,
30 ShortAddressOwrAoaRangingMeasurement, ShortAddressTwoWayRangingMeasurement, StatusCode,
31 UpdateMulticastListAction,
32 };
33 pub(crate) use uwb_uci_packets::{UciControlPacket, UciDataPacket, UciDataPacketHal};
34
35 use crate::error::Error;
36
37 /// The type of the session identifier.
38 pub type SessionId = u32;
39 /// The type of the sub-session identifier.
40 pub type SubSessionId = u32;
41 /// The type of the session handle.
42 pub type SessionHandle = u32;
43 /// Generic type used to represent either a session id or session handle.
44 pub type SessionToken = u32;
45
46 /// Wrap the original AppConfigTlv type to redact the PII fields when logging.
47 #[derive(Clone, PartialEq)]
48 pub struct AppConfigTlv {
49 tlv: RawAppConfigTlv,
50 }
51
52 impl std::fmt::Debug for AppConfigTlv {
fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error>53 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
54 static REDACTED_STR: &str = "redacted";
55
56 let mut ds = f.debug_struct("AppConfigTlv");
57 ds.field("cfg_id", &self.tlv.cfg_id);
58 if self.tlv.cfg_id == AppConfigTlvType::VendorId
59 || self.tlv.cfg_id == AppConfigTlvType::StaticStsIv
60 {
61 ds.field("v", &REDACTED_STR);
62 } else {
63 ds.field("v", &self.tlv.v);
64 }
65 ds.finish()
66 }
67 }
68
69 impl AppConfigTlv {
70 /// Create a wrapper of uwb_uci_packets::AppConfigTlv.
71 ///
72 /// The argument is the same as the uwb_uci_packets::AppConfigTlv's struct.
new(cfg_id: AppConfigTlvType, v: Vec<u8>) -> Self73 pub fn new(cfg_id: AppConfigTlvType, v: Vec<u8>) -> Self {
74 Self { tlv: RawAppConfigTlv { cfg_id, v } }
75 }
76
77 /// Consumes the outter wrapper type, returning the wrapped uwb_uci_packets::AppConfigTlv.
into_inner(self) -> RawAppConfigTlv78 pub fn into_inner(self) -> RawAppConfigTlv {
79 self.tlv
80 }
81 }
82
83 impl From<RawAppConfigTlv> for AppConfigTlv {
from(tlv: RawAppConfigTlv) -> Self84 fn from(tlv: RawAppConfigTlv) -> Self {
85 Self { tlv }
86 }
87 }
88
89 impl std::ops::Deref for AppConfigTlv {
90 type Target = RawAppConfigTlv;
deref(&self) -> &Self::Target91 fn deref(&self) -> &Self::Target {
92 &self.tlv
93 }
94 }
95
96 impl std::ops::DerefMut for AppConfigTlv {
deref_mut(&mut self) -> &mut Self::Target97 fn deref_mut(&mut self) -> &mut Self::Target {
98 &mut self.tlv
99 }
100 }
101
102 /// Compare if two AppConfigTlv array are equal. Convert the array to HashMap before comparing
103 /// because the order of TLV elements doesn't matter.
104 #[allow(dead_code)]
app_config_tlvs_eq(a: &[AppConfigTlv], b: &[AppConfigTlv]) -> bool105 pub fn app_config_tlvs_eq(a: &[AppConfigTlv], b: &[AppConfigTlv]) -> bool {
106 app_config_tlvs_to_map(a) == app_config_tlvs_to_map(b)
107 }
108
app_config_tlvs_to_map( tlvs: &[AppConfigTlv], ) -> HashMap<AppConfigTlvType, &Vec<u8>, RandomState>109 fn app_config_tlvs_to_map(
110 tlvs: &[AppConfigTlv],
111 ) -> HashMap<AppConfigTlvType, &Vec<u8>, RandomState> {
112 HashMap::from_iter(tlvs.iter().map(|config| (config.cfg_id, &config.v)))
113 }
114
115 /// Compare if two DeviceConfigTlv array are equal. Convert the array to HashMap before comparing
116 /// because the order of TLV elements doesn't matter.
117 #[allow(dead_code)]
device_config_tlvs_eq(a: &[DeviceConfigTlv], b: &[DeviceConfigTlv]) -> bool118 pub fn device_config_tlvs_eq(a: &[DeviceConfigTlv], b: &[DeviceConfigTlv]) -> bool {
119 device_config_tlvs_to_map(a) == device_config_tlvs_to_map(b)
120 }
121
device_config_tlvs_to_map( tlvs: &[DeviceConfigTlv], ) -> HashMap<DeviceConfigId, &Vec<u8>, RandomState>122 fn device_config_tlvs_to_map(
123 tlvs: &[DeviceConfigTlv],
124 ) -> HashMap<DeviceConfigId, &Vec<u8>, RandomState> {
125 HashMap::from_iter(tlvs.iter().map(|config| (config.cfg_id, &config.v)))
126 }
127
128 /// The response of the UciManager::core_set_config() method.
129 #[derive(Debug, Clone, PartialEq)]
130 pub struct CoreSetConfigResponse {
131 /// The status code of the response.
132 pub status: StatusCode,
133 /// The status of each config TLV.
134 pub config_status: Vec<DeviceConfigStatus>,
135 }
136
137 /// The response of the UciManager::session_set_app_config() method.
138 #[derive(Debug, Clone, PartialEq)]
139 pub struct SetAppConfigResponse {
140 /// The status code of the response.
141 pub status: StatusCode,
142 /// The status of each config TLV.
143 pub config_status: Vec<AppConfigStatus>,
144 }
145
146 /// The response from UciManager::session_update_dt_tag_ranging_rounds() method.
147 #[derive(Debug, Clone, PartialEq, Eq)]
148 pub struct SessionUpdateDtTagRangingRoundsResponse {
149 /// The status code of the response.
150 pub status: StatusCode,
151 /// Indexes of unsuccessful ranging rounds.
152 pub ranging_round_indexes: Vec<u8>,
153 }
154
155 /// The country code struct that contains 2 uppercase ASCII characters.
156 #[derive(Debug, Clone, PartialEq, Eq)]
157 pub struct CountryCode([u8; 2]);
158
159 impl CountryCode {
160 const UNKNOWN_COUNTRY_CODE: &'static [u8] = "00".as_bytes();
161
162 /// Create a CountryCode instance.
new(code: &[u8; 2]) -> Option<Self>163 pub fn new(code: &[u8; 2]) -> Option<Self> {
164 if code != CountryCode::UNKNOWN_COUNTRY_CODE
165 && !code.iter().all(|x| (*x as char).is_ascii_alphabetic())
166 {
167 None
168 } else {
169 Some(Self((*code).to_ascii_uppercase().try_into().ok()?))
170 }
171 }
172 }
173
174 impl From<CountryCode> for [u8; 2] {
from(item: CountryCode) -> [u8; 2]175 fn from(item: CountryCode) -> [u8; 2] {
176 item.0
177 }
178 }
179
180 impl TryFrom<String> for CountryCode {
181 type Error = Error;
try_from(item: String) -> Result<Self, Self::Error>182 fn try_from(item: String) -> Result<Self, Self::Error> {
183 let code = item.as_bytes().try_into().map_err(|_| Error::BadParameters)?;
184 Self::new(code).ok_or(Error::BadParameters)
185 }
186 }
187
188 /// The response of the UciManager::core_get_device_info() method.
189 #[derive(Debug, Clone, PartialEq, Eq)]
190 pub struct GetDeviceInfoResponse {
191 /// The UCI version.
192 pub uci_version: u16,
193 /// The MAC version.
194 pub mac_version: u16,
195 /// The physical version.
196 pub phy_version: u16,
197 /// The UCI test version.
198 pub uci_test_version: u16,
199 /// The vendor spec info.
200 pub vendor_spec_info: Vec<u8>,
201 }
202
203 /// The raw UCI message for the vendor commands.
204 #[derive(Debug, Clone, PartialEq, Eq)]
205 pub struct RawUciMessage {
206 /// The group id of the message.
207 pub gid: u32,
208 /// The opcode of the message.
209 pub oid: u32,
210 /// The payload of the message.
211 pub payload: Vec<u8>,
212 }
213
214 impl From<UciControlPacket> for RawUciMessage {
from(packet: UciControlPacket) -> Self215 fn from(packet: UciControlPacket) -> Self {
216 Self {
217 gid: packet.get_group_id().into(),
218 oid: packet.get_opcode() as u32,
219 payload: packet.to_raw_payload(),
220 }
221 }
222 }
223
224 #[cfg(test)]
225 mod tests {
226 use super::*;
227
228 #[test]
test_redacted_app_config_tlv()229 fn test_redacted_app_config_tlv() {
230 // The value of VendorId and StaticStsIv should be redacted.
231 let tlv = AppConfigTlv::new(AppConfigTlvType::VendorId, vec![12, 34]);
232 let format_str = format!("{tlv:?}");
233 assert!(format_str.contains("v: \"redacted\""));
234
235 let tlv = AppConfigTlv::new(AppConfigTlvType::StaticStsIv, vec![12, 34]);
236 let format_str = format!("{tlv:?}");
237 assert!(format_str.contains("v: \"redacted\""));
238
239 // The value of DeviceType should be printed normally.
240 let tlv = AppConfigTlv::new(AppConfigTlvType::DeviceType, vec![12, 34]);
241 let format_str = format!("{tlv:?}");
242 assert_eq!(format_str, "AppConfigTlv { cfg_id: DeviceType, v: [12, 34] }");
243 }
244
245 #[test]
test_country_code()246 fn test_country_code() {
247 let _country_code_ascii: CountryCode = String::from("US").try_into().unwrap();
248 let _country_code_unknown: CountryCode = String::from("00").try_into().unwrap();
249 let country_code_invalid_1: Result<CountryCode, Error> = String::from("0S").try_into();
250 country_code_invalid_1.unwrap_err();
251 let country_code_invalid_2: Result<CountryCode, Error> = String::from("ÀÈ").try_into();
252 country_code_invalid_2.unwrap_err();
253 }
254 }
255