logger: Rewrite packet formatter to use PCAPNG format

- Generate heading Interface Block with LinkType 293
- Wrap UCI packets in Enhanced Packet Blocks

Bug: 204097686
Test: atest --host libuwb_uci_rust_tests
Change-Id: I9cb30eb4dd5f88fa402eb88796e91cba47e21c79
diff --git a/src/rust/uci/mock_uci_logger.rs b/src/rust/uci/mock_uci_logger.rs
index 3a8d6f8..ebf53c7 100644
--- a/src/rust/uci/mock_uci_logger.rs
+++ b/src/rust/uci/mock_uci_logger.rs
@@ -20,6 +20,7 @@
 use crate::uci::UwbErr;
 use async_trait::async_trait;
 use std::path::Path;
+use std::time::Duration;
 use uwb_uci_packets::{UciCommandPacket, UciNotificationPacket, UciResponsePacket};
 
 #[cfg(test)]
@@ -62,3 +63,14 @@
 pub async fn rename(_from: impl AsRef<Path>, _to: impl AsRef<Path>) -> Result<(), UwbErr> {
     Ok(())
 }
+
+#[cfg(test)]
+pub struct Instant {}
+impl Instant {
+    pub fn now() -> Instant {
+        Instant {}
+    }
+    pub fn elapsed(&self) -> Duration {
+        Duration::from_micros(0)
+    }
+}
diff --git a/src/rust/uci/uci_logger.rs b/src/rust/uci/uci_logger.rs
index 801416d..3e31e15 100644
--- a/src/rust/uci/uci_logger.rs
+++ b/src/rust/uci/uci_logger.rs
@@ -17,13 +17,14 @@
 extern crate libc;
 
 #[cfg(test)]
-use crate::uci::mock_uci_logger::{create_dir, remove_file, rename};
+use crate::uci::mock_uci_logger::{create_dir, remove_file, rename, Instant};
 use crate::uci::UwbErr;
 use async_trait::async_trait;
-use bytes::{BufMut, BytesMut};
 use log::{error, info};
 use std::marker::Unpin;
 use std::sync::Arc;
+#[cfg(not(test))]
+use std::time::Instant;
 use std::time::SystemTime;
 use tokio::fs::OpenOptions;
 #[cfg(not(test))]
@@ -32,23 +33,19 @@
 use tokio::sync::Mutex;
 use tokio::{task, time};
 use uwb_uci_packets::{
-    AppConfigTlv, AppConfigTlvType, MessageType, Packet, SessionCommandChild,
-    SessionGetAppConfigRspBuilder, SessionResponseChild, SessionSetAppConfigCmdBuilder,
-    UciCommandChild, UciCommandPacket, UciNotificationPacket, UciPacketPacket, UciResponseChild,
-    UciResponsePacket,
+    AppConfigTlv, AppConfigTlvType, Packet, SessionCommandChild, SessionGetAppConfigRspBuilder,
+    SessionResponseChild, SessionSetAppConfigCmdBuilder, UciCommandChild, UciCommandPacket,
+    UciNotificationPacket, UciPacketPacket, UciResponseChild, UciResponsePacket,
 };
 
 // micros since 0000-01-01
-const UCI_EPOCH_DELTA: u64 = 0x00dcddb30f2f8000;
 const UCI_LOG_LAST_FILE_STORE_TIME_SEC: u64 = 86400; // 24 hours
 const MAX_FILE_SIZE: usize = 102400; // 100 kb
 const MAX_BUFFER_SIZE: usize = 10240; // 10 kb
-const PKT_LOG_HEADER_SIZE: usize = 25;
 const VENDOR_ID: u64 = AppConfigTlvType::VendorId as u64;
 const STATIC_STS_IV: u64 = AppConfigTlvType::StaticStsIv as u64;
 const LOG_DIR: &str = "/data/misc/apexdata/com.android.uwb/log";
