• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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   BT_ASSERT(local_service_manager_.is_alive());
30   BT_DEBUG_ASSERT(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 service_id,
59                                              IdType chrc_id,
60                                              PeerId peer_id,
61                                              bool notify,
62                                              bool indicate) {
63     BT_DEBUG_ASSERT(chrc_id == 0u);
64 
65     // Discover the handle assigned to this characteristic if necessary.
66     if (svc_changed_handle_ == att::kInvalidHandle) {
67       LocalServiceManager::ClientCharacteristicConfig config;
68       if (!local_service_manager_->GetCharacteristicConfig(
69               service_id, chrc_id, peer_id, &config)) {
70         bt_log(DEBUG,
71                "gatt",
72                "service: Peer has not configured characteristic: %s",
73                bt_str(peer_id));
74         return;
75       }
76       svc_changed_handle_ = config.handle;
77     }
78     SetServiceChangedIndicationSubscription(peer_id, indicate);
79     if (persist_service_changed_ccc_callback_) {
80       ServiceChangedCCCPersistedData persisted = {.notify = notify,
81                                                   .indicate = indicate};
82       persist_service_changed_ccc_callback_(peer_id, persisted);
83     } else {
84       bt_log(WARN,
85              "gatt",
86              "Attempted to persist service changed ccc but no callback found.");
87     }
88   };
89 
90   service_id_ =
91       local_service_manager_->RegisterService(std::move(service),
92                                               NopReadHandler,
93                                               NopWriteHandler,
94                                               std::move(ccc_callback));
95   BT_DEBUG_ASSERT(service_id_ != kInvalidId);
96 
97   local_service_manager_->set_service_changed_callback(
98       fit::bind_member<&GenericAttributeService::OnServiceChanged>(this));
99 }
100 
SetServiceChangedIndicationSubscription(PeerId peer_id,bool indicate)101 void GenericAttributeService::SetServiceChangedIndicationSubscription(
102     PeerId peer_id, bool indicate) {
103   if (indicate) {
104     subscribed_peers_.insert(peer_id);
105     bt_log(DEBUG,
106            "gatt",
107            "service: Service Changed enabled for peer %s",
108            bt_str(peer_id));
109   } else {
110     subscribed_peers_.erase(peer_id);
111     bt_log(DEBUG,
112            "gatt",
113            "service: Service Changed disabled for peer %s",
114            bt_str(peer_id));
115   }
116 }
117 
OnServiceChanged(IdType service_id,att::Handle start,att::Handle end)118 void GenericAttributeService::OnServiceChanged(IdType service_id,
119                                                att::Handle start,
120                                                att::Handle end) {
121   // Service Changed not yet configured for indication.
122   if (svc_changed_handle_ == att::kInvalidHandle) {
123     return;
124   }
125 
126   // Don't send indications for this service's removal.
127   if (service_id_ == service_id) {
128     return;
129   }
130 
131   StaticByteBuffer<2 * sizeof(uint16_t)> value;
132 
133   value[0] = static_cast<uint8_t>(start);
134   value[1] = static_cast<uint8_t>(start >> 8);
135   value[2] = static_cast<uint8_t>(end);
136   value[3] = static_cast<uint8_t>(end >> 8);
137 
138   for (auto peer_id : subscribed_peers_) {
139     bt_log(TRACE,
140            "gatt",
141            "service: indicating peer %s of service(s) changed "
142            "(start: %#.4x, end: %#.4x)",
143            bt_str(peer_id),
144            start,
145            end);
146     send_indication_callback_(
147         service_id_, kServiceChangedChrcId, peer_id, value.view());
148   }
149 }
150 
151 }  // namespace bt::gatt
152