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