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/gatt/fake_layer.h"
16
17 #include "pw_bluetooth_sapphire/internal/host/gatt/remote_service.h"
18
19 namespace bt::gatt::testing {
20
TestPeer(pw::async::Dispatcher & pw_dispatcher)21 FakeLayer::TestPeer::TestPeer(pw::async::Dispatcher& pw_dispatcher)
22 : fake_client(pw_dispatcher) {}
23
24 std::pair<RemoteService::WeakPtr, FakeClient::WeakPtr>
AddPeerService(PeerId peer_id,const ServiceData & info,bool notify)25 FakeLayer::AddPeerService(PeerId peer_id,
26 const ServiceData& info,
27 bool notify) {
28 auto [iter, _] = peers_.try_emplace(peer_id, pw_dispatcher_);
29 auto& peer = iter->second;
30
31 BT_ASSERT(info.range_start <= info.range_end);
32 auto service =
33 std::make_unique<RemoteService>(info, peer.fake_client.GetWeakPtr());
34 RemoteService::WeakPtr service_weak = service->GetWeakPtr();
35
36 std::vector<att::Handle> removed;
37 ServiceList added;
38 ServiceList modified;
39
40 auto svc_iter = peer.services.find(info.range_start);
41 if (svc_iter != peer.services.end()) {
42 if (svc_iter->second->uuid() == info.type) {
43 modified.push_back(service_weak);
44 } else {
45 removed.push_back(svc_iter->second->handle());
46 added.push_back(service_weak);
47 }
48
49 svc_iter->second->set_service_changed(true);
50 peer.services.erase(svc_iter);
51 } else {
52 added.push_back(service_weak);
53 }
54
55 bt_log(DEBUG,
56 "gatt",
57 "services changed (removed: %zu, added: %zu, modified: %zu)",
58 removed.size(),
59 added.size(),
60 modified.size());
61
62 peer.services.emplace(info.range_start, std::move(service));
63
64 if (notify && remote_service_watchers_.count(peer_id)) {
65 remote_service_watchers_[peer_id](removed, added, modified);
66 }
67
68 return {service_weak, peer.fake_client.AsFakeWeakPtr()};
69 }
70
RemovePeerService(PeerId peer_id,att::Handle handle)71 void FakeLayer::RemovePeerService(PeerId peer_id, att::Handle handle) {
72 auto peer_iter = peers_.find(peer_id);
73 if (peer_iter == peers_.end()) {
74 return;
75 }
76 auto svc_iter = peer_iter->second.services.find(handle);
77 if (svc_iter == peer_iter->second.services.end()) {
78 return;
79 }
80 svc_iter->second->set_service_changed(true);
81 peer_iter->second.services.erase(svc_iter);
82
83 if (remote_service_watchers_.count(peer_id)) {
84 remote_service_watchers_[peer_id](
85 /*removed=*/{handle}, /*added=*/{}, /*modified=*/{});
86 }
87 }
88
AddConnection(PeerId peer_id,std::unique_ptr<Client> client,Server::FactoryFunction server_factory)89 void FakeLayer::AddConnection(PeerId peer_id,
90 std::unique_ptr<Client> client,
91 Server::FactoryFunction server_factory) {
92 peers_.try_emplace(peer_id, pw_dispatcher_);
93 }
94
RemoveConnection(PeerId peer_id)95 void FakeLayer::RemoveConnection(PeerId peer_id) { peers_.erase(peer_id); }
96
RegisterPeerMtuListener(PeerMtuListener listener)97 GATT::PeerMtuListenerId FakeLayer::RegisterPeerMtuListener(
98 PeerMtuListener listener) {
99 BT_PANIC("implement fake behavior if needed");
100 }
101
UnregisterPeerMtuListener(PeerMtuListenerId listener_id)102 bool FakeLayer::UnregisterPeerMtuListener(PeerMtuListenerId listener_id) {
103 BT_PANIC("implement fake behavior if needed");
104 }
105
RegisterService(ServicePtr service,ServiceIdCallback callback,ReadHandler read_handler,WriteHandler write_handler,ClientConfigCallback ccc_callback)106 void FakeLayer::RegisterService(ServicePtr service,
107 ServiceIdCallback callback,
108 ReadHandler read_handler,
109 WriteHandler write_handler,
110 ClientConfigCallback ccc_callback) {
111 if (register_service_fails_) {
112 callback(kInvalidId);
113 return;
114 }
115
116 IdType id = next_local_service_id_++;
117 local_services_.try_emplace(id,
118 LocalService{std::move(service),
119 std::move(read_handler),
120 std::move(write_handler),
121 std::move(ccc_callback),
122 {}});
123 callback(id);
124 }
125
UnregisterService(IdType service_id)126 void FakeLayer::UnregisterService(IdType service_id) {
127 local_services_.erase(service_id);
128 }
129
SendUpdate(IdType service_id,IdType chrc_id,PeerId peer_id,::std::vector<uint8_t> value,IndicationCallback indicate_cb)130 void FakeLayer::SendUpdate(IdType service_id,
131 IdType chrc_id,
132 PeerId peer_id,
133 ::std::vector<uint8_t> value,
134 IndicationCallback indicate_cb) {
135 auto iter = local_services_.find(service_id);
136 if (iter == local_services_.end()) {
137 indicate_cb(fit::error(att::ErrorCode::kInvalidHandle));
138 return;
139 }
140 iter->second.updates.push_back(
141 Update{chrc_id, std::move(value), std::move(indicate_cb), peer_id});
142 }
143
UpdateConnectedPeers(IdType service_id,IdType chrc_id,::std::vector<uint8_t> value,IndicationCallback indicate_cb)144 void FakeLayer::UpdateConnectedPeers(IdType service_id,
145 IdType chrc_id,
146 ::std::vector<uint8_t> value,
147 IndicationCallback indicate_cb) {
148 auto iter = local_services_.find(service_id);
149 if (iter == local_services_.end()) {
150 indicate_cb(fit::error(att::ErrorCode::kInvalidHandle));
151 return;
152 }
153 iter->second.updates.push_back(
154 Update{chrc_id, std::move(value), std::move(indicate_cb), std::nullopt});
155 }
156
SetPersistServiceChangedCCCCallback(PersistServiceChangedCCCCallback callback)157 void FakeLayer::SetPersistServiceChangedCCCCallback(
158 PersistServiceChangedCCCCallback callback) {
159 if (set_persist_service_changed_ccc_cb_cb_) {
160 set_persist_service_changed_ccc_cb_cb_();
161 }
162 persist_service_changed_ccc_cb_ = std::move(callback);
163 }
164
SetRetrieveServiceChangedCCCCallback(RetrieveServiceChangedCCCCallback callback)165 void FakeLayer::SetRetrieveServiceChangedCCCCallback(
166 RetrieveServiceChangedCCCCallback callback) {
167 if (set_retrieve_service_changed_ccc_cb_cb_) {
168 set_retrieve_service_changed_ccc_cb_cb_();
169 }
170 retrieve_service_changed_ccc_cb_ = std::move(callback);
171 }
172
InitializeClient(PeerId peer_id,std::vector<UUID> services_to_discover)173 void FakeLayer::InitializeClient(PeerId peer_id,
174 std::vector<UUID> services_to_discover) {
175 std::vector<UUID> uuids = std::move(services_to_discover);
176 if (initialize_client_cb_) {
177 initialize_client_cb_(peer_id, uuids);
178 }
179
180 auto iter = peers_.find(peer_id);
181 if (iter == peers_.end()) {
182 return;
183 }
184
185 std::vector<RemoteService::WeakPtr> added;
186 if (uuids.empty()) {
187 for (auto& svc_pair : iter->second.services) {
188 added.push_back(svc_pair.second->GetWeakPtr());
189 }
190 } else {
191 for (auto& svc_pair : iter->second.services) {
192 auto uuid_iter =
193 std::find_if(uuids.begin(), uuids.end(), [&svc_pair](auto uuid) {
194 return svc_pair.second->uuid() == uuid;
195 });
196 if (uuid_iter != uuids.end()) {
197 added.push_back(svc_pair.second->GetWeakPtr());
198 }
199 }
200 }
201
202 if (remote_service_watchers_.count(peer_id)) {
203 remote_service_watchers_[peer_id](
204 /*removed=*/{}, /*added=*/added, /*modified=*/{});
205 }
206 }
207
RegisterRemoteServiceWatcherForPeer(PeerId peer_id,RemoteServiceWatcher watcher)208 GATT::RemoteServiceWatcherId FakeLayer::RegisterRemoteServiceWatcherForPeer(
209 PeerId peer_id, RemoteServiceWatcher watcher) {
210 BT_ASSERT(remote_service_watchers_.count(peer_id) == 0);
211 remote_service_watchers_[peer_id] = std::move(watcher);
212 // Use the PeerId as the watcher ID because FakeLayer only needs to support 1
213 // watcher per peer.
214 return peer_id.value();
215 }
UnregisterRemoteServiceWatcher(RemoteServiceWatcherId watcher_id)216 bool FakeLayer::UnregisterRemoteServiceWatcher(
217 RemoteServiceWatcherId watcher_id) {
218 bool result = remote_service_watchers_.count(PeerId(watcher_id));
219 remote_service_watchers_.erase(PeerId(watcher_id));
220 return result;
221 }
222
ListServices(PeerId peer_id,std::vector<UUID> uuids,ServiceListCallback callback)223 void FakeLayer::ListServices(PeerId peer_id,
224 std::vector<UUID> uuids,
225 ServiceListCallback callback) {
226 if (pause_list_services_) {
227 return;
228 }
229
230 ServiceList services;
231
232 auto iter = peers_.find(peer_id);
233 if (iter != peers_.end()) {
234 for (auto& svc_pair : iter->second.services) {
235 auto pred = [&](const UUID& uuid) {
236 return svc_pair.second->uuid() == uuid;
237 };
238 if (uuids.empty() ||
239 std::find_if(uuids.begin(), uuids.end(), pred) != uuids.end()) {
240 services.push_back(svc_pair.second->GetWeakPtr());
241 }
242 }
243 }
244
245 callback(list_services_status_, std::move(services));
246 }
247
FindService(PeerId peer_id,IdType service_id)248 RemoteService::WeakPtr FakeLayer::FindService(PeerId peer_id,
249 IdType service_id) {
250 auto peer_iter = peers_.find(peer_id);
251 if (peer_iter == peers_.end()) {
252 return RemoteService::WeakPtr();
253 }
254 auto svc_iter = peer_iter->second.services.find(service_id);
255 if (svc_iter == peer_iter->second.services.end()) {
256 return RemoteService::WeakPtr();
257 }
258 return svc_iter->second->GetWeakPtr();
259 }
260
SetInitializeClientCallback(InitializeClientCallback cb)261 void FakeLayer::SetInitializeClientCallback(InitializeClientCallback cb) {
262 initialize_client_cb_ = std::move(cb);
263 }
264
set_list_services_status(att::Result<> status)265 void FakeLayer::set_list_services_status(att::Result<> status) {
266 list_services_status_ = status;
267 }
268
SetSetPersistServiceChangedCCCCallbackCallback(SetPersistServiceChangedCCCCallbackCallback cb)269 void FakeLayer::SetSetPersistServiceChangedCCCCallbackCallback(
270 SetPersistServiceChangedCCCCallbackCallback cb) {
271 set_persist_service_changed_ccc_cb_cb_ = std::move(cb);
272 }
273
SetSetRetrieveServiceChangedCCCCallbackCallback(SetRetrieveServiceChangedCCCCallbackCallback cb)274 void FakeLayer::SetSetRetrieveServiceChangedCCCCallbackCallback(
275 SetRetrieveServiceChangedCCCCallbackCallback cb) {
276 set_retrieve_service_changed_ccc_cb_cb_ = std::move(cb);
277 }
278
CallPersistServiceChangedCCCCallback(PeerId peer_id,bool notify,bool indicate)279 void FakeLayer::CallPersistServiceChangedCCCCallback(PeerId peer_id,
280 bool notify,
281 bool indicate) {
282 persist_service_changed_ccc_cb_(peer_id,
283 {.notify = notify, .indicate = indicate});
284 }
285
286 std::optional<ServiceChangedCCCPersistedData>
CallRetrieveServiceChangedCCCCallback(PeerId peer_id)287 FakeLayer::CallRetrieveServiceChangedCCCCallback(PeerId peer_id) {
288 return retrieve_service_changed_ccc_cb_(peer_id);
289 }
290
291 } // namespace bt::gatt::testing
292