• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <endian.h>
18 #include <lib/fit/function.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   BT_ASSERT(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 = le16toh(pdu.To<att::OpCode>());
52   switch (opcode) {
53     case att::kExchangeMTURequest:
54       // Always reply back with the default ATT_MTU.
55       Send(conn,
56            StaticByteBuffer(att::kExchangeMTUResponse, att::kLEMinMTU, 0x00));
57       break;
58     case att::kReadByGroupTypeRequest:
59       HandleReadByGrpType(conn, pdu.view(sizeof(att::OpCode)));
60       break;
61     case att::kFindByTypeValueRequest:
62       HandleFindByTypeValue(conn, pdu.view(sizeof(att::OpCode)));
63       break;
64     default:
65       SendErrorRsp(conn, opcode, 0, att::ErrorCode::kRequestNotSupported);
66       break;
67   }
68 }
69 
RegisterWithL2cap(FakeL2cap * l2cap_)70 void FakeGattServer::RegisterWithL2cap(FakeL2cap* l2cap_) {
71   auto cb = fit::bind_member<&FakeGattServer::HandlePdu>(this);
72   l2cap_->RegisterHandler(l2cap::kATTChannelId, cb);
73 }
74 
HandleReadByGrpType(hci_spec::ConnectionHandle conn,const ByteBuffer & bytes)75 void FakeGattServer::HandleReadByGrpType(hci_spec::ConnectionHandle conn,
76                                          const ByteBuffer& bytes) {
77   // Don't support 128-bit group types.
78   if (bytes.size() != sizeof(att::ReadByGroupTypeRequestParams16)) {
79     SendErrorRsp(
80         conn, att::kReadByGroupTypeRequest, 0, att::ErrorCode::kInvalidPDU);
81     return;
82   }
83 
84   const auto& params = bytes.To<att::ReadByGroupTypeRequestParams16>();
85   att::Handle start = le16toh(params.start_handle);
86   att::Handle end = le16toh(params.end_handle);
87   if (!start || end < start) {
88     SendErrorRsp(conn,
89                  att::kReadByGroupTypeRequest,
90                  start,
91                  att::ErrorCode::kInvalidHandle);
92     return;
93   }
94 
95   // Only support primary service discovery.
96   uint16_t grp_type = le16toh(params.type);
97   if (grp_type != gatt::types::kPrimaryService16 || start > att::kHandleMin) {
98     SendErrorRsp(conn,
99                  att::kReadByGroupTypeRequest,
100                  start,
101                  att::ErrorCode::kAttributeNotFound);
102     return;
103   }
104 
105   // We report back the standard services.
106   // TODO(armansito): Support standard characteristics and more services.
107   constexpr uint8_t entry_size =
108       (sizeof(att::AttributeGroupDataEntry) + sizeof(att::AttributeType16));
109   DynamicByteBuffer rsp(sizeof(att::OpCode) +
110                         sizeof(att::ReadByGroupTypeResponseParams) +
111                         services_.size() * entry_size);
112   att::PacketWriter rsp_writer(att::kReadByGroupTypeResponse, &rsp);
113   auto rsp_params =
114       rsp_writer.mutable_payload<att::ReadByGroupTypeResponseParams>();
115   rsp_params->length = entry_size;
116   MutableBufferView next_entry = rsp_writer.mutable_payload_data().mutable_view(
117       sizeof(att::ReadByGroupTypeResponseParams));
118 
119   for (auto& [_, service] : services_) {
120     // FakeGattServer only supports 16bit UUIDs currently.
121     BT_ASSERT(service.type.CompactSize(/*allow_32bit=*/false) ==
122               UUIDElemSize::k16Bit);
123     att::AttributeGroupDataEntry* entry =
124         next_entry.AsMutable<att::AttributeGroupDataEntry>();
125     entry->start_handle = htole16(service.start_handle);
126     entry->group_end_handle = htole16(service.end_handle);
127     next_entry.Write(service.type.CompactView(/*allow_32bit=*/false),
128                      sizeof(att::AttributeGroupDataEntry));
129     next_entry = next_entry.mutable_view(entry_size);
130   }
131 
132   Send(conn, rsp);
133 }
134 
HandleFindByTypeValue(hci_spec::ConnectionHandle conn,const ByteBuffer & bytes)135 void FakeGattServer::HandleFindByTypeValue(hci_spec::ConnectionHandle conn,
136                                            const ByteBuffer& bytes) {
137   if (bytes.size() < sizeof(att::FindByTypeValueRequestParams)) {
138     bt_log(WARN, "fake-gatt", "find by type value request buffer too small");
139     SendErrorRsp(
140         conn, att::kFindByTypeValueRequest, 0, att::ErrorCode::kInvalidPDU);
141     return;
142   }
143 
144   // It is safe to read members because bytes is at least the size of the
145   // params, and the params struct is packed.
146   att::Handle start = le16toh(
147       bytes.ReadMember<&att::FindByTypeValueRequestParams::start_handle>());
148   att::Handle end = le16toh(
149       bytes.ReadMember<&att::FindByTypeValueRequestParams::end_handle>());
150   att::AttributeType16 service_kind =
151       le16toh(bytes.ReadMember<&att::FindByTypeValueRequestParams::type>());
152   BufferView service_uuid_bytes =
153       bytes.view(sizeof(att::FindByTypeValueRequestParams));
154   UUID service_uuid;
155   if (!UUID::FromBytes(service_uuid_bytes, &service_uuid)) {
156     bt_log(WARN,
157            "fake-gatt",
158            "find by type value request has invalid service UUID");
159     SendErrorRsp(
160         conn, att::kFindByTypeValueRequest, 0, att::ErrorCode::kInvalidPDU);
161     return;
162   }
163 
164   // Support only primary service discovery by service UUID. Support only a
165   // single request/response per UUID (additional requests return
166   // kAttributeNotFound, ending the procedure).
167   if (service_kind != gatt::types::kPrimaryService16 ||
168       start > att::kHandleMin || end < att::kHandleMax) {
169     SendErrorRsp(conn,
170                  att::kFindByTypeValueRequest,
171                  start,
172                  att::ErrorCode::kAttributeNotFound);
173     return;
174   }
175 
176   // Send a response with the first service with a matching UUID.
177   auto entry =
178       std::find_if(services_.begin(), services_.end(), [&](auto entry) {
179         return entry.second.type == service_uuid;
180       });
181   if (entry == services_.end()) {
182     bt_log(WARN,
183            "fake-gatt",
184            "attempt to discover unsupported service UUID (uuid: %s)",
185            bt_str(service_uuid));
186     SendErrorRsp(conn,
187                  att::kFindByTypeValueRequest,
188                  start,
189                  att::ErrorCode::kAttributeNotFound);
190     return;
191   }
192 
193   Service service = entry->second;
194   StaticByteBuffer<sizeof(att::OpCode) +
195                    sizeof(att::FindByTypeValueResponseParams)>
196       rsp;
197   att::PacketWriter writer(att::kFindByTypeValueResponse, &rsp);
198   att::FindByTypeValueResponseParams* rsp_params =
199       writer.mutable_payload<att::FindByTypeValueResponseParams>();
200   rsp_params->handles_information_list[0].handle =
201       htole16(service.start_handle);
202   rsp_params->handles_information_list[0].group_end_handle =
203       htole16(service.end_handle);
204   Send(conn, rsp);
205 }
206 
Send(hci_spec::ConnectionHandle conn,const ByteBuffer & pdu)207 void FakeGattServer::Send(hci_spec::ConnectionHandle conn,
208                           const ByteBuffer& pdu) {
209   if (dev_->controller()) {
210     dev_->controller()->SendL2CAPBFrame(conn, l2cap::kATTChannelId, pdu);
211   } else {
212     bt_log(WARN, "fake-hci", "no assigned FakeController!");
213   }
214 }
215 
SendErrorRsp(hci_spec::ConnectionHandle conn,att::OpCode opcode,att::Handle handle,att::ErrorCode ecode)216 void FakeGattServer::SendErrorRsp(hci_spec::ConnectionHandle conn,
217                                   att::OpCode opcode,
218                                   att::Handle handle,
219                                   att::ErrorCode ecode) {
220   StaticByteBuffer<sizeof(att::ErrorResponseParams) + sizeof(att::OpCode)>
221       buffer;
222   att::PacketWriter writer(att::kErrorResponse, &buffer);
223   auto* params = writer.mutable_payload<att::ErrorResponseParams>();
224   params->request_opcode = htole16(opcode);
225   params->attribute_handle = htole16(handle);
226   params->error_code = ecode;
227 
228   Send(conn, buffer);
229 }
230 
231 }  // namespace bt::testing
232