1 // Copyright 2023 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 
15 #pragma once
16 #include <lib/fit/function.h>
17 #include <lib/fit/result.h>
18 
19 #include "pw_bluetooth_sapphire/internal/host/att/att.h"
20 #include "pw_bluetooth_sapphire/internal/host/att/bearer.h"
21 #include "pw_bluetooth_sapphire/internal/host/att/write_queue.h"
22 #include "pw_bluetooth_sapphire/internal/host/common/uuid.h"
23 #include "pw_bluetooth_sapphire/internal/host/gatt/gatt_defs.h"
24 
25 namespace bt::gatt {
26 
27 // Implements GATT client-role procedures. A client operates over a single ATT
28 // data bearer. Client objects are solely used to map GATT procedures to ATT
29 // protocol methods and do not maintain service state.
30 class Client {
31  public:
32   // Constructs a new Client. |bearer| must outlive this object.
33   static std::unique_ptr<Client> Create(att::Bearer::WeakPtr bearer);
34 
35   virtual ~Client() = default;
36 
37   // Returns a weak pointer to this Client. The weak pointer should be checked
38   // on the data bearer's thread only as Client can only be accessed on that
39   // thread.
40   using WeakPtr = WeakSelf<Client>::WeakPtr;
41   virtual Client::WeakPtr GetWeakPtr() = 0;
42 
43   // Returns the current ATT MTU.
44   virtual uint16_t mtu() const = 0;
45 
46   // Initiates an MTU exchange and adjusts the bearer's MTU as outlined in Core
47   // Spec v5.3 Vol. 3 Part F 3.4.2. The request will be made using the
48   // locally-preferred MTU.
49   //
50   // Upon successful exchange, the bearer will be updated to use the resulting
51   // MTU and |callback| will be notified with the the resulting MTU.
52   //
53   // MTU exchange support is optional per v5.3 Vol. 3 Part F Table 4.1, so if
54   // the exchange fails because the peer doesn't support it, the bearer's MTU
55   // will be |kLEMinMTU| and initialization should proceed. In this case,
56   // |mtu_result| will be |att::Error(att::kRequestNotSupported)|.
57   //
58   // If the MTU exchange otherwise fails, |mtu_result| will be an error and the
59   // bearer's MTU will not change.
60   using MTUCallback = fit::callback<void(att::Result<uint16_t> mtu_result)>;
61   virtual void ExchangeMTU(MTUCallback callback) = 0;
62 
63   // Performs a modified version of the "Discover All Primary Services"
64   // procedure defined in v5.0, Vol 3, Part G, 4.4.1, genericized over primary
65   // and secondary services.
66   //
67   // |service_callback| is run for each discovered service in order of start
68   // handle. |status_callback| is run with the status of the operation.
69   //
70   // The |kind| parameter can be used to control whether primary or secondary
71   // services get discovered.
72   //
73   // NOTE: |service_callback| will be called asynchronously as services are
74   // discovered so a caller can start processing the results immediately while
75   // the procedure is in progress. Since discovery usually occurs over multiple
76   // ATT transactions, it is possible for |status_callback| to be called with an
77   // error even if some services have been discovered. It is up to the client
78   // to clear any cached state in this case.
79   using ServiceCallback = fit::function<void(const ServiceData&)>;
80   virtual void DiscoverServices(ServiceKind kind,
81                                 ServiceCallback svc_callback,
82                                 att::ResultFunction<> status_callback) = 0;
83 
84   // Same as DiscoverServices, but only discovers services in the range
85   // [range_start, range_end]. |range_start| must be less than |range_end|.
86   virtual void DiscoverServicesInRange(
87       ServiceKind kind,
88       att::Handle range_start,
89       att::Handle range_end,
90       ServiceCallback svc_callback,
91       att::ResultFunction<> status_callback) = 0;
92 
93   // Performs the "Discover All Primary Services by UUID" procedure defined in
94   // v5.0, Vol 3, Part G, 4.4.2. |service_callback| is run for each discovered
95   // service in order of start handle. |status_callback| is run with the result
96   // of the operation.
97   //
98   // The |kind| parameter can be used to control whether primary or secondary
99   // services get discovered.
100   //
101   // NOTE: |service_callback| will be called asynchronously as services are
102   // discovered so a caller can start processing the results immediately while
103   // the procedure is in progress. Since discovery usually occurs over multiple
104   // ATT transactions, it is possible for |status_callback| to be called with an
105   // error even if some services have been discovered. It is up to the client
106   // to clear any cached state in this case.
107   virtual void DiscoverServicesWithUuids(ServiceKind kind,
108                                          ServiceCallback svc_callback,
109                                          att::ResultFunction<> status_callback,
110                                          std::vector<UUID> uuids) = 0;
111 
112   // Same as DiscoverServicesWithUuids, but only discovers services in the range
113   // [range_start, range_end]. |range_start| must be <= |range_end|.
114   virtual void DiscoverServicesWithUuidsInRange(
115       ServiceKind kind,
116       att::Handle range_start,
117       att::Handle range_end,
118       ServiceCallback svc_callback,
119       att::ResultFunction<> status_callback,
120       std::vector<UUID> uuids) = 0;
121 
122   // Performs the "Discover All Characteristics of a Service" procedure defined
123   // in v5.0, Vol 3, Part G, 4.6.1.
124   using CharacteristicCallback = fit::function<void(const CharacteristicData&)>;
125   virtual void DiscoverCharacteristics(
126       att::Handle range_start,
127       att::Handle range_end,
128       CharacteristicCallback chrc_callback,
129       att::ResultFunction<> status_callback) = 0;
130 
131   // Performs the "Discover All Characteristic Descriptors" procedure defined in
132   // Vol 3, Part G, 4.7.1.
133   using DescriptorCallback = fit::function<void(const DescriptorData&)>;
134   virtual void DiscoverDescriptors(att::Handle range_start,
135                                    att::Handle range_end,
136                                    DescriptorCallback desc_callback,
137                                    att::ResultFunction<> status_callback) = 0;
138 
139   // Sends an ATT Read Request with the requested attribute |handle| and returns
140   // the resulting value in |callback|. This can be used to send a (short) read
141   // request to any attribute. (Vol 3, Part F, 3.4.4.3).
142   //
143   // Reports the status of the procedure and the resulting value in |callback|.
144   // Returns an empty buffer if the status is an error.
145   // If the attribute value might be longer than the reported value,
146   // |maybe_truncated| will be true. This can happen if the MTU is too small to
147   // read the complete value. ReadBlobRequest() should be used to read the
148   // complete value.
149   using ReadCallback = fit::function<void(
150       att::Result<>, const ByteBuffer&, bool maybe_truncated)>;
151   virtual void ReadRequest(att::Handle handle, ReadCallback callback) = 0;
152 
153   // Sends an ATT Read by Type Request with the requested attribute handle range
154   // |start_handle| to |end_handle| (inclusive), and returns a ReadByTypeResult
155   // containing either the handle-value pairs successfully read, or an error
156   // status and related handle (if any).
157   //
158   // Attribute values may be truncated, as indicated by
159   // ReadByTypeValue.maybe_truncated. ReadRequest() or ReadBlobRequest() should
160   // be used to read the complete values. (Core Spec v5.2, Vol 3, Part
161   // F, 3.4.4.2)
162   //
163   // The attributes returned will be the attributes with the lowest handles in
164   // the handle range in ascending order, and may not include all matching
165   // attributes. To read all attributes, make additional requests with an
166   // updated |start_handle| until an error response with error code "Attribute
167   // Not Found" is received, or the entire handle range has been read. (Core
168   // Spec v5.2, Vol 3, Part F, 3.4.4.1).
169   struct ReadByTypeValue {
170     att::Handle handle;
171     // The underlying value buffer is only valid for the duration of |callback|.
172     // Callers must make a copy if they need to retain the buffer.
173     BufferView value;
174     // True if |value| might be truncated.
175     bool maybe_truncated;
176   };
177   struct ReadByTypeError {
178     att::Error error;
179     // Only some att protocol errors include a handle. This handle can either be
180     // |start_handle| or the handle of the attribute causing the error,
181     // depending on the error (Core Spec v5.2, Vol 3, Part F, 3.4.4.1).
182     std::optional<att::Handle> handle;
183   };
184   using ReadByTypeResult =
185       fit::result<ReadByTypeError, std::vector<ReadByTypeValue>>;
186   using ReadByTypeCallback = fit::function<void(ReadByTypeResult)>;
187   virtual void ReadByTypeRequest(const UUID& type,
188                                  att::Handle start_handle,
189                                  att::Handle end_handle,
190                                  ReadByTypeCallback callback) = 0;
191 
192   // Sends an ATT Read Blob request with the requested attribute |handle| and
193   // returns the result value in |callback|. This can be called multiple times
194   // to read the value of a characteristic that is larger than (ATT_MTU - 1).
195   // If the attribute value might be longer than the reported value, the
196   // |maybe_truncated| callback parameter will be true. (Vol 3, Part G, 4.8.3)
197   virtual void ReadBlobRequest(att::Handle handle,
198                                uint16_t offset,
199                                ReadCallback callback) = 0;
200 
201   // Sends an ATT Write Request with the requested attribute |handle| and
202   // |value|. This can be used to send a write request to any attribute.
203   // (Vol 3, Part F, 3.4.5.1).
204   //
205   // Reports the status of the procedure in |callback|.
206   // HostError::kPacketMalformed is returned if |value| is too large to write in
207   // a single ATT request.
208   virtual void WriteRequest(att::Handle handle,
209                             const ByteBuffer& value,
210                             att::ResultFunction<> callback) = 0;
211 
212   // Adds a new PrepareWriteQueue to be sent. This sends multiple
213   // PrepareWriteRequests followed by an ExecuteWriteRequest. The request will
214   // be enqueued if there are any pending.
215   //
216   // Reports the status of the procedure in |callback|.
217   // HostError::kPacketMalformed is returned if any writes in the queue are too
218   // large to write in a single ATT request.
219   virtual void ExecutePrepareWrites(att::PrepareWriteQueue prep_write_queue,
220                                     ReliableMode reliable_mode,
221                                     att::ResultFunction<> callback) = 0;
222 
223   // Sends an ATT Prepare Write Request with the requested attribute |handle|,
224   // |offset|, and |part_value|. This can be used to send a long write request
225   // to any attribute by following with 0-N prepare write requests and finally
226   // an Execute Write Request.
227   // (Vol 3, Part G, 4.9.4)
228   //
229   // Reports the status of the procedure in |callback|, along with mirroring the
230   // data written to the buffer.
231   // HostError::kPacketMalformed is returned if |part_value| is too large to
232   // write in a single ATT request.
233   using PrepareCallback = fit::function<void(att::Result<>, const ByteBuffer&)>;
234   virtual void PrepareWriteRequest(att::Handle handle,
235                                    uint16_t offset,
236                                    const ByteBuffer& part_value,
237                                    PrepareCallback callback) = 0;
238 
239   // Following a series of Prepare Write Requests, this will write the series if
240   // the input is kWritePending, or cancel all pending if it is kCancelAll.
241   // (Vol 3, Part G, 4.9.4)
242   //
243   // Reports the status of the procedure in |callback|.
244   virtual void ExecuteWriteRequest(att::ExecuteWriteFlag flag,
245                                    att::ResultFunction<> callback) = 0;
246 
247   // Sends an ATT Write Command with the requested |handle| and |value|. This
248   // should only be used with characteristics that support the "Write Without
249   // Response" property.
250   //
251   // Reports the status of the procedure in |callback|.
252   virtual void WriteWithoutResponse(att::Handle handle,
253                                     const ByteBuffer& value,
254                                     att::ResultFunction<> callback) = 0;
255 
256   // Assigns a callback that will be called when a notification or indication
257   // PDU is received.
258   using NotificationCallback = fit::function<void(bool indication,
259                                                   att::Handle handle,
260                                                   const ByteBuffer& value,
261                                                   bool maybe_truncated)>;
262   virtual void SetNotificationHandler(NotificationCallback handler) = 0;
263 };
264 
265 }  // namespace bt::gatt
266