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 #pragma once 16 #include <lib/fit/function.h> 17 18 #include <map> 19 #include <mutex> 20 #include <unordered_map> 21 #include <unordered_set> 22 23 #include "pw_bluetooth_sapphire/internal/host/common/identifier.h" 24 #include "pw_bluetooth_sapphire/internal/host/sdp/client.h" 25 #include "pw_bluetooth_sapphire/internal/host/sdp/sdp.h" 26 27 namespace bt::sdp { 28 29 // The Service Discoverer keeps track of which services are of interest to 30 // the host, and searches for those services on a remote device when directed 31 // to, reporting back to those interested asynchronously. 32 // 33 // Usually only one ServiceDiscoverer will exist per host. 34 // This class is thread-hostile: all functions must be called on the creation 35 // thread. 36 class ServiceDiscoverer final { 37 public: 38 ServiceDiscoverer(); 39 40 // Destroying the ServiceDiscoverer will mean all current searches will end 41 // and all current clients will be disconnected. 42 ~ServiceDiscoverer() = default; 43 44 using SearchId = uint64_t; 45 46 constexpr static SearchId kInvalidSearchId = 0u; 47 48 // Add an interest in discovering a remote service. 49 // Discoverer will search for services with |uuid| in their records, and 50 // return via |callback| all of the attributes specified in |attributes|, 51 // along with the peer's |device_id|. 52 // If |attributes| is empty, all attributes will be requested. 53 // Returns a SearchId can be used to remove the search later if successful, 54 // or kInvalidSearchId if adding the search failed. 55 // |callback| will be called on the creation thread of ServiceDiscoverer. 56 using ResultCallback = 57 fit::function<void(PeerId, const std::map<AttributeId, DataElement>&)>; 58 SearchId AddSearch(const UUID& uuid, 59 std::unordered_set<AttributeId> attributes, 60 ResultCallback callback); 61 62 // Remove a search previously added with AddSearch(). 63 // Returns true if a search was removed and false if it was not found. 64 // This function is idempotent. 65 bool RemoveSearch(SearchId id); 66 67 // Tries to add a single search using the SDP |client| connected to |peer_id| 68 // given for the search identified by |search_id|. Results from the search are 69 // delivered asynchronously via the ResultCallback registered via AddSearch. 70 // Does nothing if |search_id| is not currently registered. 71 // If |client| is nullptr, this search will only be performed if a client is 72 // already open to the peer. 73 void SingleSearch(SearchId search_id, 74 PeerId peer_id, 75 std::unique_ptr<Client> client); 76 77 // Searches for all the registered services using a SDP |client| 78 // asynchronously. The client is destroyed (disconnected) afterwards. 79 // If a search is already being performed on the same |peer_id|, the client 80 // is immediately dropped. 81 // Returns true if discovery was started, and false otherwise. 82 bool StartServiceDiscovery(PeerId peer_id, std::unique_ptr<Client> client); 83 84 // Returns the number of searches that will be performed on a 85 // StartServiceDiscovery. 86 size_t search_count() const; 87 88 private: 89 // A registered search. 90 struct Search { 91 UUID uuid; 92 std::unordered_set<AttributeId> attributes; 93 ResultCallback callback; 94 }; 95 96 // A Discovery Session happens using a Client, and ends when no registered 97 // searches still need to be completed. 98 struct DiscoverySession { 99 std::unique_ptr<Client> client; 100 // The set of Searches that have yet to complete. 101 // Should always be non-empty if this session exists. 102 std::unordered_set<SearchId> active; 103 }; 104 105 // Finish the Discovery Session for |peer_id| searching |search_id|, 106 // releasing the client if all searches are complete. 107 void FinishPeerSearch(PeerId peer_id, SearchId search_id); 108 109 // Next likely search id 110 SearchId next_id_; 111 112 // Registered searches 113 std::unordered_map<SearchId, Search> searches_; 114 115 // Clients that searches are still being performed on, based on the remote 116 // peer id. 117 std::unordered_map<PeerId, DiscoverySession> sessions_; 118 }; 119 120 } // namespace bt::sdp 121