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