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