1 // Copyright 2024 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/fuchsia/host/fidl/low_energy_central_server.h"
16
17 #include <cpp-string/string_printf.h>
18 #include <gmock/gmock.h>
19
20 #include <cstddef>
21
22 #include "fuchsia/bluetooth/gatt/cpp/fidl.h"
23 #include "fuchsia/bluetooth/le/cpp/fidl.h"
24 #include "lib/fidl/cpp/interface_request.h"
25 #include "pw_bluetooth_sapphire/fuchsia/host/fidl/adapter_test_fixture.h"
26 #include "pw_bluetooth_sapphire/fuchsia/host/fidl/fake_adapter_test_fixture.h"
27 #include "pw_bluetooth_sapphire/fuchsia/host/fidl/helpers.h"
28 #include "pw_bluetooth_sapphire/fuchsia/host/fidl/measure_tape/hlcpp_measure_tape_for_peer.h"
29 #include "pw_bluetooth_sapphire/internal/host/sm/types.h"
30 #include "pw_bluetooth_sapphire/internal/host/testing/fake_controller.h"
31 #include "pw_bluetooth_sapphire/internal/host/testing/fake_peer.h"
32
33 namespace bthost {
34 namespace {
35
36 namespace fble = fuchsia::bluetooth::le;
37 namespace fgatt = fuchsia::bluetooth::gatt;
38
39 const bt::DeviceAddress kTestAddr(bt::DeviceAddress::Type::kLEPublic,
40 {0x01, 0, 0, 0, 0, 0});
41 const size_t kLEMaxNumPackets = 10;
42 const bt::hci::DataBufferInfo kLEDataBufferInfo(
43 bt::hci_spec::kMaxACLPayloadSize, kLEMaxNumPackets);
44
ScanOptionsWithEmptyFilter()45 fble::ScanOptions ScanOptionsWithEmptyFilter() {
46 fble::ScanOptions options;
47 fble::Filter filter;
48 std::vector<fble::Filter> filters;
49 filters.emplace_back(std::move(filter));
50 options.set_filters(std::move(filters));
51 return options;
52 }
53
MaxPeersPerScanResultWatcherChannel(const bt::gap::Peer & peer)54 size_t MaxPeersPerScanResultWatcherChannel(const bt::gap::Peer& peer) {
55 const size_t kPeerSize = measure_tape::fuchsia::bluetooth::le::Measure(
56 fidl_helpers::PeerToFidlLe(peer))
57 .num_bytes;
58 const size_t kVectorOverhead =
59 sizeof(fidl_message_header_t) + sizeof(fidl_vector_t);
60 const size_t kMaxBytes = ZX_CHANNEL_MAX_MSG_BYTES - kVectorOverhead;
61 return kMaxBytes / kPeerSize;
62 }
63
64 using TestingBase = bthost::testing::AdapterTestFixture;
65
66 class LowEnergyCentralServerTest : public TestingBase {
67 public:
68 LowEnergyCentralServerTest() = default;
69 ~LowEnergyCentralServerTest() override = default;
70
SetUp()71 void SetUp() override {
72 AdapterTestFixture::SetUp();
73
74 // Create a LowEnergyCentralServer and bind it to a local client.
75 fidl::InterfaceHandle<fble::Central> handle;
76 gatt_ = take_gatt();
77 server_ = std::make_unique<LowEnergyCentralServer>(
78 adapter(), handle.NewRequest(), gatt_->GetWeakPtr());
79 proxy_.Bind(std::move(handle));
80
81 bt::testing::FakeController::Settings settings;
82 settings.ApplyLegacyLEConfig();
83 test_device()->set_settings(settings);
84 }
85
TearDown()86 void TearDown() override {
87 RunLoopUntilIdle();
88
89 proxy_ = nullptr;
90 server_ = nullptr;
91 gatt_ = nullptr;
92
93 RunLoopUntilIdle();
94 AdapterTestFixture::TearDown();
95 }
96
97 protected:
98 // Returns true if the given gatt.Client handle was closed after the event
99 // loop finishes processing. Returns false if the handle was not closed.
100 // Ownership of |handle| remains with the caller when this method returns.
IsClientHandleClosedAfterLoop(fidl::InterfaceHandle<fgatt::Client> * handle)101 bool IsClientHandleClosedAfterLoop(
102 fidl::InterfaceHandle<fgatt::Client>* handle) {
103 PW_CHECK(handle);
104
105 fgatt::ClientPtr proxy;
106 proxy.Bind(std::move(*handle));
107
108 bool closed = false;
109 proxy.set_error_handler([&](zx_status_t s) {
110 EXPECT_EQ(ZX_ERR_PEER_CLOSED, s);
111 closed = true;
112 });
113 RunLoopUntilIdle();
114
115 *handle = proxy.Unbind();
116 return closed;
117 }
118
119 // Destroys the FIDL server. The le.Central proxy will be shut down and
120 // subsequent calls to `server()` will return nullptr.
DestroyServer()121 void DestroyServer() { server_ = nullptr; }
122
server() const123 LowEnergyCentralServer* server() const { return server_.get(); }
central_proxy() const124 fuchsia::bluetooth::le::Central* central_proxy() const {
125 return proxy_.get();
126 }
127
128 private:
129 std::unique_ptr<LowEnergyCentralServer> server_;
130 fble::CentralPtr proxy_;
131 std::unique_ptr<bt::gatt::GATT> gatt_;
132
133 BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(LowEnergyCentralServerTest);
134 };
135
136 class LowEnergyCentralServerTestFakeAdapter
137 : public bt::fidl::testing::FakeAdapterTestFixture {
138 public:
SetUp()139 void SetUp() override {
140 bt::fidl::testing::FakeAdapterTestFixture::SetUp();
141
142 // Create a LowEnergyCentralServer and bind it to a local client.
143 fidl::InterfaceHandle<fble::Central> handle;
144 gatt_ = std::make_unique<bt::gatt::testing::FakeLayer>(pw_dispatcher());
145 server_ = std::make_unique<LowEnergyCentralServer>(
146 adapter()->AsWeakPtr(), handle.NewRequest(), gatt_->GetWeakPtr());
147 proxy_.Bind(std::move(handle));
148 }
149
central_proxy() const150 fuchsia::bluetooth::le::Central* central_proxy() const {
151 return proxy_.get();
152 }
153
154 private:
155 std::unique_ptr<LowEnergyCentralServer> server_;
156 fble::CentralPtr proxy_;
157 std::unique_ptr<bt::gatt::GATT> gatt_;
158 };
159
160 class LowEnergyCentralServerTestFakeAdapterBoolParam
161 : public LowEnergyCentralServerTestFakeAdapter,
162 public ::testing::WithParamInterface<bool> {};
163
164 // Tests that connecting to a peripheral with
165 // LowEnergyConnectionOptions.bondable_mode unset results in a bondable
166 // connection ref being stored in LowEnergyConnectionManager
TEST_F(LowEnergyCentralServerTest,ConnectDefaultResultsBondableConnectionRef)167 TEST_F(LowEnergyCentralServerTest, ConnectDefaultResultsBondableConnectionRef) {
168 auto* const peer =
169 adapter()->peer_cache()->NewPeer(kTestAddr, /*connectable=*/true);
170 ASSERT_TRUE(peer);
171
172 test_device()->AddPeer(
173 std::make_unique<bt::testing::FakePeer>(kTestAddr, pw_dispatcher()));
174
175 fble::ConnectionOptions options;
176
177 fidl::InterfaceHandle<fuchsia::bluetooth::gatt::Client> gatt_client;
178 fidl::InterfaceRequest<fuchsia::bluetooth::gatt::Client> gatt_client_req =
179 gatt_client.NewRequest();
180
181 auto status = fidl_helpers::NewFidlError(
182 fuchsia::bluetooth::ErrorCode::BAD_STATE, "this should change");
183 auto callback = [&status](::fuchsia::bluetooth::Status cb_status) {
184 ASSERT_EQ(cb_status.error, nullptr);
185 status = std::move(cb_status);
186 };
187 central_proxy()->ConnectPeripheral(peer->identifier().ToString(),
188 std::move(options),
189 std::move(gatt_client_req),
190 callback);
191 ASSERT_FALSE(server()->FindConnectionForTesting(peer->identifier()));
192 RunLoopUntilIdle();
193 auto conn_ref = server()->FindConnectionForTesting(peer->identifier());
194 ASSERT_EQ(status.error, nullptr);
195 ASSERT_TRUE(conn_ref.has_value());
196 ASSERT_TRUE(conn_ref.value());
197 ASSERT_EQ(conn_ref.value()->bondable_mode(), bt::sm::BondableMode::Bondable);
198 }
199
200 // Tests that setting LowEnergyConnectionOptions.bondable_mode to true and
201 // connecting to a peer in bondable mode results in a bondable connection ref
202 // being stored in LowEnergyConnectionManager
TEST_F(LowEnergyCentralServerTest,ConnectBondableResultsBondableConnectionRef)203 TEST_F(LowEnergyCentralServerTest,
204 ConnectBondableResultsBondableConnectionRef) {
205 auto* const peer =
206 adapter()->peer_cache()->NewPeer(kTestAddr, /*connectable=*/true);
207 ASSERT_TRUE(peer);
208
209 test_device()->AddPeer(
210 std::make_unique<bt::testing::FakePeer>(kTestAddr, pw_dispatcher()));
211
212 fble::ConnectionOptions options;
213 options.set_bondable_mode(true);
214
215 fidl::InterfaceHandle<fuchsia::bluetooth::gatt::Client> gatt_client;
216 fidl::InterfaceRequest<fuchsia::bluetooth::gatt::Client> gatt_client_req =
217 gatt_client.NewRequest();
218
219 auto status = fidl_helpers::NewFidlError(
220 fuchsia::bluetooth::ErrorCode::BAD_STATE, "this should change");
221 auto callback = [&status](::fuchsia::bluetooth::Status cb_status) {
222 ASSERT_EQ(cb_status.error, nullptr);
223 status = std::move(cb_status);
224 };
225 central_proxy()->ConnectPeripheral(peer->identifier().ToString(),
226 std::move(options),
227 std::move(gatt_client_req),
228 callback);
229 ASSERT_FALSE(server()->FindConnectionForTesting(peer->identifier()));
230 RunLoopUntilIdle();
231 auto conn_ref = server()->FindConnectionForTesting(peer->identifier());
232 ASSERT_EQ(status.error, nullptr);
233 ASSERT_TRUE(conn_ref.has_value());
234 ASSERT_TRUE(conn_ref.value());
235 ASSERT_EQ(conn_ref.value()->bondable_mode(), bt::sm::BondableMode::Bondable);
236 }
237
238 // Tests that setting LowEnergyConnectionOptions.bondable_mode to false and
239 // connecting to a peer results in a non-bondable connection ref being stored in
240 // LowEnergyConnectionManager.
TEST_F(LowEnergyCentralServerTest,ConnectNonBondableResultsNonBondableConnectionRef)241 TEST_F(LowEnergyCentralServerTest,
242 ConnectNonBondableResultsNonBondableConnectionRef) {
243 auto* const peer =
244 adapter()->peer_cache()->NewPeer(kTestAddr, /*connectable=*/true);
245 ASSERT_TRUE(peer);
246
247 test_device()->AddPeer(
248 std::make_unique<bt::testing::FakePeer>(kTestAddr, pw_dispatcher()));
249
250 fble::ConnectionOptions options;
251 options.set_bondable_mode(false);
252
253 fidl::InterfaceHandle<fuchsia::bluetooth::gatt::Client> gatt_client;
254 fidl::InterfaceRequest<fuchsia::bluetooth::gatt::Client> gatt_client_req =
255 gatt_client.NewRequest();
256
257 auto status = fidl_helpers::NewFidlError(
258 fuchsia::bluetooth::ErrorCode::BAD_STATE, "this should change");
259 auto callback = [&status](::fuchsia::bluetooth::Status cb_status) {
260 ASSERT_EQ(cb_status.error, nullptr);
261 status = std::move(cb_status);
262 };
263 central_proxy()->ConnectPeripheral(peer->identifier().ToString(),
264 std::move(options),
265 std::move(gatt_client_req),
266 callback);
267 ASSERT_FALSE(server()->FindConnectionForTesting(peer->identifier()));
268 RunLoopUntilIdle();
269 auto conn_ref = server()->FindConnectionForTesting(peer->identifier());
270 ASSERT_EQ(status.error, nullptr);
271 ASSERT_TRUE(conn_ref.has_value());
272 ASSERT_TRUE(conn_ref.value());
273 ASSERT_EQ(conn_ref.value()->bondable_mode(),
274 bt::sm::BondableMode::NonBondable);
275 }
276
TEST_F(LowEnergyCentralServerTest,DisconnectUnconnectedPeripheralReturnsSuccess)277 TEST_F(LowEnergyCentralServerTest,
278 DisconnectUnconnectedPeripheralReturnsSuccess) {
279 auto status = fidl_helpers::NewFidlError(
280 fuchsia::bluetooth::ErrorCode::BAD_STATE, "this should change");
281 auto callback = [&status](::fuchsia::bluetooth::Status cb_status) {
282 status = std::move(cb_status);
283 };
284 central_proxy()->DisconnectPeripheral(bt::PeerId(1).ToString(),
285 std::move(callback));
286 RunLoopUntilIdle();
287 EXPECT_EQ(status.error, nullptr);
288 }
289
TEST_F(LowEnergyCentralServerTest,FailedConnectionCleanedUp)290 TEST_F(LowEnergyCentralServerTest, FailedConnectionCleanedUp) {
291 auto* const peer =
292 adapter()->peer_cache()->NewPeer(kTestAddr, /*connectable=*/true);
293 ASSERT_TRUE(peer);
294
295 test_device()->AddPeer(
296 std::make_unique<bt::testing::FakePeer>(kTestAddr, pw_dispatcher()));
297
298 fble::ConnectionOptions options;
299
300 fidl::InterfaceHandle<fuchsia::bluetooth::gatt::Client> gatt_client;
301 fidl::InterfaceRequest<fuchsia::bluetooth::gatt::Client> gatt_client_req =
302 gatt_client.NewRequest();
303
304 fuchsia::bluetooth::Status status;
305 auto callback = [&status](::fuchsia::bluetooth::Status cb_status) {
306 status = std::move(cb_status);
307 };
308
309 test_device()->SetDefaultCommandStatus(
310 bt::hci_spec::kReadRemoteVersionInfo,
311 pw::bluetooth::emboss::StatusCode::CONNECTION_LIMIT_EXCEEDED);
312
313 ASSERT_FALSE(
314 server()->FindConnectionForTesting(peer->identifier()).has_value());
315 central_proxy()->ConnectPeripheral(peer->identifier().ToString(),
316 std::move(options),
317 std::move(gatt_client_req),
318 callback);
319 RunLoopUntilIdle();
320 auto conn = server()->FindConnectionForTesting(peer->identifier());
321 EXPECT_NE(status.error, nullptr);
322 EXPECT_FALSE(conn.has_value());
323 }
324
TEST_F(LowEnergyCentralServerTest,ConnectPeripheralAlreadyConnectedInLecm)325 TEST_F(LowEnergyCentralServerTest, ConnectPeripheralAlreadyConnectedInLecm) {
326 auto* const peer =
327 adapter()->peer_cache()->NewPeer(kTestAddr, /*connectable=*/true);
328 test_device()->AddPeer(
329 std::make_unique<bt::testing::FakePeer>(kTestAddr, pw_dispatcher()));
330
331 std::unique_ptr<bt::gap::LowEnergyConnectionHandle> le_conn;
332 adapter()->le()->Connect(
333 peer->identifier(),
334 [&le_conn](auto result) {
335 ASSERT_EQ(fit::ok(), result);
336 le_conn = std::move(result).value();
337 },
338 bt::gap::LowEnergyConnectionOptions());
339 RunLoopUntilIdle();
340 ASSERT_TRUE(le_conn);
341 ASSERT_FALSE(
342 server()->FindConnectionForTesting(peer->identifier()).has_value());
343
344 fuchsia::bluetooth::Status status;
345 auto callback = [&status](::fuchsia::bluetooth::Status cb_status) {
346 status = std::move(cb_status);
347 };
348
349 fble::ConnectionOptions options;
350 fidl::InterfaceHandle<fuchsia::bluetooth::gatt::Client> gatt_client;
351 fidl::InterfaceRequest<fuchsia::bluetooth::gatt::Client> gatt_client_req =
352 gatt_client.NewRequest();
353 central_proxy()->ConnectPeripheral(peer->identifier().ToString(),
354 std::move(options),
355 std::move(gatt_client_req),
356 callback);
357 RunLoopUntilIdle();
358 EXPECT_EQ(status.error, nullptr);
359 auto server_conn = server()->FindConnectionForTesting(peer->identifier());
360 ASSERT_TRUE(server_conn.has_value());
361 EXPECT_NE(server_conn.value(), nullptr);
362 }
363
TEST_F(LowEnergyCentralServerTest,ConnectPeripheralUnknownPeer)364 TEST_F(LowEnergyCentralServerTest, ConnectPeripheralUnknownPeer) {
365 fuchsia::bluetooth::Status status;
366 auto callback = [&status](::fuchsia::bluetooth::Status cb_status) {
367 status = std::move(cb_status);
368 };
369
370 const bt::PeerId peer_id(1);
371
372 fble::ConnectionOptions options;
373 fidl::InterfaceHandle<fuchsia::bluetooth::gatt::Client> gatt_client;
374 fidl::InterfaceRequest<fuchsia::bluetooth::gatt::Client> gatt_client_req =
375 gatt_client.NewRequest();
376 central_proxy()->ConnectPeripheral(peer_id.ToString(),
377 std::move(options),
378 std::move(gatt_client_req),
379 callback);
380 RunLoopUntilIdle();
381 ASSERT_TRUE(status.error);
382 EXPECT_EQ(status.error->error_code, fuchsia::bluetooth::ErrorCode::NOT_FOUND);
383 auto server_conn = server()->FindConnectionForTesting(peer_id);
384 EXPECT_FALSE(server_conn.has_value());
385 }
386
TEST_F(LowEnergyCentralServerTest,DisconnectPeripheralClosesCorrectGattHandle)387 TEST_F(LowEnergyCentralServerTest,
388 DisconnectPeripheralClosesCorrectGattHandle) {
389 const bt::DeviceAddress kAddr1 = kTestAddr;
390 const bt::DeviceAddress kAddr2(bt::DeviceAddress::Type::kLEPublic,
391 {2, 0, 0, 0, 0, 0});
392 auto* const peer1 =
393 adapter()->peer_cache()->NewPeer(kAddr1, /*connectable=*/true);
394 auto* const peer2 =
395 adapter()->peer_cache()->NewPeer(kAddr2, /*connectable=*/true);
396
397 test_device()->AddPeer(
398 std::make_unique<bt::testing::FakePeer>(kAddr1, pw_dispatcher()));
399 test_device()->AddPeer(
400 std::make_unique<bt::testing::FakePeer>(kAddr2, pw_dispatcher()));
401
402 // Establish two connections.
403 fidl::InterfaceHandle<fgatt::Client> handle1, handle2;
404 central_proxy()->ConnectPeripheral(peer1->identifier().ToString(),
405 fble::ConnectionOptions{},
406 handle1.NewRequest(),
407 [](auto) {});
408 central_proxy()->ConnectPeripheral(peer2->identifier().ToString(),
409 fble::ConnectionOptions{},
410 handle2.NewRequest(),
411 [](auto) {});
412 RunLoopUntilIdle();
413 ASSERT_TRUE(server()->FindConnectionForTesting(peer1->identifier()));
414 ASSERT_TRUE(server()->FindConnectionForTesting(peer2->identifier()));
415 EXPECT_FALSE(IsClientHandleClosedAfterLoop(&handle1));
416 EXPECT_FALSE(IsClientHandleClosedAfterLoop(&handle2));
417
418 // Disconnect peer1. Only its gatt.Client handle should close.
419 central_proxy()->DisconnectPeripheral(peer1->identifier().ToString(),
420 [](auto) {});
421 EXPECT_TRUE(IsClientHandleClosedAfterLoop(&handle1));
422 EXPECT_FALSE(IsClientHandleClosedAfterLoop(&handle2));
423
424 // Disconnect peer2. Its handle should close now.
425 central_proxy()->DisconnectPeripheral(peer2->identifier().ToString(),
426 [](auto) {});
427 EXPECT_TRUE(IsClientHandleClosedAfterLoop(&handle2));
428 }
429
TEST_F(LowEnergyCentralServerTest,PeerDisconnectClosesCorrectHandle)430 TEST_F(LowEnergyCentralServerTest, PeerDisconnectClosesCorrectHandle) {
431 const bt::DeviceAddress kAddr1 = kTestAddr;
432 const bt::DeviceAddress kAddr2(bt::DeviceAddress::Type::kLEPublic,
433 {2, 0, 0, 0, 0, 0});
434 auto* const peer1 =
435 adapter()->peer_cache()->NewPeer(kAddr1, /*connectable=*/true);
436 auto* const peer2 =
437 adapter()->peer_cache()->NewPeer(kAddr2, /*connectable=*/true);
438
439 test_device()->AddPeer(
440 std::make_unique<bt::testing::FakePeer>(kAddr1, pw_dispatcher()));
441 test_device()->AddPeer(
442 std::make_unique<bt::testing::FakePeer>(kAddr2, pw_dispatcher()));
443
444 // Establish two connections.
445 fidl::InterfaceHandle<fgatt::Client> handle1, handle2;
446 central_proxy()->ConnectPeripheral(peer1->identifier().ToString(),
447 fble::ConnectionOptions{},
448 handle1.NewRequest(),
449 [](auto) {});
450 central_proxy()->ConnectPeripheral(peer2->identifier().ToString(),
451 fble::ConnectionOptions{},
452 handle2.NewRequest(),
453 [](auto) {});
454 RunLoopUntilIdle();
455 ASSERT_TRUE(server()->FindConnectionForTesting(peer1->identifier()));
456 ASSERT_TRUE(server()->FindConnectionForTesting(peer2->identifier()));
457 EXPECT_FALSE(IsClientHandleClosedAfterLoop(&handle1));
458 EXPECT_FALSE(IsClientHandleClosedAfterLoop(&handle2));
459
460 // Disconnect peer1. Only its gatt.Client handle should close.
461 test_device()->Disconnect(kAddr1);
462 EXPECT_TRUE(IsClientHandleClosedAfterLoop(&handle1));
463 EXPECT_FALSE(IsClientHandleClosedAfterLoop(&handle2));
464
465 // Disconnect peer2. Its handle should close now.
466 test_device()->Disconnect(kAddr2);
467 EXPECT_TRUE(IsClientHandleClosedAfterLoop(&handle2));
468 }
469
TEST_F(LowEnergyCentralServerTest,ClosingCentralHandleClosesAssociatedGattClientHandles)470 TEST_F(LowEnergyCentralServerTest,
471 ClosingCentralHandleClosesAssociatedGattClientHandles) {
472 const bt::DeviceAddress kAddr1 = kTestAddr;
473 const bt::DeviceAddress kAddr2(bt::DeviceAddress::Type::kLEPublic,
474 {2, 0, 0, 0, 0, 0});
475 auto* const peer1 =
476 adapter()->peer_cache()->NewPeer(kAddr1, /*connectable=*/true);
477 auto* const peer2 =
478 adapter()->peer_cache()->NewPeer(kAddr2, /*connectable=*/true);
479
480 test_device()->AddPeer(
481 std::make_unique<bt::testing::FakePeer>(kAddr1, pw_dispatcher()));
482 test_device()->AddPeer(
483 std::make_unique<bt::testing::FakePeer>(kAddr2, pw_dispatcher()));
484
485 // Establish two connections.
486 fidl::InterfaceHandle<fgatt::Client> handle1, handle2;
487 central_proxy()->ConnectPeripheral(peer1->identifier().ToString(),
488 fble::ConnectionOptions{},
489 handle1.NewRequest(),
490 [](auto) {});
491 central_proxy()->ConnectPeripheral(peer2->identifier().ToString(),
492 fble::ConnectionOptions{},
493 handle2.NewRequest(),
494 [](auto) {});
495 RunLoopUntilIdle();
496 ASSERT_TRUE(server()->FindConnectionForTesting(peer1->identifier()));
497 ASSERT_TRUE(server()->FindConnectionForTesting(peer2->identifier()));
498 EXPECT_FALSE(IsClientHandleClosedAfterLoop(&handle1));
499 EXPECT_FALSE(IsClientHandleClosedAfterLoop(&handle2));
500
501 DestroyServer();
502 EXPECT_TRUE(IsClientHandleClosedAfterLoop(&handle1));
503 EXPECT_TRUE(IsClientHandleClosedAfterLoop(&handle2));
504 }
505
TEST_F(LowEnergyCentralServerTest,ScanWithEmptyScanOptionsFails)506 TEST_F(LowEnergyCentralServerTest, ScanWithEmptyScanOptionsFails) {
507 fidl::InterfaceHandle<fble::ScanResultWatcher> result_watcher_handle;
508 auto result_watcher_server = result_watcher_handle.NewRequest();
509
510 auto result_watcher_client = result_watcher_handle.Bind();
511 std::optional<zx_status_t> result_watcher_epitaph;
512 result_watcher_client.set_error_handler(
513 [&](zx_status_t epitaph) { result_watcher_epitaph = epitaph; });
514
515 bool scan_stopped = false;
516 central_proxy()->Scan(fble::ScanOptions(),
517 std::move(result_watcher_server),
518 [&]() { scan_stopped = true; });
519 RunLoopUntilIdle();
520 EXPECT_TRUE(scan_stopped);
521 ASSERT_TRUE(result_watcher_epitaph.has_value());
522 EXPECT_EQ(result_watcher_epitaph.value(), ZX_ERR_INVALID_ARGS);
523 }
524
TEST_F(LowEnergyCentralServerTest,ScanWithNoFiltersFails)525 TEST_F(LowEnergyCentralServerTest, ScanWithNoFiltersFails) {
526 fble::ScanOptions options;
527 std::vector<fble::Filter> filters;
528 options.set_filters(std::move(filters));
529
530 fidl::InterfaceHandle<fble::ScanResultWatcher> result_watcher_handle;
531 auto result_watcher_server = result_watcher_handle.NewRequest();
532
533 auto result_watcher_client = result_watcher_handle.Bind();
534 std::optional<zx_status_t> result_watcher_epitaph;
535 result_watcher_client.set_error_handler(
536 [&](zx_status_t epitaph) { result_watcher_epitaph = epitaph; });
537
538 bool scan_stopped = false;
539 central_proxy()->Scan(std::move(options),
540 std::move(result_watcher_server),
541 [&]() { scan_stopped = true; });
542 RunLoopUntilIdle();
543 EXPECT_TRUE(scan_stopped);
544 ASSERT_TRUE(result_watcher_epitaph.has_value());
545 EXPECT_EQ(result_watcher_epitaph.value(), ZX_ERR_INVALID_ARGS);
546 }
547
TEST_F(LowEnergyCentralServerTest,ScanReceivesPeerPreviouslyAddedToPeerCache)548 TEST_F(LowEnergyCentralServerTest, ScanReceivesPeerPreviouslyAddedToPeerCache) {
549 bt::gap::Peer* peer =
550 adapter()->peer_cache()->NewPeer(kTestAddr, /*connectable=*/false);
551
552 fidl::InterfaceHandle<fble::ScanResultWatcher> result_watcher_handle;
553 auto result_watcher_server = result_watcher_handle.NewRequest();
554 auto result_watcher_client = result_watcher_handle.Bind();
555 std::optional<zx_status_t> epitaph;
556 result_watcher_client.set_error_handler(
557 [&](zx_status_t cb_epitaph) { epitaph = cb_epitaph; });
558
559 bool scan_stopped = false;
560 central_proxy()->Scan(ScanOptionsWithEmptyFilter(),
561 std::move(result_watcher_server),
562 [&]() { scan_stopped = true; });
563
564 RunLoopUntilIdle();
565 EXPECT_FALSE(scan_stopped);
566 EXPECT_FALSE(epitaph);
567
568 std::optional<std::vector<fble::Peer>> peers;
569 result_watcher_client->Watch(
570 [&](std::vector<fble::Peer> updated) { peers = std::move(updated); });
571 RunLoopUntilIdle();
572 ASSERT_TRUE(peers.has_value());
573 ASSERT_EQ(peers->size(), 1u);
574 ASSERT_TRUE(peers->front().has_id());
575 EXPECT_EQ(peers->front().id().value, peer->identifier().value());
576
577 result_watcher_client.Unbind();
578 RunLoopUntilIdle();
579 EXPECT_TRUE(scan_stopped);
580 }
581
TEST_F(LowEnergyCentralServerTest,ScanReceivesPeerAddedToPeerCacheAfterScanStart)582 TEST_F(LowEnergyCentralServerTest,
583 ScanReceivesPeerAddedToPeerCacheAfterScanStart) {
584 fidl::InterfaceHandle<fble::ScanResultWatcher> result_watcher_handle;
585 auto result_watcher_server = result_watcher_handle.NewRequest();
586 auto result_watcher_client = result_watcher_handle.Bind();
587 std::optional<zx_status_t> epitaph;
588 result_watcher_client.set_error_handler(
589 [&](zx_status_t cb_epitaph) { epitaph = cb_epitaph; });
590
591 bool scan_stopped = false;
592 central_proxy()->Scan(ScanOptionsWithEmptyFilter(),
593 std::move(result_watcher_server),
594 [&]() { scan_stopped = true; });
595
596 RunLoopUntilIdle();
597 EXPECT_FALSE(scan_stopped);
598 EXPECT_FALSE(epitaph);
599
600 std::optional<std::vector<fble::Peer>> peers;
601 result_watcher_client->Watch(
602 [&](std::vector<fble::Peer> updated) { peers = std::move(updated); });
603 RunLoopUntilIdle();
604 ASSERT_FALSE(peers.has_value());
605
606 bt::gap::Peer* peer =
607 adapter()->peer_cache()->NewPeer(kTestAddr, /*connectable=*/false);
608 RunLoopUntilIdle();
609 ASSERT_TRUE(peers.has_value());
610 ASSERT_EQ(peers->size(), 1u);
611 ASSERT_TRUE(peers->front().has_id());
612 EXPECT_EQ(peers->front().id().value, peer->identifier().value());
613
614 result_watcher_client.Unbind();
615 RunLoopUntilIdle();
616 EXPECT_TRUE(scan_stopped);
617 }
618
TEST_F(LowEnergyCentralServerTest,PeerAddedToPeerCacheAfterScanEndDoesNotCrash)619 TEST_F(LowEnergyCentralServerTest,
620 PeerAddedToPeerCacheAfterScanEndDoesNotCrash) {
621 fidl::InterfaceHandle<fble::ScanResultWatcher> result_watcher_handle;
622 auto result_watcher_server = result_watcher_handle.NewRequest();
623 auto result_watcher_client = result_watcher_handle.Bind();
624 std::optional<zx_status_t> epitaph;
625 result_watcher_client.set_error_handler(
626 [&](zx_status_t cb_epitaph) { epitaph = cb_epitaph; });
627
628 bool scan_stopped = false;
629 central_proxy()->Scan(ScanOptionsWithEmptyFilter(),
630 std::move(result_watcher_server),
631 [&]() { scan_stopped = true; });
632
633 RunLoopUntilIdle();
634 EXPECT_FALSE(scan_stopped);
635 EXPECT_FALSE(epitaph);
636
637 RunLoopUntilIdle();
638
639 result_watcher_client.Unbind();
640 RunLoopUntilIdle();
641 EXPECT_TRUE(scan_stopped);
642
643 adapter()->peer_cache()->NewPeer(kTestAddr, /*connectable=*/false);
644 RunLoopUntilIdle();
645 }
646
TEST_F(LowEnergyCentralServerTest,ConcurrentScansFail)647 TEST_F(LowEnergyCentralServerTest, ConcurrentScansFail) {
648 fidl::InterfaceHandle<fble::ScanResultWatcher> result_watcher_handle_0;
649 auto result_watcher_server_0 = result_watcher_handle_0.NewRequest();
650 auto result_watcher_client_0 = result_watcher_handle_0.Bind();
651 bool scan_stopped_0 = false;
652 central_proxy()->Scan(ScanOptionsWithEmptyFilter(),
653 std::move(result_watcher_server_0),
654 [&]() { scan_stopped_0 = true; });
655 RunLoopUntilIdle();
656 EXPECT_FALSE(scan_stopped_0);
657
658 fidl::InterfaceHandle<fble::ScanResultWatcher> result_watcher_handle_1;
659 auto result_watcher_server_1 = result_watcher_handle_1.NewRequest();
660 auto result_watcher_client_1 = result_watcher_handle_1.Bind();
661 std::optional<zx_status_t> epitaph_1;
662 result_watcher_client_1.set_error_handler(
663 [&](zx_status_t cb_epitaph) { epitaph_1 = cb_epitaph; });
664
665 bool scan_stopped_1 = false;
666 central_proxy()->Scan(ScanOptionsWithEmptyFilter(),
667 std::move(result_watcher_server_1),
668 [&]() { scan_stopped_1 = true; });
669 RunLoopUntilIdle();
670 EXPECT_FALSE(scan_stopped_0);
671 EXPECT_TRUE(scan_stopped_1);
672 ASSERT_TRUE(epitaph_1);
673 EXPECT_EQ(epitaph_1.value(), ZX_ERR_ALREADY_EXISTS);
674
675 result_watcher_client_0.Unbind();
676 RunLoopUntilIdle();
677 EXPECT_TRUE(scan_stopped_0);
678 }
679
TEST_F(LowEnergyCentralServerTest,SequentialScansSucceed)680 TEST_F(LowEnergyCentralServerTest, SequentialScansSucceed) {
681 fidl::InterfaceHandle<fble::ScanResultWatcher> result_watcher_handle_0;
682 auto result_watcher_server_0 = result_watcher_handle_0.NewRequest();
683 auto result_watcher_client_0 = result_watcher_handle_0.Bind();
684 bool scan_stopped_0 = false;
685 central_proxy()->Scan(ScanOptionsWithEmptyFilter(),
686 std::move(result_watcher_server_0),
687 [&]() { scan_stopped_0 = true; });
688 RunLoopUntilIdle();
689 EXPECT_FALSE(scan_stopped_0);
690
691 result_watcher_client_0.Unbind();
692 RunLoopUntilIdle();
693 EXPECT_TRUE(scan_stopped_0);
694
695 fidl::InterfaceHandle<fble::ScanResultWatcher> result_watcher_handle_1;
696 auto result_watcher_server_1 = result_watcher_handle_1.NewRequest();
697 auto result_watcher_client_1 = result_watcher_handle_1.Bind();
698 bool scan_stopped_1 = false;
699 central_proxy()->Scan(ScanOptionsWithEmptyFilter(),
700 std::move(result_watcher_server_1),
701 [&]() { scan_stopped_1 = true; });
702 RunLoopUntilIdle();
703 EXPECT_FALSE(scan_stopped_1);
704
705 result_watcher_client_1.Unbind();
706 RunLoopUntilIdle();
707 EXPECT_TRUE(scan_stopped_1);
708 }
709
TEST_F(LowEnergyCentralServerTest,IgnorePeersThatDoNotMatchFilter)710 TEST_F(LowEnergyCentralServerTest, IgnorePeersThatDoNotMatchFilter) {
711 fble::ScanOptions options;
712 fble::Filter filter;
713 filter.set_connectable(true);
714 std::vector<fble::Filter> filters;
715 filters.emplace_back(std::move(filter));
716 options.set_filters(std::move(filters));
717
718 fidl::InterfaceHandle<fble::ScanResultWatcher> result_watcher_handle;
719 auto result_watcher_server = result_watcher_handle.NewRequest();
720 auto result_watcher_client = result_watcher_handle.Bind();
721 std::optional<zx_status_t> epitaph;
722 result_watcher_client.set_error_handler(
723 [&](zx_status_t cb_epitaph) { epitaph = cb_epitaph; });
724
725 bool scan_stopped = false;
726 central_proxy()->Scan(std::move(options),
727 std::move(result_watcher_server),
728 [&]() { scan_stopped = true; });
729
730 RunLoopUntilIdle();
731 EXPECT_FALSE(scan_stopped);
732 EXPECT_FALSE(epitaph);
733
734 std::optional<std::vector<fble::Peer>> peers;
735 result_watcher_client->Watch(
736 [&](std::vector<fble::Peer> updated) { peers = std::move(updated); });
737 RunLoopUntilIdle();
738 ASSERT_FALSE(peers.has_value());
739
740 // Peer is not LE
741 adapter()->peer_cache()->NewPeer(
742 bt::DeviceAddress(bt::DeviceAddress::Type::kBREDR, {1, 0, 0, 0, 0, 0}),
743 /*connectable=*/true);
744 // Peer is not connectable
745 adapter()->peer_cache()->NewPeer(
746 bt::DeviceAddress(bt::DeviceAddress::Type::kLEPublic, {2, 0, 0, 0, 0, 0}),
747 /*connectable=*/false);
748
749 RunLoopUntilIdle();
750 EXPECT_FALSE(peers.has_value());
751
752 result_watcher_client.Unbind();
753 RunLoopUntilIdle();
754 EXPECT_TRUE(scan_stopped);
755 }
756
TEST_F(LowEnergyCentralServerTest,IgnorePeerThatDoesNotMatchServiceDataFilter)757 TEST_F(LowEnergyCentralServerTest,
758 IgnorePeerThatDoesNotMatchServiceDataFilter) {
759 fble::ScanOptions options;
760 fble::Filter filter;
761 const bt::UUID kServiceUuid(static_cast<uint16_t>(2));
762 filter.set_connectable(true);
763 filter.set_service_data_uuid(fuchsia::bluetooth::Uuid{kServiceUuid.value()});
764 std::vector<fble::Filter> filters;
765 filters.emplace_back(std::move(filter));
766 options.set_filters(std::move(filters));
767
768 fidl::InterfaceHandle<fble::ScanResultWatcher> result_watcher_handle;
769 auto result_watcher_server = result_watcher_handle.NewRequest();
770 auto result_watcher_client = result_watcher_handle.Bind();
771 std::optional<zx_status_t> epitaph;
772 result_watcher_client.set_error_handler(
773 [&](zx_status_t cb_epitaph) { epitaph = cb_epitaph; });
774
775 bool scan_stopped = false;
776 central_proxy()->Scan(std::move(options),
777 std::move(result_watcher_server),
778 [&]() { scan_stopped = true; });
779
780 RunLoopUntilIdle();
781 EXPECT_FALSE(scan_stopped);
782 EXPECT_FALSE(epitaph);
783
784 std::optional<std::vector<fble::Peer>> peers;
785 result_watcher_client->Watch(
786 [&](std::vector<fble::Peer> updated) { peers = std::move(updated); });
787 RunLoopUntilIdle();
788 ASSERT_FALSE(peers.has_value());
789
790 // Peer is connectable but doesn't have any service data.
791 adapter()->peer_cache()->NewPeer(
792 bt::DeviceAddress(bt::DeviceAddress::Type::kLEPublic, {2, 0, 0, 0, 0, 0}),
793 /*connectable=*/true);
794
795 RunLoopUntilIdle();
796 EXPECT_FALSE(peers.has_value());
797
798 result_watcher_client.Unbind();
799 RunLoopUntilIdle();
800 EXPECT_TRUE(scan_stopped);
801 }
802
TEST_F(LowEnergyCentralServerTest,DoNotNotifyResultWatcherWithPeerThatWasRemovedFromPeerCacheWhileQueued)803 TEST_F(LowEnergyCentralServerTest,
804 DoNotNotifyResultWatcherWithPeerThatWasRemovedFromPeerCacheWhileQueued) {
805 bt::gap::Peer* peer =
806 adapter()->peer_cache()->NewPeer(kTestAddr, /*connectable=*/false);
807
808 fidl::InterfaceHandle<fble::ScanResultWatcher> result_watcher_handle;
809 auto result_watcher_server = result_watcher_handle.NewRequest();
810 auto result_watcher_client = result_watcher_handle.Bind();
811 std::optional<zx_status_t> epitaph;
812 result_watcher_client.set_error_handler(
813 [&](zx_status_t cb_epitaph) { epitaph = cb_epitaph; });
814
815 bool scan_stopped = false;
816 central_proxy()->Scan(ScanOptionsWithEmptyFilter(),
817 std::move(result_watcher_server),
818 [&]() { scan_stopped = true; });
819
820 RunLoopUntilIdle();
821 EXPECT_FALSE(scan_stopped);
822 EXPECT_FALSE(epitaph);
823
824 // Peer is in ScanResultWatcher queue. Remove it from PeerCache before Watch()
825 // is called.
826 EXPECT_TRUE(
827 adapter()->peer_cache()->RemoveDisconnectedPeer(peer->identifier()));
828
829 std::optional<std::vector<fble::Peer>> peers;
830 result_watcher_client->Watch(
831 [&](std::vector<fble::Peer> updated) { peers = std::move(updated); });
832 RunLoopUntilIdle();
833 EXPECT_FALSE(peers.has_value());
834
835 result_watcher_client.Unbind();
836 RunLoopUntilIdle();
837 EXPECT_TRUE(scan_stopped);
838 }
839
TEST_F(LowEnergyCentralServerTest,MaxQueuedScanResultWatcherPeers)840 TEST_F(LowEnergyCentralServerTest, MaxQueuedScanResultWatcherPeers) {
841 // Create smallest possible peer
842 bt::gap::Peer* peer_0 = adapter()->peer_cache()->NewPeer(
843 bt::DeviceAddress(bt::DeviceAddress::Type::kLEPublic, {0, 0, 0, 0, 0, 0}),
844 /*connectable=*/false);
845 const size_t kMaxPeersPerChannel =
846 MaxPeersPerScanResultWatcherChannel(*peer_0);
847 ASSERT_GT(kMaxPeersPerChannel,
848 LowEnergyCentralServer::kMaxPendingScanResultWatcherPeers);
849
850 // Queue 1 more peer than queue size limit.
851 ASSERT_LE(LowEnergyCentralServer::kMaxPendingScanResultWatcherPeers,
852 std::numeric_limits<uint8_t>::max());
853 for (size_t i = 1;
854 i < LowEnergyCentralServer::kMaxPendingScanResultWatcherPeers + 1;
855 i++) {
856 SCOPED_TRACE(i);
857 ASSERT_TRUE(adapter()->peer_cache()->NewPeer(
858 bt::DeviceAddress(bt::DeviceAddress::Type::kLEPublic,
859 {static_cast<uint8_t>(i), 0, 0, 0, 0, 0}),
860 /*connectable=*/false));
861 }
862
863 fidl::InterfaceHandle<fble::ScanResultWatcher> result_watcher_handle;
864 auto result_watcher_server = result_watcher_handle.NewRequest();
865 auto result_watcher_client = result_watcher_handle.Bind();
866 std::optional<zx_status_t> epitaph;
867 result_watcher_client.set_error_handler(
868 [&](zx_status_t cb_epitaph) { epitaph = cb_epitaph; });
869
870 bool scan_stopped = false;
871 central_proxy()->Scan(ScanOptionsWithEmptyFilter(),
872 std::move(result_watcher_server),
873 [&]() { scan_stopped = true; });
874
875 RunLoopUntilIdle();
876 EXPECT_FALSE(scan_stopped);
877 EXPECT_FALSE(epitaph);
878
879 std::optional<std::vector<fble::Peer>> peers;
880 result_watcher_client->Watch(
881 [&](std::vector<fble::Peer> updated) { peers = std::move(updated); });
882 RunLoopUntilIdle();
883 ASSERT_TRUE(peers.has_value());
884 EXPECT_EQ(peers->size(),
885 LowEnergyCentralServer::kMaxPendingScanResultWatcherPeers);
886 peers.reset();
887
888 // Additional calls to Watch should hang
889 result_watcher_client->Watch(
890 [&](std::vector<fble::Peer> updated) { peers = std::move(updated); });
891 RunLoopUntilIdle();
892 EXPECT_FALSE(peers.has_value());
893
894 result_watcher_client.Unbind();
895 RunLoopUntilIdle();
896 EXPECT_TRUE(scan_stopped);
897 }
898
TEST_F(LowEnergyCentralServerTest,ScanResultWatcherMeasureTape)899 TEST_F(LowEnergyCentralServerTest, ScanResultWatcherMeasureTape) {
900 // Create a very large Peer
901 bt::gap::Peer* peer_0 = adapter()->peer_cache()->NewPeer(
902 bt::DeviceAddress(bt::DeviceAddress::Type::kLEPublic, {0, 0, 0, 0, 0, 0}),
903 /*connectable=*/true);
904 bt::AdvertisingData adv_data;
905 for (int i = 0; i < 100; i++) {
906 SCOPED_TRACE(i);
907 ASSERT_TRUE(adv_data.AddUri(
908 bt_lib_cpp_string::StringPrintf("uri:a-really-long-uri-%d", i)));
909 }
910 adv_data.CalculateBlockSize();
911 bt::DynamicByteBuffer adv_buffer(adv_data.CalculateBlockSize());
912 adv_data.WriteBlock(&adv_buffer, std::nullopt);
913 peer_0->MutLe().SetAdvertisingData(
914 /*rssi=*/0, adv_buffer, pw::chrono::SystemClock::time_point());
915
916 const size_t kMaxPeersPerChannel =
917 MaxPeersPerScanResultWatcherChannel(*peer_0);
918
919 // Queue 1 more peer than will fit in the channel.
920 // Start at i = 1 because peer_0 was created above.
921 ASSERT_LE(kMaxPeersPerChannel, std::numeric_limits<uint8_t>::max());
922 ASSERT_GT(LowEnergyCentralServer::kMaxPendingScanResultWatcherPeers,
923 kMaxPeersPerChannel);
924 for (size_t i = 1; i < kMaxPeersPerChannel + 1; i++) {
925 SCOPED_TRACE(i);
926 bt::gap::Peer* peer = adapter()->peer_cache()->NewPeer(
927 bt::DeviceAddress(bt::DeviceAddress::Type::kLEPublic,
928 {static_cast<uint8_t>(i), 0, 0, 0, 0, 0}),
929 /*connectable=*/false);
930 ASSERT_TRUE(peer);
931 peer->MutLe().SetAdvertisingData(
932 /*rssi=*/0, adv_buffer, pw::chrono::SystemClock::time_point());
933 }
934
935 fidl::InterfaceHandle<fble::ScanResultWatcher> result_watcher_handle;
936 auto result_watcher_server = result_watcher_handle.NewRequest();
937 auto result_watcher_client = result_watcher_handle.Bind();
938 std::optional<zx_status_t> epitaph;
939 result_watcher_client.set_error_handler(
940 [&](zx_status_t cb_epitaph) { epitaph = cb_epitaph; });
941
942 bool scan_stopped = false;
943 central_proxy()->Scan(ScanOptionsWithEmptyFilter(),
944 std::move(result_watcher_server),
945 [&]() { scan_stopped = true; });
946
947 RunLoopUntilIdle();
948 EXPECT_FALSE(scan_stopped);
949 EXPECT_FALSE(epitaph);
950
951 std::optional<std::vector<fble::Peer>> peers;
952 result_watcher_client->Watch(
953 [&](std::vector<fble::Peer> updated) { peers = std::move(updated); });
954 RunLoopUntilIdle();
955 ASSERT_TRUE(peers.has_value());
956 EXPECT_EQ(peers->size(), kMaxPeersPerChannel);
957 peers.reset();
958
959 // Additional call to Watch should return the 1 peer that exceeded the channel
960 // size limit.
961 result_watcher_client->Watch(
962 [&](std::vector<fble::Peer> updated) { peers = std::move(updated); });
963 RunLoopUntilIdle();
964 ASSERT_TRUE(peers.has_value());
965 EXPECT_EQ(peers->size(), 1u);
966
967 result_watcher_client.Unbind();
968 RunLoopUntilIdle();
969 EXPECT_TRUE(scan_stopped);
970 }
971
TEST_F(LowEnergyCentralServerTest,ScanResultsMatchPeerFromAnyFilter)972 TEST_F(LowEnergyCentralServerTest, ScanResultsMatchPeerFromAnyFilter) {
973 const int8_t kRssi = 0;
974 // Peer that matches neither filter
975 adapter()->peer_cache()->NewPeer(
976 bt::DeviceAddress(bt::DeviceAddress::Type::kLEPublic, {0, 0, 0, 0, 0, 0}),
977 /*connectable=*/false);
978
979 // Peer that matches filter_0
980 bt::gap::Peer* peer_0 = adapter()->peer_cache()->NewPeer(
981 bt::DeviceAddress(bt::DeviceAddress::Type::kLEPublic, {1, 0, 0, 0, 0, 0}),
982 /*connectable=*/true);
983 ASSERT_TRUE(peer_0);
984 const auto kAdvData0 =
985 bt::StaticByteBuffer(0x02, // Length
986 0x09, // AD type: Complete Local Name
987 '0');
988 peer_0->MutLe().SetAdvertisingData(
989 kRssi, kAdvData0, pw::chrono::SystemClock::time_point());
990 // Peer that matches filter_1
991 bt::gap::Peer* peer_1 = adapter()->peer_cache()->NewPeer(
992 bt::DeviceAddress(bt::DeviceAddress::Type::kLEPublic, {2, 0, 0, 0, 0, 0}),
993 /*connectable=*/false);
994 ASSERT_TRUE(peer_1);
995 const auto kAdvData1 =
996 bt::StaticByteBuffer(0x02, // Length
997 0x09, // AD type: Complete Local Name
998 '1');
999 peer_1->MutLe().SetAdvertisingData(
1000 kRssi, kAdvData1, pw::chrono::SystemClock::time_point());
1001
1002 fble::ScanOptions options;
1003 fble::Filter filter_0;
1004 filter_0.set_connectable(true);
1005 filter_0.set_name("0");
1006 fble::Filter filter_1;
1007 filter_1.set_connectable(false);
1008 filter_1.set_name("1");
1009 std::vector<fble::Filter> filters;
1010 filters.emplace_back(std::move(filter_0));
1011 filters.emplace_back(std::move(filter_1));
1012 options.set_filters(std::move(filters));
1013
1014 fidl::InterfaceHandle<fble::ScanResultWatcher> result_watcher_handle;
1015 auto result_watcher_server = result_watcher_handle.NewRequest();
1016 auto result_watcher_client = result_watcher_handle.Bind();
1017 std::optional<zx_status_t> epitaph;
1018 result_watcher_client.set_error_handler(
1019 [&](zx_status_t cb_epitaph) { epitaph = cb_epitaph; });
1020
1021 bool scan_stopped = false;
1022 central_proxy()->Scan(std::move(options),
1023 std::move(result_watcher_server),
1024 [&]() { scan_stopped = true; });
1025
1026 RunLoopUntilIdle();
1027 EXPECT_FALSE(scan_stopped);
1028 EXPECT_FALSE(epitaph);
1029
1030 std::optional<std::vector<bt::PeerId>> peers;
1031 result_watcher_client->Watch([&](std::vector<fble::Peer> updated) {
1032 peers = std::vector<bt::PeerId>();
1033 std::transform(updated.begin(),
1034 updated.end(),
1035 std::back_inserter(*peers),
1036 [](auto& p) { return bt::PeerId(p.id().value); });
1037 });
1038 RunLoopUntilIdle();
1039 ASSERT_TRUE(peers.has_value());
1040 EXPECT_THAT(
1041 peers.value(),
1042 ::testing::UnorderedElementsAre(::testing::Eq(peer_0->identifier()),
1043 ::testing::Eq(peer_1->identifier())));
1044 result_watcher_client.Unbind();
1045 RunLoopUntilIdle();
1046 EXPECT_TRUE(scan_stopped);
1047 }
1048
TEST_F(LowEnergyCentralServerTest,DiscoveryStartJustAfterScanCanceledShouldBeIgnored)1049 TEST_F(LowEnergyCentralServerTest,
1050 DiscoveryStartJustAfterScanCanceledShouldBeIgnored) {
1051 // Pause discovery so that we can cancel scanning before resuming discovery.
1052 fit::closure start_discovery;
1053 test_device()->pause_responses_for_opcode(
1054 bt::hci_spec::kLESetScanEnable, [&](auto resume_set_scan_enable) {
1055 start_discovery = std::move(resume_set_scan_enable);
1056 });
1057
1058 fidl::InterfaceHandle<fble::ScanResultWatcher> result_watcher_handle;
1059 auto result_watcher_server = result_watcher_handle.NewRequest();
1060 auto result_watcher_client = result_watcher_handle.Bind();
1061
1062 bool scan_stopped = false;
1063 central_proxy()->Scan(ScanOptionsWithEmptyFilter(),
1064 std::move(result_watcher_server),
1065 [&]() { scan_stopped = true; });
1066
1067 RunLoopUntilIdle();
1068 EXPECT_FALSE(scan_stopped);
1069 EXPECT_TRUE(start_discovery);
1070
1071 result_watcher_client.Unbind();
1072 RunLoopUntilIdle();
1073 EXPECT_TRUE(scan_stopped);
1074
1075 start_discovery();
1076 RunLoopUntilIdle();
1077 }
1078
TEST_F(LowEnergyCentralServerTest,ScanFailsToStart)1079 TEST_F(LowEnergyCentralServerTest, ScanFailsToStart) {
1080 test_device()->SetDefaultResponseStatus(
1081 bt::hci_spec::kLESetScanEnable,
1082 pw::bluetooth::emboss::StatusCode::CONTROLLER_BUSY);
1083
1084 fidl::InterfaceHandle<fble::ScanResultWatcher> result_watcher_handle;
1085 auto result_watcher_server = result_watcher_handle.NewRequest();
1086 auto result_watcher_client = result_watcher_handle.Bind();
1087 std::optional<zx_status_t> epitaph;
1088 result_watcher_client.set_error_handler(
1089 [&](zx_status_t cb_epitaph) { epitaph = cb_epitaph; });
1090
1091 bool scan_stopped = false;
1092 central_proxy()->Scan(ScanOptionsWithEmptyFilter(),
1093 std::move(result_watcher_server),
1094 [&]() { scan_stopped = true; });
1095
1096 RunLoopUntilIdle();
1097 EXPECT_TRUE(scan_stopped);
1098 ASSERT_TRUE(epitaph);
1099 EXPECT_EQ(*epitaph, ZX_ERR_INTERNAL);
1100 }
1101
TEST_F(LowEnergyCentralServerTest,ScanSessionErrorCancelsScan)1102 TEST_F(LowEnergyCentralServerTest, ScanSessionErrorCancelsScan) {
1103 zx::duration kTestScanPeriod = zx::sec(1);
1104 pw::chrono::SystemClock::duration kPwTestScanPeriod = std::chrono::seconds(1);
1105 adapter()->le()->set_scan_period_for_testing(kPwTestScanPeriod);
1106 std::vector<bool> scan_states;
1107 test_device()->set_scan_state_callback([&](bool enabled) {
1108 scan_states.push_back(enabled);
1109 // Wait for 2 state transitions: -> enabled -> disabled.
1110 // Then disable restarting scanning, so that an error is sent to sessions.
1111 if (scan_states.size() == 2u) {
1112 EXPECT_FALSE(enabled);
1113 test_device()->SetDefaultResponseStatus(
1114 bt::hci_spec::kLESetScanEnable,
1115 pw::bluetooth::emboss::StatusCode::COMMAND_DISALLOWED);
1116 }
1117 });
1118
1119 fidl::InterfaceHandle<fble::ScanResultWatcher> result_watcher_handle;
1120 auto result_watcher_server = result_watcher_handle.NewRequest();
1121 auto result_watcher_client = result_watcher_handle.Bind();
1122 std::optional<zx_status_t> epitaph;
1123 result_watcher_client.set_error_handler(
1124 [&](zx_status_t cb_epitaph) { epitaph = cb_epitaph; });
1125
1126 bool scan_stopped = false;
1127 central_proxy()->Scan(ScanOptionsWithEmptyFilter(),
1128 std::move(result_watcher_server),
1129 [&]() { scan_stopped = true; });
1130 RunLoopFor(kTestScanPeriod);
1131 EXPECT_TRUE(scan_stopped);
1132 ASSERT_TRUE(epitaph);
1133 EXPECT_EQ(*epitaph, ZX_ERR_INTERNAL);
1134 }
1135
TEST_F(LowEnergyCentralServerTest,ScanResultWatcherWatchCalledBeforePreviousWatchReceivedResponse)1136 TEST_F(LowEnergyCentralServerTest,
1137 ScanResultWatcherWatchCalledBeforePreviousWatchReceivedResponse) {
1138 fidl::InterfaceHandle<fble::ScanResultWatcher> result_watcher_handle;
1139 auto result_watcher_server = result_watcher_handle.NewRequest();
1140 auto result_watcher_client = result_watcher_handle.Bind();
1141 std::optional<zx_status_t> epitaph;
1142 result_watcher_client.set_error_handler(
1143 [&](zx_status_t cb_epitaph) { epitaph = cb_epitaph; });
1144
1145 bool scan_stopped = false;
1146 central_proxy()->Scan(ScanOptionsWithEmptyFilter(),
1147 std::move(result_watcher_server),
1148 [&]() { scan_stopped = true; });
1149 bool watch_response_0 = false;
1150 result_watcher_client->Watch([&](auto) { watch_response_0 = true; });
1151 bool watch_response_1 = false;
1152 result_watcher_client->Watch([&](auto) { watch_response_1 = true; });
1153 RunLoopUntilIdle();
1154 EXPECT_FALSE(watch_response_0);
1155 EXPECT_FALSE(watch_response_1);
1156 EXPECT_TRUE(scan_stopped);
1157 ASSERT_TRUE(epitaph);
1158 EXPECT_EQ(*epitaph, ZX_ERR_CANCELED);
1159 }
1160
TEST_F(LowEnergyCentralServerTest,ConnectToAlreadyConnectedPeerFails)1161 TEST_F(LowEnergyCentralServerTest, ConnectToAlreadyConnectedPeerFails) {
1162 auto* const peer =
1163 adapter()->peer_cache()->NewPeer(kTestAddr, /*connectable=*/true);
1164 ASSERT_TRUE(peer);
1165 test_device()->AddPeer(
1166 std::make_unique<bt::testing::FakePeer>(kTestAddr, pw_dispatcher()));
1167
1168 fble::ConnectionPtr conn_client_0;
1169 std::optional<zx_status_t> epitaph_0;
1170 conn_client_0.set_error_handler(
1171 [&](zx_status_t cb_epitaph) { epitaph_0 = cb_epitaph; });
1172
1173 const fuchsia::bluetooth::PeerId peer_id{peer->identifier().value()};
1174 fble::ConnectionOptions options_0;
1175 central_proxy()->Connect(
1176 peer_id, std::move(options_0), conn_client_0.NewRequest());
1177 RunLoopUntilIdle();
1178 EXPECT_FALSE(epitaph_0.has_value());
1179
1180 fble::ConnectionPtr conn_client_1;
1181 std::optional<zx_status_t> epitaph_1;
1182 conn_client_1.set_error_handler(
1183 [&](zx_status_t cb_epitaph) { epitaph_1 = cb_epitaph; });
1184
1185 fble::ConnectionOptions options_1;
1186 central_proxy()->Connect(
1187 peer_id, std::move(options_1), conn_client_1.NewRequest());
1188 RunLoopUntilIdle();
1189 EXPECT_FALSE(epitaph_0.has_value());
1190 ASSERT_TRUE(epitaph_1.has_value());
1191 EXPECT_EQ(epitaph_1.value(), ZX_ERR_ALREADY_BOUND);
1192 }
1193
TEST_F(LowEnergyCentralServerTest,ConnectToPeerWithRequestPending)1194 TEST_F(LowEnergyCentralServerTest, ConnectToPeerWithRequestPending) {
1195 auto* const peer =
1196 adapter()->peer_cache()->NewPeer(kTestAddr, /*connectable=*/true);
1197 ASSERT_TRUE(peer);
1198
1199 auto fake_peer =
1200 std::make_unique<bt::testing::FakePeer>(kTestAddr, pw_dispatcher());
1201 fake_peer->force_pending_connect();
1202 test_device()->AddPeer(std::move(fake_peer));
1203
1204 fble::ConnectionPtr conn_client_0;
1205 std::optional<zx_status_t> epitaph_0;
1206 conn_client_0.set_error_handler(
1207 [&](zx_status_t cb_epitaph) { epitaph_0 = cb_epitaph; });
1208
1209 const fuchsia::bluetooth::PeerId peer_id{peer->identifier().value()};
1210 fble::ConnectionOptions options_0;
1211 central_proxy()->Connect(
1212 peer_id, std::move(options_0), conn_client_0.NewRequest());
1213 RunLoopUntilIdle();
1214 EXPECT_FALSE(epitaph_0.has_value());
1215
1216 fble::ConnectionPtr conn_client_1;
1217 std::optional<zx_status_t> epitaph_1;
1218 conn_client_1.set_error_handler(
1219 [&](zx_status_t cb_epitaph) { epitaph_1 = cb_epitaph; });
1220
1221 fble::ConnectionOptions options_1;
1222 central_proxy()->Connect(
1223 peer_id, std::move(options_1), conn_client_1.NewRequest());
1224 RunLoopUntilIdle();
1225 EXPECT_FALSE(epitaph_0.has_value());
1226 ASSERT_TRUE(epitaph_1.has_value());
1227 EXPECT_EQ(epitaph_1.value(), ZX_ERR_ALREADY_BOUND);
1228 }
1229
TEST_F(LowEnergyCentralServerTest,ConnectToPeerAlreadyConnectedInLowEnergyConnectionManager)1230 TEST_F(LowEnergyCentralServerTest,
1231 ConnectToPeerAlreadyConnectedInLowEnergyConnectionManager) {
1232 auto* const peer =
1233 adapter()->peer_cache()->NewPeer(kTestAddr, /*connectable=*/true);
1234 ASSERT_TRUE(peer);
1235 test_device()->AddPeer(
1236 std::make_unique<bt::testing::FakePeer>(kTestAddr, pw_dispatcher()));
1237
1238 std::unique_ptr<bt::gap::LowEnergyConnectionHandle> le_conn;
1239 adapter()->le()->Connect(
1240 peer->identifier(),
1241 [&le_conn](auto result) {
1242 ASSERT_EQ(fit::ok(), result);
1243 le_conn = std::move(result).value();
1244 },
1245 bt::gap::LowEnergyConnectionOptions());
1246 RunLoopUntilIdle();
1247 ASSERT_TRUE(le_conn);
1248
1249 fble::ConnectionPtr conn_client1;
1250 std::optional<zx_status_t> epitaph1;
1251 conn_client1.set_error_handler(
1252 [&](zx_status_t cb_epitaph) { epitaph1 = cb_epitaph; });
1253
1254 const fuchsia::bluetooth::PeerId kFidlPeerId{peer->identifier().value()};
1255 fble::ConnectionOptions options1;
1256 central_proxy()->Connect(
1257 kFidlPeerId, std::move(options1), conn_client1.NewRequest());
1258 RunLoopUntilIdle();
1259 EXPECT_FALSE(epitaph1.has_value());
1260 }
1261
TEST_F(LowEnergyCentralServerTest,ConnectThenPeerDisconnectThenReconnect)1262 TEST_F(LowEnergyCentralServerTest, ConnectThenPeerDisconnectThenReconnect) {
1263 auto* const peer =
1264 adapter()->peer_cache()->NewPeer(kTestAddr, /*connectable=*/true);
1265 ASSERT_TRUE(peer);
1266 const fuchsia::bluetooth::PeerId kFidlPeerId{peer->identifier().value()};
1267
1268 std::unique_ptr<bt::testing::FakePeer> fake_peer =
1269 std::make_unique<bt::testing::FakePeer>(kTestAddr, pw_dispatcher());
1270 test_device()->AddPeer(std::move(fake_peer));
1271
1272 fble::ConnectionPtr conn_client_0;
1273 std::optional<zx_status_t> epitaph_0;
1274 conn_client_0.set_error_handler(
1275 [&](zx_status_t cb_epitaph) { epitaph_0 = cb_epitaph; });
1276
1277 fble::ConnectionOptions options_0;
1278 central_proxy()->Connect(
1279 kFidlPeerId, std::move(options_0), conn_client_0.NewRequest());
1280 RunLoopUntilIdle();
1281 EXPECT_FALSE(epitaph_0.has_value());
1282
1283 test_device()->Disconnect(kTestAddr);
1284 RunLoopUntilIdle();
1285 EXPECT_TRUE(epitaph_0.has_value());
1286
1287 fble::ConnectionPtr conn_client_1;
1288 std::optional<zx_status_t> epitaph_1;
1289 conn_client_1.set_error_handler(
1290 [&](zx_status_t cb_epitaph) { epitaph_1 = cb_epitaph; });
1291
1292 fble::ConnectionOptions options_1;
1293 central_proxy()->Connect(
1294 kFidlPeerId, std::move(options_1), conn_client_1.NewRequest());
1295 RunLoopUntilIdle();
1296 EXPECT_FALSE(epitaph_1.has_value());
1297 }
1298
TEST_F(LowEnergyCentralServerTest,ConnectFailsDueToPeerNotConnectableThenConnectSuceeds)1299 TEST_F(LowEnergyCentralServerTest,
1300 ConnectFailsDueToPeerNotConnectableThenConnectSuceeds) {
1301 bt::gap::Peer* peer =
1302 adapter()->peer_cache()->NewPeer(kTestAddr, /*connectable=*/false);
1303 ASSERT_TRUE(peer);
1304 auto fake_peer =
1305 std::make_unique<bt::testing::FakePeer>(kTestAddr, pw_dispatcher());
1306 test_device()->AddPeer(std::move(fake_peer));
1307
1308 fble::ConnectionPtr conn_client_0;
1309 std::optional<zx_status_t> epitaph_0;
1310 conn_client_0.set_error_handler(
1311 [&](zx_status_t cb_epitaph) { epitaph_0 = cb_epitaph; });
1312
1313 central_proxy()->Connect(
1314 fuchsia::bluetooth::PeerId{peer->identifier().value()},
1315 fble::ConnectionOptions{},
1316 conn_client_0.NewRequest());
1317 RunLoopUntilIdle();
1318 ASSERT_TRUE(epitaph_0.has_value());
1319 EXPECT_EQ(epitaph_0.value(), ZX_ERR_NOT_CONNECTED);
1320
1321 // Connect to peer to verify connection state was cleaned up on previous
1322 // error.
1323 peer->set_connectable(true);
1324
1325 fble::ConnectionPtr conn_client_1;
1326 std::optional<zx_status_t> epitaph_1;
1327 conn_client_1.set_error_handler(
1328 [&](zx_status_t cb_epitaph) { epitaph_1 = cb_epitaph; });
1329
1330 central_proxy()->Connect(
1331 fuchsia::bluetooth::PeerId{peer->identifier().value()},
1332 fble::ConnectionOptions{},
1333 conn_client_1.NewRequest());
1334 RunLoopUntilIdle();
1335 EXPECT_FALSE(epitaph_1.has_value());
1336 }
1337
TEST_F(LowEnergyCentralServerTestFakeAdapter,ConnectWithConnectionOptionsNonBondableAndServiceFilter)1338 TEST_F(LowEnergyCentralServerTestFakeAdapter,
1339 ConnectWithConnectionOptionsNonBondableAndServiceFilter) {
1340 const bt::PeerId kPeerId(1);
1341 const bt::UUID kServiceUuid(static_cast<uint16_t>(2));
1342
1343 fble::ConnectionPtr conn_client;
1344 std::optional<zx_status_t> epitaph;
1345 conn_client.set_error_handler(
1346 [&](zx_status_t cb_epitaph) { epitaph = cb_epitaph; });
1347
1348 fble::ConnectionOptions options;
1349 options.set_bondable_mode(false);
1350 options.set_service_filter(fuchsia::bluetooth::Uuid{kServiceUuid.value()});
1351 central_proxy()->Connect(fuchsia::bluetooth::PeerId{kPeerId.value()},
1352 std::move(options),
1353 conn_client.NewRequest());
1354 RunLoopUntilIdle();
1355 EXPECT_FALSE(epitaph.has_value());
1356
1357 auto& connections = adapter()->fake_le()->connections();
1358 auto conn_iter = connections.find(kPeerId);
1359 ASSERT_NE(conn_iter, connections.end());
1360 EXPECT_EQ(conn_iter->second.options.bondable_mode,
1361 bt::sm::BondableMode::NonBondable);
1362 ASSERT_TRUE(conn_iter->second.options.service_uuid.has_value());
1363 EXPECT_EQ(conn_iter->second.options.service_uuid, kServiceUuid);
1364 EXPECT_EQ(conn_iter->second.options.auto_connect, false);
1365 }
1366
TEST_P(LowEnergyCentralServerTestFakeAdapterBoolParam,ConnectConnectionOptionsBondable)1367 TEST_P(LowEnergyCentralServerTestFakeAdapterBoolParam,
1368 ConnectConnectionOptionsBondable) {
1369 const bt::PeerId kPeerId(1);
1370
1371 fble::ConnectionPtr conn_client;
1372 std::optional<zx_status_t> epitaph;
1373 conn_client.set_error_handler(
1374 [&](zx_status_t cb_epitaph) { epitaph = cb_epitaph; });
1375
1376 fble::ConnectionOptions options;
1377 // Bondable mode option defaults to true, so behavior shouldn't change whether
1378 // or not it is explicitly set to true.
1379 if (GetParam()) {
1380 options.set_bondable_mode(true);
1381 }
1382 central_proxy()->Connect(fuchsia::bluetooth::PeerId{kPeerId.value()},
1383 std::move(options),
1384 conn_client.NewRequest());
1385 RunLoopUntilIdle();
1386 EXPECT_FALSE(epitaph.has_value());
1387
1388 auto& connections = adapter()->fake_le()->connections();
1389 auto conn_iter = connections.find(kPeerId);
1390 ASSERT_NE(conn_iter, connections.end());
1391 EXPECT_EQ(conn_iter->second.options.bondable_mode,
1392 bt::sm::BondableMode::Bondable);
1393 }
1394
1395 INSTANTIATE_TEST_SUITE_P(LowEnergyCentralServerTestFakeAdapterBoolParamTests,
1396 LowEnergyCentralServerTestFakeAdapterBoolParam,
1397 ::testing::Bool());
1398
1399 } // namespace
1400 } // namespace bthost
1401