1 // Copyright 2022 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 #pragma once 15 16 #include <memory> 17 18 #include "pw_bluetooth/gatt/constants.h" 19 #include "pw_bluetooth/gatt/error.h" 20 #include "pw_bluetooth/gatt/types.h" 21 #include "pw_bluetooth/internal/raii_ptr.h" 22 #include "pw_bluetooth/result.h" 23 #include "pw_bluetooth/types.h" 24 #include "pw_containers/vector.h" 25 #include "pw_function/function.h" 26 #include "pw_span/span.h" 27 28 namespace pw::bluetooth::gatt { 29 30 /// Represents a GATT service on a remote GATT server. 31 /// Clients should call `SetErrorCallback` before using in order to handle fatal 32 /// errors. 33 class RemoteService { 34 public: 35 enum class RemoteServiceError { 36 /// The service has been modified or removed. 37 kServiceRemoved = 0, 38 39 /// The peer serving this service has disconnected. 40 kPeerDisconnected = 1, 41 }; 42 43 /// Wrapper around a possible truncated value received from the server. 44 struct ReadValue { 45 /// Characteristic or descriptor handle. 46 Handle handle; 47 48 /// The value of the characteristic or descriptor. 49 Vector<std::byte> value; 50 51 /// True if `value` might be truncated (the buffer was completely filled by 52 /// the server and the read was a short read). `ReadCharacteristic` or 53 /// `ReadDescriptor` should be used to read the complete value. 54 bool maybe_truncated; 55 }; 56 57 /// A result returned by `ReadByType`. 58 struct ReadByTypeResult { 59 /// Characteristic or descriptor handle. 60 Handle handle; 61 62 /// The value of the characteristic or descriptor, if it was read 63 /// successfully, or an error explaining why the value could not be read. 64 Result<Error, ReadValue> result; 65 }; 66 67 /// Represents the supported options to read a long characteristic or 68 /// descriptor value from a server. Long values are those that may not fit in 69 /// a single message (longer than 22 bytes). 70 struct LongReadOptions { 71 /// The byte to start the read at. Must be less than the length of the 72 /// value. 73 uint16_t offset = 0; 74 75 /// The maximum number of bytes to read. 76 uint16_t max_bytes = kMaxValueLength; 77 }; 78 79 /// Represents the supported write modes for writing characteristics & 80 /// descriptors to the server. 81 enum class WriteMode : uint8_t { 82 /// Wait for a response from the server before returning but do not verify 83 /// the echo response. Supported for both characteristics and descriptors. 84 kDefault = 0, 85 86 /// Every value blob is verified against an echo response from the server. 87 /// The procedure is aborted if a value blob has not been reliably delivered 88 /// to the peer. Only supported for characteristics. 89 kReliable = 1, 90 91 /// Delivery will not be confirmed before returning. Writing without a 92 /// response is only supported for short characteristics with the 93 /// `WRITE_WITHOUT_RESPONSE` property. The value must fit into a single 94 /// message. It is guaranteed that at least 20 bytes will fit into a single 95 /// message. If the value does not fit, a `kFailure` error will be produced. 96 /// The value will be written at offset 0. Only supported for 97 /// characteristics. 98 kWithoutResponse = 2, 99 }; 100 101 /// Represents the supported options to write a characteristic/descriptor 102 /// value to a server. 103 struct WriteOptions { 104 /// The mode of the write operation. For descriptors, only 105 /// `WriteMode::kDefault` is supported 106 WriteMode mode = WriteMode::kDefault; 107 108 /// Request a write starting at the byte indicated. 109 /// Must be 0 if `mode` is `WriteMode.kWithoutResponse`. 110 uint16_t offset = 0; 111 }; 112 113 using ReadByTypeCallback = Function<void(Result<Vector<ReadByTypeResult>>)>; 114 using ReadCallback = Function<void(Result<ReadValue>)>; 115 using NotificationCallback = Function<void(ReadValue)>; 116 117 /// Set a callback that will be called when there is an error with this 118 /// RemoteService, after which this RemoteService will be invalid. 119 void SetErrorCallback(Function<void(RemoteServiceError)>&& error_callback); 120 121 /// Calls `characteristic_callback` with the characteristics and descriptors 122 /// in this service. 123 void DiscoverCharacteristics( 124 Function<void(Characteristic)>&& characteristic_callback); 125 126 /// Reads characteristics and descriptors with the specified type. This method 127 /// is useful for reading values before discovery has completed, thereby 128 /// reducing latency. 129 /// @param uuid The UUID of the characteristics/descriptors to read. 130 /// @param result_callback Results are returned via this callback. Results may 131 /// be empty if no matching values are read. If reading a value results in a 132 /// permission error, the handle and error will be included. 133 /// 134 /// This may fail with the following errors: 135 /// - kInvalidParameters: if `uuid` refers to an internally reserved 136 /// descriptor type (e.g. the Client Characteristic Configuration descriptor). 137 /// - kTooManyResults: More results were read than can fit in a Vector. 138 /// Consider reading characteristics/descriptors individually after performing 139 /// discovery. 140 /// - kFailure: The server returned an error not specific to a single result. 141 void ReadByType(Uuid uuid, ReadByTypeCallback&& result_callback); 142 143 /// Reads the value of a characteristic. 144 /// @param handle The handle of the characteristic to be read. 145 /// @param options If null, a short read will be performed, which may be 146 /// truncated to what fits in a single message (at least 22 bytes). If long 147 /// read options are present, performs a long read with the indicated options. 148 /// @param result_callback called with the result of the read and the value of 149 /// the characteristic if successful. 150 /// @retval kInvalidHandle `handle` is invalid. 151 /// @retval kInvalidParameters `options` is invalid. 152 /// @retval kReadNotPermitted The server rejected the request. 153 /// @retval kInsufficient* The server rejected the request. 154 /// @retval kFailure The server returned an error not covered by the above. 155 void ReadCharacteristic(Handle handle, 156 std::optional<LongReadOptions> options, 157 ReadCallback&& result_callback); 158 159 /// Writes `value` to the characteristic with `handle` using the provided 160 /// `options`. It is not recommended to send additional writes while a write 161 /// is already in progress (the server may receive simultaneous writes in any 162 /// order). 163 /// 164 /// @param handle Handle of the characteristic to be written to 165 /// @param value The value to be written. 166 /// @param options Options that apply to the write. 167 /// @param result_callback Returns a result upon completion of the write. 168 /// @retval kInvalidHandle `handle` is invalid. 169 /// @retval kInvalidParameters`options is invalid. 170 /// @retval kWriteNotPermitted The server rejected the request. 171 /// @retval kInsufficient* The server rejected the request. 172 /// @retval kFailure The server returned an error not covered by the above 173 /// errors. 174 void WriteCharacteristic(Handle handle, 175 span<const std::byte> value, 176 WriteOptions options, 177 Function<void(Result<Error>)>&& result_callback); 178 179 /// Reads the value of the characteristic descriptor with `handle` and 180 /// returns it in the reply. 181 /// @param handle The descriptor handle to read. 182 /// @param options Options that apply to the read. 183 /// @param result_callback Returns a result containing the value of the 184 /// descriptor on success. 185 /// @retval kInvalidHandle `handle` is invalid. 186 /// @retval kInvalidParameters`options` is invalid. 187 /// @retval kReadNotPermitted 188 /// @retval kInsufficient* The server rejected the request. 189 /// @retval kFailure The server returned an error not covered by the above 190 /// errors. 191 void ReadDescriptor(Handle handle, 192 std::optional<LongReadOptions> options, 193 ReadCallback&& result_callback); 194 195 /// Writes `value` to the descriptor with `handle` using the provided 196 /// `options`. It is not recommended to send additional writes while a write 197 /// is already in progress (the server may receive simultaneous writes in any 198 /// order). 199 /// 200 /// @param handle Handle of the descriptor to be written to 201 /// @param value The value to be written. 202 /// @param options Options that apply to the write. 203 /// @param result_callback Returns a result upon completion of the write. 204 /// @retval kInvalidHandle `handle` is invalid. 205 /// @retval kInvalidParameters `options is invalid 206 /// @retval kWriteNotPermitted The server rejected the request. 207 /// @retval kInsufficient* The server rejected the request. 208 /// @retval kFailure The server returned an error not covered by the above 209 /// errors. 210 void WriteDescriptor(Handle handle, 211 span<const std::byte> value, 212 WriteOptions options, 213 Function<void(Result<Error>)>&& result_callback); 214 215 /// Subscribe to notifications & indications from the characteristic with 216 /// the given `handle`. 217 /// 218 /// Either notifications or indications will be enabled depending on 219 /// characteristic properties. Indications will be preferred if they are 220 /// supported. This operation fails if the characteristic does not have the 221 /// "notify" or "indicate" property. 222 /// 223 /// A write request will be issued to configure the characteristic for 224 /// notifications/indications if it contains a Client Characteristic 225 /// Configuration descriptor. This method fails if an error occurs while 226 /// writing to the descriptor. 227 /// 228 /// On success, `notification_callback` will be called when 229 /// the peer sends a notification or indication. Indications are 230 /// automatically confirmed. 231 /// 232 /// Subscriptions can be canceled with `StopNotifications`. 233 /// 234 /// @param handle the handle of the characteristic to subscribe to. 235 /// @param notification_callback will be called with the values of 236 /// notifications/indications when received. 237 /// @param result_callback called with the result of enabling 238 /// notifications/indications. 239 /// @retval kFailure The characteristic does not support notifications or 240 /// indications. 241 /// @retval kInvalidHandle `handle` is invalid. 242 /// @retval kWriteNotPermitted CCC descriptor write error. 243 /// @retval Insufficient* CCC descriptor write error. 244 void RegisterNotificationCallback( 245 Handle handle, 246 NotificationCallback&& notification_callback, 247 Function<void(Result<Error>)>&& result_callback); 248 249 /// Stops notifications for the characteristic with the given `handle`. 250 void StopNotifications(Handle handle); 251 252 private: 253 /// Disconnect from the remote service. This method is called by the 254 /// ~RemoteService::Ptr() when it goes out of scope, the API client should 255 /// never call this method. 256 void Disconnect(); 257 258 public: 259 /// Movable RemoteService smart pointer. The remote server will remain 260 /// connected until the returned RemoteService::Ptr is destroyed. 261 using Ptr = internal::RaiiPtr<RemoteService, &RemoteService::Disconnect>; 262 }; 263 264 /// Represents a GATT client that interacts with services on a GATT server. 265 class Client { 266 public: 267 /// Represents a remote GATT service. 268 struct RemoteServiceInfo { 269 /// Uniquely identifies this GATT service. 270 Handle handle; 271 272 /// Indicates whether this is a primary or secondary service. 273 bool primary; 274 275 /// The UUID that identifies the type of this service. 276 /// There may be multiple services with the same UUID. 277 Uuid type; 278 }; 279 280 virtual ~Client() = default; 281 282 /// Enumerates existing services found on the peer that this Client 283 /// represents, and provides a stream of updates thereafter. Results can be 284 /// filtered by specifying a list of UUIDs in `uuids`. To further interact 285 /// with services, clients must obtain a RemoteService protocol by calling 286 /// ConnectToService(). `uuid_allowlist` - The allowlist of UUIDs to filter 287 /// services with. `updated_callback` - Will be called with services that are 288 /// updated/modified. 289 /// `removed_callback` - Called with the handles of services 290 /// that have been removed. Note that handles may be reused. 291 virtual void WatchServices( 292 Vector<Uuid> uuid_allowlist, 293 Function<void(RemoteServiceInfo)>&& updated_callback, 294 Function<void(Handle)>&& removed_callback) = 0; 295 296 /// Stops service watching if started by `WatchServices`. 297 virtual void StopWatchingServices(); 298 299 /// Connects to a RemoteService. Only 1 connection per service is allowed. 300 /// `handle` - the handle of the service to connect to. 301 /// 302 /// This may fail with the following errors: 303 /// kInvalidParameters - `handle` does not correspond to a known service. 304 virtual Result<Error, RemoteService::Ptr> ConnectToService(Handle handle) = 0; 305 }; 306 307 } // namespace pw::bluetooth::gatt 308