1 use log::warn; 2 3 use crate::packets::{AttOpcode, AttView, AttWriteCommandView, Packet}; 4 5 use super::att_database::AttDatabase; 6 7 /// This struct handles all ATT commands. 8 pub struct AttCommandHandler<Db: AttDatabase> { 9 db: Db, 10 } 11 12 impl<Db: AttDatabase> AttCommandHandler<Db> { new(db: Db) -> Self13 pub fn new(db: Db) -> Self { 14 Self { db } 15 } 16 process_packet(&self, packet: AttView<'_>)17 pub fn process_packet(&self, packet: AttView<'_>) { 18 let snapshotted_db = self.db.snapshot(); 19 match packet.get_opcode() { 20 AttOpcode::WRITE_COMMAND => { 21 let Ok(packet) = AttWriteCommandView::try_parse(packet) else { 22 warn!("failed to parse WRITE_COMMAND packet"); 23 return; 24 }; 25 snapshotted_db.write_no_response_attribute( 26 packet.get_handle().into(), 27 &packet.get_value().get_raw_payload().collect::<Vec<_>>(), 28 ); 29 } 30 _ => { 31 warn!("Dropping unsupported opcode {:?}", packet.get_opcode()); 32 } 33 } 34 } 35 } 36 37 #[cfg(test)] 38 mod test { 39 use crate::{ 40 core::uuid::Uuid, 41 gatt::{ 42 ids::AttHandle, 43 server::{ 44 att_database::{AttAttribute, AttDatabase}, 45 command_handler::AttCommandHandler, 46 gatt_database::AttPermissions, 47 test::test_att_db::TestAttDatabase, 48 }, 49 }, 50 packets::{ 51 AttAttributeDataBuilder, AttAttributeDataChild, AttErrorCode, AttErrorResponseBuilder, 52 AttOpcode, AttWriteCommandBuilder, 53 }, 54 utils::{packet::build_att_view_or_crash, task::block_on_locally}, 55 }; 56 57 #[test] test_write_command()58 fn test_write_command() { 59 // arrange 60 let db = TestAttDatabase::new(vec![( 61 AttAttribute { 62 handle: AttHandle(3), 63 type_: Uuid::new(0x1234), 64 permissions: AttPermissions::READABLE | AttPermissions::WRITABLE_WITHOUT_RESPONSE, 65 }, 66 vec![1, 2, 3], 67 )]); 68 let handler = AttCommandHandler { db: db.clone() }; 69 let data = [1, 2]; 70 71 // act: send write command 72 let att_view = build_att_view_or_crash(AttWriteCommandBuilder { 73 handle: AttHandle(3).into(), 74 value: AttAttributeDataBuilder { 75 _child_: AttAttributeDataChild::RawData(data.to_vec().into_boxed_slice()), 76 }, 77 }); 78 handler.process_packet(att_view.view()); 79 80 // assert: the db has been updated 81 assert_eq!(block_on_locally(db.read_attribute(AttHandle(3))).unwrap(), data); 82 } 83 84 #[test] test_unsupported_command()85 fn test_unsupported_command() { 86 // arrange 87 let db = TestAttDatabase::new(vec![]); 88 let handler = AttCommandHandler { db }; 89 90 // act: send a packet that should not be handled here 91 let att_view = build_att_view_or_crash(AttErrorResponseBuilder { 92 opcode_in_error: AttOpcode::EXCHANGE_MTU_REQUEST, 93 handle_in_error: AttHandle(1).into(), 94 error_code: AttErrorCode::UNLIKELY_ERROR, 95 }); 96 handler.process_packet(att_view.view()); 97 98 // assert: nothing happens (we crash if anything is unhandled within a mock) 99 } 100 } 101