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