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