| // Copyright 2020 Google LLC |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "internal/platform/bluetooth_classic.h" |
| |
| #include <memory> |
| |
| #include "gmock/gmock.h" |
| #include "protobuf-matchers/protocol-buffer-matchers.h" |
| #include "gtest/gtest.h" |
| #include "absl/time/time.h" |
| #include "internal/platform/medium_environment.h" |
| #include "internal/platform/bluetooth_adapter.h" |
| #include "internal/platform/count_down_latch.h" |
| #include "internal/platform/logging.h" |
| #include "internal/platform/single_thread_executor.h" |
| |
| namespace location { |
| namespace nearby { |
| namespace { |
| |
| using FeatureFlags = FeatureFlags::Flags; |
| |
| constexpr FeatureFlags kTestCases[] = { |
| FeatureFlags{ |
| .enable_cancellation_flag = true, |
| }, |
| FeatureFlags{ |
| .enable_cancellation_flag = false, |
| }, |
| }; |
| |
| class BluetoothClassicMediumTest |
| : public ::testing::TestWithParam<FeatureFlags> { |
| protected: |
| using DiscoveryCallback = BluetoothClassicMedium::DiscoveryCallback; |
| BluetoothClassicMediumTest() { |
| env_.Start(); |
| env_.Reset(); |
| adapter_a_ = std::make_unique<BluetoothAdapter>(); |
| adapter_b_ = std::make_unique<BluetoothAdapter>(); |
| bt_a_ = std::make_unique<BluetoothClassicMedium>(*adapter_a_); |
| bt_b_ = std::make_unique<BluetoothClassicMedium>(*adapter_b_); |
| adapter_a_->SetName("Device-A"); |
| adapter_b_->SetName("Device-B"); |
| adapter_a_->SetStatus(BluetoothAdapter::Status::kEnabled); |
| adapter_b_->SetStatus(BluetoothAdapter::Status::kEnabled); |
| env_.Sync(); |
| } |
| ~BluetoothClassicMediumTest() override { |
| env_.Sync(false); |
| adapter_a_->SetStatus(BluetoothAdapter::Status::kDisabled); |
| adapter_b_->SetStatus(BluetoothAdapter::Status::kDisabled); |
| bt_a_.reset(); |
| bt_b_.reset(); |
| env_.Sync(false); |
| adapter_a_.reset(); |
| adapter_b_.reset(); |
| env_.Reset(); |
| env_.Stop(); |
| } |
| |
| MediumEnvironment& env_{MediumEnvironment::Instance()}; |
| |
| std::unique_ptr<BluetoothAdapter> adapter_a_; |
| std::unique_ptr<BluetoothAdapter> adapter_b_; |
| std::unique_ptr<BluetoothClassicMedium> bt_a_; |
| std::unique_ptr<BluetoothClassicMedium> bt_b_; |
| }; |
| |
| TEST_P(BluetoothClassicMediumTest, CanConnectToService) { |
| FeatureFlags feature_flags = GetParam(); |
| env_.SetFeatureFlags(feature_flags); |
| |
| adapter_a_->SetScanMode(BluetoothAdapter::ScanMode::kConnectable); |
| CountDownLatch found_latch(1); |
| BluetoothDevice* discovered_device = nullptr; |
| bt_a_->StartDiscovery(DiscoveryCallback{ |
| .device_discovered_cb = |
| [this, &found_latch, &discovered_device](BluetoothDevice& device) { |
| NEARBY_LOG(INFO, "Device discovered: %s", device.GetName().c_str()); |
| EXPECT_EQ(device.GetName(), adapter_b_->GetName()); |
| discovered_device = &device; |
| found_latch.CountDown(); |
| }, |
| }); |
| adapter_b_->SetScanMode(BluetoothAdapter::ScanMode::kConnectableDiscoverable); |
| EXPECT_EQ(adapter_b_->GetScanMode(), |
| BluetoothAdapter::ScanMode::kConnectableDiscoverable); |
| EXPECT_TRUE(found_latch.Await(absl::Milliseconds(1000)).result()); |
| std::string service_name{"service"}; |
| std::string service_uuid("service-uuid"); |
| BluetoothServerSocket server_socket = |
| bt_b_->ListenForService(service_name, service_uuid); |
| EXPECT_TRUE(server_socket.IsValid()); |
| BluetoothSocket socket_a; |
| BluetoothSocket socket_b; |
| EXPECT_FALSE(socket_a.IsValid()); |
| EXPECT_FALSE(socket_b.IsValid()); |
| { |
| CancellationFlag flag; |
| SingleThreadExecutor server_executor; |
| SingleThreadExecutor client_executor; |
| client_executor.Execute([this, &socket_a, discovered_device, &service_uuid, |
| &server_socket, &flag]() { |
| socket_a = |
| bt_a_->ConnectToService(*discovered_device, service_uuid, &flag); |
| if (!socket_a.IsValid()) server_socket.Close(); |
| }); |
| server_executor.Execute([&socket_b, &server_socket]() { |
| socket_b = server_socket.Accept(); |
| if (!socket_b.IsValid()) server_socket.Close(); |
| }); |
| } |
| EXPECT_TRUE(socket_a.IsValid()); |
| EXPECT_TRUE(socket_b.IsValid()); |
| server_socket.Close(); |
| } |
| |
| TEST_P(BluetoothClassicMediumTest, CanCancelConnect) { |
| FeatureFlags feature_flags = GetParam(); |
| env_.SetFeatureFlags(feature_flags); |
| |
| adapter_a_->SetScanMode(BluetoothAdapter::ScanMode::kConnectable); |
| CountDownLatch found_latch(1); |
| BluetoothDevice* discovered_device = nullptr; |
| bt_a_->StartDiscovery(DiscoveryCallback{ |
| .device_discovered_cb = |
| [this, &found_latch, &discovered_device](BluetoothDevice& device) { |
| NEARBY_LOG(INFO, "Device discovered: %s", device.GetName().c_str()); |
| EXPECT_EQ(device.GetName(), adapter_b_->GetName()); |
| discovered_device = &device; |
| found_latch.CountDown(); |
| }, |
| }); |
| adapter_b_->SetScanMode(BluetoothAdapter::ScanMode::kConnectableDiscoverable); |
| EXPECT_EQ(adapter_b_->GetScanMode(), |
| BluetoothAdapter::ScanMode::kConnectableDiscoverable); |
| EXPECT_TRUE(found_latch.Await(absl::Milliseconds(1000)).result()); |
| std::string service_name{"service"}; |
| std::string service_uuid("service-uuid"); |
| BluetoothServerSocket server_socket = |
| bt_b_->ListenForService(service_name, service_uuid); |
| EXPECT_TRUE(server_socket.IsValid()); |
| BluetoothSocket socket_a; |
| BluetoothSocket socket_b; |
| EXPECT_FALSE(socket_a.IsValid()); |
| EXPECT_FALSE(socket_b.IsValid()); |
| { |
| CancellationFlag flag(true); |
| SingleThreadExecutor server_executor; |
| SingleThreadExecutor client_executor; |
| client_executor.Execute([this, &socket_a, discovered_device, &service_uuid, |
| &server_socket, &flag]() { |
| socket_a = |
| bt_a_->ConnectToService(*discovered_device, service_uuid, &flag); |
| if (!socket_a.IsValid()) server_socket.Close(); |
| }); |
| server_executor.Execute([&socket_b, &server_socket]() { |
| socket_b = server_socket.Accept(); |
| if (!socket_b.IsValid()) server_socket.Close(); |
| }); |
| } |
| // If FeatureFlag is disabled, Cancelled is false as no-op. |
| if (!feature_flags.enable_cancellation_flag) { |
| EXPECT_TRUE(socket_a.IsValid()); |
| EXPECT_TRUE(socket_b.IsValid()); |
| } else { |
| EXPECT_FALSE(socket_a.IsValid()); |
| EXPECT_FALSE(socket_b.IsValid()); |
| } |
| server_socket.Close(); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(ParametrisedBluetoothClassicMediumTest, |
| BluetoothClassicMediumTest, |
| ::testing::ValuesIn(kTestCases)); |
| |
| TEST_F(BluetoothClassicMediumTest, ConstructorDestructorWorks) { |
| // Make sure we can create functional adapters. |
| ASSERT_TRUE(adapter_a_->IsValid()); |
| ASSERT_TRUE(adapter_b_->IsValid()); |
| |
| // Make sure we can create 2 distinct adapters. |
| // NOTE: multiple adapters are supported on a test platform, but not |
| // necessarily on every available HW platform. |
| // Often, HW platform supports only one BT adapter. |
| EXPECT_NE(&adapter_a_->GetImpl(), &adapter_b_->GetImpl()); |
| |
| // Make sure we can create functional mediums. |
| ASSERT_TRUE(bt_a_->IsValid()); |
| ASSERT_TRUE(bt_b_->IsValid()); |
| |
| // Make sure we can create 2 distinct mediums. |
| EXPECT_NE(&bt_a_->GetImpl(), &bt_b_->GetImpl()); |
| } |
| |
| TEST_F(BluetoothClassicMediumTest, CanStartDiscovery) { |
| adapter_a_->SetScanMode(BluetoothAdapter::ScanMode::kConnectable); |
| CountDownLatch found_latch(1); |
| CountDownLatch lost_latch(1); |
| bt_a_->StartDiscovery(DiscoveryCallback{ |
| .device_discovered_cb = |
| [this, &found_latch](BluetoothDevice& device) { |
| NEARBY_LOG(INFO, "Device discovered: %s", device.GetName().c_str()); |
| EXPECT_EQ(device.GetName(), adapter_b_->GetName()); |
| found_latch.CountDown(); |
| }, |
| .device_lost_cb = |
| [this, &lost_latch](BluetoothDevice& device) { |
| NEARBY_LOG(INFO, "Device lost: %s", device.GetName().c_str()); |
| EXPECT_EQ(device.GetName(), adapter_b_->GetName()); |
| lost_latch.CountDown(); |
| }, |
| }); |
| adapter_b_->SetScanMode(BluetoothAdapter::ScanMode::kConnectableDiscoverable); |
| EXPECT_EQ(adapter_b_->GetScanMode(), |
| BluetoothAdapter::ScanMode::kConnectableDiscoverable); |
| EXPECT_TRUE(found_latch.Await(absl::Milliseconds(1000)).result()); |
| adapter_b_->SetStatus(BluetoothAdapter::Status::kDisabled); |
| EXPECT_FALSE(adapter_b_->IsEnabled()); |
| EXPECT_TRUE(lost_latch.Await(absl::Milliseconds(1000)).result()); |
| } |
| |
| TEST_F(BluetoothClassicMediumTest, CanStopDiscovery) { |
| adapter_a_->SetScanMode(BluetoothAdapter::ScanMode::kConnectable); |
| CountDownLatch found_latch(1); |
| CountDownLatch lost_latch(1); |
| bt_a_->StartDiscovery(DiscoveryCallback{ |
| .device_discovered_cb = |
| [this, &found_latch](BluetoothDevice& device) { |
| NEARBY_LOG(INFO, "Device discovered: %s", device.GetName().c_str()); |
| EXPECT_EQ(device.GetName(), adapter_b_->GetName()); |
| found_latch.CountDown(); |
| }, |
| .device_lost_cb = |
| [this, &lost_latch](BluetoothDevice& device) { |
| NEARBY_LOG(INFO, "Device lost: %s", device.GetName().c_str()); |
| EXPECT_EQ(device.GetName(), adapter_b_->GetName()); |
| lost_latch.CountDown(); |
| }, |
| }); |
| adapter_b_->SetScanMode(BluetoothAdapter::ScanMode::kConnectableDiscoverable); |
| EXPECT_EQ(adapter_b_->GetScanMode(), |
| BluetoothAdapter::ScanMode::kConnectableDiscoverable); |
| EXPECT_TRUE(found_latch.Await(absl::Milliseconds(1000)).result()); |
| bt_a_->StopDiscovery(); |
| adapter_b_->SetStatus(BluetoothAdapter::Status::kDisabled); |
| EXPECT_FALSE(adapter_b_->IsEnabled()); |
| EXPECT_FALSE(lost_latch.Await(absl::Milliseconds(1000)).result()); |
| } |
| |
| TEST_F(BluetoothClassicMediumTest, CanListenForService) { |
| adapter_a_->SetScanMode(BluetoothAdapter::ScanMode::kConnectable); |
| CountDownLatch found_latch(1); |
| bt_a_->StartDiscovery(DiscoveryCallback{ |
| .device_discovered_cb = |
| [this, &found_latch](BluetoothDevice& device) { |
| NEARBY_LOG(INFO, "Device discovered: %s", device.GetName().c_str()); |
| EXPECT_EQ(device.GetName(), adapter_b_->GetName()); |
| found_latch.CountDown(); |
| }, |
| }); |
| adapter_b_->SetScanMode(BluetoothAdapter::ScanMode::kConnectableDiscoverable); |
| EXPECT_EQ(adapter_b_->GetScanMode(), |
| BluetoothAdapter::ScanMode::kConnectableDiscoverable); |
| EXPECT_TRUE(found_latch.Await(absl::Milliseconds(1000)).result()); |
| std::string service_name{"service"}; |
| std::string service_uuid("service-uuid"); |
| BluetoothServerSocket server_socket = |
| bt_b_->ListenForService(service_name, service_uuid); |
| EXPECT_TRUE(server_socket.IsValid()); |
| server_socket.Close(); |
| } |
| |
| TEST_F(BluetoothClassicMediumTest, FailIfDiscovering) { |
| EXPECT_TRUE(bt_a_->StartDiscovery({})); |
| EXPECT_FALSE(bt_a_->StartDiscovery({})); |
| } |
| |
| } // namespace |
| } // namespace nearby |
| } // namespace location |