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