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/low_energy_interrogator.h"
16
17 #include "pw_bluetooth_sapphire/internal/host/gap/peer_cache.h"
18 #include "pw_bluetooth_sapphire/internal/host/hci-spec/protocol.h"
19 #include "pw_bluetooth_sapphire/internal/host/l2cap/fake_l2cap.h"
20 #include "pw_bluetooth_sapphire/internal/host/l2cap/l2cap_defs.h"
21 #include "pw_bluetooth_sapphire/internal/host/testing/controller_test.h"
22 #include "pw_bluetooth_sapphire/internal/host/testing/mock_controller.h"
23 #include "pw_bluetooth_sapphire/internal/host/testing/test_helpers.h"
24 #include "pw_bluetooth_sapphire/internal/host/testing/test_packets.h"
25 #include "pw_bluetooth_sapphire/internal/host/transport/error.h"
26
27 namespace bt::gap {
28
29 constexpr hci_spec::ConnectionHandle kConnectionHandle = 0x0BAA;
30 constexpr uint64_t kLEFeaturesHasSca =
31 0x0123456789a |
32 static_cast<uint64_t>(
33 hci_spec::LESupportedFeature::kSleepClockAccuracyUpdates);
34 constexpr uint64_t kLEFeaturesNoSca =
35 0x0123456789a &
36 ~static_cast<uint64_t>(
37 hci_spec::LESupportedFeature::kSleepClockAccuracyUpdates);
38 constexpr auto kDefaultScaRange =
39 pw::bluetooth::emboss::LESleepClockAccuracyRange::PPM_51_TO_75;
40 const DeviceAddress kTestDevAddr(DeviceAddress::Type::kLERandom, {1});
41
42 const auto kReadRemoteVersionInfoRsp =
43 testing::CommandStatusPacket(hci_spec::kReadRemoteVersionInfo,
44 pw::bluetooth::emboss::StatusCode::SUCCESS);
45 const auto kLEReadRemoteFeaturesRsp =
46 testing::CommandStatusPacket(hci_spec::kLEReadRemoteFeatures,
47 pw::bluetooth::emboss::StatusCode::SUCCESS);
48 const auto kLERequestPeerScaRsp = testing::CommandStatusPacket(
49 hci_spec::kLERequestPeerSCA, pw::bluetooth::emboss::StatusCode::SUCCESS);
50
51 using TestingBase =
52 bt::testing::FakeDispatcherControllerTest<bt::testing::MockController>;
53
54 class LowEnergyInterrogatorTest : public TestingBase {
55 public:
56 LowEnergyInterrogatorTest() = default;
57 ~LowEnergyInterrogatorTest() override = default;
58
SetUp()59 void SetUp() override {
60 TestingBase::SetUp();
61
62 peer_cache_ = std::make_unique<PeerCache>(dispatcher());
63
64 peer_ = peer_cache()->NewPeer(kTestDevAddr, /*connectable=*/true);
65 ASSERT_TRUE(peer_->le());
66 EXPECT_FALSE(peer_->version());
67 EXPECT_FALSE(peer_->le()->feature_interrogation_complete());
68 EXPECT_FALSE(peer_->le()->features());
69 EXPECT_FALSE(peer_->le()->sleep_clock_accuracy());
70
71 CreateInterrogator(/*supports_sca=*/true);
72 }
73
TearDown()74 void TearDown() override {
75 RunUntilIdle();
76 test_device()->Stop();
77 interrogator_ = nullptr;
78 peer_cache_ = nullptr;
79 TestingBase::TearDown();
80 }
81
82 protected:
QueueSuccessfulInterrogation(hci_spec::ConnectionHandle conn,hci_spec::LESupportedFeatures features=0) const83 void QueueSuccessfulInterrogation(
84 hci_spec::ConnectionHandle conn,
85 hci_spec::LESupportedFeatures features = 0) const {
86 const auto remote_version_complete_packet =
87 testing::ReadRemoteVersionInfoCompletePacket(conn);
88
89 EXPECT_CMD_PACKET_OUT(test_device(),
90 testing::ReadRemoteVersionInfoPacket(conn),
91 &kReadRemoteVersionInfoRsp,
92 &remote_version_complete_packet);
93
94 const auto le_remote_features_complete_packet =
95 testing::LEReadRemoteFeaturesCompletePacket(conn, features);
96 EXPECT_CMD_PACKET_OUT(test_device(),
97 testing::LEReadRemoteFeaturesPacket(conn),
98 &kLEReadRemoteFeaturesRsp,
99 &le_remote_features_complete_packet);
100
101 // Expect a SCA request, if supported by the peer and the controller
102 if ((features &
103 static_cast<uint64_t>(
104 hci_spec::LESupportedFeature::kSleepClockAccuracyUpdates)) &&
105 controller_supports_sca_) {
106 const auto le_request_peer_sca_complete_packet =
107 testing::LERequestPeerScaCompletePacket(conn, kDefaultScaRange);
108 EXPECT_CMD_PACKET_OUT(test_device(),
109 testing::LERequestPeerScaPacket(conn),
110 &kLERequestPeerScaRsp,
111 &le_request_peer_sca_complete_packet);
112 }
113 }
114
CreateInterrogator(bool supports_sca)115 void CreateInterrogator(bool supports_sca) {
116 controller_supports_sca_ = supports_sca;
117 interrogator_ =
118 std::make_unique<LowEnergyInterrogator>(peer_->GetWeakPtr(),
119 kConnectionHandle,
120 cmd_channel()->AsWeakPtr(),
121 supports_sca);
122 }
123
DestroyInterrogator()124 void DestroyInterrogator() { interrogator_.reset(); }
125
peer() const126 Peer* peer() const { return peer_; }
127
peer_cache() const128 PeerCache* peer_cache() const { return peer_cache_.get(); }
129
interrogator() const130 LowEnergyInterrogator* interrogator() const { return interrogator_.get(); }
131
132 private:
133 Peer* peer_ = nullptr;
134 std::unique_ptr<PeerCache> peer_cache_;
135 std::unique_ptr<LowEnergyInterrogator> interrogator_;
136 bool controller_supports_sca_;
137
138 BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(LowEnergyInterrogatorTest);
139 };
140
141 using GAP_LowEnergyInterrogatorTest = LowEnergyInterrogatorTest;
142
TEST_F(LowEnergyInterrogatorTest,SuccessfulInterrogation)143 TEST_F(LowEnergyInterrogatorTest, SuccessfulInterrogation) {
144 // As of Core Spec v5.4, the Feature Set mask has 44 bits (5.5 bytes) in use.
145 const hci_spec::LESupportedFeatures kFeatures{kLEFeaturesHasSca};
146 QueueSuccessfulInterrogation(kConnectionHandle, kFeatures);
147
148 std::optional<hci::Result<>> status;
149 interrogator()->Start(
150 [&status](hci::Result<> cb_status) { status = cb_status; });
151 RunUntilIdle();
152
153 ASSERT_TRUE(status.has_value());
154 EXPECT_EQ(fit::ok(), *status);
155
156 EXPECT_TRUE(peer()->version());
157 ASSERT_TRUE(peer()->le()->feature_interrogation_complete());
158 ASSERT_TRUE(peer()->le()->features());
159 EXPECT_EQ(kFeatures, peer()->le()->features());
160 ASSERT_TRUE(peer()->le()->sleep_clock_accuracy());
161 EXPECT_EQ(*(peer()->le()->sleep_clock_accuracy()), kDefaultScaRange);
162 }
163
TEST_F(LowEnergyInterrogatorTest,SuccessfulInterrogationPeerAlreadyHasLEFeatures)164 TEST_F(LowEnergyInterrogatorTest,
165 SuccessfulInterrogationPeerAlreadyHasLEFeatures) {
166 // As of Core Spec v5.4, the Feature Set mask has 44 bits (5.5 bytes) in use.
167 const hci_spec::LESupportedFeatures kFeatures{kLEFeaturesHasSca};
168
169 const auto remote_version_complete_packet =
170 testing::ReadRemoteVersionInfoCompletePacket(kConnectionHandle);
171 EXPECT_CMD_PACKET_OUT(test_device(),
172 testing::ReadRemoteVersionInfoPacket(kConnectionHandle),
173 &kReadRemoteVersionInfoRsp,
174 &remote_version_complete_packet);
175
176 // We should still query peer's SCA
177 constexpr auto kScaRange =
178 pw::bluetooth::emboss::LESleepClockAccuracyRange::PPM_0_TO_20;
179 const auto le_request_peer_sca_complete_packet =
180 testing::LERequestPeerScaCompletePacket(kConnectionHandle, kScaRange);
181 EXPECT_CMD_PACKET_OUT(test_device(),
182 testing::LERequestPeerScaPacket(kConnectionHandle),
183 &kLERequestPeerScaRsp,
184 &le_request_peer_sca_complete_packet);
185
186 peer()->MutLe().SetFeatures(kFeatures);
187 peer()->MutLe().SetFeatureInterrogationComplete();
188
189 std::optional<hci::Result<>> status;
190 interrogator()->Start(
191 [&status](hci::Result<> cb_status) { status = cb_status; });
192 RunUntilIdle();
193 ASSERT_TRUE(status.has_value());
194 EXPECT_EQ(fit::ok(), *status);
195 ASSERT_TRUE(peer()->le()->feature_interrogation_complete());
196 ASSERT_TRUE(peer()->le()->features());
197 EXPECT_EQ(kFeatures, peer()->le()->features());
198 ASSERT_TRUE(peer()->le()->sleep_clock_accuracy());
199 EXPECT_EQ(*(peer()->le()->sleep_clock_accuracy()), kScaRange);
200 }
201
TEST_F(LowEnergyInterrogatorTest,SuccessfulReinterrogation)202 TEST_F(LowEnergyInterrogatorTest, SuccessfulReinterrogation) {
203 const hci_spec::LESupportedFeatures kFeatures{kLEFeaturesHasSca};
204 QueueSuccessfulInterrogation(kConnectionHandle, kFeatures);
205
206 std::optional<hci::Result<>> status;
207 interrogator()->Start(
208 [&status](hci::Result<> cb_status) { status = cb_status; });
209 RunUntilIdle();
210
211 ASSERT_TRUE(status.has_value());
212 EXPECT_EQ(fit::ok(), *status);
213 status = std::nullopt;
214
215 // Remote version should always be read, even if already known.
216 const auto remote_version_complete_packet =
217 testing::ReadRemoteVersionInfoCompletePacket(kConnectionHandle);
218 EXPECT_CMD_PACKET_OUT(test_device(),
219 testing::ReadRemoteVersionInfoPacket(kConnectionHandle),
220 &kReadRemoteVersionInfoRsp,
221 &remote_version_complete_packet);
222
223 // SCA should be read at each connection event.
224 constexpr auto kScaRange =
225 pw::bluetooth::emboss::LESleepClockAccuracyRange::PPM_251_TO_500;
226 const auto le_request_peer_sca_complete_packet =
227 testing::LERequestPeerScaCompletePacket(kConnectionHandle, kScaRange);
228 EXPECT_CMD_PACKET_OUT(test_device(),
229 testing::LERequestPeerScaPacket(kConnectionHandle),
230 &kLERequestPeerScaRsp,
231 &le_request_peer_sca_complete_packet);
232
233 interrogator()->Start(
234 [&status](hci::Result<> cb_status) { status = cb_status; });
235
236 RunUntilIdle();
237 ASSERT_TRUE(status.has_value());
238 EXPECT_EQ(fit::ok(), *status);
239 ASSERT_TRUE(peer()->le()->sleep_clock_accuracy());
240 EXPECT_EQ(*(peer()->le()->sleep_clock_accuracy()), kScaRange);
241 }
242
TEST_F(LowEnergyInterrogatorTest,LEReadRemoteFeaturesErrorStatus)243 TEST_F(LowEnergyInterrogatorTest, LEReadRemoteFeaturesErrorStatus) {
244 const auto remote_version_complete_packet =
245 testing::ReadRemoteVersionInfoCompletePacket(kConnectionHandle);
246 const auto le_read_remote_features_error_status_packet =
247 testing::CommandStatusPacket(
248 hci_spec::kLEReadRemoteFeatures,
249 pw::bluetooth::emboss::StatusCode::UNKNOWN_COMMAND);
250 EXPECT_CMD_PACKET_OUT(test_device(),
251 testing::ReadRemoteVersionInfoPacket(kConnectionHandle),
252 &kReadRemoteVersionInfoRsp,
253 &remote_version_complete_packet);
254 EXPECT_CMD_PACKET_OUT(test_device(),
255 testing::LEReadRemoteFeaturesPacket(kConnectionHandle),
256 &le_read_remote_features_error_status_packet);
257
258 std::optional<hci::Result<>> status;
259 interrogator()->Start(
260 [&status](hci::Result<> cb_status) { status = cb_status; });
261 RunUntilIdle();
262 ASSERT_TRUE(status.has_value());
263 EXPECT_FALSE(status->is_ok());
264 EXPECT_TRUE(peer()->le()->feature_interrogation_complete());
265 EXPECT_FALSE(peer()->le()->features().has_value());
266
267 // When previous operations fail, we shouldn't try to read SCA.
268 EXPECT_FALSE(peer()->le()->sleep_clock_accuracy());
269 }
270
271 // Test proper operation when a peer doesn't support reading LE remote features
TEST_F(LowEnergyInterrogatorTest,LEReadRemoteFeaturesUnsupported)272 TEST_F(LowEnergyInterrogatorTest, LEReadRemoteFeaturesUnsupported) {
273 const auto remote_version_complete_packet =
274 testing::ReadRemoteVersionInfoCompletePacket(kConnectionHandle);
275 const auto le_read_remote_features_unsupported_status_packet =
276 testing::CommandStatusPacket(
277 hci_spec::kLEReadRemoteFeatures,
278 pw::bluetooth::emboss::StatusCode::UNSUPPORTED_REMOTE_FEATURE);
279 EXPECT_CMD_PACKET_OUT(test_device(),
280 testing::ReadRemoteVersionInfoPacket(kConnectionHandle),
281 &kReadRemoteVersionInfoRsp,
282 &remote_version_complete_packet);
283 EXPECT_CMD_PACKET_OUT(test_device(),
284 testing::LEReadRemoteFeaturesPacket(kConnectionHandle),
285 &le_read_remote_features_unsupported_status_packet);
286
287 std::optional<hci::Result<>> status;
288 interrogator()->Start(
289 [&status](hci::Result<> cb_status) { status = cb_status; });
290 RunUntilIdle();
291 ASSERT_TRUE(status.has_value());
292 EXPECT_TRUE(status->is_ok());
293 EXPECT_TRUE(peer()->le()->feature_interrogation_complete());
294 EXPECT_FALSE(peer()->le()->features().has_value());
295
296 // When previous operations fail, we shouldn't try to read SCA.
297 EXPECT_FALSE(peer()->le()->sleep_clock_accuracy());
298 }
299
TEST_F(LowEnergyInterrogatorTest,ReadRemoteVersionErrorStatus)300 TEST_F(LowEnergyInterrogatorTest, ReadRemoteVersionErrorStatus) {
301 const auto remote_version_error_status_packet = testing::CommandStatusPacket(
302 hci_spec::kReadRemoteVersionInfo,
303 pw::bluetooth::emboss::StatusCode::UNKNOWN_COMMAND);
304 const auto le_remote_features_complete_packet =
305 testing::LEReadRemoteFeaturesCompletePacket(kConnectionHandle,
306 /*features=*/0);
307 EXPECT_CMD_PACKET_OUT(test_device(),
308 testing::ReadRemoteVersionInfoPacket(kConnectionHandle),
309 &remote_version_error_status_packet);
310 EXPECT_CMD_PACKET_OUT(test_device(),
311 testing::LEReadRemoteFeaturesPacket(kConnectionHandle),
312 &kLEReadRemoteFeaturesRsp,
313 &le_remote_features_complete_packet);
314
315 std::optional<hci::Result<>> status;
316 interrogator()->Start(
317 [&status](hci::Result<> cb_status) { status = cb_status; });
318 RunUntilIdle();
319 ASSERT_TRUE(status.has_value());
320 EXPECT_FALSE(status->is_ok());
321 EXPECT_FALSE(peer()->version());
322
323 // When previous operations fail, we shouldn't try to read SCA.
324 EXPECT_FALSE(peer()->le()->sleep_clock_accuracy());
325 }
326
TEST_F(LowEnergyInterrogatorTest,ReadLERemoteFeaturesCallbackHandlesCanceledInterrogation)327 TEST_F(LowEnergyInterrogatorTest,
328 ReadLERemoteFeaturesCallbackHandlesCanceledInterrogation) {
329 const auto remote_version_complete_packet =
330 testing::ReadRemoteVersionInfoCompletePacket(kConnectionHandle);
331 const auto le_remote_features_complete_packet =
332 testing::LEReadRemoteFeaturesCompletePacket(
333 kConnectionHandle, hci_spec::LESupportedFeatures{0});
334
335 EXPECT_CMD_PACKET_OUT(test_device(),
336 testing::ReadRemoteVersionInfoPacket(kConnectionHandle),
337 &kReadRemoteVersionInfoRsp,
338 &remote_version_complete_packet);
339 EXPECT_CMD_PACKET_OUT(test_device(),
340 testing::LEReadRemoteFeaturesPacket(kConnectionHandle),
341 &kLEReadRemoteFeaturesRsp);
342
343 std::optional<hci::Result<>> result;
344 interrogator()->Start(
345 [&result](hci::Result<> cb_result) { result = cb_result; });
346 RunUntilIdle();
347 EXPECT_FALSE(result.has_value());
348
349 interrogator()->Cancel();
350 RunUntilIdle();
351 ASSERT_TRUE(result.has_value());
352 EXPECT_TRUE(result->is_error());
353 EXPECT_EQ(result.value(), ToResult(HostError::kCanceled));
354 result.reset();
355
356 test_device()->SendCommandChannelPacket(le_remote_features_complete_packet);
357 RunUntilIdle();
358 EXPECT_FALSE(result.has_value());
359 // The read remote features handler should not update the features of a
360 // canceled interrogation.
361 ASSERT_FALSE(peer()->le()->feature_interrogation_complete());
362 EXPECT_FALSE(peer()->le()->features().has_value());
363 EXPECT_FALSE(peer()->le()->sleep_clock_accuracy());
364 }
365
TEST_F(LowEnergyInterrogatorTest,ReadRemoteVersionCallbackHandlesCanceledInterrogation)366 TEST_F(LowEnergyInterrogatorTest,
367 ReadRemoteVersionCallbackHandlesCanceledInterrogation) {
368 const auto remote_version_complete_packet =
369 testing::ReadRemoteVersionInfoCompletePacket(kConnectionHandle);
370 const auto le_remote_features_complete_packet =
371 testing::LEReadRemoteFeaturesCompletePacket(
372 kConnectionHandle, hci_spec::LESupportedFeatures{0});
373
374 EXPECT_CMD_PACKET_OUT(test_device(),
375 testing::ReadRemoteVersionInfoPacket(kConnectionHandle),
376 &kReadRemoteVersionInfoRsp);
377 EXPECT_CMD_PACKET_OUT(test_device(),
378 testing::LEReadRemoteFeaturesPacket(kConnectionHandle),
379 &kLEReadRemoteFeaturesRsp,
380 &le_remote_features_complete_packet);
381
382 std::optional<hci::Result<>> result;
383 interrogator()->Start(
384 [&result](hci::Result<> cb_result) { result = cb_result; });
385 RunUntilIdle();
386 EXPECT_FALSE(result.has_value());
387
388 interrogator()->Cancel();
389 RunUntilIdle();
390 ASSERT_TRUE(result.has_value());
391 EXPECT_TRUE(result->is_error());
392 EXPECT_EQ(result.value(), ToResult(HostError::kCanceled));
393 result.reset();
394
395 test_device()->SendCommandChannelPacket(remote_version_complete_packet);
396 RunUntilIdle();
397 EXPECT_FALSE(result.has_value());
398 // The read remote version handler should not update the version after a
399 // canceled interrogation.
400 EXPECT_FALSE(peer()->version());
401 EXPECT_FALSE(peer()->le()->sleep_clock_accuracy());
402 }
403
TEST_F(LowEnergyInterrogatorTest,ScaUpdateNotSupportedOnController)404 TEST_F(LowEnergyInterrogatorTest, ScaUpdateNotSupportedOnController) {
405 CreateInterrogator(/*supports_sca=*/false);
406
407 const hci_spec::LESupportedFeatures kFeatures{kLEFeaturesHasSca};
408 QueueSuccessfulInterrogation(kConnectionHandle, kFeatures);
409
410 std::optional<hci::Result<>> status;
411 interrogator()->Start(
412 [&status](hci::Result<> cb_status) { status = cb_status; });
413 RunUntilIdle();
414
415 ASSERT_TRUE(status.has_value());
416 EXPECT_EQ(fit::ok(), *status);
417
418 EXPECT_TRUE(peer()->version());
419 ASSERT_TRUE(peer()->le()->feature_interrogation_complete());
420 ASSERT_TRUE(peer()->le()->features());
421 EXPECT_EQ(kFeatures, peer()->le()->features());
422 ASSERT_FALSE(peer()->le()->sleep_clock_accuracy());
423 }
424
TEST_F(LowEnergyInterrogatorTest,ScaUpdateNotSupportedOnPeer)425 TEST_F(LowEnergyInterrogatorTest, ScaUpdateNotSupportedOnPeer) {
426 // Disable peer support for SCA updates.
427 const hci_spec::LESupportedFeatures kFeatures{kLEFeaturesNoSca};
428 QueueSuccessfulInterrogation(kConnectionHandle, kFeatures);
429
430 std::optional<hci::Result<>> status;
431 interrogator()->Start(
432 [&status](hci::Result<> cb_status) { status = cb_status; });
433 RunUntilIdle();
434
435 ASSERT_TRUE(status.has_value());
436 EXPECT_EQ(fit::ok(), *status);
437
438 EXPECT_TRUE(peer()->version());
439 ASSERT_TRUE(peer()->le()->feature_interrogation_complete());
440 ASSERT_TRUE(peer()->le()->features());
441 EXPECT_EQ(kFeatures, peer()->le()->features());
442 ASSERT_FALSE(peer()->le()->sleep_clock_accuracy());
443 }
444
TEST_F(LowEnergyInterrogatorTest,DestroyInterrogatorInCompleteCallback)445 TEST_F(LowEnergyInterrogatorTest, DestroyInterrogatorInCompleteCallback) {
446 // As of Core Spec v5.4, the Feature Set mask has 44 bits (5.5 bytes) in use.
447 const hci_spec::LESupportedFeatures kFeatures{kLEFeaturesHasSca};
448 QueueSuccessfulInterrogation(kConnectionHandle, kFeatures);
449
450 std::optional<hci::Result<>> status;
451 interrogator()->Start([this, &status](hci::Result<> cb_status) {
452 status = cb_status;
453 DestroyInterrogator();
454 });
455 RunUntilIdle();
456 ASSERT_TRUE(status.has_value());
457 EXPECT_TRUE(status->is_ok());
458 ASSERT_TRUE(peer()->le()->feature_interrogation_complete());
459 ASSERT_TRUE(peer()->le()->features());
460 EXPECT_EQ(kFeatures, peer()->le()->features());
461 ASSERT_TRUE(peer()->le()->sleep_clock_accuracy());
462 EXPECT_EQ(*(peer()->le()->sleep_clock_accuracy()), kDefaultScaRange);
463 }
464
465 } // namespace bt::gap
466