xref: /aosp_15_r20/external/pigweed/pw_bluetooth_sapphire/host/gatt/generic_attribute_service.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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 #include "pw_bluetooth_sapphire/internal/host/gatt/generic_attribute_service.h"
16 
17 #include "pw_bluetooth_sapphire/internal/host/common/assert.h"
18 #include "pw_bluetooth_sapphire/internal/host/common/byte_buffer.h"
19 #include "pw_bluetooth_sapphire/internal/host/common/log.h"
20 #include "pw_bluetooth_sapphire/internal/host/gatt/gatt_defs.h"
21 
22 namespace bt::gatt {
23 
GenericAttributeService(LocalServiceManager::WeakPtr local_service_manager,SendIndicationCallback send_indication_callback)24 GenericAttributeService::GenericAttributeService(
25     LocalServiceManager::WeakPtr local_service_manager,
26     SendIndicationCallback send_indication_callback)
27     : local_service_manager_(std::move(local_service_manager)),
28       send_indication_callback_(std::move(send_indication_callback)) {
29   PW_CHECK(local_service_manager_.is_alive());
30   PW_DCHECK(send_indication_callback_);
31 
32   Register();
33 }
34 
~GenericAttributeService()35 GenericAttributeService::~GenericAttributeService() {
36   if (local_service_manager_.is_alive() && service_id_ != kInvalidId) {
37     local_service_manager_->UnregisterService(service_id_);
38   }
39 }
40 
Register()41 void GenericAttributeService::Register() {
42   const att::AccessRequirements kDisallowed;
43   const att::AccessRequirements kAllowedNoSecurity(/*encryption=*/false,
44                                                    /*authentication=*/false,
45                                                    /*authorization=*/false);
46   CharacteristicPtr service_changed_chr = std::make_unique<Characteristic>(
47       kServiceChangedChrcId,                 // id
48       types::kServiceChangedCharacteristic,  // type
49       Property::kIndicate,                   // properties
50       0u,                                    // extended_properties
51       kDisallowed,                           // read
52       kDisallowed,                           // write
53       kAllowedNoSecurity);                   // update
54   auto service =
55       std::make_unique<Service>(true, types::kGenericAttributeService);
56   service->AddCharacteristic(std::move(service_changed_chr));
57 
58   ClientConfigCallback ccc_callback = [this](IdType,
59                                              IdType chrc_id,
60                                              PeerId peer_id,
61                                              bool notify,
62                                              bool indicate) {
63     PW_DCHECK(chrc_id == 0u);
64 
65     SetServiceChangedIndicationSubscription(peer_id, indicate);
66     if (persist_service_changed_ccc_callback_) {
67       ServiceChangedCCCPersistedData persisted = {.notify = notify,
68                                                   .indicate = indicate};
69       persist_service_changed_ccc_callback_(peer_id, persisted);
70     } else {
71       bt_log(WARN,
72              "gatt",
73              "Attempted to persist service changed ccc but no callback found.");
74     }
75   };
76 
77   // Server Supported Features (Vol 3, Part G, Section 7.4)
78   CharacteristicPtr server_features_chr = std::make_unique<Characteristic>(
79       kServerSupportedFeaturesChrcId,                 // id
80       types::kServerSupportedFeaturesCharacteristic,  // type
81       Property::kRead,                                // properties
82       0u,                                             // extended_properties
83       kAllowedNoSecurity,                             // read
84       kDisallowed,                                    // write
85       kDisallowed);                                   // update
86   service->AddCharacteristic(std::move(server_features_chr));
87 
88   ReadHandler read_handler =
89       [](PeerId, IdType, IdType chrc_id, uint16_t, ReadResponder responder) {
90         // The stack shouldn't send us any read requests other than this id,
91         // none of the other characteristics or descriptors support it.
92         PW_DCHECK(chrc_id == kServerSupportedFeaturesChrcId);
93 
94         // The only octet is the first octet.  The only bit is the EATT
95         // supported bit.
96         // TODO(fxbug.dev/364660604): Support EATT, then flip this bit to 1.
97         responder(fit::ok(), StaticByteBuffer(0x00));
98       };
99 
100   service_id_ =
101       local_service_manager_->RegisterService(std::move(service),
102                                               std::move(read_handler),
103                                               NopWriteHandler,
104                                               std::move(ccc_callback));
105   PW_DCHECK(service_id_ != kInvalidId);
106 
107   local_service_manager_->set_service_changed_callback(
108       fit::bind_member<&GenericAttributeService::OnServiceChanged>(this));
109 }
110 
SetServiceChangedIndicationSubscription(PeerId peer_id,bool indicate)111 void GenericAttributeService::SetServiceChangedIndicationSubscription(
112     PeerId peer_id, bool indicate) {
113   if (indicate) {
114     subscribed_peers_.insert(peer_id);
115     bt_log(DEBUG,
116            "gatt",
117            "service: Service Changed enabled for peer %s",
118            bt_str(peer_id));
119   } else {
120     subscribed_peers_.erase(peer_id);
121     bt_log(DEBUG,
122            "gatt",
123            "service: Service Changed disabled for peer %s",
124            bt_str(peer_id));
125   }
126 }
127 
OnServiceChanged(IdType service_id,att::Handle start,att::Handle end)128 void GenericAttributeService::OnServiceChanged(IdType service_id,
129                                                att::Handle start,
130                                                att::Handle end) {
131   // Don't send indications for this service's removal.
132   if (service_id_ == service_id) {
133     return;
134   }
135 
136   StaticByteBuffer<2 * sizeof(uint16_t)> value;
137 
138   value[0] = static_cast<uint8_t>(start);
139   value[1] = static_cast<uint8_t>(start >> 8);
140   value[2] = static_cast<uint8_t>(end);
141   value[3] = static_cast<uint8_t>(end >> 8);
142 
143   for (auto peer_id : subscribed_peers_) {
144     bt_log(TRACE,
145            "gatt",
146            "service: indicating peer %s of service(s) changed "
147            "(start: %#.4x, end: %#.4x)",
148            bt_str(peer_id),
149            start,
150            end);
151     send_indication_callback_(
152         service_id_, kServiceChangedChrcId, peer_id, value.view());
153   }
154 }
155 
156 }  // namespace bt::gatt
157