xref: /aosp_15_r20/external/pigweed/pw_bluetooth/public/pw_bluetooth/gatt/client2.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2024 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 "pw_async2/dispatcher.h"
17 #include "pw_async2/once_sender.h"
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_containers/vector.h"
23 #include "pw_function/function.h"
24 #include "pw_multibuf/multibuf.h"
25 #include "pw_result/expected.h"
26 
27 namespace pw::bluetooth::gatt {
28 
29 /// An interface for interacting with a GATT service on a peer device.
30 class RemoteService2 {
31  public:
32   enum class RemoteServiceError {
33     /// The service has been modified or removed.
34     kServiceRemoved = 0,
35 
36     /// The peer serving this service has disconnected.
37     kPeerDisconnected = 1,
38   };
39 
40   /// Wrapper around a possible truncated value received from the server.
41   struct ReadValue {
42     /// Characteristic or descriptor handle.
43     Handle handle;
44 
45     /// The value of the characteristic or descriptor.
46     multibuf::MultiBuf value;
47 
48     /// True if `value` might be truncated (the buffer was completely filled by
49     /// the server and the read was a short read).  `ReadCharacteristic` or
50     /// `ReadDescriptor` should be used to read the complete value.
51     bool maybe_truncated;
52   };
53 
54   /// A result returned by `ReadByType`.
55   struct ReadByTypeResult {
56     /// Characteristic or descriptor handle.
57     Handle handle;
58 
59     /// The value of the characteristic or descriptor, if it was read
60     /// successfully, or an error explaining why the value could not be read.
61     pw::expected<ReadValue, Error> result;
62   };
63 
64   /// Represents the supported options to read a long characteristic or
65   /// descriptor value from a server. Long values are those that may not fit in
66   /// a single message.
67   struct LongReadOptions {
68     /// The byte to start the read at. Must be less than the length of the
69     /// value.
70     uint16_t offset = 0;
71 
72     /// The maximum number of bytes to read.
73     uint16_t max_bytes = kMaxValueLength;
74   };
75 
76   /// Represents the supported write modes for writing characteristics &
77   /// descriptors to the server.
78   enum class WriteMode : uint8_t {
79     /// Wait for a response from the server before returning but do not verify
80     /// the echo response. Supported for both characteristics and descriptors.
81     kDefault,
82 
83     /// Every value blob is verified against an echo response from the server.
84     /// The procedure is aborted if a value blob has not been reliably delivered
85     /// to the peer. Only supported for characteristics.
86     kReliable,
87 
88     /// Delivery will not be confirmed before returning. Writing without a
89     /// response is only supported for short characteristics with the
90     /// `WRITE_WITHOUT_RESPONSE` property. The value must fit into a single
91     /// message. It is guaranteed that at least 20 bytes will fit into a single
92     /// message. If the value does not fit, a `kFailure` error will be produced.
93     /// The value will be written at offset 0. Only supported for
94     /// characteristics.
95     kWithoutResponse,
96   };
97 
98   /// Represents the supported options to write a characteristic/descriptor
99   /// value to a server.
100   struct WriteOptions {
101     /// The mode of the write operation. For descriptors, only
102     /// `WriteMode::kDefault` is supported
103     WriteMode mode = WriteMode::kDefault;
104 
105     /// Request a write starting at the byte indicated.
106     /// Must be 0 if `mode` is `WriteMode.kWithoutResponse`.
107     uint16_t offset = 0;
108   };
109 
110   virtual ~RemoteService2() = default;
111 
112   /// Poll for an Error status on this service, waking `cx` and returning
113   /// Ready when there is an error condition. When an error condition is
114   /// present, any previous `RemoteService2` `Waker` and `OnceReceiver`
115   /// instances may or may not be woken and all other methods will be no-ops.
116   /// Only 1 waker can be set at a time (additional calls will replace the
117   /// existing waker).
118   virtual async2::Poll<RemoteServiceError> PendError(async2::Context& cx) = 0;
119 
120   /// Asynchronously sends the characteristics in this service, up to
121   /// `Vector::.max_size()`. May perform service discovery if the
122   /// characteristics are not yet known.
123   virtual void DiscoverCharacteristics(
124       async2::OnceRefSender<Vector<Characteristic2>>
125           characteristics_sender) = 0;
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   /// @return The result of the read. Results may be empty if no matching values
132   /// are read. If reading a value results in a permission error, the handle and
133   /// 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 the Vector.
139   /// Consider reading characteristics/descriptors individually after performing
140   /// discovery.
141   /// - `kFailure`: The server returned an error not specific to a single
142   /// result.
143   virtual async2::OnceReceiver<pw::expected<Vector<ReadByTypeResult, 5>, Error>>
144   ReadByType(Uuid uuid) = 0;
145 
146   /// Reads the value of a characteristic.
147   /// @param handle The handle of the characteristic to be read.
148   /// @param options If null, a short read will be performed, which may be
149   /// truncated to what fits in a single message (at least 22 bytes). If long
150   /// read options are present, performs a long read with the indicated options.
151   /// @return The result of the read and the value of the characteristic if
152   /// successful.
153   /// Returns the following errors:
154   /// - kInvalidHandle `handle` is invalid.
155   /// - kInvalidParameters `options` is invalid.
156   /// - kReadNotPermitted The server rejected the request.
157   /// - kInsufficient* The server rejected the request.
158   /// - kApplicationError* An application error was returned by the GATT
159   ///     profile.
160   /// - kFailure The server returned an error not covered by the above.
161   virtual async2::OnceReceiver<pw::expected<ReadValue, Error>>
162   ReadCharacteristic(Handle handle, std::optional<LongReadOptions> options) = 0;
163 
164   /// Writes `value` to the characteristic with `handle` using the provided
165   /// `options`.
166   ///
167   /// @param handle Handle of the characteristic to be written to
168   /// @param value The value to be written.
169   /// @param options Options that apply to the write.
170   /// @return A result is returned when a response to the write is
171   ///     received. For WriteWithoutResponse, this is set as soon as the write
172   ///     is sent.
173   /// Returns the following errors:
174   /// - kInvalidHandle `handle` is invalid.
175   /// - kInvalidParameters`options is invalid.
176   /// - kWriteNotPermitted The server rejected the request.
177   /// - kInsufficient* The server rejected the request.
178   /// - kApplicationError* An application error was returned by the GATT
179   ///     profile.
180   /// - kFailure The server returned an error not covered by the above
181   /// errors.
182   virtual async2::OnceReceiver<pw::expected<void, Error>> WriteCharacteristic(
183       Handle handle, pw::multibuf::MultiBuf&& value, WriteOptions options) = 0;
184 
185   /// Reads the value of the characteristic descriptor with `handle` and
186   /// returns it in the reply.
187   /// @param handle The descriptor handle to read.
188   /// @param options Options that apply to the read.
189   /// @param result_sender Set to a result containing the value of the
190   /// descriptor on success.
191   /// @retval kInvalidHandle `handle` is invalid.
192   /// @retval kInvalidParameters `options` is invalid.
193   /// @retval kReadNotPermitted
194   /// @retval kInsufficient* The server rejected the request.
195   /// @retval kApplicationError* An application error was returned by the GATT
196   ///     profile.
197   /// @retval kFailure The server returned an error not covered by the above
198   /// errors.
199   virtual async2::OnceReceiver<pw::expected<ReadValue, Error>> ReadDescriptor(
200       Handle handle, std::optional<LongReadOptions> options) = 0;
201 
202   /// Writes `value` to the descriptor with `handle` using the provided. It is
203   /// not recommended to send additional writes while a write is already in
204   /// progress.
205   ///
206   /// @param handle Handle of the descriptor to be written to.
207   /// @param value The value to be written.
208   /// @return The result upon completion of the write.
209   /// Possible errors:
210   /// - kInvalidHandle `handle` is invalid.
211   /// - kInvalidParameters `options is invalid.
212   /// - kWriteNotPermitted The server rejected the request.
213   /// - kInsufficient* The server rejected the request.
214   /// - kApplicationError* An application error was returned by the GATT
215   ///     profile.
216   /// - kFailure The server returned an error not covered by the above
217   /// errors.
218   virtual async2::OnceReceiver<pw::expected<void, Error>> WriteDescriptor(
219       Handle handle, pw::multibuf::MultiBuf&& value) = 0;
220 
221   /// Subscribe to notifications & indications from the characteristic with
222   /// the given `handle`.
223   ///
224   /// Either notifications or indications will be enabled depending on
225   /// characteristic properties. Indications will be preferred if they are
226   /// supported. This operation fails if the characteristic does not have the
227   /// "notify" or "indicate" property.
228   ///
229   /// A write request will be issued to configure the characteristic for
230   /// notifications/indications if it contains a Client Characteristic
231   /// Configuration (CCC) descriptor. This method fails if an error occurs while
232   /// writing to the descriptor.
233   ///
234   /// On success, `PendNotification` will return Ready when the peer sends a
235   /// notification or indication. Indications are automatically confirmed.
236   ///
237   /// Subscriptions can be canceled with `StopNotifications`.
238   ///
239   /// @param handle the handle of the characteristic to subscribe to.
240   /// @return The result of enabling notifications/indications.
241   /// - kFailure The characteristic does not support notifications or
242   ///     indications.
243   /// - kInvalidHandle `handle` is invalid.
244   /// - kWriteNotPermitted CCC descriptor write error.
245   /// - kInsufficient*  Insufficient security properties to write to CCC
246   ///     descriptor.
247   virtual async2::OnceReceiver<pw::expected<void, Error>> EnableNotifications(
248       Handle handle) = 0;
249 
250   /// After notifications have been enabled with `EnableNotifications`, this
251   /// method can be used to check for notifications. This method will safely
252   /// return Pending when notifications are disabled.
253   /// @param handle The handle of the characteristic to await for notifications.
254   /// @param cx The Context to awaken when a notification is available. Only
255   ///     one Waker per handle is supported at a time (subsequent calls will
256   ///     overwrite the old Waker).
257   virtual async2::Poll<ReadValue> PendNotification(Handle handle,
258                                                    async2::Context& cx) = 0;
259 
260   /// Stops notifications for the characteristic with the given `handle`.
261   /// @return The result of disabling notifications/indications.
262   /// Possible errors:
263   /// - kFailure The characteristic does not support notifications or
264   /// indications.
265   /// - kInvalidHandle `handle` is invalid.
266   /// - kWriteNotPermitted CCC descriptor write error.
267   /// - Insufficient* CCC descriptor write error.
268   virtual async2::OnceReceiver<pw::expected<void, Error>> StopNotifications(
269       Handle handle) = 0;
270 
271  private:
272   /// Disconnect from the remote service. This method is called by the
273   /// ~RemoteService::Ptr() when it goes out of scope, the API client should
274   /// never call this method.
275   virtual void Disconnect() = 0;
276 
277  public:
278   /// Movable `RemoteService2` smart pointer. The remote server will remain
279   /// connected until the returned `RemoteService2::Ptr` is destroyed.
280   using Ptr = internal::RaiiPtr<RemoteService2, &RemoteService2::Disconnect>;
281 };
282 
283 /// Represents a GATT client that interacts with services on a GATT server.
284 class Client2 {
285  public:
286   /// Represents a remote GATT service.
287   struct RemoteServiceInfo {
288     /// Uniquely identifies this GATT service.
289     ServiceHandle handle;
290 
291     /// Indicates whether this is a primary or secondary service.
292     bool primary;
293 
294     /// The UUID that identifies the type of this service.
295     /// There may be multiple services with the same UUID.
296     Uuid type;
297   };
298 
299   virtual ~Client2() = default;
300 
301   /// Enumerates existing services found on the peer that this object
302   /// represents and notifies of modifications to services or new services
303   /// thereafter. If service discovery hasn't happened yet, it may be started.
304   /// To further interact with services, clients must obtain a `RemoteService2`
305   /// protocol by calling `ConnectToService`.
306   /// @return  Will return `Ready` with `RemoteServiceInfo` when there are
307   /// services that are updated/modified. This can can be called repeatedly
308   /// until `Pending` is returned to get all previously discovered services.
309   virtual async2::Poll<RemoteServiceInfo> PendServiceUpdate(
310       async2::Context& cx);
311 
312   /// Returns the handles of services that have been removed. Note that handles
313   /// may be reused, so it is recommended to check for removed services before
314   /// calling `PendServiceUpdate`. This should be called repeatedly until
315   /// `Pending` is returned.
316   ///
317   /// @param cx Awoken when a service is removed after Pending is returned.
318   virtual async2::Poll<ServiceHandle> PendServiceRemoved(async2::Context& cx);
319 
320   /// Connects to a `RemoteService2`. Only 1 connection per service is allowed.
321   ///
322   /// @param handle The handle of the service to connect to.
323   ///
324   /// @return kInvalidParameters `handle` does not correspond to a known
325   /// service.
326   virtual pw::expected<RemoteService2::Ptr, Error> ConnectToService(
327       ServiceHandle handle) = 0;
328 };
329 
330 }  // namespace pw::bluetooth::gatt
331