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