blob: 328cfef67622588ec192bddbd58653bacaebc91e [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef GARNET_DRIVERS_BLUETOOTH_LIB_SDP_PDU_H_
#define GARNET_DRIVERS_BLUETOOTH_LIB_SDP_PDU_H_
#include "garnet/drivers/bluetooth/lib/sdp/sdp.h"
namespace btlib {
namespace sdp {
constexpr uint64_t kInvalidContState = 0xFFFFFFFF;
// Maximum length of continuation information is 16 bytes, and the InfoLength
// is one byte. See v5.0, Vol 3, Part B, Sec 4.3
constexpr size_t kMaxContStateLength = 17;
// Minimum length allowed by the Maximum Attribute Byte Count in
// ServiceAttribute and ServiceSearchAttribute requests
constexpr size_t kMinMaximumAttributeByteCount = 0x0007;
class Request {
public:
Request();
virtual ~Request() = default;
// Returns true if the request is valid.
virtual bool valid() const = 0;
// Gets a buffer containing the PDU representation of this request.
// Returns nullptr if the request is not valid.
virtual common::ByteBufferPtr GetPDU(TransactionId tid) const = 0;
// Returns a view with the current continuation state.
// In a response packet with more than one packet, this contains the most
// recent continuaton state (so it can be read to request a continuation).
const common::BufferView ContinuationState() const {
return cont_state_.view(1, cont_info_size());
}
// Sets the continuation state for this request.
void SetContinuationState(const common::ByteBuffer& buf);
protected:
// Parses the continuation state portion of a packet, which is in |buf|.
// Returns true if the parsing succeeded.
bool ParseContinuationState(const common::ByteBuffer& buf);
// Writes the continuation state to |buf|, which must have at least
// cont_info_size() + 1 bytes avaiable.
size_t WriteContinuationState(common::MutableByteBuffer* buf) const;
uint8_t cont_info_size() const { return cont_state_.data()[0]; }
private:
// Continuation information, including the length.
common::StaticByteBuffer<kMaxContStateLength> cont_state_;
};
// SDP Response objects are used in two places:
// - to construct a response for returning from a request on the server
// - to receive responses from a server as a client, possibly building from
// multiple response PDUs
class Response {
public:
// Returns true if these parameters represent a complete response.
virtual bool complete() const = 0;
// Returns the continuation state from a partial response, used to
// make an additional request. Returns an empty view if this packet
// is complete.
virtual const common::BufferView ContinuationState() const = 0;
// Parses parameters from a PDU response, storing a partial result if
// necessary.
// Returns a success status if the parameters could ba parsed, or a status
// containing:
// - kNotReady if this response is already complete.
// - kPacketMalformed: if the parameters couldn't be parsed.
// - kOutOfMemory: if memory isn't available to store a partial response.
virtual Status Parse(const common::ByteBuffer& buf) = 0;
// Returns a buffer containing the PDU representation of this response,
// including the header.
// |max| will control the maximum size of the parameters based on the
// transaction type:
// - for ServiceSearchResponse, this should be the maximum count of records
// to be included.
// - for ServiceAttributeResponse or ServiceSearchAttributeResponse, this
// is the MaximumAttributeByteCount from the request
// The buffer parameters will contain continuation state if it is not the
// last valid packet representing a response.
// If that continuation state is passed to this function with the same
// |max_size| argument it will produce the next parameters of response.
virtual common::MutableByteBufferPtr GetPDU(
uint16_t max, TransactionId tid,
const common::ByteBuffer& cont_state) const = 0;
};
// Error Response PDU, generated when the SDP server can't respond to a PDU
// because it is malformed or for another reason.
// See v5.0, Vol 3, Part B, 4.4.1
class ErrorResponse : public Response {
public:
ErrorResponse(ErrorCode code = ErrorCode::kReserved) : error_code_(code) {}
// Response overrides.
bool complete() const override { return error_code_ != ErrorCode::kReserved; }
const common::BufferView ContinuationState() const override {
// ErrorResponses never have continuation state.
return common::BufferView();
}
Status Parse(const common::ByteBuffer& buf) override;
// Note: |max_size| and |cont_state| are ignored.
// Error Responses do not have a valid continuation.
common::MutableByteBufferPtr GetPDU(
uint16_t max, TransactionId tid,
const common::ByteBuffer& cont_state) const override;
ErrorCode error_code() const { return error_code_; }
void set_error_code(ErrorCode code) { error_code_ = code; }
private:
ErrorCode error_code_;
};
// Used to locate service records that match a pattern.
// Note: there is no mechanism to retrieve all service records.
// See v5.0, Vol 3, Part B, 4.5.1
class ServiceSearchRequest : public Request {
public:
// Create an empty search request.
ServiceSearchRequest();
// Parse the parameters given in |params| to initialize this request.
explicit ServiceSearchRequest(const common::ByteBuffer& params);
// Request overrides
bool valid() const override;
common::ByteBufferPtr GetPDU(TransactionId tid) const override;
// A service search pattern matches if every UUID in the pattern is contained
// within one of the services' attribute values. They don't need to be in any
// specific attribute or in any particular order, and extraneous UUIDs are
// allowed to exist in the attribute value.
// See v5.0, Volume 3, Part B, Sec 2.5.2
void set_search_pattern(std::unordered_set<common::UUID> pattern) {
service_search_pattern_ = pattern;
}
const std::unordered_set<common::UUID>& service_search_pattern() const {
return service_search_pattern_;
}
// The maximum count of records that should be included in any
// response.
void set_max_service_record_count(uint16_t count) {
max_service_record_count_ = count;
}
uint16_t max_service_record_count() const {
return max_service_record_count_;
}
private:
std::unordered_set<common::UUID> service_search_pattern_;
uint16_t max_service_record_count_;
};
// Generated by the SDP server in response to a ServiceSearchRequest.
// See v5.0, Volume 3, Part B, Sec 4.5.2
class ServiceSearchResponse : public Response {
public:
ServiceSearchResponse();
// Response overrides
bool complete() const override;
const common::BufferView ContinuationState() const override;
Status Parse(const common::ByteBuffer& buf) override;
common::MutableByteBufferPtr GetPDU(
uint16_t max, TransactionId tid,
const common::ByteBuffer& cont_state) const override;
// The ServiceRecordHandleList contains as list of service record handles.
// This should be set to the list of handles that match the request.
// Limiting the response to the maximum requested is handled by
// GetParameters();
void set_service_record_handle_list(std::vector<ServiceHandle> handles) {
service_record_handle_list_ = handles;
total_service_record_count_ = handles.size();
}
std::vector<ServiceHandle> service_record_handle_list() const {
return service_record_handle_list_;
}
private:
// The list of service record handles.
std::vector<ServiceHandle> service_record_handle_list_;
// The total number of service records in the full response.
uint16_t total_service_record_count_;
common::ByteBufferPtr continuation_state_;
};
// Represents a range of attributes, inclusive of |start| and |end|.
struct AttributeRange {
AttributeRange(AttributeId start, AttributeId end) : start(start), end(end) {
ZX_DEBUG_ASSERT(start <= end);
}
AttributeId start;
AttributeId end;
};
// Used to retrieve a set of attributes from a specific service record.
// See v5.0, Volume 3, Part B, Sec 4.6.1
class ServiceAttributeRequest : public Request {
public:
// Create an empty search request.
ServiceAttributeRequest();
// Parse the parameters in |params| to initialize this request.
// valid() will be false if |params| don't represent valid a valid request.
explicit ServiceAttributeRequest(const common::ByteBuffer& params);
// Request overrides
bool valid() const override;
common::ByteBufferPtr GetPDU(TransactionId tid) const override;
void set_service_record_handle(ServiceHandle handle) {
service_record_handle_ = handle;
}
ServiceHandle service_record_handle() const { return service_record_handle_; }
// Set the maximum size allowed in the response in the Attribute list
// Not allowed to be lower than kMinMaximumAttributeByteCount (7)
void set_max_attribute_byte_count(uint16_t count) {
ZX_DEBUG_ASSERT(count >= kMinMaximumAttributeByteCount);
max_attribute_byte_count_ = count;
}
uint16_t max_attribute_byte_count() const {
return max_attribute_byte_count_;
}
// Adds a single attribute to the requested IDs. Used to ensure a specific
// attribute is requested.
// Automatically merges attribute ranges that are contiguous to save bytes in
// the request.
void AddAttribute(AttributeId id);
// Adds a range of attributes to the requested IDs.
// Like AddAttribute(), attribute ranges that are contiguous are merged to
// save bytes in the resulting request.
void AddAttributeRange(AttributeId start, AttributeId end);
const std::list<AttributeRange>& attribute_ranges() {
return attribute_ranges_;
}
private:
// The service record handle for which attributes should be retrieved.
// Should be obtained by using a ServiceSearch transaction.
ServiceHandle service_record_handle_;
// Maximum number of bytes of attribute data to be returned in the response.
// If the attributes don't fit, the server decides how to segment them.
// Clients should use continuation state to request more data.
uint16_t max_attribute_byte_count_;
// The attribute(s) to retrieve.
// This is a list of ranges, inclusive of the ends.
// They are non-overlapping and sorted by the start id of each range.
std::list<AttributeRange> attribute_ranges_;
};
// Generated upon receiving a ServiceAttributeRequest.
// See v5.0, Volume 3, Part B, Sec 4.6.2
class ServiceAttributeResponse : public Response {
public:
ServiceAttributeResponse();
// Response overrides
const common::BufferView ContinuationState() const override;
bool complete() const override;
Status Parse(const common::ByteBuffer& buf) override;
common::MutableByteBufferPtr GetPDU(
uint16_t max, TransactionId tid,
const common::ByteBuffer& cont_state) const override;
void set_attribute(AttributeId id, DataElement value) {
attributes_.emplace(id, std::move(value));
}
const std::map<AttributeId, DataElement>& attributes() const {
return attributes_;
}
private:
// The list of attributes that matched the search and their values.
// This is sorted (it is in ascending order in the response).
std::map<AttributeId, DataElement> attributes_;
// Attribute List(s) can be truncated due to:
// - Response too long for MTU
// - MaxAttributeListByteCount is set too low
// - Because the server wants to
//
// This contains the partial attribute list response if there is continuation
// state.
common::MutableByteBufferPtr partial_response_;
common::MutableByteBufferPtr continuation_state_;
};
// Combines the capabilities of ServiceSearchRequest and ServiceAttributeRequest
// Note that the record handle is not included in the response by default, and
// myst be requested if needed.
// See v5.0, Volume 3, Part B,Sec 4.7.1
class ServiceSearchAttributeRequest : public Request {
public:
// Create an empty service search attribute request.
ServiceSearchAttributeRequest();
// Parse the parameters in |params| to initialize this request.
explicit ServiceSearchAttributeRequest(const common::ByteBuffer& params);
// Request overrides
bool valid() const override;
common::ByteBufferPtr GetPDU(TransactionId tid) const override;
// A service search pattern matches if every UUID in the pattern is contained
// within one of the services' attribute values. They don't need to be in any
// specific attribute or in any particular order, and extraneous UUIDs are
// allowed to exist in the attribute value.
// See v5.0, Volume 3, Part B, Sec 2.5.2.
void set_search_pattern(std::unordered_set<common::UUID> pattern) {
service_search_pattern_ = pattern;
}
const std::unordered_set<common::UUID>& service_search_pattern() const {
return service_search_pattern_;
}
// Set the maximum size allowed in the response in the Attribute list
// Not allowed to be lower than kMinMaximumAttributeByteCount (7)
void set_max_attribute_byte_count(uint16_t count) {
ZX_DEBUG_ASSERT(count >= kMinMaximumAttributeByteCount);
max_attribute_byte_count_ = count;
}
uint16_t max_attribute_byte_count() const {
return max_attribute_byte_count_;
}
// Adds a single attribute to the requested IDs
void AddAttribute(AttributeId id);
// Adds a range of attributes to the requested IDs.
void AddAttributeRange(AttributeId start, AttributeId end);
const std::list<AttributeRange>& attribute_ranges() {
return attribute_ranges_;
}
private:
// The service search pattern to match services.
std::unordered_set<common::UUID> service_search_pattern_;
// Maximum number of bytes of attribute data to be returned in the response.
// If the attributes don't fit, the server decides how to segment them.
// Clients should use continuation state to request more data.
uint16_t max_attribute_byte_count_;
// The attribute(s) to retrieve.
// This is a list of ranges, inclusive of the ends.
// They are non-overlapping and sorted by the first attribute id.
std::list<AttributeRange> attribute_ranges_;
};
// Generated in response to a ServiceSearchAttributeRequest
// See v5.0, Volume 3, Part B,Sec 4.7.2
class ServiceSearchAttributeResponse : public Response {
public:
ServiceSearchAttributeResponse();
// Response overrides
const common::BufferView ContinuationState() const override;
bool complete() const override;
Status Parse(const common::ByteBuffer& buf) override;
common::MutableByteBufferPtr GetPDU(
uint16_t max, TransactionId tid,
const common::ByteBuffer& cont_state) const override;
// Set an attribute to be included in the response.
// |idx| is used to group attributes and does not need to be contiguous for
// convenience (i.e. a service's handle), although parsed responses will
// be numbered starting from 0.
void SetAttribute(uint32_t idx, AttributeId id, DataElement value);
// The number of attribute lists in this response.
size_t num_attribute_lists() const { return attribute_lists_.size(); }
// Retrieve attributes in response from a specific index.
// Attribute lists are numbered starting from 0 when parsed.
const std::map<AttributeId, DataElement>& attributes(uint32_t idx) const {
return attribute_lists_.at(idx);
}
private:
// The list of lists that is to be returned / was returned in the response.
// They are in ascending order of index, which has no relation to the
// service IDs (they may not be included).
std::map<uint32_t, std::map<AttributeId, DataElement>> attribute_lists_;
// The Attribute Lists can be truncated due to:
// - Response too long for MTU
// - MaxAttributeListByteCount is set too low
// - Because the server wants to
//
// This contains the partial attribute list response if there is continuation
// state.
common::MutableByteBufferPtr partial_response_;
common::MutableByteBufferPtr continuation_state_;
};
} // namespace sdp
} // namespace btlib
#endif // GARNET_DRIVERS_BLUETOOTH_LIB_SDP_PDU_H_