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 26 .write_no_response_attribute(packet.get_handle().into(), packet.get_value()); 27 } 28 _ => { 29 warn!("Dropping unsupported opcode {:?}", packet.get_opcode()); 30 } 31 } 32 } 33 } 34 35 #[cfg(test)] 36 mod test { 37 use crate::{ 38 core::uuid::Uuid, 39 gatt::{ 40 ids::AttHandle, 41 server::{ 42 att_database::{AttAttribute, AttDatabase}, 43 command_handler::AttCommandHandler, 44 gatt_database::AttPermissions, 45 test::test_att_db::TestAttDatabase, 46 }, 47 }, 48 packets::{ 49 AttAttributeDataChild, AttErrorCode, AttErrorResponseBuilder, AttOpcode, 50 AttWriteCommandBuilder, 51 }, 52 utils::{ 53 packet::{build_att_data, build_att_view_or_crash}, 54 task::block_on_locally, 55 }, 56 }; 57 58 #[test] test_write_command()59 fn test_write_command() { 60 // arrange 61 let db = TestAttDatabase::new(vec![( 62 AttAttribute { 63 handle: AttHandle(3), 64 type_: Uuid::new(0x1234), 65 permissions: AttPermissions::READABLE | AttPermissions::WRITABLE_WITHOUT_RESPONSE, 66 }, 67 vec![1, 2, 3], 68 )]); 69 let handler = AttCommandHandler { db: db.clone() }; 70 let data = AttAttributeDataChild::RawData([1, 2].into()); 71 72 // act: send write command 73 let att_view = build_att_view_or_crash(AttWriteCommandBuilder { 74 handle: AttHandle(3).into(), 75 value: build_att_data(data.clone()), 76 }); 77 handler.process_packet(att_view.view()); 78 79 // assert: the db has been updated 80 assert_eq!(block_on_locally(db.read_attribute(AttHandle(3))).unwrap(), data); 81 } 82 83 #[test] test_unsupported_command()84 fn test_unsupported_command() { 85 // arrange 86 let db = TestAttDatabase::new(vec![]); 87 let handler = AttCommandHandler { db }; 88 89 // act: send a packet that should not be handled here 90 let att_view = build_att_view_or_crash(AttErrorResponseBuilder { 91 opcode_in_error: AttOpcode::EXCHANGE_MTU_REQUEST, 92 handle_in_error: AttHandle(1).into(), 93 error_code: AttErrorCode::UNLIKELY_ERROR, 94 }); 95 handler.process_packet(att_view.view()); 96 97 // assert: nothing happens (we crash if anything is unhandled within a mock) 98 } 99 } 100