• 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/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