• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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