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