xref: /aosp_15_r20/external/uwb/src/rust/uwb_core/src/uci/uci_logger.rs (revision e0df40009cb5d71e642272d38ba1bb7ffccfce41)
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 //! Trait definition for UciLogger.
16 use std::convert::TryFrom;
17 
18 use pdl_runtime::Packet;
19 use uwb_uci_packets::{
20     AppConfigTlv, AppConfigTlvType, SessionConfigCommandChild, SessionConfigResponseChild,
21     SessionGetAppConfigRspBuilder, SessionSetAppConfigCmdBuilder, UciCommandChild,
22     UciControlPacket, UciControlPacketChild, UciDataPacket, UciResponse, UciResponseChild,
23     UCI_PACKET_HAL_HEADER_LEN,
24 };
25 
26 use crate::error::{Error, Result};
27 use crate::uci::UciCommand;
28 
29 /// UCI Log mode.
30 #[derive(Clone, Debug, PartialEq, Eq)]
31 pub enum UciLoggerMode {
32     /// Log is disabled.
33     Disabled,
34     /// Logs all uci packets without filtering PII information.
35     Unfiltered,
36     /// Logs uci packets, with PII filtered.
37     Filtered,
38 }
39 
40 impl TryFrom<String> for UciLoggerMode {
41     type Error = Error;
42     /// Parse log mode from string.
try_from(log_mode_string: String) -> Result<UciLoggerMode>43     fn try_from(log_mode_string: String) -> Result<UciLoggerMode> {
44         match log_mode_string.as_str() {
45             "disabled" => Ok(UciLoggerMode::Disabled),
46             "unfiltered" => Ok(UciLoggerMode::Unfiltered),
47             "filtered" => Ok(UciLoggerMode::Filtered),
48             _ => Err(Error::BadParameters),
49         }
50     }
51 }
52 
53 /// Trait definition for the thread-safe uci logger
54 pub trait UciLogger: 'static + Send + Sync {
55     /// Logs Uci Control Packet.
log_uci_control_packet(&mut self, packet: UciControlPacket)56     fn log_uci_control_packet(&mut self, packet: UciControlPacket);
57     /// Logs Uci Data Packet. This is being passed as a reference since most of the time logging is
58     /// disabled, and so this will avoid copying the data payload.
log_uci_data_packet(&mut self, packet: &UciDataPacket)59     fn log_uci_data_packet(&mut self, packet: &UciDataPacket);
60     /// Logs hal open event.
log_hal_open(&mut self, result: Result<()>)61     fn log_hal_open(&mut self, result: Result<()>);
62     /// Logs hal close event.
log_hal_close(&mut self, result: Result<()>)63     fn log_hal_close(&mut self, result: Result<()>);
64 }
65 
filter_tlv(mut tlv: AppConfigTlv) -> AppConfigTlv66 fn filter_tlv(mut tlv: AppConfigTlv) -> AppConfigTlv {
67     if tlv.cfg_id == AppConfigTlvType::VendorId || tlv.cfg_id == AppConfigTlvType::StaticStsIv {
68         tlv.v = vec![0; tlv.v.len()];
69     }
70     tlv
71 }
72 
filter_uci_command(cmd: UciControlPacket) -> UciControlPacket73 fn filter_uci_command(cmd: UciControlPacket) -> UciControlPacket {
74     match cmd.specialize() {
75         UciControlPacketChild::UciCommand(control_cmd) => match control_cmd.specialize() {
76             UciCommandChild::SessionConfigCommand(session_cmd) => match session_cmd.specialize() {
77                 SessionConfigCommandChild::SessionSetAppConfigCmd(set_config_cmd) => {
78                     let session_token = set_config_cmd.get_session_token();
79                     let tlvs = set_config_cmd.get_tlvs().to_owned();
80                     let filtered_tlvs = tlvs.into_iter().map(filter_tlv).collect();
81                     SessionSetAppConfigCmdBuilder { session_token, tlvs: filtered_tlvs }
82                         .build()
83                         .into()
84                 }
85                 _ => session_cmd.into(),
86             },
87             _ => cmd,
88         },
89         _ => cmd,
90     }
91 }
92 
filter_uci_response(rsp: UciResponse) -> UciResponse93 fn filter_uci_response(rsp: UciResponse) -> UciResponse {
94     match rsp.specialize() {
95         UciResponseChild::SessionConfigResponse(session_rsp) => match session_rsp.specialize() {
96             SessionConfigResponseChild::SessionGetAppConfigRsp(rsp) => {
97                 let status = rsp.get_status();
98                 let tlvs = rsp.get_tlvs().to_owned();
99                 let filtered_tlvs = tlvs.into_iter().map(filter_tlv).collect();
100                 SessionGetAppConfigRspBuilder { status, tlvs: filtered_tlvs }.build().into()
101             }
102             _ => session_rsp.into(),
103         },
104         _ => rsp,
105     }
106 }
107 
108 // Log only the Data Packet header bytes, so that we don't log any PII (payload bytes).
filter_uci_data( packet: &UciDataPacket, ) -> std::result::Result<UciDataPacket, pdl_runtime::DecodeError>109 fn filter_uci_data(
110     packet: &UciDataPacket,
111 ) -> std::result::Result<UciDataPacket, pdl_runtime::DecodeError> {
112     // Initialize a (zeroed out) Vec to the same length as the data packet, and then copy over
113     // only the Data Packet header bytes into it. This masks out all the payload bytes to 0.
114     let data_packet_bytes: Vec<u8> = packet.encode_to_vec().unwrap();
115     let mut filtered_data_packet_bytes: Vec<u8> = vec![0; data_packet_bytes.len()];
116     for (i, &b) in data_packet_bytes[..UCI_PACKET_HAL_HEADER_LEN].iter().enumerate() {
117         filtered_data_packet_bytes[i] = b;
118     }
119     UciDataPacket::parse(&filtered_data_packet_bytes)
120 }
121 
122 /// Wrapper struct that filters messages feeded to UciLogger.
123 pub(crate) struct UciLoggerWrapper<T: UciLogger> {
124     mode: UciLoggerMode,
125     logger: T,
126 }
127 impl<T: UciLogger> UciLoggerWrapper<T> {
new(logger: T, mode: UciLoggerMode) -> Self128     pub fn new(logger: T, mode: UciLoggerMode) -> Self {
129         Self { mode, logger }
130     }
131 
set_logger_mode(&mut self, mode: UciLoggerMode)132     pub fn set_logger_mode(&mut self, mode: UciLoggerMode) {
133         self.mode = mode;
134     }
135 
136     /// Logs hal open event.
log_hal_open(&mut self, result: &Result<()>)137     pub fn log_hal_open(&mut self, result: &Result<()>) {
138         if self.mode != UciLoggerMode::Disabled {
139             self.logger.log_hal_open(result.clone());
140         }
141     }
142 
143     /// Logs hal close event.
log_hal_close(&mut self, result: &Result<()>)144     pub fn log_hal_close(&mut self, result: &Result<()>) {
145         if self.mode != UciLoggerMode::Disabled {
146             self.logger.log_hal_close(result.clone());
147         }
148     }
149 
log_uci_command(&mut self, cmd: &UciCommand)150     pub fn log_uci_command(&mut self, cmd: &UciCommand) {
151         match self.mode {
152             UciLoggerMode::Disabled => (),
153             UciLoggerMode::Unfiltered => {
154                 if let Ok(packet) = UciControlPacket::try_from(cmd.clone()) {
155                     self.logger.log_uci_control_packet(packet);
156                 };
157             }
158             UciLoggerMode::Filtered => {
159                 if let Ok(packet) = UciControlPacket::try_from(cmd.clone()) {
160                     self.logger.log_uci_control_packet(filter_uci_command(packet));
161                 };
162             }
163         }
164     }
165 
log_uci_response_or_notification(&mut self, packet: &UciControlPacket)166     pub fn log_uci_response_or_notification(&mut self, packet: &UciControlPacket) {
167         match self.mode {
168             UciLoggerMode::Disabled => (),
169             UciLoggerMode::Unfiltered => self.logger.log_uci_control_packet(packet.clone()),
170             UciLoggerMode::Filtered => match packet.clone().specialize() {
171                 uwb_uci_packets::UciControlPacketChild::UciResponse(packet) => {
172                     self.logger.log_uci_control_packet(filter_uci_response(packet).into())
173                 }
174                 uwb_uci_packets::UciControlPacketChild::UciNotification(packet) => {
175                     self.logger.log_uci_control_packet(packet.into())
176                 }
177                 _ => (),
178             },
179         }
180     }
181 
log_uci_data(&mut self, packet: &UciDataPacket)182     pub fn log_uci_data(&mut self, packet: &UciDataPacket) {
183         if self.mode == UciLoggerMode::Disabled {
184             return;
185         }
186         if let Ok(filtered_packet) = filter_uci_data(packet) {
187             self.logger.log_uci_data_packet(&filtered_packet);
188         }
189     }
190 }
191 
192 /// A placeholder UciLogger implementation that does nothing.
193 #[derive(Default)]
194 pub struct NopUciLogger {}
195 
196 impl UciLogger for NopUciLogger {
log_uci_control_packet(&mut self, _packet: UciControlPacket)197     fn log_uci_control_packet(&mut self, _packet: UciControlPacket) {}
198 
log_uci_data_packet(&mut self, _packet: &UciDataPacket)199     fn log_uci_data_packet(&mut self, _packet: &UciDataPacket) {}
200 
log_hal_open(&mut self, _result: Result<()>)201     fn log_hal_open(&mut self, _result: Result<()>) {}
202 
log_hal_close(&mut self, _result: Result<()>)203     fn log_hal_close(&mut self, _result: Result<()>) {}
204 }
205 
206 #[cfg(test)]
207 mod tests {
208     use super::*;
209 
210     use std::convert::TryInto;
211 
212     use tokio::sync::mpsc;
213 
214     use crate::params::uci_packets::StatusCode;
215     use crate::uci::mock_uci_logger::{MockUciLogger, UciLogEvent};
216     use uwb_uci_packets::{DataPacketFormat, MessageType, UciDataPacketBuilder};
217 
218     #[test]
test_log_command_filter() -> Result<()>219     fn test_log_command_filter() -> Result<()> {
220         let set_config_cmd = UciCommand::SessionSetAppConfig {
221             session_token: 0x1,
222             config_tlvs: vec![
223                 // Filtered to 0-filled of same length
224                 AppConfigTlv { cfg_id: AppConfigTlvType::VendorId, v: vec![0, 1, 2] }.into(),
225                 // Invariant after filter
226                 AppConfigTlv { cfg_id: AppConfigTlvType::AoaResultReq, v: vec![0, 1, 2, 3] }.into(),
227             ],
228         };
229         let (log_sender, mut log_receiver) = mpsc::unbounded_channel::<UciLogEvent>();
230         let mut logger =
231             UciLoggerWrapper::new(MockUciLogger::new(log_sender), UciLoggerMode::Filtered);
232         logger.log_uci_command(&set_config_cmd);
233         assert_eq!(
234             TryInto::<Vec<u8>>::try_into(log_receiver.blocking_recv().unwrap())?,
235             vec!(
236                 0x21, 0x3, 0, 0x10, 0, 0, 0, 0x1, 0, 0, 0, 0x2, // other info
237                 0x27, 0x3, 0, 0, 0, // filtered vendor ID
238                 0xd, 0x4, 0, 0x1, 0x2, 0x3 // unfiltered tlv
239             )
240         );
241         Ok(())
242     }
243 
244     #[test]
test_log_response_filter() -> Result<()>245     fn test_log_response_filter() -> Result<()> {
246         let unfiltered_rsp: UciControlPacket = SessionGetAppConfigRspBuilder {
247             status: StatusCode::UciStatusOk,
248             tlvs: vec![
249                 AppConfigTlv { cfg_id: AppConfigTlvType::StaticStsIv, v: vec![0, 1, 2] },
250                 AppConfigTlv { cfg_id: AppConfigTlvType::AoaResultReq, v: vec![0, 1, 2, 3] },
251             ],
252         }
253         .build()
254         .into();
255         let (log_sender, mut log_receiver) = mpsc::unbounded_channel::<UciLogEvent>();
256         let mut logger =
257             UciLoggerWrapper::new(MockUciLogger::new(log_sender), UciLoggerMode::Filtered);
258         logger.log_uci_response_or_notification(&unfiltered_rsp);
259         assert_eq!(
260             TryInto::<Vec<u8>>::try_into(log_receiver.blocking_recv().unwrap())?,
261             vec!(
262                 0x41, 0x4, 0, 0xd, 0, 0, 0, 0, 0x2, // other info
263                 0x28, 0x3, 0, 0, 0, //filtered StaticStsIv
264                 0xd, 0x4, 0, 0x1, 0x2, 0x3 // unfiltered tlv
265             )
266         );
267         Ok(())
268     }
269 
270     #[test]
test_log_data_filter() -> Result<()>271     fn test_log_data_filter() -> Result<()> {
272         let unfiltered_data_packet: UciDataPacket = UciDataPacketBuilder {
273             data_packet_format: DataPacketFormat::DataSnd,
274             message_type: MessageType::Data,
275             payload: Some(vec![0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8].into()),
276         }
277         .build();
278         let (log_sender, mut log_receiver) = mpsc::unbounded_channel::<UciLogEvent>();
279         let mut logger =
280             UciLoggerWrapper::new(MockUciLogger::new(log_sender), UciLoggerMode::Filtered);
281         logger.log_uci_data(&unfiltered_data_packet);
282         assert_eq!(
283             TryInto::<Vec<u8>>::try_into(log_receiver.blocking_recv().unwrap())?,
284             vec!(0x1, 0x0, 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
285         );
286         Ok(())
287     }
288 }
289