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