xref: /aosp_15_r20/external/pigweed/pw_bluetooth_sapphire/host/testing/fake_gatt_server.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2023 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 
15 #include "pw_bluetooth_sapphire/internal/host/testing/fake_gatt_server.h"
16 
17 #include <lib/fit/function.h>
18 #include <pw_bytes/endian.h>
19 
20 #include "pw_bluetooth_sapphire/internal/host/att/packet.h"
21 #include "pw_bluetooth_sapphire/internal/host/common/assert.h"
22 #include "pw_bluetooth_sapphire/internal/host/common/log.h"
23 #include "pw_bluetooth_sapphire/internal/host/gap/gap.h"
24 #include "pw_bluetooth_sapphire/internal/host/gatt/gatt_defs.h"
25 #include "pw_bluetooth_sapphire/internal/host/testing/fake_controller.h"
26 #include "pw_bluetooth_sapphire/internal/host/testing/fake_peer.h"
27 
28 namespace bt::testing {
29 
FakeGattServer(FakePeer * dev)30 FakeGattServer::FakeGattServer(FakePeer* dev) : dev_(dev) {
31   PW_CHECK(dev_);
32 
33   // Initialize services
34   services_.insert({/*start_handle=*/1,
35                     Service{.start_handle = 1,
36                             .end_handle = 1,
37                             .type = gap::kGenericAccessService}});
38   services_.insert({/*start_handle=*/2,
39                     Service{.start_handle = 2,
40                             .end_handle = 2,
41                             .type = gatt::types::kGenericAttributeService}});
42 }
43 
HandlePdu(hci_spec::ConnectionHandle conn,const ByteBuffer & pdu)44 void FakeGattServer::HandlePdu(hci_spec::ConnectionHandle conn,
45                                const ByteBuffer& pdu) {
46   if (pdu.size() < sizeof(att::OpCode)) {
47     bt_log(WARN, "fake-hci", "malformed ATT packet!");
48     return;
49   }
50 
51   att::OpCode opcode =
52       pw::bytes::ConvertOrderFrom(cpp20::endian::little, pdu.To<att::OpCode>());
53   switch (opcode) {
54     case att::kExchangeMTURequest:
55       // Always reply back with the default ATT_MTU.
56       Send(conn,
57            StaticByteBuffer(att::kExchangeMTUResponse, att::kLEMinMTU, 0x00));
58       break;
59     case att::kReadByGroupTypeRequest:
60       HandleReadByGrpType(conn, pdu.view(sizeof(att::OpCode)));
61       break;
62     case att::kFindByTypeValueRequest:
63       HandleFindByTypeValue(conn, pdu.view(sizeof(att::OpCode)));
64       break;
65     default:
66       SendErrorRsp(conn, opcode, 0, att::ErrorCode::kRequestNotSupported);
67       break;
68   }
69 }
70 
RegisterWithL2cap(FakeL2cap * l2cap_)71 void FakeGattServer::RegisterWithL2cap(FakeL2cap* l2cap_) {
72   auto cb = fit::bind_member<&FakeGattServer::HandlePdu>(this);
73   l2cap_->RegisterHandler(l2cap::kATTChannelId, cb);
74 }
75 
HandleReadByGrpType(hci_spec::ConnectionHandle conn,const ByteBuffer & bytes)76 void FakeGattServer::HandleReadByGrpType(hci_spec::ConnectionHandle conn,
77                                          const ByteBuffer& bytes) {
78   // Don't support 128-bit group types.
79   if (bytes.size() != sizeof(att::ReadByGroupTypeRequestParams16)) {
80     SendErrorRsp(
81         conn, att::kReadByGroupTypeRequest, 0, att::ErrorCode::kInvalidPDU);
82     return;
83   }
84 
85   const auto& params = bytes.To<att::ReadByGroupTypeRequestParams16>();
86   att::Handle start =
87       pw::bytes::ConvertOrderFrom(cpp20::endian::little, params.start_handle);
88   att::Handle end =
89       pw::bytes::ConvertOrderFrom(cpp20::endian::little, params.end_handle);
90   if (!start || end < start) {
91     SendErrorRsp(conn,
92                  att::kReadByGroupTypeRequest,
93                  start,
94                  att::ErrorCode::kInvalidHandle);
95     return;
96   }
97 
98   // Only support primary service discovery.
99   uint16_t grp_type =
100       pw::bytes::ConvertOrderFrom(cpp20::endian::little, params.type);
101   if (grp_type != gatt::types::kPrimaryService16 || start > att::kHandleMin) {
102     SendErrorRsp(conn,
103                  att::kReadByGroupTypeRequest,
104                  start,
105                  att::ErrorCode::kAttributeNotFound);
106     return;
107   }
108 
109   // We report back the standard services.
110   // TODO(armansito): Support standard characteristics and more services.
111   constexpr uint8_t entry_size =
112       (sizeof(att::AttributeGroupDataEntry) + sizeof(att::AttributeType16));
113   DynamicByteBuffer rsp(sizeof(att::OpCode) +
114                         sizeof(att::ReadByGroupTypeResponseParams) +
115                         services_.size() * entry_size);
116   att::PacketWriter rsp_writer(att::kReadByGroupTypeResponse, &rsp);
117   auto rsp_params =
118       rsp_writer.mutable_payload<att::ReadByGroupTypeResponseParams>();
119   rsp_params->length = entry_size;
120   MutableBufferView next_entry = rsp_writer.mutable_payload_data().mutable_view(
121       sizeof(att::ReadByGroupTypeResponseParams));
122 
123   for (auto& [_, service] : services_) {
124     // FakeGattServer only supports 16bit UUIDs currently.
125     PW_CHECK(service.type.CompactSize(/*allow_32bit=*/false) ==
126              UUIDElemSize::k16Bit);
127     att::AttributeGroupDataEntry* entry =
128         next_entry.AsMutable<att::AttributeGroupDataEntry>();
129     entry->start_handle =
130         pw::bytes::ConvertOrderTo(cpp20::endian::little, service.start_handle);
131     entry->group_end_handle =
132         pw::bytes::ConvertOrderTo(cpp20::endian::little, service.end_handle);
133     next_entry.Write(service.type.CompactView(/*allow_32bit=*/false),
134                      sizeof(att::AttributeGroupDataEntry));
135     next_entry = next_entry.mutable_view(entry_size);
136   }
137 
138   Send(conn, rsp);
139 }
140 
HandleFindByTypeValue(hci_spec::ConnectionHandle conn,const ByteBuffer & bytes)141 void FakeGattServer::HandleFindByTypeValue(hci_spec::ConnectionHandle conn,
142                                            const ByteBuffer& bytes) {
143   if (bytes.size() < sizeof(att::FindByTypeValueRequestParams)) {
144     bt_log(WARN, "fake-gatt", "find by type value request buffer too small");
145     SendErrorRsp(
146         conn, att::kFindByTypeValueRequest, 0, att::ErrorCode::kInvalidPDU);
147     return;
148   }
149 
150   // It is safe to read members because bytes is at least the size of the
151   // params, and the params struct is packed.
152   att::Handle start = pw::bytes::ConvertOrderFrom(
153       cpp20::endian::little,
154       bytes.ReadMember<&att::FindByTypeValueRequestParams::start_handle>());
155   att::Handle end = pw::bytes::ConvertOrderFrom(
156       cpp20::endian::little,
157       bytes.ReadMember<&att::FindByTypeValueRequestParams::end_handle>());
158   att::AttributeType16 service_kind = pw::bytes::ConvertOrderFrom(
159       cpp20::endian::little,
160       bytes.ReadMember<&att::FindByTypeValueRequestParams::type>());
161   BufferView service_uuid_bytes =
162       bytes.view(sizeof(att::FindByTypeValueRequestParams));
163   UUID service_uuid;
164   if (!UUID::FromBytes(service_uuid_bytes, &service_uuid)) {
165     bt_log(WARN,
166            "fake-gatt",
167            "find by type value request has invalid service UUID");
168     SendErrorRsp(
169         conn, att::kFindByTypeValueRequest, 0, att::ErrorCode::kInvalidPDU);
170     return;
171   }
172 
173   // Support only primary service discovery by service UUID. Support only a
174   // single request/response per UUID (additional requests return
175   // kAttributeNotFound, ending the procedure).
176   if (service_kind != gatt::types::kPrimaryService16 ||
177       start > att::kHandleMin || end < att::kHandleMax) {
178     SendErrorRsp(conn,
179                  att::kFindByTypeValueRequest,
180                  start,
181                  att::ErrorCode::kAttributeNotFound);
182     return;
183   }
184 
185   // Send a response with the first service with a matching UUID.
186   auto entry =
187       std::find_if(services_.begin(), services_.end(), [&](auto entry) {
188         return entry.second.type == service_uuid;
189       });
190   if (entry == services_.end()) {
191     bt_log(WARN,
192            "fake-gatt",
193            "attempt to discover unsupported service UUID (uuid: %s)",
194            bt_str(service_uuid));
195     SendErrorRsp(conn,
196                  att::kFindByTypeValueRequest,
197                  start,
198                  att::ErrorCode::kAttributeNotFound);
199     return;
200   }
201 
202   Service service = entry->second;
203   StaticByteBuffer<sizeof(att::OpCode) +
204                    sizeof(att::FindByTypeValueResponseParams)>
205       rsp;
206   att::PacketWriter writer(att::kFindByTypeValueResponse, &rsp);
207   att::FindByTypeValueResponseParams* rsp_params =
208       writer.mutable_payload<att::FindByTypeValueResponseParams>();
209   rsp_params->handles_information_list[0].handle =
210       pw::bytes::ConvertOrderTo(cpp20::endian::little, service.start_handle);
211   rsp_params->handles_information_list[0].group_end_handle =
212       pw::bytes::ConvertOrderTo(cpp20::endian::little, service.end_handle);
213   Send(conn, rsp);
214 }
215 
Send(hci_spec::ConnectionHandle conn,const ByteBuffer & pdu)216 void FakeGattServer::Send(hci_spec::ConnectionHandle conn,
217                           const ByteBuffer& pdu) {
218   if (dev_->controller()) {
219     dev_->controller()->SendL2CAPBFrame(conn, l2cap::kATTChannelId, pdu);
220   } else {
221     bt_log(WARN, "fake-hci", "no assigned FakeController!");
222   }
223 }
224 
SendErrorRsp(hci_spec::ConnectionHandle conn,att::OpCode opcode,att::Handle handle,att::ErrorCode ecode)225 void FakeGattServer::SendErrorRsp(hci_spec::ConnectionHandle conn,
226                                   att::OpCode opcode,
227                                   att::Handle handle,
228                                   att::ErrorCode ecode) {
229   StaticByteBuffer<sizeof(att::ErrorResponseParams) + sizeof(att::OpCode)>
230       buffer;
231   att::PacketWriter writer(att::kErrorResponse, &buffer);
232   auto* params = writer.mutable_payload<att::ErrorResponseParams>();
233   params->request_opcode =
234       pw::bytes::ConvertOrderTo(cpp20::endian::little, opcode);
235   params->attribute_handle =
236       pw::bytes::ConvertOrderTo(cpp20::endian::little, handle);
237   params->error_code = ecode;
238 
239   Send(conn, buffer);
240 }
241 
242 }  // namespace bt::testing
243