-const FILE_NAME: &str = "uwb_uci.log";
-const LOG_HEADER: &[u8] = b"ucilogging";
+const FILE_NAME: &str = "uwb_uci.pcapng";
 
 type SyncFile = Arc<Mutex<dyn AsyncWrite + Send + Sync + Unpin>>;
 type SyncFactory = Arc<Mutex<dyn FileFactory + Send + Sync>>;
@@ -61,13 +58,6 @@
 }
 
 #[derive(Clone)]
-enum Type {
-    Command = 1,
-    Response,
-    Notification,
-}
-
-#[derive(Clone)]
 pub struct UciLogConfig {
     path: String,
     mode: UciLogMode,
@@ -90,7 +80,7 @@
 struct BufferedFile {
     file: Option<SyncFile>,
     size_count: usize,
-    buffer: BytesMut,
+    buffer: Vec<u8>,
     deleter_handle: Option<task::JoinHandle<()>>,
 }
 
@@ -115,7 +105,7 @@
             };
         }));
         let mut file = factory.lock().await.create_file_using_open_options(path).await?;
-        write(&mut file, LOG_HEADER).await;
+        write_pcapng_header(&mut file).await;
         self.file = Some(file);
         Ok(())
     }
@@ -135,6 +125,7 @@
     config: UciLogConfig,
     buf_file: Mutex<BufferedFile>,
     file_factory: SyncFactory,
+    start_time: Instant,
 }
 
 impl UciLoggerImpl {
@@ -143,57 +134,55 @@
         let mut factory = file_factory.lock().await;
         factory.set_config(config.clone()).await;
         let (file, size) = factory.new_file().await;
-        let buf_file =
-            BufferedFile { size_count: size, file, buffer: BytesMut::new(), deleter_handle: None };
-        let ret =
-            Self { config, buf_file: Mutex::new(buf_file), file_factory: file_factory.clone() };
+        let buf_file = BufferedFile {
+            size_count: size,
+            file,
+            buffer: Vec::with_capacity(MAX_BUFFER_SIZE),
+            deleter_handle: None,
+        };
+        let ret = Self {
+            config,
+            buf_file: Mutex::new(buf_file),
+            file_factory: file_factory.clone(),
+            start_time: Instant::now(),
+        };
         info!("UCI logger created");
         ret
     }
 
     async fn log_uci_packet(&self, packet: UciPacketPacket) {
-        let mt = packet.get_message_type();
-        let bytes = packet.to_vec();
-        let mt_byte = match mt {
-            MessageType::Command => Type::Command as u8,
-            MessageType::Response => Type::Response as u8,
-            MessageType::Notification => Type::Notification as u8,
-        };
-        let flags = match mt {
-            MessageType::Command => 0b10,      // down direction
-            MessageType::Response => 0b01,     // up direction
-            MessageType::Notification => 0b01, // up direction
-        };
-        let timestamp = u64::try_from(
-            SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_micros(),
-        )
-        .unwrap()
-            + UCI_EPOCH_DELTA;
+        const ENHANCED_BLOCK_SIZE: u32 = 32;
+        let mut bytes = packet.to_vec();
+        let packet_data_padding: usize = 4 - bytes.len() % 4;
+        let block_total_length: u32 =
+            bytes.len() as u32 + packet_data_padding as u32 + ENHANCED_BLOCK_SIZE;
+        let timestamp = self.start_time.elapsed().as_micros() as u64;
 
-        let length = u32::try_from(bytes.len()).unwrap() + 1;
-
-        // Check whether exceeded the size limit
+        // Check whether the Enhanced Packet Block exceeds the size limit.
         let mut buf_file = self.buf_file.lock().await;
-        if buf_file.size_count + bytes.len() + PKT_LOG_HEADER_SIZE > MAX_FILE_SIZE {
+        if buf_file.size_count + block_total_length as usize > MAX_FILE_SIZE {
             match buf_file.open_next_file(self.file_factory.clone(), &self.config.path).await {
-                Ok(()) => info!("New file created"),
-                Err(e) => error!("Open next file failed: {:?}", e),
+                Ok(()) => info!("Created new pcagng log file"),
+                Err(e) => error!("Failed to open new pcapng log file: {:?}", e),
             }
-        } else if buf_file.buffer.len() + bytes.len() + PKT_LOG_HEADER_SIZE > MAX_BUFFER_SIZE {
-            let temp_buf = buf_file.buffer.clone();
-            if let Some(file) = &mut buf_file.file {
-                write(file, &temp_buf).await;
-                buf_file.buffer.clear();
+        } else if buf_file.buffer.len() + block_total_length as usize > MAX_BUFFER_SIZE {
+            if let BufferedFile { file: Some(ref mut file), ref mut buffer, .. } = *buf_file {
+                write(file, buffer).await;
+                buffer.clear();
             }
         }
-        buf_file.buffer.put_u32(length); // original length
-        buf_file.buffer.put_u32(length); // captured length
-        buf_file.buffer.put_u32(flags); // flags
-        buf_file.buffer.put_u32(0); // dropped packets
-        buf_file.buffer.put_u64(timestamp); // timestamp
-        buf_file.buffer.put_u8(mt_byte); // type
-        buf_file.buffer.put_slice(&bytes); // full packet.
-        buf_file.size_count += bytes.len() + PKT_LOG_HEADER_SIZE;
+
+        // Wrap the packet inside an Enhanced Packet Block.
+        buf_file.buffer.extend_from_slice(&u32::to_le_bytes(0x00000006)); // Block Type
+        buf_file.buffer.extend_from_slice(&u32::to_le_bytes(block_total_length));
+        buf_file.buffer.extend_from_slice(&u32::to_le_bytes(0)); // Interface ID
+        buf_file.buffer.extend_from_slice(&u32::to_le_bytes((timestamp >> 32) as u32)); // Timestamp (High)
+        buf_file.buffer.extend_from_slice(&u32::to_le_bytes(timestamp as u32)); // Timestamp (Low)
+        buf_file.buffer.extend_from_slice(&u32::to_le_bytes(bytes.len() as u32)); // Captured Packet Length
+        buf_file.buffer.extend_from_slice(&u32::to_le_bytes(bytes.len() as u32)); // Original Packet Length
+        buf_file.buffer.append(&mut bytes);
+        buf_file.buffer.extend_from_slice(&vec![0; packet_data_padding]);
+        buf_file.buffer.extend_from_slice(&u32::to_le_bytes(block_total_length));
     }
 }
 
@@ -207,6 +196,30 @@
     }
 }
 
