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 18 #include <unordered_set> 19 20 #include "pw_bluetooth_sapphire/internal/host/att/att.h" 21 #include "pw_bluetooth_sapphire/internal/host/common/byte_buffer.h" 22 #include "pw_bluetooth_sapphire/internal/host/gatt/local_service_manager.h" 23 #include "pw_bluetooth_sapphire/internal/host/gatt/persisted_data.h" 24 25 namespace bt::gatt { 26 constexpr IdType kServiceChangedChrcId = 0u; 27 28 // Callback to send an indication. Used to inject the GATT object's 29 // update-sending ability without requiring this service to carry a reference to 30 // GATT or Server. 31 // |chrc_id|: the service-defined ID of the characteristic to indicate 32 // |svc_id|: the gatt::GATT-defined ID of the service containing |chrc_id|. 33 // For example, to indicate a new service to a peer via the Service Changed 34 // chrc, one would invoke this with svc_id = the GenericAttributeService's 35 // service_id_, chrc_id = kServiceChangedChrcId, peer_id of the peer, and value 36 // = the att::Handle range of the new service. 37 using SendIndicationCallback = fit::function<void( 38 IdType svc_id, IdType chrc_id, PeerId peer_id, BufferView value)>; 39 40 // Implements the "Generic Attribute Profile Service" containing the "Service 41 // Changed" characteristic that is "...used to indicate to connected devices 42 // that services have changed (Vol 3, Part G, 7)." 43 class GenericAttributeService final { 44 public: 45 // Registers this service and makes this service the callee of the Service 46 // Changed callback. GATT remote clients must still request that they be sent 47 // indications for the Service Changed characteristic. Holds the 48 // LocalServiceManager pointer for this object's lifetime. Do not register 49 // multiple instances of this service in a single bt-host. 50 GenericAttributeService(LocalServiceManager::WeakPtr local_service_manager, 51 SendIndicationCallback send_indication_callback); 52 ~GenericAttributeService(); 53 54 // This callback is called when a client changes the CCC for the service 55 // changed characteristic to inform the upper layers of the stack to persist 56 // this value. SetPersistServiceChangedCCCCallback(PersistServiceChangedCCCCallback callback)57 void SetPersistServiceChangedCCCCallback( 58 PersistServiceChangedCCCCallback callback) { 59 persist_service_changed_ccc_callback_ = std::move(callback); 60 } 61 62 // Set the service changed indication subscription for a given peer. 63 void SetServiceChangedIndicationSubscription(PeerId peer_id, bool indicate); 64 service_id()65 inline IdType service_id() const { return service_id_; } 66 67 private: 68 void Register(); 69 70 // Send indications to subscribed clients when a service has changed. 71 void OnServiceChanged(IdType service_id, att::Handle start, att::Handle end); 72 73 // Data store against which to register and unregister this service. It must 74 // outlive this instance. 75 LocalServiceManager::WeakPtr local_service_manager_; 76 const SendIndicationCallback send_indication_callback_; 77 78 // Peers that have subscribed to indications. 79 std::unordered_set<PeerId> subscribed_peers_; 80 81 // Handle for the Service Changed characteristic that is read when it is first 82 // configured for indications. 83 att::Handle svc_changed_handle_ = att::kInvalidHandle; 84 85 // Local service ID; hidden because registration is tied to instance lifetime. 86 IdType service_id_ = kInvalidId; 87 88 // Callback to inform uper stack layers to persist service changed CCC. 89 PersistServiceChangedCCCCallback persist_service_changed_ccc_callback_; 90 }; 91 92 } // namespace bt::gatt 93