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