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