Merge "[uwb-core] Add support support for custom vendor message type"
diff --git a/src/rust/uwb_core/protos/uwb_service.proto b/src/rust/uwb_core/protos/uwb_service.proto
index bc71f8f..35e989e 100644
--- a/src/rust/uwb_core/protos/uwb_service.proto
+++ b/src/rust/uwb_core/protos/uwb_service.proto
@@ -549,6 +549,7 @@
   uint32 gid = 1;
   uint32 oid = 2;
   bytes payload = 3;
+  uint32 mt = 4;
 }
 
 // Response of the UwbService::SendVendorCmd() method.
diff --git a/src/rust/uwb_core/src/service/proto_uwb_service.rs b/src/rust/uwb_core/src/service/proto_uwb_service.rs
index 91e3f7a..6cf7d5a 100644
--- a/src/rust/uwb_core/src/service/proto_uwb_service.rs
+++ b/src/rust/uwb_core/src/service/proto_uwb_service.rs
@@ -200,7 +200,7 @@
     pub fn raw_uci_cmd(&self, request: &[u8]) -> Result<Vec<u8>> {
         let request = parse_from_bytes::<SendVendorCmdRequest>(request)?;
         let mut resp = SendVendorCmdResponse::new();
-        match self.service.raw_uci_cmd(request.gid, request.oid, request.payload) {
+        match self.service.raw_uci_cmd(request.mt, request.gid, request.oid, request.payload) {
             Ok(msg) => {
                 resp.set_status(Ok(()).into());
                 resp.set_gid(msg.gid);
diff --git a/src/rust/uwb_core/src/service/uwb_service.rs b/src/rust/uwb_core/src/service/uwb_service.rs
index 8bc594b..8c47243 100644
--- a/src/rust/uwb_core/src/service/uwb_service.rs
+++ b/src/rust/uwb_core/src/service/uwb_service.rs
@@ -229,8 +229,14 @@
     }
 
     /// Send a raw UCI message.
-    pub fn raw_uci_cmd(&self, gid: u32, oid: u32, payload: Vec<u8>) -> Result<RawUciMessage> {
-        match self.block_on_cmd(Command::RawUciCmd { gid, oid, payload })? {
+    pub fn raw_uci_cmd(
+        &self,
+        mt: u32,
+        gid: u32,
+        oid: u32,
+        payload: Vec<u8>,
+    ) -> Result<RawUciMessage> {
+        match self.block_on_cmd(Command::RawUciCmd { mt, gid, oid, payload })? {
             Response::RawUciMessage(msg) => Ok(msg),
             _ => panic!("raw_uci_cmd() should return RawUciMessage"),
         }
@@ -409,8 +415,8 @@
                 let stats = self.uci_manager.android_get_power_stats().await?;
                 Ok(Response::PowerStats(stats))
             }
-            Command::RawUciCmd { gid, oid, payload } => {
-                let msg = self.uci_manager.raw_uci_cmd(gid, oid, payload).await?;
+            Command::RawUciCmd { mt, gid, oid, payload } => {
+                let msg = self.uci_manager.raw_uci_cmd(mt, gid, oid, payload).await?;
                 Ok(Response::RawUciMessage(msg))
             }
             Command::GetParams { session_id } => {
@@ -544,6 +550,7 @@
     },
     AndroidGetPowerStats,
     RawUciCmd {
+        mt: u32,
         gid: u32,
         oid: u32,
         payload: Vec<u8>,
@@ -751,6 +758,7 @@
 
     #[test]
     fn test_send_raw_cmd() {
+        let mt = 0x01;
         let gid = 0x09;
         let oid = 0x35;
         let cmd_payload = vec![0x12, 0x34];
@@ -758,6 +766,7 @@
 
         let mut uci_manager = MockUciManager::new();
         uci_manager.expect_raw_uci_cmd(
+            mt,
             gid,
             oid,
             cmd_payload.clone(),
@@ -765,7 +774,7 @@
         );
         let (service, _, _runtime) = setup_uwb_service(uci_manager);
 
-        let result = service.raw_uci_cmd(gid, oid, cmd_payload).unwrap();
+        let result = service.raw_uci_cmd(mt, gid, oid, cmd_payload).unwrap();
         assert_eq!(result, RawUciMessage { gid, oid, payload: resp_payload });
     }
 
diff --git a/src/rust/uwb_core/src/uci/command.rs b/src/rust/uwb_core/src/uci/command.rs
index 0f2eac0..8168f81 100644
--- a/src/rust/uwb_core/src/uci/command.rs
+++ b/src/rust/uwb_core/src/uci/command.rs
@@ -23,7 +23,7 @@
     AppConfigTlv, AppConfigTlvType, Controlees, CountryCode, DeviceConfigId, DeviceConfigTlv,
     ResetConfig, SessionId, SessionType, UpdateMulticastListAction,
 };
-use uwb_uci_packets::build_session_update_controller_multicast_list_cmd;
+use uwb_uci_packets::{build_session_update_controller_multicast_list_cmd, GroupId, MessageType};
 
 /// The enum to represent the UCI commands. The definition of each field should follow UCI spec.
 #[allow(missing_docs)]
@@ -82,13 +82,14 @@
     },
     AndroidGetPowerStats,
     RawUciCmd {
+        mt: u32,
         gid: u32,
         oid: u32,
         payload: Vec<u8>,
     },
 }
 
-impl TryFrom<UciCommand> for uwb_uci_packets::UciCommandPacket {
+impl TryFrom<UciCommand> for uwb_uci_packets::UciControlPacketPacket {
     type Error = Error;
     fn try_from(cmd: UciCommand) -> std::result::Result<Self, Self::Error> {
         let packet = match cmd {
@@ -153,8 +154,8 @@
             UciCommand::AndroidGetPowerStats => {
                 uwb_uci_packets::AndroidGetPowerStatsCmdBuilder {}.build().into()
             }
-            UciCommand::RawUciCmd { gid, oid, payload } => {
-                build_raw_uci_cmd_packet(gid, oid, payload)?
+            UciCommand::RawUciCmd { mt, gid, oid, payload } => {
+                build_raw_uci_cmd_packet(mt, gid, oid, payload)?
             }
             UciCommand::SessionGetCount => {
                 uwb_uci_packets::SessionGetCountCmdBuilder {}.build().into()
@@ -178,11 +179,11 @@
 }
 
 fn build_raw_uci_cmd_packet(
+    mt: u32,
     gid: u32,
     oid: u32,
     payload: Vec<u8>,
-) -> Result<uwb_uci_packets::UciCommandPacket> {
-    use uwb_uci_packets::GroupId;
+) -> Result<uwb_uci_packets::UciControlPacketPacket> {
     let group_id = GroupId::from_u32(gid).ok_or_else(|| {
         error!("Invalid GroupId: {}", gid);
         Error::BadParameters
@@ -192,5 +193,12 @@
         error!("Invalid opcod: {}", oid);
         Error::BadParameters
     })?;
-    Ok(uwb_uci_packets::UciCommandBuilder { opcode, group_id, payload }.build())
+    let message_type = MessageType::from_u32(mt).ok_or_else(|| {
+        error!("Invalid MessageType: {}", mt);
+        Error::BadParameters
+    })?;
+    match uwb_uci_packets::build_uci_control_packet(message_type, group_id, opcode, payload) {
+        Some(cmd) => Ok(cmd),
+        None => Err(Error::BadParameters),
+    }
 }
diff --git a/src/rust/uwb_core/src/uci/mock_uci_manager.rs b/src/rust/uwb_core/src/uci/mock_uci_manager.rs
index f511551..249b563 100644
--- a/src/rust/uwb_core/src/uci/mock_uci_manager.rs
+++ b/src/rust/uwb_core/src/uci/mock_uci_manager.rs
@@ -352,12 +352,14 @@
     /// MockUciManager expects call with parameters, returns out as response.
     pub fn expect_raw_uci_cmd(
         &mut self,
+        expected_mt: u32,
         expected_gid: u32,
         expected_oid: u32,
         expected_payload: Vec<u8>,
         out: Result<RawUciMessage>,
     ) {
         self.expected_calls.lock().unwrap().push_back(ExpectedCall::RawUciCmd {
+            expected_mt,
             expected_gid,
             expected_oid,
             expected_payload,
@@ -798,11 +800,25 @@
         }
     }
 
-    async fn raw_uci_cmd(&self, gid: u32, oid: u32, payload: Vec<u8>) -> Result<RawUciMessage> {
+    async fn raw_uci_cmd(
+        &self,
+        mt: u32,
+        gid: u32,
+        oid: u32,
+        payload: Vec<u8>,
+    ) -> Result<RawUciMessage> {
         let mut expected_calls = self.expected_calls.lock().unwrap();
         match expected_calls.pop_front() {
-            Some(ExpectedCall::RawUciCmd { expected_gid, expected_oid, expected_payload, out })
-                if expected_gid == gid && expected_oid == oid && expected_payload == payload =>
+            Some(ExpectedCall::RawUciCmd {
+                expected_mt,
+                expected_gid,
+                expected_oid,
+                expected_payload,
+                out,
+            }) if expected_mt == mt
+                && expected_gid == gid
+                && expected_oid == oid
+                && expected_payload == payload =>
             {
                 self.expect_call_consumed.notify_one();
                 out
@@ -907,6 +923,7 @@
         out: Result<PowerStats>,
     },
     RawUciCmd {
+        expected_mt: u32,
         expected_gid: u32,
         expected_oid: u32,
         expected_payload: Vec<u8>,
diff --git a/src/rust/uwb_core/src/uci/uci_hal.rs b/src/rust/uwb_core/src/uci/uci_hal.rs
index f82d20e..27c593d 100644
--- a/src/rust/uwb_core/src/uci/uci_hal.rs
+++ b/src/rust/uwb_core/src/uci/uci_hal.rs
@@ -18,9 +18,7 @@
 
 use async_trait::async_trait;
 use tokio::sync::mpsc;
-use uwb_uci_packets::{
-    Packet, UciCommandPacket, UciControlPacketHalPacket, UciControlPacketPacket,
-};
+use uwb_uci_packets::{Packet, UciControlPacketHalPacket, UciControlPacketPacket};
 
 use crate::error::Result;
 use crate::params::uci_packets::SessionId;
@@ -59,8 +57,7 @@
         // A UCI command message may consist of multiple UCI packets when the payload is over the
         // maximum packet size. We convert the command into list of UciHalPacket, then send the
         // packets via send_packet().
-        let packet: UciCommandPacket = cmd.try_into()?;
-        let packet: UciControlPacketPacket = packet.into();
+        let packet: UciControlPacketPacket = cmd.try_into()?;
         let fragmented_packets: Vec<UciControlPacketHalPacket> = packet.into();
         for packet in fragmented_packets.into_iter() {
             self.send_packet(packet.to_vec()).await?;
diff --git a/src/rust/uwb_core/src/uci/uci_logger.rs b/src/rust/uwb_core/src/uci/uci_logger.rs
index 51a71de..e3e5630 100644
--- a/src/rust/uwb_core/src/uci/uci_logger.rs
+++ b/src/rust/uwb_core/src/uci/uci_logger.rs
@@ -17,7 +17,7 @@
 
 use uwb_uci_packets::{
     AppConfigTlv, AppConfigTlvType, Packet, SessionCommandChild, SessionGetAppConfigRspBuilder,
-    SessionResponseChild, SessionSetAppConfigCmdBuilder, UciCommandChild, UciCommandPacket,
+    SessionResponseChild, SessionSetAppConfigCmdBuilder, UciCommandChild, UciControlPacketChild,
     UciControlPacketPacket, UciDataPacketPacket, UciResponseChild, UciResponsePacket,
     UCI_PACKET_HAL_HEADER_LEN,
 };
@@ -69,16 +69,19 @@
     tlv
 }
 
-fn filter_uci_command(cmd: UciCommandPacket) -> UciCommandPacket {
+fn filter_uci_command(cmd: UciControlPacketPacket) -> UciControlPacketPacket {
     match cmd.specialize() {
-        UciCommandChild::SessionCommand(session_cmd) => match session_cmd.specialize() {
-            SessionCommandChild::SessionSetAppConfigCmd(set_config_cmd) => {
-                let session_id = set_config_cmd.get_session_id();
-                let tlvs = set_config_cmd.get_tlvs().to_owned();
-                let filtered_tlvs = tlvs.into_iter().map(filter_tlv).collect();
-                SessionSetAppConfigCmdBuilder { session_id, tlvs: filtered_tlvs }.build().into()
-            }
-            _ => session_cmd.into(),
+        UciControlPacketChild::UciCommand(control_cmd) => match control_cmd.specialize() {
+            UciCommandChild::SessionCommand(session_cmd) => match session_cmd.specialize() {
+                SessionCommandChild::SessionSetAppConfigCmd(set_config_cmd) => {
+                    let session_id = set_config_cmd.get_session_id();
+                    let tlvs = set_config_cmd.get_tlvs().to_owned();
+                    let filtered_tlvs = tlvs.into_iter().map(filter_tlv).collect();
+                    SessionSetAppConfigCmdBuilder { session_id, tlvs: filtered_tlvs }.build().into()
+                }
+                _ => session_cmd.into(),
+            },
+            _ => cmd,
         },
         _ => cmd,
     }
@@ -145,13 +148,13 @@
         match self.mode {
             UciLoggerMode::Disabled => (),
             UciLoggerMode::Unfiltered => {
-                if let Ok(packet) = UciCommandPacket::try_from(cmd.clone()) {
-                    self.logger.log_uci_control_packet(packet.into());
+                if let Ok(packet) = UciControlPacketPacket::try_from(cmd.clone()) {
+                    self.logger.log_uci_control_packet(packet);
                 };
             }
             UciLoggerMode::Filtered => {
-                if let Ok(packet) = UciCommandPacket::try_from(cmd.clone()) {
-                    self.logger.log_uci_control_packet(filter_uci_command(packet).into());
+                if let Ok(packet) = UciControlPacketPacket::try_from(cmd.clone()) {
+                    self.logger.log_uci_control_packet(filter_uci_command(packet));
                 };
             }
         }
diff --git a/src/rust/uwb_core/src/uci/uci_manager.rs b/src/rust/uwb_core/src/uci/uci_manager.rs
index 81271b7..7367500 100644
--- a/src/rust/uwb_core/src/uci/uci_manager.rs
+++ b/src/rust/uwb_core/src/uci/uci_manager.rs
@@ -122,7 +122,13 @@
     async fn android_get_power_stats(&self) -> Result<PowerStats>;
 
     // Send a raw uci command.
-    async fn raw_uci_cmd(&self, gid: u32, oid: u32, payload: Vec<u8>) -> Result<RawUciMessage>;
+    async fn raw_uci_cmd(
+        &self,
+        mt: u32,
+        gid: u32,
+        oid: u32,
+        payload: Vec<u8>,
+    ) -> Result<RawUciMessage>;
 }
 
 /// UciManagerImpl is the main implementation of UciManager. Using the actor model, UciManagerImpl
@@ -414,8 +420,14 @@
         }
     }
 
-    async fn raw_uci_cmd(&self, gid: u32, oid: u32, payload: Vec<u8>) -> Result<RawUciMessage> {
-        let cmd = UciCommand::RawUciCmd { gid, oid, payload };
+    async fn raw_uci_cmd(
+        &self,
+        mt: u32,
+        gid: u32,
+        oid: u32,
+        payload: Vec<u8>,
+    ) -> Result<RawUciMessage> {
+        let cmd = UciCommand::RawUciCmd { mt, gid, oid, payload };
         match self.send_cmd(UciManagerCmd::SendUciCommand { cmd }).await {
             Ok(UciResponse::RawUciCmd(resp)) => resp,
             Ok(_) => Err(Error::Unknown),
@@ -628,7 +640,7 @@
 
                 // Remember that this command is a raw UCI command, we'll use this later
                 // to send a raw UCI response.
-                if let UciCommand::RawUciCmd { gid, oid, payload: _ } = cmd.clone() {
+                if let UciCommand::RawUciCmd { mt: _, gid, oid, payload: _ } = cmd.clone() {
                     let gid = GroupId::from_u32(gid);
                     let oid = oid.to_u8();
                     if oid.is_none() || gid.is_none() {
@@ -1494,6 +1506,7 @@
 
     #[tokio::test]
     async fn test_raw_uci_cmd_vendor_gid_ok() {
+        let mt = 0x1;
         let gid = 0xF; // Vendor reserved GID.
         let oid = 0x3;
         let cmd_payload = vec![0x11, 0x22, 0x33, 0x44];
@@ -1503,7 +1516,7 @@
 
         let (uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal(
             move |hal| {
-                let cmd = UciCommand::RawUciCmd { gid, oid, payload: cmd_payload_clone };
+                let cmd = UciCommand::RawUciCmd { mt, gid, oid, payload: cmd_payload_clone };
                 let resp = into_uci_hal_packets(uwb_uci_packets::UciVendor_F_ResponseBuilder {
                     opcode: oid as u8,
                     payload: Some(Bytes::from(resp_payload_clone)),
@@ -1517,13 +1530,14 @@
         .await;
 
         let expected_result = RawUciMessage { gid, oid, payload: resp_payload };
-        let result = uci_manager.raw_uci_cmd(gid, oid, cmd_payload).await.unwrap();
+        let result = uci_manager.raw_uci_cmd(mt, gid, oid, cmd_payload).await.unwrap();
         assert_eq!(result, expected_result);
         assert!(mock_hal.wait_expected_calls_done().await);
     }
 
     #[tokio::test]
     async fn test_raw_uci_cmd_fira_gid_ok() {
+        let mt = 0x1;
         let gid = 0x1; // SESSION_CONFIG GID.
         let oid = 0x3;
         let cmd_payload = vec![0x11, 0x22, 0x33, 0x44];
@@ -1536,7 +1550,7 @@
 
         let (uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal(
             move |hal| {
-                let cmd = UciCommand::RawUciCmd { gid, oid, payload: cmd_payload_clone };
+                let cmd = UciCommand::RawUciCmd { mt, gid, oid, payload: cmd_payload_clone };
                 let resp = into_uci_hal_packets(uwb_uci_packets::SessionSetAppConfigRspBuilder {
                     status,
                     cfg_status,
@@ -1550,7 +1564,41 @@
         .await;
 
         let expected_result = RawUciMessage { gid, oid, payload: resp_payload };
-        let result = uci_manager.raw_uci_cmd(gid, oid, cmd_payload).await.unwrap();
+        let result = uci_manager.raw_uci_cmd(mt, gid, oid, cmd_payload).await.unwrap();
+        assert_eq!(result, expected_result);
+        assert!(mock_hal.wait_expected_calls_done().await);
+    }
+
+    #[tokio::test]
+    async fn test_raw_uci_cmd_mt_testing_ok() {
+        let mt = 0x4;
+        let gid = 0x1; // SESSION_CONFIG GID.
+        let oid = 0x3;
+        let cmd_payload = vec![0x11, 0x22, 0x33, 0x44];
+        let cmd_payload_clone = cmd_payload.clone();
+        let resp_payload = vec![0x00, 0x01, 0x07, 0x00];
+        let status = StatusCode::UciStatusOk;
+        let cfg_id = AppConfigTlvType::DstMacAddress;
+        let app_config = AppConfigStatus { cfg_id, status };
+        let cfg_status = vec![app_config];
+
+        let (uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal(
+            move |hal| {
+                let cmd = UciCommand::RawUciCmd { mt, gid, oid, payload: cmd_payload_clone };
+                let resp = into_uci_hal_packets(uwb_uci_packets::SessionSetAppConfigRspBuilder {
+                    status,
+                    cfg_status,
+                });
+
+                hal.expected_send_command(cmd, resp, Ok(()));
+            },
+            UciLoggerMode::Disabled,
+            mpsc::unbounded_channel::<UciLogEvent>().0,
+        )
+        .await;
+
+        let expected_result = RawUciMessage { gid, oid, payload: resp_payload };
+        let result = uci_manager.raw_uci_cmd(mt, gid, oid, cmd_payload).await.unwrap();
         assert_eq!(result, expected_result);
         assert!(mock_hal.wait_expected_calls_done().await);
     }
@@ -1561,6 +1609,7 @@
         // with SESSION_CONFIG GID.
         // In this case, UciManager should return Error::Unknown.
 
+        let mt = 0x1;
         let gid = 0x0; // CORE GID.
         let oid = 0x1;
         let cmd_payload = vec![0x11, 0x22, 0x33, 0x44];
@@ -1572,7 +1621,7 @@
 
         let (uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal(
             move |hal| {
-                let cmd = UciCommand::RawUciCmd { gid, oid, payload: cmd_payload_clone };
+                let cmd = UciCommand::RawUciCmd { mt, gid, oid, payload: cmd_payload_clone };
                 let resp = into_uci_hal_packets(uwb_uci_packets::SessionSetAppConfigRspBuilder {
                     status,
                     cfg_status,
@@ -1586,7 +1635,7 @@
         .await;
 
         let expected_result = Err(Error::Unknown);
-        let result = uci_manager.raw_uci_cmd(gid, oid, cmd_payload).await;
+        let result = uci_manager.raw_uci_cmd(mt, gid, oid, cmd_payload).await;
         assert_eq!(result, expected_result);
         assert!(mock_hal.wait_expected_calls_done().await);
     }
diff --git a/src/rust/uwb_core/src/uci/uci_manager_sync.rs b/src/rust/uwb_core/src/uci/uci_manager_sync.rs
index c957d37..96c310a 100644
--- a/src/rust/uwb_core/src/uci/uci_manager_sync.rs
+++ b/src/rust/uwb_core/src/uci/uci_manager_sync.rs
@@ -333,8 +333,14 @@
     }
 
     /// Send a raw UCI command.
-    pub fn raw_uci_cmd(&self, gid: u32, oid: u32, payload: Vec<u8>) -> Result<RawUciMessage> {
-        self.runtime_handle.block_on(self.uci_manager.raw_uci_cmd(gid, oid, payload))
+    pub fn raw_uci_cmd(
+        &self,
+        mt: u32,
+        gid: u32,
+        oid: u32,
+        payload: Vec<u8>,
+    ) -> Result<RawUciMessage> {
+        self.runtime_handle.block_on(self.uci_manager.raw_uci_cmd(mt, gid, oid, payload))
     }
 }
 
diff --git a/src/rust/uwb_uci_packets/src/lib.rs b/src/rust/uwb_uci_packets/src/lib.rs
index 049d7da..4a77f1d 100644
--- a/src/rust/uwb_uci_packets/src/lib.rs
+++ b/src/rust/uwb_uci_packets/src/lib.rs
@@ -297,11 +297,28 @@
 
 fn is_uci_control_packet(message_type: MessageType) -> bool {
     match message_type {
-        MessageType::Command | MessageType::Response | MessageType::Notification => true,
+        MessageType::Command
+        | MessageType::Response
+        | MessageType::Notification
+        | MessageType::ReservedForTesting1
+        | MessageType::ReservedForTesting2 => true,
         _ => false,
     }
 }
 
+pub fn build_uci_control_packet(
+    message_type: MessageType,
+    group_id: GroupId,
+    opcode: u8,
+    payload: Option<Bytes>,
+) -> Option<UciControlPacketPacket> {
+    if !is_uci_control_packet(message_type) {
+        error!("Only control packets are allowed, MessageType: {}", message_type);
+        return None;
+    }
+    Some(UciControlPacketBuilder { group_id, message_type, opcode, payload }.build())
+}
+
 // Ensure that the new packet fragment belong to the same packet.
 fn is_same_control_packet(header: &UciControlPacketHeader, packet: &UciPacketHalPacket) -> bool {
     is_uci_control_packet(header.message_type)
diff --git a/src/rust/uwb_uci_packets/uci_packets.pdl b/src/rust/uwb_uci_packets/uci_packets.pdl
index 3e9a39f..6c99fa7 100644
--- a/src/rust/uwb_uci_packets/uci_packets.pdl
+++ b/src/rust/uwb_uci_packets/uci_packets.pdl
@@ -340,6 +340,8 @@
     COMMAND = 0x01,
     RESPONSE = 0x02,
     NOTIFICATION = 0x03,
+    RESERVED_FOR_TESTING_1 = 0x04,
+    RESERVED_FOR_TESTING_2 = 0x05,
 }
 
 // UCI packet description in compliance with the FIRA UCI spec.