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