• 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/sdp/service_discoverer.h"
16 
17 #include <pw_assert/check.h>
18 
19 #include <cinttypes>
20 #include <functional>
21 
22 namespace bt::sdp {
23 
ServiceDiscoverer()24 ServiceDiscoverer::ServiceDiscoverer() : next_id_(1) {}
25 
AddSearch(const UUID & uuid,std::unordered_set<AttributeId> attributes,ResultCallback callback)26 ServiceDiscoverer::SearchId ServiceDiscoverer::AddSearch(
27     const UUID& uuid,
28     std::unordered_set<AttributeId> attributes,
29     ResultCallback callback) {
30   Search s;
31   s.uuid = uuid;
32   s.attributes = std::move(attributes);
33   s.callback = std::move(callback);
34   PW_DCHECK(next_id_ < std::numeric_limits<ServiceDiscoverer::SearchId>::max());
35   ServiceDiscoverer::SearchId id = next_id_++;
36   auto [it, placed] = searches_.emplace(id, std::move(s));
37   PW_DCHECK(placed, "Should always be able to place new search");
38   return id;
39 }
40 
RemoveSearch(SearchId id)41 bool ServiceDiscoverer::RemoveSearch(SearchId id) {
42   auto it = sessions_.begin();
43   while (it != sessions_.end()) {
44     if (it->second.active.erase(id) && it->second.active.empty()) {
45       it = sessions_.erase(it);
46     } else {
47       it++;
48     }
49   }
50   return searches_.erase(id);
51 }
52 
SingleSearch(SearchId search_id,PeerId peer_id,std::unique_ptr<Client> client)53 void ServiceDiscoverer::SingleSearch(SearchId search_id,
54                                      PeerId peer_id,
55                                      std::unique_ptr<Client> client) {
56   auto session_iter = sessions_.find(peer_id);
57   if (session_iter == sessions_.end()) {
58     if (client == nullptr) {
59       // Can't do a search if we don't have an open channel
60       bt_log(WARN,
61              "sdp",
62              "Can't start a new session without a channel (peer_id %s)",
63              bt_str(peer_id));
64       return;
65     }
66     // Setup the session.
67     DiscoverySession session;
68     session.client = std::move(client);
69     auto placed = sessions_.emplace(peer_id, std::move(session));
70     PW_DCHECK(placed.second);
71     session_iter = placed.first;
72   }
73   PW_DCHECK(session_iter != sessions_.end());
74   auto search_it = searches_.find(search_id);
75   if (search_it == searches_.end()) {
76     bt_log(INFO, "sdp", "Couldn't find search with id %" PRIu64, search_id);
77     return;
78   }
79   Search& search = search_it->second;
80   Client::SearchResultFunction result_cb =
81       [this, peer_id, search_id](
82           fit::result<
83               Error<>,
84               std::reference_wrapper<const std::map<AttributeId, DataElement>>>
85               attributes_result) {
86         auto it = searches_.find(search_id);
87         if (it == searches_.end() || attributes_result.is_error()) {
88           FinishPeerSearch(peer_id, search_id);
89           return false;
90         }
91         it->second.callback(peer_id, attributes_result.value());
92         return true;
93       };
94   session_iter->second.active.emplace(search_id);
95   session_iter->second.client->ServiceSearchAttributes(
96       {search.uuid}, search.attributes, std::move(result_cb));
97 }
98 
StartServiceDiscovery(PeerId peer_id,std::unique_ptr<Client> client)99 bool ServiceDiscoverer::StartServiceDiscovery(PeerId peer_id,
100                                               std::unique_ptr<Client> client) {
101   // If discovery is already happening on this peer, then we can't start it
102   // again.
103   if (sessions_.count(peer_id)) {
104     return false;
105   }
106   // If there aren't any searches to do, we're done.
107   if (searches_.empty()) {
108     return true;
109   }
110   for (auto& [search_id, _] : searches_) {
111     SingleSearch(search_id, peer_id, std::move(client));
112     client = nullptr;
113   }
114   return true;
115 }
116 
search_count() const117 size_t ServiceDiscoverer::search_count() const { return searches_.size(); }
118 
FinishPeerSearch(PeerId peer_id,SearchId search_id)119 void ServiceDiscoverer::FinishPeerSearch(PeerId peer_id, SearchId search_id) {
120   auto it = sessions_.find(peer_id);
121   if (it == sessions_.end()) {
122     bt_log(INFO,
123            "sdp",
124            "Couldn't find session to finish search for peer %s",
125            bt_str(peer_id));
126     return;
127   }
128   if (it->second.active.erase(search_id) && it->second.active.empty()) {
129     // This peer search is over.
130     sessions_.erase(it);
131   }
132 }
133 
134 }  // namespace bt::sdp
135