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/common/assert.h"
21 #include "pw_bluetooth_sapphire/internal/host/common/macros.h"
22 #include "pw_bluetooth_sapphire/internal/host/gatt/client.h"
23 #include "pw_bluetooth_sapphire/internal/host/gatt/remote_characteristic.h"
24 
25 namespace bt::gatt {
26 
27 class RemoteService;
28 
29 using ServiceList = std::vector<WeakSelf<RemoteService>::WeakPtr>;
30 
31 // Callback type invoked when GATT services are removed, added, or modified.
32 // `added` and `modified` are not combined into `updated` for flexibility and
33 // debuggability. Modified service handles are not included in `removed`. NOTE:
34 // `removed` services should be handled first because they may share handles
35 // with `added` services.
36 using RemoteServiceWatcher = fit::function<void(
37     std::vector<att::Handle> removed, ServiceList added, ServiceList modified)>;
38 
39 using ServiceListCallback = fit::function<void(att::Result<>, ServiceList)>;
40 
41 using DescriptorMap = std::map<DescriptorHandle, DescriptorData>;
42 using CharacteristicMap =
43     std::map<CharacteristicHandle,
44              std::pair<CharacteristicData, DescriptorMap>>;
45 
46 namespace internal {
47 class RemoteServiceManager;
48 }  // namespace internal
49 
50 // Represents the state of a GATT service that was discovered on a remote
51 // device. Clients can interact with a remote GATT service by obtaining a
52 // RemoteService object from the GATT system.
53 class RemoteService final {
54  public:
55   // In production, a RemoteService should only be constructed by a
56   // RemoteServiceManager. The constructor and destructor are made available for
57   // testing.
58   RemoteService(const ServiceData& service_data, Client::WeakPtr client);
59   ~RemoteService();
60 
61   // If true, a Service Changed notification for this service was received. This
62   // service may no longer exist or may have been modified (and so no writes
63   // should be performed upon destruction).
set_service_changed(bool service_changed)64   void set_service_changed(bool service_changed) {
65     service_changed_ = service_changed;
66   }
67 
info()68   const ServiceData& info() const { return service_data_; }
69 
70   // Returns the service range start handle. This is used to uniquely identify
71   // this service.
handle()72   att::Handle handle() const { return service_data_.range_start; }
73 
74   // Returns the service UUID.
uuid()75   const UUID& uuid() const { return service_data_.type; }
76 
77   // The current ATT_MTU.
att_mtu()78   uint16_t att_mtu() const { return client_->mtu(); }
79 
80   // Adds a handler which will be called when this service gets removed.
81   // Returns false if the service was already shut down.
82   bool AddRemovedHandler(fit::closure handler);
83 
84   // Returns true if all contents of this service have been discovered. This is
85   // primarily intended for unit tests. Clients should not rely on this and use
86   // DiscoverCharacteristics() to guarantee discovery.
87   bool IsDiscovered() const;
88 
89   // Performs characteristic discovery and reports the result asynchronously in
90   // |callback|. Returns the cached results if characteristics were already
91   // discovered.
92   using CharacteristicCallback =
93       fit::function<void(att::Result<>, const CharacteristicMap&)>;
94   void DiscoverCharacteristics(CharacteristicCallback callback);
95 
96   // Sends a read request to the characteristic with the given identifier. Fails
97   // if characteristics have not been discovered.
98   // |maybe_truncated| indicates whether the full value might be longer than the
99   // reported value.
100   using ReadValueCallback = fit::function<void(
101       att::Result<>, const ByteBuffer&, bool maybe_truncated)>;
102   void ReadCharacteristic(CharacteristicHandle id, ReadValueCallback callback);
103 
104   // Performs the "Read Long Characteristic Values" procedure which allows
105   // characteristic values larger than the ATT_MTU to be read over multiple
106   // requests.
107   //
108   // The read will start at |offset| and will return at most |max_bytes| octets.
109   // The resulting value will be returned via |callback|.
110   // The value of |maybe_truncated| reported to the callback indicates whether
111   // the full value may be larger than the reported value. This is only possible
112   // if |max_bytes| are read, and |max_bytes| is less than
113   // att::kMaxAttributeValueLength.
114   void ReadLongCharacteristic(CharacteristicHandle id,
115                               uint16_t offset,
116                               size_t max_bytes,
117                               ReadValueCallback callback);
118 
119   // Sends a read by type request for attribute values in this service with the
120   // given |type| and returns read values via |callback|. If no matching
121   // attributes are found, the callback status will indicate success and the
122   // vector of values will be empty.
123   //
124   // If a permission error occurs for an attribute, the error and handle of the
125   // attribute that caused the error will be included in the results and the
126   // overall status will indicate success. If a general error occurs, the status
127   // will indicate the error and no results will be returned.
128   //
129   // |type| must be the UUID of a characteristic or descriptor value, NOT an
130   // internal GATT UUID such as a service or characteristic declaration (the
131   // callback will be invoked with an error in this case).
132   //
133   // NOTE: The values returned may be truncated, as indicated by
134   // ReadByTypeResult.maybe_truncated. ReadCharacteristic(),
135   // ReadLongCharacteristic(), ReadDescriptor(), and ReadLongDescriptor() should
136   // be used to read complete values.
137   struct ReadByTypeResult {
138     CharacteristicHandle handle;
139     fit::result<att::ErrorCode, ByteBufferPtr> result;
140     bool maybe_truncated;
141   };
142   using ReadByTypeCallback =
143       fit::function<void(att::Result<>, std::vector<ReadByTypeResult>)>;
144   void ReadByType(const UUID& type, ReadByTypeCallback callback);
145 
146   // Sends a write request to the characteristic with the given identifier.
147   //
148   // TODO(armansito): Add a ByteBuffer version.
149   void WriteCharacteristic(CharacteristicHandle id,
150                            std::vector<uint8_t> value,
151                            att::ResultFunction<> callback);
152 
153   // Sends a write request to the characteristic with the given identifier at
154   // the given offset, will write over multiple requests if needed. Fails if
155   // characteristics have not been discovered.
156   //
157   // TODO(armansito): Add a ByteBuffer version.
158   void WriteLongCharacteristic(CharacteristicHandle id,
159                                uint16_t offset,
160                                std::vector<uint8_t> value,
161                                ReliableMode reliable_mode,
162                                att::ResultFunction<> callback);
163 
164   // Sends a "Write Without Response" to the characteristic with the given
165   // identifier. Fails if characteristics have not been discovered.
166   void WriteCharacteristicWithoutResponse(CharacteristicHandle id,
167                                           std::vector<uint8_t> value,
168                                           att::ResultFunction<> cb);
169 
170   // Performs the "Read Characteristic Descriptors" procedure (v5.0, Vol 3, Part
171   // G, 4.12.1).
172   // The callback parameter |maybe_truncated| indicates whether the full value
173   // might be longer than the reported value.
174   void ReadDescriptor(DescriptorHandle id, ReadValueCallback callback);
175 
176   // Performs the "Read Long Characteristic Descriptors" procedure (v5.0, Vol 3,
177   // Part G, 4.12.2).
178   // The callback parameter |maybe_truncated| indicates whether the full value
179   // may be larger than the reported value. This is only possible if |max_bytes|
180   // are read, and |max_bytes| is less than att::kMaxAttributeValueLength.
181   void ReadLongDescriptor(DescriptorHandle id,
182                           uint16_t offset,
183                           size_t max_bytes,
184                           ReadValueCallback callback);
185 
186   // Performs the "Write Characteristic Descriptors" procedure (v5.0, Vol 3,
187   // Part G, 4.12.3).
188   //
189   // TODO(armansito): Add a ByteBuffer version.
190   void WriteDescriptor(DescriptorHandle id,
191                        std::vector<uint8_t> value,
192                        att::ResultFunction<> callback);
193 
194   // Performs the "Write Long Characteristic Descriptors" procedure (v5.0, Vol
195   // 3, Part G, 4.12.4).
196   //
197   // TODO(armansito): Add a ByteBuffer version.
198   void WriteLongDescriptor(DescriptorHandle id,
199                            uint16_t offset,
200                            std::vector<uint8_t> value,
201                            att::ResultFunction<> callback);
202 
203   // Subscribe to characteristic handle/value notifications or indications
204   // from the characteristic with the given identifier. Either notifications or
205   // indications will be enabled depending on the characteristic properties.
206   //
207   // This method can be called more than once to register multiple subscribers.
208   // The remote Client Characteristic Configuration descriptor will be written
209   // only if this is called for the first subscriber.
210   //
211   // |status_callback| will be called with the status of the operation. On
212   // success, a |handler_id| will be returned that can be used to unregister the
213   // handler.
214   //
215   // On success, notifications will be delivered to |callback|.
216   using ValueCallback = RemoteCharacteristic::ValueCallback;
217   using NotifyStatusCallback = RemoteCharacteristic::NotifyStatusCallback;
218   void EnableNotifications(CharacteristicHandle id,
219                            ValueCallback callback,
220                            NotifyStatusCallback status_callback);
221 
222   // Disables characteristic notifications for the given |handler_id| previously
223   // obtained via EnableNotifications. The value of the Client Characteristic
224   // Configuration descriptor will be cleared if no subscribers remain.
225   void DisableNotifications(CharacteristicHandle characteristic_id,
226                             IdType handler_id,
227                             att::ResultFunction<> status_callback);
228 
229   // Simulate receiving a notification. HandleNotification is usually called by
230   // RemoteServiceManager, but tests without a RemoteServiceManager may use this
231   // method.
HandleNotificationForTesting(att::Handle value_handle,const ByteBuffer & value,bool maybe_truncated)232   void HandleNotificationForTesting(att::Handle value_handle,
233                                     const ByteBuffer& value,
234                                     bool maybe_truncated) {
235     HandleNotification(value_handle, value, maybe_truncated);
236   }
237 
238   using WeakPtr = WeakSelf<RemoteService>::WeakPtr;
GetWeakPtr()239   RemoteService::WeakPtr GetWeakPtr() { return weak_self_.GetWeakPtr(); }
240 
241  private:
242   friend class internal::RemoteServiceManager;
243 
244   static constexpr size_t kSentinel = std::numeric_limits<size_t>::max();
245 
246   // Returns a pointer to the characteristic with |id|. Returns nullptr if not
247   // found.
248   fit::result<Error<>> GetCharacteristic(CharacteristicHandle id,
249                                          RemoteCharacteristic** out_char);
250 
251   // Returns a pointer to the characteristic descriptor with |id|. Returns
252   // nullptr if not found.
253   fit::result<Error<>> GetDescriptor(DescriptorHandle id,
254                                      const DescriptorData** out_desc);
255 
256   // Called immediately after characteristic discovery to initiate descriptor
257   // discovery.
258   void StartDescriptorDiscovery();
259 
260   // Completes all pending characteristic discovery requests.
261   void CompleteCharacteristicDiscovery(att::Result<> status);
262 
263   // Breaks Long Write requests down into a PrepareWriteQueue, then enqueues
264   // for the client to process. Drives the "Write Long Characteristic/
265   // Descriptor Values" procedure. Called by WriteCharacteristic() and
266   // WriteDescriptor().
267   void SendLongWriteRequest(att::Handle value_handle,
268                             uint16_t offset,
269                             BufferView value,
270                             ReliableMode reliable_mode,
271                             att::ResultFunction<> callback);
272 
273   // Helper function that drives the recursive "Read Long Characteristic Values"
274   // procedure. Called by ReadLongCharacteristic().
275   void ReadLongHelper(att::Handle value_handle,
276                       uint16_t offset,
277                       MutableByteBufferPtr buffer,
278                       size_t bytes_read,
279                       ReadValueCallback callback);
280 
281   // Helper function that drives the recursive "Read by Type" procedure.
282   // Accumulates attribute values in |results| until either |start| > |end| or
283   // an error occurs. On completion, accumulated |results| and the status are
284   // passed to |callback|. Called by ReadByType().
285   void ReadByTypeHelper(const UUID& type,
286                         att::Handle start,
287                         att::Handle end,
288                         std::vector<ReadByTypeResult> results,
289                         ReadByTypeCallback callback);
290 
291   // Returns true if characteristic discovery has completed.
HasCharacteristics()292   inline bool HasCharacteristics() const {
293     return remaining_descriptor_requests_ == 0u;
294   }
295 
296   // Called by RemoteServiceManager when a notification is received for one of
297   // this service's characteristics.
298   void HandleNotification(att::Handle value_handle,
299                           const ByteBuffer& value,
300                           bool maybe_truncated);
301 
302   ServiceData service_data_;
303 
304   // The GATT Client bearer for performing remote procedures.
305   Client::WeakPtr client_;
306 
307   // Queued discovery requests.
308   using PendingDiscoveryList = std::vector<CharacteristicCallback>;
309   PendingDiscoveryList pending_discov_reqs_;
310 
311   // The known characteristics of this service. If not |characteristics_ready_|,
312   // this may contain a partial list of characteristics stored during the
313   // discovery process.
314   //
315   // The id of each characteristic corresponds to its index in this vector.
316   std::map<CharacteristicHandle, RemoteCharacteristic> characteristics_;
317 
318   // The number of pending characteristic descriptor discoveries.
319   // Characteristics get marked as ready when this number reaches 0.
320   size_t remaining_descriptor_requests_;
321 
322   // Indicates whether the service was changed, as indicated by a service
323   // changed notification.
324   bool service_changed_ = false;
325 
326   // Called by ShutDown().
327   std::vector<fit::callback<void()>> rm_handlers_;
328 
329   WeakSelf<RemoteService> weak_self_{this};
330 
331   BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(RemoteService);
332 };
333 
334 }  // namespace bt::gatt
335