• 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 #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