blob: 2c181bcf5b912a476d5f39b07143479e320fe040 [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 SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_GATT_REMOTE_SERVICE_H_
#define SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_GATT_REMOTE_SERVICE_H_
#include <lib/fit/function.h>
#include <lib/fitx/result.h>
#include <zircon/assert.h>
#include <fbl/intrusive_hash_table.h>
#include <fbl/macros.h>
#include <fbl/ref_counted.h>
#include <fbl/ref_ptr.h>
#include "src/connectivity/bluetooth/core/bt-host/att/att.h"
#include "src/connectivity/bluetooth/core/bt-host/gatt/client.h"
#include "src/connectivity/bluetooth/core/bt-host/gatt/remote_characteristic.h"
#include "src/lib/fxl/memory/weak_ptr.h"
namespace bt::gatt {
class RemoteService;
// Callback type invoked when GATT services are removed, added, or modified.
// `added` and `modified` are not combined into `updated` for flexibility and debuggability.
// Modified service handles are not included in `removed`.
// NOTE: `removed` services should be handled first because they may share handles with `added`
// services.
using RemoteServiceWatcher = fit::function<void(std::vector<att::Handle> removed,
std::vector<fbl::RefPtr<RemoteService>> added,
std::vector<fbl::RefPtr<RemoteService>> modified)>;
using ServiceList = std::vector<fbl::RefPtr<RemoteService>>;
using ServiceListCallback = fit::function<void(att::Result<>, ServiceList)>;
using RemoteServiceCallback = fit::function<void(fbl::RefPtr<RemoteService>)>;
using DescriptorMap = std::map<DescriptorHandle, DescriptorData>;
using CharacteristicMap =
std::map<CharacteristicHandle, std::pair<CharacteristicData, DescriptorMap>>;
namespace internal {
class RemoteServiceManager;
} // namespace internal
// Represents the state of a GATT service that was discovered on a remote
// device. Clients can interact with a remote GATT service by obtaining a
// RemoteService object from the GATT system.
//
// TODO(fxbug.dev/83509): Remove RefCounted/RefPtr now that RemoteService doesn't need to be thread
// safe.
class RemoteService final : public fbl::RefCounted<RemoteService> {
public:
// In production, a RemoteService should only be constructed by a RemoteServiceManager.
// The constructor and destructor are made available for testing.
RemoteService(const ServiceData& service_data, fxl::WeakPtr<Client> client);
// TODO(fxbug.dev/83509): Perform clean up in the destructor.
~RemoteService();
// Shuts down this service. Called when the service gets removed (e.g. due to disconnection or
// because it was removed by the peer) or modified (via the Service Changed notification).
// `service_changed` indicates whether shut down is occurring due to a Service Changed
// notification, in which case this service may no longer exist or may have been modified (and so
// no writes should be performed).
//
// This method is used for clean up instead of the destructor because
// ownership is shared across threads and clean up needs to be thread safe.
// TODO(fxbug.dev/83509): Remove this method and perform clean up in the destructor.
void ShutDown(bool service_changed = false);
const ServiceData& info() const { return service_data_; }
// Returns the service range start handle. This is used to uniquely identify
// this service.
att::Handle handle() const { return service_data_.range_start; }
// Returns the service UUID.
const UUID& uuid() const { return service_data_.type; }
// The current ATT_MTU.
uint16_t att_mtu() const { return client_->mtu(); }
// Adds a handler which will be called when this service gets removed.
// Returns false if the service was already shut down.
bool AddRemovedHandler(fit::closure handler);
// Returns true if all contents of this service have been discovered. This is primarily intended
// for unit tests. Clients should not rely on this and use DiscoverCharacteristics() to guarantee
// discovery.
bool IsDiscovered() const;
// Performs characteristic discovery and reports the result asynchronously in
// |callback|. Returns the cached results if characteristics were already
// discovered.
using CharacteristicCallback = fit::function<void(att::Result<>, const CharacteristicMap&)>;
void DiscoverCharacteristics(CharacteristicCallback callback);
// Sends a read request to the characteristic with the given identifier. Fails
// if characteristics have not been discovered.
// |maybe_truncated| indicates whether the full value might be longer than the reported value.
using ReadValueCallback =
fit::function<void(att::Result<>, const ByteBuffer&, bool maybe_truncated)>;
void ReadCharacteristic(CharacteristicHandle id, ReadValueCallback callback);
// Performs the "Read Long Characteristic Values" procedure which allows
// characteristic values larger than the ATT_MTU to be read over multiple
// requests.
//
// The read will start at |offset| and will return at most |max_bytes| octets.
// The resulting value will be returned via |callback|.
// The value of |maybe_truncated| reported to the callback indicates whether the full value may be
// larger than the reported value. This is only possible if |max_bytes| are read, and |max_bytes|
// is less than att::kMaxAttributeValueLength.
void ReadLongCharacteristic(CharacteristicHandle id, uint16_t offset, size_t max_bytes,
ReadValueCallback callback);
// Sends a read by type request for attribute values in this service with the given |type|
// and returns read values via |callback|. If no matching attributes are found, the callback
// status will indicate success and the vector of values will be empty.
//
// If a permission error occurs for an attribute, the error and handle of the attribute that
// caused the error will be included in the results and the overall status will indicate success.
// If a general error occurs, the status will indicate the error and no results will be returned.
//
// |type| must be the UUID of a characteristic or descriptor value, NOT an internal GATT UUID
// such as a service or characteristic declaration (the callback will be invoked with an error in
// this case).
//
// NOTE: The values returned may be truncated, as indicated by ReadByTypeResult.maybe_truncated.
// ReadCharacteristic(), ReadLongCharacteristic(), ReadDescriptor(), and ReadLongDescriptor()
// should be used to read complete values.
struct ReadByTypeResult {
CharacteristicHandle handle;
fitx::result<att::ErrorCode, ByteBufferPtr> result;
bool maybe_truncated;
};
using ReadByTypeCallback = fit::function<void(att::Result<>, std::vector<ReadByTypeResult>)>;
void ReadByType(const UUID& type, ReadByTypeCallback callback);
// Sends a write request to the characteristic with the given identifier.
//
// TODO(armansito): Add a ByteBuffer version.
void WriteCharacteristic(CharacteristicHandle id, std::vector<uint8_t> value,
att::ResultFunction<> callback);
// Sends a write request to the characteristic with the given identifier at
// the given offset, will write over multiple requests if needed. Fails if
// characteristics have not been discovered.
//
// TODO(armansito): Add a ByteBuffer version.
void WriteLongCharacteristic(CharacteristicHandle id, uint16_t offset, std::vector<uint8_t> value,
ReliableMode reliable_mode, att::ResultFunction<> callback);
// Sends a "Write Without Response" to the characteristic with the given
// identifier. Fails if characteristics have not been discovered.
void WriteCharacteristicWithoutResponse(CharacteristicHandle id, std::vector<uint8_t> value,
att::ResultFunction<> cb);
// Performs the "Read Characteristic Descriptors" procedure (v5.0, Vol 3, Part
// G, 4.12.1).
// The callback parameter |maybe_truncated| indicates whether the full value might be longer than
// the reported value.
void ReadDescriptor(DescriptorHandle id, ReadValueCallback callback);
// Performs the "Read Long Characteristic Descriptors" procedure (v5.0, Vol 3,
// Part G, 4.12.2).
// The callback parameter |maybe_truncated| indicates whether the full value may be
// larger than the reported value. This is only possible if |max_bytes| are read, and |max_bytes|
// is less than att::kMaxAttributeValueLength.
void ReadLongDescriptor(DescriptorHandle id, uint16_t offset, size_t max_bytes,
ReadValueCallback callback);
// Performs the "Write Characteristic Descriptors" procedure (v5.0, Vol 3,
// Part G, 4.12.3).
//
// TODO(armansito): Add a ByteBuffer version.
void WriteDescriptor(DescriptorHandle id, std::vector<uint8_t> value,
att::ResultFunction<> callback);
// Performs the "Write Long Characteristic Descriptors" procedure (v5.0, Vol 3,
// Part G, 4.12.4).
//
// TODO(armansito): Add a ByteBuffer version.
void WriteLongDescriptor(DescriptorHandle id, uint16_t offset, std::vector<uint8_t> value,
att::ResultFunction<> callback);
// Subscribe to characteristic handle/value notifications or indications
// from the characteristic with the given identifier. Either notifications or
// indications will be enabled depending on the characteristic properties.
//
// This method can be called more than once to register multiple subscribers.
// The remote Client Characteristic Configuration descriptor will be written
// only if this is called for the first subscriber.
//
// |status_callback| will be called with the status of the operation. On
// success, a |handler_id| will be returned that can be used to unregister the
// handler.
//
// On success, notifications will be delivered to |callback|.
using ValueCallback = RemoteCharacteristic::ValueCallback;
using NotifyStatusCallback = RemoteCharacteristic::NotifyStatusCallback;
void EnableNotifications(CharacteristicHandle id, ValueCallback callback,
NotifyStatusCallback status_callback);
// Disables characteristic notifications for the given |handler_id| previously
// obtained via EnableNotifications. The value of the Client Characteristic
// Configuration descriptor will be cleared if no subscribers remain.
void DisableNotifications(CharacteristicHandle characteristic_id, IdType handler_id,
att::ResultFunction<> status_callback);
// Simulate receiving a notification. HandleNotification is usually called by
// RemoteServiceManager, but tests without a RemoteServiceManager may use this method.
void HandleNotificationForTesting(att::Handle value_handle, const ByteBuffer& value,
bool maybe_truncated) {
HandleNotification(value_handle, value, maybe_truncated);
}
private:
friend class fbl::RefPtr<RemoteService>;
friend class internal::RemoteServiceManager;
static constexpr size_t kSentinel = std::numeric_limits<size_t>::max();
bool alive() const { return !shut_down_; }
// Returns a pointer to the characteristic with |id|. Returns nullptr if not
// found.
fitx::result<Error<>> GetCharacteristic(CharacteristicHandle id, RemoteCharacteristic** out_char);
// Returns a pointer to the characteristic descriptor with |id|. Returns
// nullptr if not found.
fitx::result<Error<>> GetDescriptor(DescriptorHandle id, const DescriptorData** out_desc);
// Called immediately after characteristic discovery to initiate descriptor
// discovery.
void StartDescriptorDiscovery();
// Completes all pending characteristic discovery requests.
void CompleteCharacteristicDiscovery(att::Result<> status);
// Breaks Long Write requests down into a PrepareWriteQueue, then enqueues
// for the client to process. Drives the "Write Long Characteristic/
// Descriptor Values" procedure. Called by WriteCharacteristic() and
// WriteDescriptor().
void SendLongWriteRequest(att::Handle value_handle, uint16_t offset, BufferView value,
ReliableMode reliable_mode, att::ResultFunction<> callback);
// Helper function that drives the recursive "Read Long Characteristic Values"
// procedure. Called by ReadLongCharacteristic().
void ReadLongHelper(att::Handle value_handle, uint16_t offset, MutableByteBufferPtr buffer,
size_t bytes_read, ReadValueCallback callback);
// Helper function that drives the recursive "Read by Type" procedure.
// Accumulates attribute values in |values| until either |start| > |end| or an error occurs. On
// completion, accumulated |values| and the status are passed to |callback|.
// Called by ReadByType().
void ReadByTypeHelper(const UUID& type, att::Handle start, att::Handle end,
std::vector<ReadByTypeResult> values, ReadByTypeCallback callback);
// Returns true if characteristic discovery has completed.
inline bool HasCharacteristics() const { return remaining_descriptor_requests_ == 0u; }
// Called by RemoteServiceManager when a notification is received for one of
// this service's characteristics.
void HandleNotification(att::Handle value_handle, const ByteBuffer& value, bool maybe_truncated);
ServiceData service_data_;
// The GATT Client bearer for performing remote procedures.
fxl::WeakPtr<Client> client_;
// Queued discovery requests.
using PendingDiscoveryList = std::vector<CharacteristicCallback>;
PendingDiscoveryList pending_discov_reqs_;
// The known characteristics of this service. If not |characteristics_ready_|,
// this may contain a partial list of characteristics stored during the
// discovery process.
//
// The id of each characteristic corresponds to its index in this vector.
std::map<CharacteristicHandle, RemoteCharacteristic> characteristics_;
// The number of pending characteristic descriptor discoveries.
// Characteristics get marked as ready when this number reaches 0.
size_t remaining_descriptor_requests_;
// Set to true by ShutDown() which makes this service defunct. This happens
// when the remote device that this service was found on removes this service
// or gets disconnected.
bool shut_down_;
// Called by ShutDown().
std::vector<fit::callback<void()>> rm_handlers_;
DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(RemoteService);
};
} // namespace bt::gatt
#endif // SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_GATT_REMOTE_SERVICE_H_