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