xref: /aosp_15_r20/external/pigweed/pw_bluetooth_sapphire/public/pw_bluetooth_sapphire/internal/host/sdp/pdu.h (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 #pragma once
16 #include <lib/fit/result.h>
17 
18 #include "pw_bluetooth_sapphire/internal/host/common/error.h"
19 #include "pw_bluetooth_sapphire/internal/host/sdp/sdp.h"
20 
21 namespace bt::sdp {
22 
23 constexpr uint64_t kInvalidContState = 0xFFFFFFFF;
24 
25 // Maximum length of continuation information is 16 bytes, and the InfoLength
26 // is one byte. See v5.0, Vol 3, Part B, Sec 4.3
27 constexpr size_t kMaxContStateLength = 17;
28 
29 // Minimum length allowed by the Maximum Attribute Byte Count in
30 // ServiceAttribute and ServiceSearchAttribute requests
31 constexpr size_t kMinMaximumAttributeByteCount = 0x0007;
32 
33 // Selected to be larger than FIDL limit of 512. Prevent poor performance in
34 // worst case scenarios. Clients should use larger ranges if they need anywhere
35 // near this number of attributes.
36 constexpr size_t kMaxAttributeRangesInRequest = 520;
37 
38 class Request {
39  public:
40   Request();
41   virtual ~Request() = default;
42 
43   // Returns true if the request is valid.
44   virtual bool valid() const = 0;
45 
46   // Gets a buffer containing the PDU representation of this request.
47   // Returns nullptr if the request is not valid.
48   virtual ByteBufferPtr GetPDU(TransactionId tid) const = 0;
49 
50   // Returns a view with the current continuation state.
51   // In a response packet with more than one packet, this contains the most
52   // recent continuation state (so it can be read to request a continuation).
ContinuationState()53   const BufferView ContinuationState() const {
54     return cont_state_.view(1, cont_info_size());
55   }
56 
57   // Sets the continuation state for this request.
58   void SetContinuationState(const ByteBuffer& buf);
59 
60  protected:
61   // Parses the continuation state portion of a packet, which is in |buf|.
62   // Returns true if the parsing succeeded.
63   bool ParseContinuationState(const ByteBuffer& buf);
64 
65   // Writes the continuation state to |buf|, which must have at least
66   // cont_info_size() + 1 bytes available.
67   size_t WriteContinuationState(MutableByteBuffer* buf) const;
68 
cont_info_size()69   uint8_t cont_info_size() const { return cont_state_.data()[0]; }
70 
71  private:
72   // Continuation information, including the length.
73   StaticByteBuffer<kMaxContStateLength> cont_state_;
74 };
75 
76 // SDP Response objects are used in two places:
77 //  - to construct a response for returning from a request on the server
78 //  - to receive responses from a server as a client, possibly building from
79 //    multiple response PDUs
80 class Response {
81  public:
82   virtual ~Response() = default;
83 
84   // Returns true if these parameters represent a complete response.
85   virtual bool complete() const = 0;
86 
87   // Returns the continuation state from a partial response, used to
88   // make an additional request.  Returns an empty view if this packet
89   // is complete.
90   virtual const BufferView ContinuationState() const = 0;
91 
92   // Parses parameters from a PDU response, storing a partial result if
93   // necessary.
94   // Returns a success status if the parameters could ba parsed, or a status
95   // containing:
96   //  - kNotReady if this response is already complete.
97   //  - kPacketMalformed: if the parameters couldn't be parsed.
98   //  - kOutOfMemory: if memory isn't available to store a partial response.
99   virtual fit::result<Error<>> Parse(const ByteBuffer& buf) = 0;
100 
101   // Returns a buffer containing the PDU representation of this response,
102   // including the header, which will have the transaction id |tid|.
103   // |req_max| will control the maximum size of the parameters based on the
104   // transaction type:
105   //  - for ServiceSearchResponse, this should be the maximum records requested
106   //    to be included from the ServiceSearchRequest
107   //  - for ServiceAttributeResponse or ServiceSearchAttributeResponse, this
108   //    is the MaximumAttributeByteCount from the request
109   // |max_size| is the maximum size of a PDU generated by this method.
110   // The buffer parameters will contain continuation state if it does not
111   // contain the end of the response. If that continuation state is passed to
112   // this function with the same |req_max| argument it will produce the next
113   // section of response.
114   virtual MutableByteBufferPtr GetPDU(uint16_t req_max,
115                                       TransactionId tid,
116                                       uint16_t max_size,
117                                       const ByteBuffer& cont_state) const = 0;
118 };
119 
120 // Error Response PDU, generated when the SDP server can't respond to a PDU
121 // because it is malformed or for another reason.
122 // See v5.0, Vol 3, Part B, 4.4.1
123 class ErrorResponse : public Response {
124  public:
125   ErrorResponse(std::optional<ErrorCode> code = std::nullopt)
error_code_(code)126       : error_code_(code) {}
127   // Response overrides.
complete()128   bool complete() const override { return error_code_.has_value(); }
129 
ContinuationState()130   const BufferView ContinuationState() const override {
131     // ErrorResponses never have continuation state.
132     return BufferView();
133   }
134 
135   fit::result<Error<>> Parse(const ByteBuffer& buf) override;
136 
137   // Note: |max_size| and |cont_state| are ignored.
138   // Error Responses do not have a valid continuation.
139   MutableByteBufferPtr GetPDU(uint16_t req_max,
140                               TransactionId tid,
141                               uint16_t max_size,
142                               const ByteBuffer& cont_state) const override;
143 
error_code()144   const std::optional<ErrorCode>& error_code() const { return error_code_; }
set_error_code(ErrorCode code)145   void set_error_code(ErrorCode code) { error_code_ = code; }
146 
147  private:
148   std::optional<ErrorCode> error_code_;
149 };
150 
151 // Used to locate service records that match a pattern.
152 // Note: there is no mechanism to retrieve all service records.
153 // See v5.0, Vol 3, Part B, 4.5.1
154 class ServiceSearchRequest : public Request {
155  public:
156   // Create an empty search request.
157   ServiceSearchRequest();
158   // Parse the parameters given in |params| to initialize this request.
159   explicit ServiceSearchRequest(const ByteBuffer& params);
160 
161   // Request overrides
162   bool valid() const override;
163   ByteBufferPtr GetPDU(TransactionId tid) const override;
164 
165   // A service search pattern matches if every UUID in the pattern is contained
166   // within one of the services' attribute values.  They don't need to be in any
167   // specific attribute or in any particular order, and extraneous UUIDs are
168   // allowed to exist in the attribute value.
169   // See v5.0, Volume 3, Part B, Sec 2.5.2
set_search_pattern(std::unordered_set<UUID> pattern)170   void set_search_pattern(std::unordered_set<UUID> pattern) {
171     service_search_pattern_ = pattern;
172   }
service_search_pattern()173   const std::unordered_set<UUID>& service_search_pattern() const {
174     return service_search_pattern_;
175   }
176 
177   // The maximum count of records that should be included in any
178   // response.
set_max_service_record_count(uint16_t count)179   void set_max_service_record_count(uint16_t count) {
180     max_service_record_count_ = count;
181   }
max_service_record_count()182   uint16_t max_service_record_count() const {
183     return max_service_record_count_;
184   }
185 
186  private:
187   std::unordered_set<UUID> service_search_pattern_;
188   uint16_t max_service_record_count_;
189 };
190 
191 // Generated by the SDP server in response to a ServiceSearchRequest.
192 // See v5.0, Volume 3, Part B, Sec 4.5.2
193 class ServiceSearchResponse : public Response {
194  public:
195   ServiceSearchResponse();
196 
197   // Response overrides
198   bool complete() const override;
199   const BufferView ContinuationState() const override;
200   fit::result<Error<>> Parse(const ByteBuffer& buf) override;
201   MutableByteBufferPtr GetPDU(uint16_t req_max,
202                               TransactionId tid,
203                               uint16_t max_size,
204                               const ByteBuffer& cont_state) const override;
205 
206   // The ServiceRecordHandleList contains as list of service record handles.
207   // This should be set to the list of handles that match the request.
208   // Limiting the response to the maximum requested is handled by
209   // GetPDU();
set_service_record_handle_list(std::vector<ServiceHandle> handles)210   void set_service_record_handle_list(std::vector<ServiceHandle> handles) {
211     service_record_handle_list_ = handles;
212     total_service_record_count_ = static_cast<uint16_t>(handles.size());
213   }
service_record_handle_list()214   std::vector<ServiceHandle> service_record_handle_list() const {
215     return service_record_handle_list_;
216   }
217 
218  private:
219   // The list of service record handles.
220   std::vector<ServiceHandle> service_record_handle_list_;
221   // The total number of service records in the full response.
222   uint16_t total_service_record_count_;
223 
224   MutableByteBufferPtr continuation_state_;
225 };
226 
227 // Represents a range of attributes, inclusive of |start| and |end|.
228 struct AttributeRange {
AttributeRangeAttributeRange229   AttributeRange(AttributeId attribute_range_start,
230                  AttributeId attribute_range_end)
231       : start(attribute_range_start), end(attribute_range_end) {
232     PW_DCHECK(start <= end);
233   }
234 
235   AttributeId start;
236   AttributeId end;
237 };
238 
239 // Used to retrieve a set of attributes from a specific service record.
240 // See v5.0, Volume 3, Part B, Sec 4.6.1
241 class ServiceAttributeRequest : public Request {
242  public:
243   // Create an empty search request.
244   ServiceAttributeRequest();
245   // Parse the parameters in |params| to initialize this request.
246   // valid() will be false if |params| don't represent valid a valid request.
247   explicit ServiceAttributeRequest(const ByteBuffer& params);
248 
249   // Request overrides
250   bool valid() const override;
251   ByteBufferPtr GetPDU(TransactionId tid) const override;
252 
set_service_record_handle(ServiceHandle handle)253   void set_service_record_handle(ServiceHandle handle) {
254     service_record_handle_ = handle;
255   }
service_record_handle()256   ServiceHandle service_record_handle() const { return service_record_handle_; }
257 
258   // Set the maximum size allowed in the response in the Attribute list
259   // Not allowed to be lower than kMinMaximumAttributeByteCount (7)
set_max_attribute_byte_count(uint16_t count)260   void set_max_attribute_byte_count(uint16_t count) {
261     PW_DCHECK(count >= kMinMaximumAttributeByteCount);
262     max_attribute_byte_count_ = count;
263   }
max_attribute_byte_count()264   uint16_t max_attribute_byte_count() const {
265     return max_attribute_byte_count_;
266   }
267 
268   // Adds a single attribute to the requested IDs. Used to ensure a specific
269   // attribute is requested.
270   // Automatically merges attribute ranges that are contiguous to save bytes in
271   // the request.
272   void AddAttribute(AttributeId id);
273 
274   // Adds a range of attributes to the requested IDs.
275   // Like AddAttribute(), attribute ranges that are contiguous are merged to
276   // save bytes in the resulting request.
277   void AddAttributeRange(AttributeId start, AttributeId end);
278 
attribute_ranges()279   const std::list<AttributeRange>& attribute_ranges() {
280     return attribute_ranges_;
281   }
282 
283  private:
284   // The service record handle for which attributes should be retrieved.
285   // Should be obtained by using a ServiceSearch transaction.
286   ServiceHandle service_record_handle_;
287 
288   // Maximum number of bytes of attribute data to be returned in the response.
289   // If the attributes don't fit, the server decides how to segment them.
290   // Clients should use continuation state to request more data.
291   uint16_t max_attribute_byte_count_;
292 
293   // The attribute(s) to retrieve.
294   // This is a list of ranges, inclusive of the ends.
295   // They are non-overlapping and sorted by the start id of each range.
296   std::list<AttributeRange> attribute_ranges_;
297 };
298 
299 // Generated upon receiving a ServiceAttributeRequest.
300 // See v5.0, Volume 3, Part B, Sec 4.6.2
301 class ServiceAttributeResponse : public Response {
302  public:
303   ServiceAttributeResponse();
304 
305   // Response overrides
306   const BufferView ContinuationState() const override;
307   bool complete() const override;
308   fit::result<Error<>> Parse(const ByteBuffer& buf) override;
309   MutableByteBufferPtr GetPDU(uint16_t req_max,
310                               TransactionId tid,
311                               uint16_t max_size,
312                               const ByteBuffer& cont_state) const override;
313 
set_attribute(AttributeId id,DataElement value)314   void set_attribute(AttributeId id, DataElement value) {
315     attributes_.emplace(id, std::move(value));
316   }
attributes()317   const std::map<AttributeId, DataElement>& attributes() const {
318     return attributes_;
319   }
320 
321  private:
322   // The list of attributes that matched the search and their values.
323   // This is sorted (it is in ascending order in the response).
324   std::map<AttributeId, DataElement> attributes_;
325 
326   // Attribute List(s) can be truncated due to:
327   //  - Response too long for MTU
328   //  - MaxAttributeListByteCount is set too low
329   //  - Because the server wants to
330   //
331   // This contains the partial attribute list response if there is continuation
332   // state.
333   MutableByteBufferPtr partial_response_;
334 
335   MutableByteBufferPtr continuation_state_;
336 };
337 
338 // Combines the capabilities of ServiceSearchRequest and ServiceAttributeRequest
339 // Note that the record handle is not included in the response by default, and
340 // myst be requested if needed.
341 // See v5.0, Volume 3, Part B,Sec 4.7.1
342 class ServiceSearchAttributeRequest : public Request {
343  public:
344   // Create an empty service search attribute request.
345   ServiceSearchAttributeRequest();
346   // Parse the parameters in |params| to initialize this request.
347   explicit ServiceSearchAttributeRequest(const ByteBuffer& params);
348 
349   // Request overrides
350   bool valid() const override;
351   ByteBufferPtr GetPDU(TransactionId tid) const override;
352 
353   // A service search pattern matches if every UUID in the pattern is contained
354   // within one of the services' attribute values.  They don't need to be in any
355   // specific attribute or in any particular order, and extraneous UUIDs are
356   // allowed to exist in the attribute value.
357   // See v5.0, Volume 3, Part B, Sec 2.5.2.
set_search_pattern(std::unordered_set<UUID> pattern)358   void set_search_pattern(std::unordered_set<UUID> pattern) {
359     service_search_pattern_ = pattern;
360   }
service_search_pattern()361   const std::unordered_set<UUID>& service_search_pattern() const {
362     return service_search_pattern_;
363   }
364 
365   // Set the maximum size allowed in the response in the Attribute list
366   // Not allowed to be lower than kMinMaximumAttributeByteCount (7)
set_max_attribute_byte_count(uint16_t count)367   void set_max_attribute_byte_count(uint16_t count) {
368     PW_DCHECK(count >= kMinMaximumAttributeByteCount);
369     max_attribute_byte_count_ = count;
370   }
max_attribute_byte_count()371   uint16_t max_attribute_byte_count() const {
372     return max_attribute_byte_count_;
373   }
374 
375   // Adds a single attribute to the requested IDs
376   void AddAttribute(AttributeId id);
377 
378   // Adds a range of attributes to the requested IDs.
379   void AddAttributeRange(AttributeId start, AttributeId end);
380 
attribute_ranges()381   const std::list<AttributeRange>& attribute_ranges() {
382     return attribute_ranges_;
383   }
384 
385  private:
386   // The service search pattern to match services.
387   std::unordered_set<UUID> service_search_pattern_;
388 
389   // Maximum number of bytes of attribute data to be returned in the response.
390   // If the attributes don't fit, the server decides how to segment them.
391   // Clients should use continuation state to request more data.
392   uint16_t max_attribute_byte_count_;
393 
394   // The attribute(s) to retrieve.
395   // This is a list of ranges, inclusive of the ends.
396   // They are non-overlapping and sorted by the first attribute id.
397   std::list<AttributeRange> attribute_ranges_;
398 };
399 
400 // Generated in response to a ServiceSearchAttributeRequest
401 // See v5.0, Volume 3, Part B,Sec 4.7.2
402 class ServiceSearchAttributeResponse : public Response {
403  public:
404   ServiceSearchAttributeResponse();
405 
406   // Response overrides
407   const BufferView ContinuationState() const override;
408   bool complete() const override;
409   fit::result<Error<>> Parse(const ByteBuffer& buf) override;
410   MutableByteBufferPtr GetPDU(uint16_t req_max,
411                               TransactionId tid,
412                               uint16_t max_size,
413                               const ByteBuffer& cont_state) const override;
414 
415   // Set an attribute to be included in the response.
416   // |idx| is used to group attributes and does not need to be contiguous for
417   // convenience (i.e. a service's handle), although parsed responses will
418   // be numbered starting from 0.
419   void SetAttribute(uint32_t idx, AttributeId id, DataElement value);
420 
421   // The number of attribute lists in this response.
num_attribute_lists()422   size_t num_attribute_lists() const { return attribute_lists_.size(); }
423 
424   // Retrieve attributes in response from a specific index.
425   // Attribute lists are numbered starting from 0 when parsed.
attributes(uint32_t idx)426   const std::map<AttributeId, DataElement>& attributes(uint32_t idx) const {
427     return attribute_lists_.at(idx);
428   }
429 
430  private:
431   // The list of lists that is to be returned / was returned in the response.
432   // They are in ascending order of index, which has no relation to the
433   // service IDs (they may not be included).
434   std::map<uint32_t, std::map<AttributeId, DataElement>> attribute_lists_;
435 
436   // The Attribute Lists can be truncated due to:
437   //  - Response too long for MTU
438   //  - MaxAttributeListByteCount is set too low
439   //  - Because the server wants to
440   //
441   // This contains the partial attribute list response if there is continuation
442   // state.
443   MutableByteBufferPtr partial_response_;
444 
445   MutableByteBufferPtr continuation_state_;
446 };
447 
448 }  // namespace bt::sdp
449