+async fn write_pcapng_header(file: &mut SyncFile) {
+    let mut bytes = vec![];
+
+    // PCAPng files must start with a Section Header Block.
+    bytes.extend_from_slice(&u32::to_le_bytes(0x0A0D0D0A)); // Block Type
+    bytes.extend_from_slice(&u32::to_le_bytes(28)); // Block Total Length
+    bytes.extend_from_slice(&u32::to_le_bytes(0x1A2B3C4D)); // Byte-Order Magic
+    bytes.extend_from_slice(&u16::to_le_bytes(1)); // Major Version
+    bytes.extend_from_slice(&u16::to_le_bytes(0)); // Minor Version
+    bytes.extend_from_slice(&u64::to_le_bytes(0xFFFFFFFFFFFFFFFF)); // Section Length (not specified)
+    bytes.extend_from_slice(&u32::to_le_bytes(28)); // Block Total Length
+
+    // Write the Interface Description Block used for all
+    // UCI records.
+    bytes.extend_from_slice(&u32::to_le_bytes(0x00000001)); // Block Type
+    bytes.extend_from_slice(&u32::to_le_bytes(20)); // Block Total Length
+    bytes.extend_from_slice(&u16::to_le_bytes(293)); // LinkType
+    bytes.extend_from_slice(&u16::to_le_bytes(0)); // Reserved
+    bytes.extend_from_slice(&u32::to_le_bytes(0)); // SnapLen (no limit)
+    bytes.extend_from_slice(&u32::to_le_bytes(20)); // Block Total Length
+
+    write(file, &bytes).await;
+}
+
 #[async_trait]
 impl UciLogger for UciLoggerImpl {
     async fn log_uci_command(&self, cmd: UciCommandPacket) {
@@ -371,6 +384,7 @@
     async fn create_file_using_open_options(&self, path: &str) -> Result<SyncFile, UwbErr> {
         Ok(Arc::new(Mutex::new(OpenOptions::new().write(true).create_new(true).open(path).await?)))
     }
+
     async fn create_file_at_path(&self, path: &str) -> Option<SyncFile> {
         if create_dir(LOG_DIR).await.is_err() {
             error!("Failed to create dir");
@@ -380,7 +394,7 @@
         }
         match self.create_file_using_open_options(path).await {
             Ok(mut f) => {
-                write(&mut f, LOG_HEADER).await;
+                write_pcapng_header(&mut f).await;
                 Some(f)
             }
             Err(e) => {
@@ -459,10 +473,12 @@
                 .await;
         let cmd: UciCommandPacket = GetDeviceInfoCmdBuilder {}.build().into();
         logger.log_uci_command(cmd).await;
-        let data = [0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00];
+        let expected_buffer = [
+            6, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 7, 0, 0, 0,
+            32, 2, 0, 0, 0, 0, 0, 0, 40, 0, 0, 0,
+        ];
         let buf_file = logger.buf_file.lock().await;
-        assert_eq!(1, buf_file.buffer[buf_file.buffer.len() - data.len() - 1]);
-        assert_eq!(data, buf_file.buffer[(buf_file.buffer.len() - data.len())..]);
+        assert_eq!(&buf_file.buffer, &expected_buffer);
         Ok(())
     }
 
@@ -482,13 +498,12 @@
         .build()
         .into();
         logger.log_uci_response(rsp).await;
-        let data = [
-            0x40, 0x02, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-            0x00, 0x00, 0x00,
+        let expected_buffer = [
+            6, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 17, 0, 0, 0,
+            64, 2, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0,
         ];
         let buf_file = logger.buf_file.lock().await;
-        assert_eq!(2, buf_file.buffer[buf_file.buffer.len() - data.len() - 1]);
-        assert_eq!(data, buf_file.buffer[(buf_file.buffer.len() - data.len())..]);
+        assert_eq!(&buf_file.buffer, &expected_buffer);
         Ok(())
     }
 
@@ -500,10 +515,12 @@
         let ntf =
             DeviceStatusNtfBuilder { device_state: DeviceState::DeviceStateReady }.build().into();
         logger.log_uci_notification(ntf).await;
-        let data = [0x60, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01];
+        let expected_buffer = [
+            6, 0, 0, 0, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0,
+            96, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 44, 0, 0, 0,
+        ];
         let buf_file = logger.buf_file.lock().await;
-        assert_eq!(3, buf_file.buffer[buf_file.buffer.len() - data.len() - 1]);
-        assert_eq!(data, buf_file.buffer[(buf_file.buffer.len() - data.len())..]);
+        assert_eq!(&buf_file.buffer, &expected_buffer);
         Ok(())
     }
 
@@ -531,10 +548,12 @@
         .build()
         .into();
         logger.log_uci_response(rsp).await;
-        let data = [0x41, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x27, 0x02, 0x00, 0x00];
+        let expected_buffer = [
+            6, 0, 0, 0, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 13, 0, 0, 0,
+            65, 4, 0, 6, 0, 0, 0, 0, 1, 39, 2, 0, 0, 0, 0, 0, 48, 0, 0, 0,
+        ];
         let buf_file = logger.buf_file.lock().await;
-        assert_eq!(2, buf_file.buffer[buf_file.buffer.len() - data.len() - 1]);
-        assert_eq!(data, buf_file.buffer[(buf_file.buffer.len() - data.len())..]);
+        assert_eq!(&buf_file.buffer, &expected_buffer);
         Ok(())
     }
 }