• 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/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