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/gap/generic_access_client.h"
16
17 #include "pw_bluetooth_sapphire/internal/host/gap/gap.h"
18
19 namespace bt::gap::internal {
20
GenericAccessClient(PeerId peer_id,gatt::RemoteService::WeakPtr service)21 GenericAccessClient::GenericAccessClient(PeerId peer_id,
22 gatt::RemoteService::WeakPtr service)
23 : WeakSelf(this), service_(std::move(service)), peer_id_(peer_id) {
24 BT_ASSERT(service_.is_alive());
25 BT_ASSERT(service_->uuid() == kGenericAccessService);
26 }
27
ReadDeviceName(DeviceNameCallback callback)28 void GenericAccessClient::ReadDeviceName(DeviceNameCallback callback) {
29 service_->DiscoverCharacteristics(
30 [self = GetWeakPtr(), cb = std::move(callback)](
31 att::Result<> result, const gatt::CharacteristicMap& chars) mutable {
32 if (!self.is_alive()) {
33 return;
34 }
35
36 if (result.is_error()) {
37 cb(result.take_error());
38 return;
39 }
40
41 std::optional<gatt::CharacteristicHandle> device_name_value_handle;
42 for (auto& [handle, chr] : chars) {
43 auto& data = chr.first;
44 if (data.type == kDeviceNameCharacteristic) {
45 device_name_value_handle.emplace(data.value_handle);
46 break;
47 }
48 }
49
50 if (!device_name_value_handle) {
51 bt_log(DEBUG,
52 "gap-le",
53 "GAP service does not have device name characteristic "
54 "(peer: %s)",
55 bt_str(self->peer_id_));
56 cb(ToResult(HostError::kNotFound).take_error());
57 return;
58 }
59
60 // according to Core Spec v5.3, Vol 3, Part C, 12.1: "0 to 248 octets in
61 // length"
62 self->service_->ReadLongCharacteristic(
63 *device_name_value_handle,
64 /*offset=*/0,
65 att::kMaxAttributeValueLength,
66 [self, cb = std::move(cb)](att::Result<> result,
67 const ByteBuffer& buffer,
68 bool /*maybe_truncated*/) mutable {
69 if (!self.is_alive()) {
70 return;
71 }
72
73 if (bt_is_error(
74 result,
75 DEBUG,
76 "gap-le",
77 "error reading device name characteristic (peer: %s)",
78 bt_str(self->peer_id_))) {
79 cb(result.take_error());
80 return;
81 }
82
83 const auto device_name_end =
84 std::find(buffer.begin(), buffer.end(), '\0');
85 cb(fit::ok(std::string(buffer.begin(), device_name_end)));
86 });
87 });
88 }
89
ReadAppearance(AppearanceCallback callback)90 void GenericAccessClient::ReadAppearance(AppearanceCallback callback) {
91 service_->DiscoverCharacteristics([self = GetWeakPtr(),
92 cb = std::move(callback)](
93 att::Result<> result,
94 const gatt::CharacteristicMap&
95 chars) mutable {
96 if (!self.is_alive()) {
97 return;
98 }
99
100 if (result.is_error()) {
101 cb(result.take_error());
102 return;
103 }
104
105 std::optional<gatt::CharacteristicHandle> appearance_value_handle;
106 for (auto& [handle, chr] : chars) {
107 auto& data = chr.first;
108 if (data.type == kAppearanceCharacteristic) {
109 appearance_value_handle.emplace(data.value_handle);
110 break;
111 }
112 }
113
114 if (!appearance_value_handle) {
115 bt_log(DEBUG,
116 "gap-le",
117 "GAP service does not have appearance characteristic "
118 "(peer: %s)",
119 bt_str(self->peer_id_));
120 cb(ToResult(HostError::kNotFound).take_error());
121 return;
122 }
123
124 // according to Core Spec v5.3, Vol 3, Part C, 12.2: "2 octets in length"
125 self->service_->ReadCharacteristic(
126 *appearance_value_handle,
127 [self, cb = std::move(cb)](att::Result<> result,
128 const ByteBuffer& buffer,
129 bool /*maybe_truncated*/) mutable {
130 if (!self.is_alive()) {
131 return;
132 }
133
134 if (bt_is_error(result,
135 DEBUG,
136 "gap-le",
137 "error reading appearance characteristic (peer: %s)",
138 bt_str(self->peer_id_))) {
139 cb(result.take_error());
140 return;
141 }
142
143 if (buffer.size() != sizeof(uint16_t)) {
144 bt_log(
145 DEBUG,
146 "gap-le",
147 "appearance characteristic has invalid value size (peer: %s)",
148 bt_str(self->peer_id_));
149 cb(ToResult(HostError::kPacketMalformed).take_error());
150 return;
151 }
152
153 uint16_t char_value = le16toh(buffer.template To<uint16_t>());
154 cb(fit::ok(char_value));
155 });
156 });
157 }
158
ReadPeripheralPreferredConnectionParameters(ConnectionParametersCallback callback)159 void GenericAccessClient::ReadPeripheralPreferredConnectionParameters(
160 ConnectionParametersCallback callback) {
161 service_->DiscoverCharacteristics([self = GetWeakPtr(),
162 cb = std::move(callback)](
163 att::Result<> result,
164 const gatt::CharacteristicMap&
165 chars) mutable {
166 if (!self.is_alive()) {
167 return;
168 }
169
170 if (result.is_error()) {
171 cb(result.take_error());
172 return;
173 }
174
175 std::optional<gatt::CharacteristicHandle> conn_params_value_handle;
176 for (auto& [handle, chr] : chars) {
177 auto& data = chr.first;
178 if (data.type == kPeripheralPreferredConnectionParametersCharacteristic) {
179 conn_params_value_handle.emplace(data.value_handle);
180 break;
181 }
182 }
183
184 if (!conn_params_value_handle) {
185 bt_log(DEBUG,
186 "gap-le",
187 "GAP service does not have peripheral preferred connection "
188 "parameters characteristic "
189 "(peer: %s)",
190 bt_str(self->peer_id_));
191 cb(ToResult(HostError::kNotFound).take_error());
192 return;
193 }
194
195 self->service_->ReadCharacteristic(
196 *conn_params_value_handle,
197 [self, cb = std::move(cb)](att::Result<> result,
198 const ByteBuffer& buffer,
199 bool /*maybe_truncated*/) mutable {
200 if (!self.is_alive()) {
201 return;
202 }
203
204 if (bt_is_error(result,
205 DEBUG,
206 "gap-le",
207 "error reading peripheral preferred connection "
208 "parameters characteristic "
209 "(peer: %s)",
210 bt_str(self->peer_id_))) {
211 cb(result.take_error());
212 return;
213 }
214
215 if (buffer.size() !=
216 sizeof(
217 PeripheralPreferredConnectionParametersCharacteristicValue)) {
218 bt_log(DEBUG,
219 "gap-le",
220 "peripheral preferred connection parameters characteristic "
221 "has invalid value size "
222 "(peer: %s)",
223 bt_str(self->peer_id_));
224 cb(ToResult(HostError::kPacketMalformed).take_error());
225 return;
226 }
227
228 auto char_value = buffer.template To<
229 PeripheralPreferredConnectionParametersCharacteristicValue>();
230 hci_spec::LEPreferredConnectionParameters params(
231 le16toh(char_value.min_interval),
232 le16toh(char_value.max_interval),
233 le16toh(char_value.max_latency),
234 le16toh(char_value.supervision_timeout));
235
236 cb(fit::ok(params));
237 });
238 });
239 }
240
241 } // namespace bt::gap::internal
242