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 <optional> 19 #include <unordered_map> 20 21 #include "pw_bluetooth_sapphire/internal/host/common/weak_self.h" 22 #include "pw_bluetooth_sapphire/internal/host/l2cap/channel_manager.h" 23 #include "pw_bluetooth_sapphire/internal/host/l2cap/l2cap_defs.h" 24 #include "pw_bluetooth_sapphire/internal/host/l2cap/scoped_channel.h" 25 #include "pw_bluetooth_sapphire/internal/host/sdp/pdu.h" 26 #include "pw_bluetooth_sapphire/internal/host/sdp/sdp.h" 27 #include "pw_bluetooth_sapphire/internal/host/sdp/service_record.h" 28 29 namespace bt::sdp { 30 31 // The SDP server object owns the Service Database and all Service Records. 32 // Only one server is expected to exist per host. 33 // This object is not thread-safe. 34 // TODO(jamuraa): make calls thread-safe or ensure single-threadedness 35 class Server final { 36 public: 37 static constexpr const char* kInspectNodeName = "sdp_server"; 38 // A placeholder value for a dynamic PSM. 39 // Note: This is not a valid PSM value itself. It is used to request a 40 // randomly generated dynamic PSM. 41 static constexpr uint16_t kDynamicPsm = 0xffff; 42 43 // A new SDP server, which starts with just a ServiceDiscoveryService record. 44 // Registers itself with |l2cap| when created. 45 explicit Server(l2cap::ChannelManager* l2cap); 46 ~Server(); 47 48 // Attach SDP server inspect node as a child node of |parent|. 49 void AttachInspect(inspect::Node& parent, 50 std::string name = kInspectNodeName); 51 52 // Initialize a new SDP profile connection with |peer_id| on |channel|. 53 // Returns false if the channel cannot be activated. 54 bool AddConnection(l2cap::Channel::WeakPtr channel); 55 56 // An identifier for a set of services that have been registered at the same 57 // time. 58 using RegistrationHandle = uint32_t; 59 60 const RegistrationHandle kNotRegistered = 0x00000000; 61 62 // Given incomplete ServiceRecords, register services that will be made 63 // available over SDP. Takes ownership of |records|. Channels created for this 64 // service will be configured using the preferred parameters in |chan_params|. 65 // 66 // A non-zero RegistrationHandle will be returned if the service was 67 // successfully registered. 68 // 69 // If any record in |records| fails registration checks, none of the services 70 // will be registered. 71 // 72 // |conn_cb| will be called for any connections made to any of the services in 73 // |records| with a connected channel and the descriptor list for the endpoint 74 // which was connected. 75 using ConnectCallback = 76 fit::function<void(l2cap::Channel::WeakPtr, const DataElement&)>; 77 RegistrationHandle RegisterService(std::vector<ServiceRecord> records, 78 l2cap::ChannelParameters chan_params, 79 ConnectCallback conn_cb); 80 81 // Unregister services previously registered with RegisterService. Idempotent. 82 // Returns |true| if any records were removed. 83 bool UnregisterService(RegistrationHandle handle); 84 85 // Returns a list of records for the provided |handle|. 86 // The returned list may be empty if |handle| is not registered. 87 std::vector<ServiceRecord> GetRegisteredServices( 88 RegistrationHandle handle) const; 89 90 // Define the ServiceDiscoveryService record for the SDP server object. 91 // This method is public for testing purposes. 92 static ServiceRecord MakeServiceDiscoveryService(); 93 94 // Construct a response based on input packet |sdu| and max size 95 // |max_tx_sdu_size|. Note that this function can both be called by means of 96 // connecting an l2cap::Channel and directly querying its database. As long 97 // as the database does not change between requests, both of these approaches 98 // are compatible. 99 // This function will drop the packet if the PDU is too short, and it will 100 // handle most errors by returning a valid packet with an ErrorResponse. 101 std::optional<ByteBufferPtr> HandleRequest(ByteBufferPtr sdu, 102 uint16_t max_tx_sdu_size); 103 104 // Returns the set of allocated L2CAP PSMs in the SDP server. 105 // TESTONLY hook and should not be used otherwise. 106 std::set<l2cap::Psm> AllocatedPsmsForTest() const; 107 108 private: 109 // Returns the next unused Service Handle, or 0 if none are available. 110 ServiceHandle GetNextHandle(); 111 112 // Performs a Service Search, returning any service record that contains 113 // all UUID from the |search_pattern| 114 ServiceSearchResponse SearchServices( 115 const std::unordered_set<UUID>& pattern) const; 116 117 // Gets Service Attributes in the |attribute_ranges| from the service record 118 // with |handle|. 119 ServiceAttributeResponse GetServiceAttributes( 120 ServiceHandle handle, const std::list<AttributeRange>& ranges) const; 121 122 // Retrieves Service Attributes in the |attribute_ranges|, using the pattern 123 // to search for the services that contain all UUIDs from the |search_pattern| 124 ServiceSearchAttributeResponse SearchAllServiceAttributes( 125 const std::unordered_set<UUID>& search_pattern, 126 const std::list<AttributeRange>& attribute_ranges) const; 127 128 // An array of PSM to ServiceHandle assignments that are used to represent 129 // the services that need to be registered in Server::QueueService. 130 using ProtocolQueue = std::vector<std::pair<l2cap::Psm, ServiceHandle>>; 131 132 /// Returns true if the |psm| is allocated in the SDP server. IsAllocated(l2cap::Psm psm)133 bool IsAllocated(l2cap::Psm psm) const { return psm_to_service_.count(psm); } 134 135 // Attempts to add the |psm| to the queue of protocols to be registered. 136 // Returns true if the PSM was successfully added to the queue, false 137 // otherwise. 138 bool AddPsmToProtocol(ProtocolQueue* protocols_to_register, 139 l2cap::Psm psm, 140 ServiceHandle handle) const; 141 142 // Returns the next available dynamic PSM. A PSM is considered available if it 143 // has not been allocated already nor reserved in |queued_psms|. Returns 144 // |kInvalidPsm| if no PSM is available. 145 l2cap::Psm GetDynamicPsm(const ProtocolQueue* queued_psms) const; 146 147 // Given a complete ServiceRecord, extracts the PSM, ProtocolDescriptorList, 148 // and any AdditionalProtocolDescriptorList information. Allocates any dynamic 149 // PSMs that were requested in the aforementioned protocol lists. 150 // Inserts the extracted info into |psm_to_register|. 151 // 152 // Returns |true| if the protocols are successfully validated and queued, 153 // |false| otherwise. 154 bool QueueService(ServiceRecord* record, 155 ProtocolQueue* protocols_to_register); 156 157 // l2cap::Channel callbacks 158 void OnChannelClosed(l2cap::Channel::UniqueId channel_id); 159 160 // Updates the property values associated with the |sdp_server_node_|. 161 void UpdateInspectProperties(); 162 163 // Send |bytes| over the channel associated with the connection handle 164 // |conn|. Logs an error if channel not found. 165 void Send(l2cap::Channel::UniqueId channel_id, ByteBufferPtr bytes); 166 167 // Used to register callbacks for the channels of services registered. 168 l2cap::ChannelManager* l2cap_; 169 170 struct InspectProperties { 171 // Inspect hierarchy node representing the sdp server. 172 inspect::Node sdp_server_node; 173 174 // Each ServiceRecord has it's record and nodes associated wth the 175 // registered PSMs. 176 struct InspectServiceRecordProperties { 177 InspectServiceRecordProperties(std::string record, 178 std::unordered_set<l2cap::Psm> psms); 179 void AttachInspect(inspect::Node& parent, std::string name); 180 inspect::Node node; 181 // The record description. 182 const std::string record; 183 inspect::StringProperty record_property; 184 // The node for the registered PSMs. 185 inspect::Node psms_node; 186 // The currently registered PSMs. 187 const std::unordered_set<l2cap::Psm> psms; 188 std::vector<std::pair<inspect::Node, inspect::StringProperty>> psm_nodes; 189 }; 190 191 // The currently registered ServiceRecords. 192 std::vector<InspectServiceRecordProperties> svc_record_properties; 193 }; 194 InspectProperties inspect_properties_; 195 196 // Map of channels that are opened to the server. Keyed by the channels 197 // unique id. 198 std::unordered_map<l2cap::Channel::UniqueId, l2cap::ScopedChannel> channels_; 199 // The map of ServiceHandles that are associated with ServiceRecords. 200 // This is a 1:1 mapping. 201 std::unordered_map<ServiceHandle, ServiceRecord> records_; 202 203 // Which PSMs are registered to services. Multiple ServiceHandles can be 204 // registered to a single PSM. 205 std::unordered_map<l2cap::Psm, std::unordered_set<ServiceHandle>> 206 psm_to_service_; 207 // The set of PSMs that are registered to a service. 208 std::unordered_map<ServiceHandle, std::unordered_set<l2cap::Psm>> 209 service_to_psms_; 210 211 // The next available ServiceHandle. 212 ServiceHandle next_handle_; 213 214 // The set of ServiceHandles that are registered together, identified by a 215 // RegistrationHandle. 216 std::unordered_map<RegistrationHandle, std::set<ServiceHandle>> 217 reg_to_service_; 218 219 // The service database state tracker. 220 uint32_t db_state_ [[maybe_unused]]; 221 222 WeakSelf<Server> weak_ptr_factory_; 223 224 BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(Server); 225 }; 226 227 } // namespace bt::sdp 228