xref: /aosp_15_r20/external/pigweed/pw_bluetooth/public/pw_bluetooth/gatt/client.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2022 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 #pragma once
15 
16 #include <memory>
17 
18 #include "pw_bluetooth/gatt/constants.h"
19 #include "pw_bluetooth/gatt/error.h"
20 #include "pw_bluetooth/gatt/types.h"
21 #include "pw_bluetooth/internal/raii_ptr.h"
22 #include "pw_bluetooth/result.h"
23 #include "pw_bluetooth/types.h"
24 #include "pw_containers/vector.h"
25 #include "pw_function/function.h"
26 #include "pw_span/span.h"
27 
28 namespace pw::bluetooth::gatt {
29 
30 /// Represents a GATT service on a remote GATT server.
31 /// Clients should call `SetErrorCallback` before using in order to handle fatal
32 /// errors.
33 class RemoteService {
34  public:
35   enum class RemoteServiceError {
36     /// The service has been modified or removed.
37     kServiceRemoved = 0,
38 
39     /// The peer serving this service has disconnected.
40     kPeerDisconnected = 1,
41   };
42 
43   /// Wrapper around a possible truncated value received from the server.
44   struct ReadValue {
45     /// Characteristic or descriptor handle.
46     Handle handle;
47 
48     /// The value of the characteristic or descriptor.
49     // TODO: b/320482584 - Consider changing capacity or making it configurable
50     Vector<std::byte, 20> value;
51 
52     /// True if `value` might be truncated (the buffer was completely filled by
53     /// the server and the read was a short read).  `ReadCharacteristic` or
54     /// `ReadDescriptor` should be used to read the complete value.
55     bool maybe_truncated;
56   };
57 
58   /// A result returned by `ReadByType`.
59   struct ReadByTypeResult {
60     /// Characteristic or descriptor handle.
61     Handle handle;
62 
63     /// The value of the characteristic or descriptor, if it was read
64     /// successfully, or an error explaining why the value could not be read.
65     Result<Error, ReadValue> result;
66   };
67 
68   /// Represents the supported options to read a long characteristic or
69   /// descriptor value from a server. Long values are those that may not fit in
70   /// a single message (longer than 22 bytes).
71   struct LongReadOptions {
72     /// The byte to start the read at. Must be less than the length of the
73     /// value.
74     uint16_t offset = 0;
75 
76     /// The maximum number of bytes to read.
77     uint16_t max_bytes = kMaxValueLength;
78   };
79 
80   /// Represents the supported write modes for writing characteristics &
81   /// descriptors to the server.
82   enum class WriteMode : uint8_t {
83     /// Wait for a response from the server before returning but do not verify
84     /// the echo response. Supported for both characteristics and descriptors.
85     kDefault = 0,
86 
87     /// Every value blob is verified against an echo response from the server.
88     /// The procedure is aborted if a value blob has not been reliably delivered
89     /// to the peer. Only supported for characteristics.
90     kReliable = 1,
91 
92     /// Delivery will not be confirmed before returning. Writing without a
93     /// response is only supported for short characteristics with the
94     /// `WRITE_WITHOUT_RESPONSE` property. The value must fit into a single
95     /// message. It is guaranteed that at least 20 bytes will fit into a single
96     /// message. If the value does not fit, a `kFailure` error will be produced.
97     /// The value will be written at offset 0. Only supported for
98     /// characteristics.
99     kWithoutResponse = 2,
100   };
101 
102   /// Represents the supported options to write a characteristic/descriptor
103   /// value to a server.
104   struct WriteOptions {
105     /// The mode of the write operation. For descriptors, only
106     /// `WriteMode::kDefault` is supported
107     WriteMode mode = WriteMode::kDefault;
108 
109     /// Request a write starting at the byte indicated.
110     /// Must be 0 if `mode` is `WriteMode.kWithoutResponse`.
111     uint16_t offset = 0;
112   };
113 
114   using ReadByTypeCallback = Function<void(Result<Vector<ReadByTypeResult>>)>;
115   using ReadCallback = Function<void(Result<ReadValue>)>;
116   using NotificationCallback = Function<void(ReadValue)>;
117 
118   /// Set a callback that will be called when there is an error with this
119   /// RemoteService, after which this RemoteService will be invalid.
120   void SetErrorCallback(Function<void(RemoteServiceError)>&& error_callback);
121 
122   /// Calls `characteristic_callback` with the characteristics and descriptors
123   /// in this service.
124   void DiscoverCharacteristics(
125       Function<void(Characteristic)>&& characteristic_callback);
126 
127   /// Reads characteristics and descriptors with the specified type. This method
128   /// is useful for reading values before discovery has completed, thereby
129   /// reducing latency.
130   /// @param uuid The UUID of the characteristics/descriptors to read.
131   /// @param result_callback Results are returned via this callback. Results may
132   /// be empty if no matching values are read. If reading a value results in a
133   /// permission error, the handle and error will be included.
134   ///
135   /// This may fail with the following errors:
136   /// - kInvalidParameters: if `uuid` refers to an internally reserved
137   /// descriptor type (e.g. the Client Characteristic Configuration descriptor).
138   /// - kTooManyResults: More results were read than can fit in a Vector.
139   /// Consider reading characteristics/descriptors individually after performing
140   /// discovery.
141   /// - kFailure: The server returned an error not specific to a single result.
142   void ReadByType(Uuid uuid, ReadByTypeCallback&& result_callback);
143 
144   /// Reads the value of a characteristic.
145   /// @param handle The handle of the characteristic to be read.
146   /// @param options If null, a short read will be performed, which may be
147   /// truncated to what fits in a single message (at least 22 bytes). If long
148   /// read options are present, performs a long read with the indicated options.
149   /// @param result_callback called with the result of the read and the value of
150   /// the characteristic if successful.
151   /// @retval kInvalidHandle `handle` is invalid.
152   /// @retval kInvalidParameters `options` is invalid.
153   /// @retval kReadNotPermitted The server rejected the request.
154   /// @retval kInsufficient* The server rejected the request.
155   /// @retval kFailure The server returned an error not covered by the above.
156   void ReadCharacteristic(Handle handle,
157                           std::optional<LongReadOptions> options,
158                           ReadCallback&& result_callback);
159 
160   /// Writes `value` to the characteristic with `handle` using the provided
161   /// `options`.  It is not recommended to send additional writes while a write
162   /// is already in progress (the server may receive simultaneous writes in any
163   /// order).
164   ///
165   /// @param handle Handle of the characteristic to be written to
166   /// @param value The value to be written.
167   /// @param options Options that apply to the write.
168   /// @param result_callback Returns a result upon completion of the write.
169   /// @retval kInvalidHandle `handle` is invalid.
170   /// @retval kInvalidParameters`options is invalid.
171   /// @retval kWriteNotPermitted The server rejected the request.
172   /// @retval kInsufficient* The server rejected the request.
173   /// @retval kFailure The server returned an error not covered by the above
174   /// errors.
175   void WriteCharacteristic(Handle handle,
176                            span<const std::byte> value,
177                            WriteOptions options,
178                            Function<void(Result<Error>)>&& result_callback);
179 
180   /// Reads the value of the characteristic descriptor with `handle` and
181   /// returns it in the reply.
182   /// @param handle The descriptor handle to read.
183   /// @param options Options that apply to the read.
184   /// @param result_callback Returns a result containing the value of the
185   /// descriptor on success.
186   /// @retval kInvalidHandle `handle` is invalid.
187   /// @retval kInvalidParameters`options` is invalid.
188   /// @retval kReadNotPermitted
189   /// @retval kInsufficient* The server rejected the request.
190   /// @retval kFailure The server returned an error not covered by the above
191   /// errors.
192   void ReadDescriptor(Handle handle,
193                       std::optional<LongReadOptions> options,
194                       ReadCallback&& result_callback);
195 
196   /// Writes `value` to the descriptor with `handle` using the provided
197   /// `options`.  It is not recommended to send additional writes while a write
198   /// is already in progress (the server may receive simultaneous writes in any
199   /// order).
200   ///
201   /// @param handle Handle of the descriptor to be written to
202   /// @param value The value to be written.
203   /// @param options Options that apply to the write.
204   /// @param result_callback Returns a result upon completion of the write.
205   /// @retval kInvalidHandle `handle` is invalid.
206   /// @retval kInvalidParameters `options is invalid
207   /// @retval kWriteNotPermitted The server rejected the request.
208   /// @retval kInsufficient* The server rejected the request.
209   /// @retval kFailure The server returned an error not covered by the above
210   /// errors.
211   void WriteDescriptor(Handle handle,
212                        span<const std::byte> value,
213                        WriteOptions options,
214                        Function<void(Result<Error>)>&& result_callback);
215 
216   /// Subscribe to notifications & indications from the characteristic with
217   /// the given `handle`.
218   ///
219   /// Either notifications or indications will be enabled depending on
220   /// characteristic properties. Indications will be preferred if they are
221   /// supported. This operation fails if the characteristic does not have the
222   /// "notify" or "indicate" property.
223   ///
224   /// A write request will be issued to configure the characteristic for
225   /// notifications/indications if it contains a Client Characteristic
226   /// Configuration descriptor. This method fails if an error occurs while
227   /// writing to the descriptor.
228   ///
229   /// On success, `notification_callback` will be called when
230   /// the peer sends a notification or indication. Indications are
231   /// automatically confirmed.
232   ///
233   /// Subscriptions can be canceled with `StopNotifications`.
234   ///
235   /// @param handle the handle of the characteristic to subscribe to.
236   /// @param notification_callback will be called with the values of
237   /// notifications/indications when received.
238   /// @param result_callback called with the result of enabling
239   /// notifications/indications.
240   /// @retval kFailure The characteristic does not support notifications or
241   /// indications.
242   /// @retval kInvalidHandle `handle` is invalid.
243   /// @retval kWriteNotPermitted CCC descriptor write error.
244   /// @retval Insufficient* CCC descriptor write error.
245   void RegisterNotificationCallback(
246       Handle handle,
247       NotificationCallback&& notification_callback,
248       Function<void(Result<Error>)>&& result_callback);
249 
250   /// Stops notifications for the characteristic with the given `handle`.
251   void StopNotifications(Handle handle);
252 
253  private:
254   /// Disconnect from the remote service. This method is called by the
255   /// ~RemoteService::Ptr() when it goes out of scope, the API client should
256   /// never call this method.
257   void Disconnect();
258 
259  public:
260   /// Movable RemoteService smart pointer. The remote server will remain
261   /// connected until the returned RemoteService::Ptr is destroyed.
262   using Ptr = internal::RaiiPtr<RemoteService, &RemoteService::Disconnect>;
263 };
264 
265 /// Represents a GATT client that interacts with services on a GATT server.
266 class Client {
267  public:
268   /// Represents a remote GATT service.
269   struct RemoteServiceInfo {
270     /// Uniquely identifies this GATT service.
271     Handle handle;
272 
273     /// Indicates whether this is a primary or secondary service.
274     bool primary;
275 
276     /// The UUID that identifies the type of this service.
277     /// There may be multiple services with the same UUID.
278     Uuid type;
279   };
280 
281   virtual ~Client() = default;
282 
283   /// Enumerates existing services found on the peer that this Client
284   /// represents, and provides a stream of updates thereafter. Results can be
285   /// filtered by specifying a list of UUIDs in `uuids`. To further interact
286   /// with services, clients must obtain a RemoteService protocol by calling
287   /// ConnectToService(). `uuid_allowlist` - The allowlist of UUIDs to filter
288   /// services with. `updated_callback` - Will be called with services that are
289   ///     updated/modified.
290   /// `removed_callback` - Called with the handles of services
291   ///     that have been removed. Note that handles may be reused.
292   virtual void WatchServices(
293       Vector<Uuid> uuid_allowlist,
294       Function<void(RemoteServiceInfo)>&& updated_callback,
295       Function<void(Handle)>&& removed_callback) = 0;
296 
297   /// Stops service watching if started by `WatchServices`.
298   virtual void StopWatchingServices();
299 
300   /// Connects to a RemoteService. Only 1 connection per service is allowed.
301   /// `handle` - the handle of the service to connect to.
302   ///
303   /// This may fail with the following errors:
304   /// kInvalidParameters - `handle` does not correspond to a known service.
305   virtual Result<Error, RemoteService::Ptr> ConnectToService(Handle handle) = 0;
306 };
307 
308 }  // namespace pw::bluetooth::gatt
309