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 // Define the ServiceDiscoveryService record for the SDP server object. 86 // This method is public for testing purposes. 87 static ServiceRecord MakeServiceDiscoveryService(); 88 89 // Construct a response based on input packet |sdu| and max size 90 // |max_tx_sdu_size|. Note that this function can both be called by means of 91 // connecting an l2cap::Channel and directly querying its database. As long 92 // as the database does not change between requests, both of these approaches 93 // are compatible. 94 // This function will drop the packet if the PDU is too short, and it will 95 // handle most errors by returning a valid packet with an ErrorResponse. 96 std::optional<ByteBufferPtr> HandleRequest(ByteBufferPtr sdu, 97 uint16_t max_tx_sdu_size); 98 99 // Returns the set of allocated L2CAP PSMs in the SDP server. 100 // This is a TEST ONLY hook and should not be used otherwise. 101 std::set<l2cap::Psm> AllocatedPsmsForTest() const; 102 103 private: 104 // Returns the next unused Service Handle, or 0 if none are available. 105 ServiceHandle GetNextHandle(); 106 107 // Performs a Service Search, returning any service record that contains 108 // all UUID from the |search_pattern| 109 ServiceSearchResponse SearchServices( 110 const std::unordered_set<UUID>& pattern) const; 111 112 // Gets Service Attributes in the |attribute_ranges| from the service record 113 // with |handle|. 114 ServiceAttributeResponse GetServiceAttributes( 115 ServiceHandle handle, const std::list<AttributeRange>& ranges) const; 116 117 // Retrieves Service Attributes in the |attribute_ranges|, using the pattern 118 // to search for the services that contain all UUIDs from the |search_pattern| 119 ServiceSearchAttributeResponse SearchAllServiceAttributes( 120 const std::unordered_set<UUID>& search_pattern, 121 const std::list<AttributeRange>& attribute_ranges) const; 122 123 // An array of PSM to ServiceHandle assignments that are used to represent 124 // the services that need to be registered in Server::QueueService. 125 using ProtocolQueue = std::vector<std::pair<l2cap::Psm, ServiceHandle>>; 126 127 /// Returns true if the |psm| is allocated in the SDP server. IsAllocated(l2cap::Psm psm)128 bool IsAllocated(l2cap::Psm psm) const { return psm_to_service_.count(psm); } 129 130 // Attempts to add the |psm| to the queue of protocols to be registered. 131 // Returns true if the PSM was successfully added to the queue, false 132 // otherwise. 133 bool AddPsmToProtocol(ProtocolQueue* protocols_to_register, 134 l2cap::Psm psm, 135 ServiceHandle handle) const; 136 137 // Returns the next available dynamic PSM. A PSM is considered available if it 138 // has not been allocated already nor reserved in |queued_psms|. Returns 139 // |kInvalidPsm| if no PSM is available. 140 l2cap::Psm GetDynamicPsm(const ProtocolQueue* queued_psms) const; 141 142 // Given a complete ServiceRecord, extracts the PSM, ProtocolDescriptorList, 143 // and any AdditionalProtocolDescriptorList information. Allocates any dynamic 144 // PSMs that were requested in the aforementioned protocol lists. 145 // Inserts the extracted info into |psm_to_register|. 146 // 147 // Returns |true| if the protocols are successfully validated and queued, 148 // |false| otherwise. 149 bool QueueService(ServiceRecord* record, 150 ProtocolQueue* protocols_to_register); 151 152 // l2cap::Channel callbacks 153 void OnChannelClosed(l2cap::Channel::UniqueId channel_id); 154 155 // Updates the property values associated with the |sdp_server_node_|. 156 void UpdateInspectProperties(); 157 158 // Send |bytes| over the channel associated with the connection handle 159 // |conn|. Logs an error if channel not found. 160 void Send(l2cap::Channel::UniqueId channel_id, ByteBufferPtr bytes); 161 162 // Used to register callbacks for the channels of services registered. 163 l2cap::ChannelManager* l2cap_; 164 165 struct InspectProperties { 166 // Inspect hierarchy node representing the sdp server. 167 inspect::Node sdp_server_node; 168 169 // Each ServiceRecord has it's record and nodes associated wth the 170 // registered PSMs. 171 struct InspectServiceRecordProperties { 172 InspectServiceRecordProperties(std::string record, 173 std::unordered_set<l2cap::Psm> psms); 174 void AttachInspect(inspect::Node& parent, std::string name); 175 inspect::Node node; 176 // The record description. 177 const std::string record; 178 inspect::StringProperty record_property; 179 // The node for the registered PSMs. 180 inspect::Node psms_node; 181 // The currently registered PSMs. 182 const std::unordered_set<l2cap::Psm> psms; 183 std::vector<std::pair<inspect::Node, inspect::StringProperty>> psm_nodes; 184 }; 185 186 // The currently registered ServiceRecords. 187 std::vector<InspectServiceRecordProperties> svc_record_properties; 188 }; 189 InspectProperties inspect_properties_; 190 191 // Map of channels that are opened to the server. Keyed by the channels 192 // unique id. 193 std::unordered_map<l2cap::Channel::UniqueId, l2cap::ScopedChannel> channels_; 194 // The map of ServiceHandles that are associated with ServiceRecords. 195 // This is a 1:1 mapping. 196 std::unordered_map<ServiceHandle, ServiceRecord> records_; 197 198 // Which PSMs are registered to services. Multiple ServiceHandles can be 199 // registered to a single PSM. 200 std::unordered_map<l2cap::Psm, std::unordered_set<ServiceHandle>> 201 psm_to_service_; 202 // The set of PSMs that are registered to a service. 203 std::unordered_map<ServiceHandle, std::unordered_set<l2cap::Psm>> 204 service_to_psms_; 205 206 // The next available ServiceHandle. 207 ServiceHandle next_handle_; 208 209 // The set of ServiceHandles that are registered together, identified by a 210 // RegistrationHandle. 211 std::unordered_map<RegistrationHandle, std::set<ServiceHandle>> 212 reg_to_service_; 213 214 // The service database state tracker. 215 uint32_t db_state_ [[maybe_unused]]; 216 217 WeakSelf<Server> weak_ptr_factory_; 218 219 BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(Server); 220 }; 221 222 } // namespace bt::sdp 223