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 <unordered_map> 17 18 #include "pw_bluetooth_sapphire/internal/host/att/database.h" 19 #include "pw_bluetooth_sapphire/internal/host/common/macros.h" 20 #include "pw_bluetooth_sapphire/internal/host/common/weak_self.h" 21 #include "pw_bluetooth_sapphire/internal/host/gatt/gatt_defs.h" 22 #include "pw_bluetooth_sapphire/internal/host/gatt/types.h" 23 24 namespace bt::gatt { 25 26 // Called to read the value of a dynamic characteristic or characteristic 27 // descriptor. 28 // - |peer_id|: The PeerId of the peer making the request. 29 // - |service_id|: Identifies the service that the object belongs to. 30 // - |id|: Identifies the object to be read. This is a user assigned 31 // identifier provided while registering the service. 32 // - |offset|: The offset into the value that is being read. 33 // - |responder|: Should be called to respond to the read request with a 34 // characteristic or descriptor value, or an ATT error code. 35 using ReadHandler = fit::function<void(PeerId peer_id, 36 IdType service_id, 37 IdType id, 38 uint16_t offset, 39 ReadResponder responder)>; 40 41 // Called to write the value of a dynamic characteristic or characteristic 42 // descriptor. 43 // - |peer_id|: The PeerId of the peer making the request. 44 // - |service_id|: Identifies the service that the object belongs to. 45 // - |id|: Identifies the object to be written. This is a user assigned 46 // identifier provided while registering the service. 47 // - |offset|: The offset into the value that is being written. 48 // - |responder|: Should be called to respond to the write request with 49 // success or an ATT error code. This can be a null callback 50 // if the client has initiated a "Write Without Response" 51 // procedure, in which case a response is not required. 52 using WriteHandler = fit::function<void(PeerId peer_id, 53 IdType service_id, 54 IdType id, 55 uint16_t offset, 56 const ByteBuffer& value, 57 WriteResponder responder)>; 58 59 // Called when the peer device with the given |peer_id| has enabled or disabled 60 // notifications/indications on the characteristic with id |chrc_id|. 61 using ClientConfigCallback = fit::function<void(IdType service_id, 62 IdType chrc_id, 63 PeerId peer_id, 64 bool notify, 65 bool indicate)>; 66 67 // Called with the ID and range of attributes handles spanned (inclusive) by a 68 // service that was added or removed. 69 using ServiceChangedCallback = 70 fit::function<void(IdType service_id, att::Handle start, att::Handle end)>; 71 72 // LocalServiceManager allows clients to implement GATT services. This 73 // internally maintains an attribute database and provides hooks for clients to 74 // respond to read and write requests, send notifications/indications, 75 // add/remove services, etc. 76 class LocalServiceManager final : public WeakSelf<LocalServiceManager> { 77 public: 78 LocalServiceManager(); 79 // Even though this is a noop, cannot be defaulted due to forward declaration 80 // of ServiceData. 81 ~LocalServiceManager(); 82 83 // Registers the GATT service hierarchy represented by |service| with the 84 // local attribute database. Once successfully registered, the service will be 85 // available for discovery and other ATT protocol requests. 86 // 87 // This method returns an opaque identifier on successful registration, which 88 // can be used by the caller to refer to the service in the future. 89 // 90 // Returns |kInvalidId| on failure. Registration can fail if the attribute 91 // database has run out of handles or if the hierarchy contains 92 // characteristics or descriptors with repeated IDs. Objects under |service| 93 // must have unique identifiers to aid in value request handling. 94 IdType RegisterService(ServicePtr service, 95 ReadHandler read_handler, 96 WriteHandler write_handler, 97 ClientConfigCallback ccc_callback); 98 99 // Unregisters the GATT service hierarchy identified by |service_id|. Returns 100 // false if |service_id| is unrecognized. 101 bool UnregisterService(IdType service_id); 102 103 // Returns the client characteristic configuration for the given |peer_id| and 104 // the characteristic identified by |service_id| and |chrc_id|. Returns false 105 // if |service_id| is unknown or no configurations exist for |chrc_id|. 106 struct ClientCharacteristicConfig { 107 att::Handle handle; 108 bool notify; 109 bool indicate; 110 }; 111 bool GetCharacteristicConfig(IdType service_id, 112 IdType chrc_id, 113 PeerId peer_id, 114 ClientCharacteristicConfig* out_config); 115 116 // Erase any client characteristic configuration associated to a specific 117 // client and invoke its ClientConfigCallback to signal that notifications and 118 // indications are now disabled. 119 void DisconnectClient(PeerId peer_id); 120 set_service_changed_callback(ServiceChangedCallback callback)121 void set_service_changed_callback(ServiceChangedCallback callback) { 122 service_changed_callback_ = std::move(callback); 123 } 124 database()125 inline att::Database::WeakPtr database() { return db_->GetWeakPtr(); } 126 127 private: 128 class ServiceData; 129 130 std::unique_ptr<att::Database> db_; 131 IdType next_service_id_; 132 133 // Mapping from service instance ids to ServiceData. 134 std::unordered_map<IdType, std::unique_ptr<ServiceData>> services_; 135 136 ServiceChangedCallback service_changed_callback_; 137 138 BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(LocalServiceManager); 139 }; 140 141 } // namespace bt::gatt 142