123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506 |
- // Copyright 2020 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "device/bluetooth/adapter.h"
- #include "base/callback_helpers.h"
- #include "base/containers/contains.h"
- #include "base/run_loop.h"
- #include "base/task/thread_pool.h"
- #include "base/test/bind.h"
- #include "base/test/gmock_callback_support.h"
- #include "base/test/metrics/histogram_tester.h"
- #include "base/test/task_environment.h"
- #include "build/build_config.h"
- #include "device/bluetooth/bluetooth_advertisement.h"
- #include "device/bluetooth/test/mock_bluetooth_adapter.h"
- #include "device/bluetooth/test/mock_bluetooth_advertisement.h"
- #include "device/bluetooth/test/mock_bluetooth_device.h"
- #include "device/bluetooth/test/mock_bluetooth_socket.h"
- #include "mojo/public/cpp/bindings/pending_remote.h"
- #include "mojo/public/cpp/bindings/remote.h"
- #include "testing/gtest/include/gtest/gtest.h"
- using base::test::RunOnceCallback;
- using testing::_;
- using testing::DoAll;
- using testing::InvokeWithoutArgs;
- using testing::NiceMock;
- using testing::Return;
- namespace {
- const char kKnownDeviceAddress[] = "00:00:00:00:01";
- const char kUnknownDeviceAddress[] = "00:00:00:00:02";
- const char kServiceName[] = "ServiceName";
- const char kServiceId[] = "0000abcd-0000-0000-0000-000000000001";
- const char kDeviceServiceDataStr[] = "ServiceData";
- std::vector<uint8_t> GetByteVector(const std::string& str) {
- return std::vector<uint8_t>(str.begin(), str.end());
- }
- class MockBluetoothAdapterWithAdvertisements
- : public device::MockBluetoothAdapter {
- public:
- void RegisterAdvertisement(
- std::unique_ptr<device::BluetoothAdvertisement::Data> advertisement_data,
- device::BluetoothAdapter::CreateAdvertisementCallback callback,
- device::BluetoothAdapter::AdvertisementErrorCallback error_callback)
- override {
- last_advertisement_data_ = std::move(advertisement_data);
- if (should_advertisement_registration_succeed_) {
- std::move(callback).Run(
- base::MakeRefCounted<device::MockBluetoothAdvertisement>());
- } else {
- std::move(error_callback)
- .Run(device::BluetoothAdvertisement::ErrorCode::
- INVALID_ADVERTISEMENT_ERROR_CODE);
- }
- }
- bool should_advertisement_registration_succeed_ = true;
- std::unique_ptr<device::BluetoothAdvertisement::Data>
- last_advertisement_data_;
- protected:
- ~MockBluetoothAdapterWithAdvertisements() override = default;
- };
- } // namespace
- namespace bluetooth {
- class AdapterTest : public testing::Test {
- public:
- AdapterTest() = default;
- ~AdapterTest() override = default;
- AdapterTest(const AdapterTest&) = delete;
- AdapterTest& operator=(const AdapterTest&) = delete;
- void SetUp() override {
- mock_bluetooth_adapter_ = base::MakeRefCounted<
- NiceMock<MockBluetoothAdapterWithAdvertisements>>();
- ON_CALL(*mock_bluetooth_adapter_, IsPresent()).WillByDefault(Return(true));
- ON_CALL(*mock_bluetooth_adapter_, IsPowered()).WillByDefault(Return(true));
- // |mock_known_bluetooth_device_| is a device found via discovery.
- mock_known_bluetooth_device_ =
- std::make_unique<testing::NiceMock<device::MockBluetoothDevice>>(
- mock_bluetooth_adapter_.get(),
- /*class=*/0, "Known Device", kKnownDeviceAddress,
- /*paired=*/false,
- /*connected=*/false);
- // |mock_unknown_bluetooth_device_| is |connected| because it is created
- // as a result of calling ConnectDevice().
- mock_unknown_bluetooth_device_ =
- std::make_unique<testing::NiceMock<device::MockBluetoothDevice>>(
- mock_bluetooth_adapter_.get(),
- /*class=*/0, "Unknown Device", kUnknownDeviceAddress,
- /*paired=*/false,
- /*connected=*/true);
- // |mock_bluetooth_adapter_| can only find |mock_known_bluetooth_device_|
- // via GetDevice(), not |mock_unknown_bluetooth_device_|.
- ON_CALL(*mock_bluetooth_adapter_, GetDevice(kKnownDeviceAddress))
- .WillByDefault(Return(mock_known_bluetooth_device_.get()));
- mock_bluetooth_socket_ =
- base::MakeRefCounted<NiceMock<device::MockBluetoothSocket>>();
- adapter_ = std::make_unique<Adapter>(mock_bluetooth_adapter_);
- }
- protected:
- void RegisterAdvertisement(bool should_succeed, bool use_scan_data) {
- mock_bluetooth_adapter_->should_advertisement_registration_succeed_ =
- should_succeed;
- auto service_data = GetByteVector(kDeviceServiceDataStr);
- mojo::Remote<mojom::Advertisement> advertisement;
- base::RunLoop run_loop;
- adapter_->RegisterAdvertisement(
- device::BluetoothUUID(kServiceId), service_data,
- /*use_scan_data=*/use_scan_data,
- base::BindLambdaForTesting([&](mojo::PendingRemote<mojom::Advertisement>
- pending_advertisement) {
- EXPECT_EQ(should_succeed, pending_advertisement.is_valid());
- run_loop.Quit();
- }));
- run_loop.Run();
- }
- void VerifyAdvertisement() {
- auto service_data = GetByteVector(kDeviceServiceDataStr);
- auto uuid_list =
- mock_bluetooth_adapter_->last_advertisement_data_->service_uuids();
- EXPECT_EQ(1u, uuid_list->size());
- EXPECT_EQ(kServiceId, (*uuid_list)[0]);
- auto last_service_data =
- mock_bluetooth_adapter_->last_advertisement_data_->service_data();
- EXPECT_EQ(service_data, last_service_data->at(kServiceId));
- EXPECT_FALSE(mock_bluetooth_adapter_->last_advertisement_data_
- ->scan_response_data());
- }
- void VerifyAdvertisementWithScanData() {
- auto service_data = GetByteVector(kDeviceServiceDataStr);
- auto uuid_list =
- mock_bluetooth_adapter_->last_advertisement_data_->service_uuids();
- EXPECT_EQ(1u, uuid_list->size());
- EXPECT_EQ(kServiceId, (*uuid_list)[0]);
- EXPECT_FALSE(
- mock_bluetooth_adapter_->last_advertisement_data_->service_data());
- auto last_scan_response_data =
- mock_bluetooth_adapter_->last_advertisement_data_->scan_response_data();
- ASSERT_TRUE(base::Contains(*last_scan_response_data, 0x16));
- const auto& raw_data = (*last_scan_response_data)[0x16];
- // First two bytes should be the identifying bits of the kServiceId UUID.
- // They should be in litten endian order (reversed).
- EXPECT_EQ(0xCD, raw_data[0]);
- EXPECT_EQ(0xAB, raw_data[1]);
- EXPECT_EQ(service_data,
- std::vector<uint8_t>(raw_data.begin() + 2, raw_data.end()));
- }
- scoped_refptr<NiceMock<MockBluetoothAdapterWithAdvertisements>>
- mock_bluetooth_adapter_;
- std::unique_ptr<NiceMock<device::MockBluetoothDevice>>
- mock_known_bluetooth_device_;
- std::unique_ptr<NiceMock<device::MockBluetoothDevice>>
- mock_unknown_bluetooth_device_;
- scoped_refptr<NiceMock<device::MockBluetoothSocket>> mock_bluetooth_socket_;
- std::unique_ptr<Adapter> adapter_;
- private:
- base::test::TaskEnvironment task_environment_;
- };
- TEST_F(AdapterTest, TestRegisterAdvertisement_Success) {
- RegisterAdvertisement(/*should_succeed=*/true, /*use_scan_data=*/false);
- VerifyAdvertisement();
- }
- TEST_F(AdapterTest, TestRegisterAdvertisement_Error) {
- RegisterAdvertisement(/*should_succeed=*/false, /*use_scan_data=*/false);
- VerifyAdvertisement();
- }
- TEST_F(AdapterTest, TestRegisterAdvertisement_ScanResponseData) {
- RegisterAdvertisement(/*should_succeed=*/true, /*use_scan_data=*/true);
- VerifyAdvertisementWithScanData();
- }
- TEST_F(AdapterTest, TestConnectToServiceInsecurely_DisallowedUuid) {
- // Do not call Adapter::AllowConnectionsForUuid();
- base::RunLoop run_loop;
- adapter_->ConnectToServiceInsecurely(
- kKnownDeviceAddress, device::BluetoothUUID(kServiceId),
- base::BindLambdaForTesting(
- [&](mojom::ConnectToServiceResultPtr connect_to_service_result) {
- EXPECT_FALSE(connect_to_service_result);
- run_loop.Quit();
- }));
- run_loop.Run();
- }
- TEST_F(AdapterTest, TestConnectToServiceInsecurely_KnownDevice_Success) {
- EXPECT_CALL(
- *mock_known_bluetooth_device_,
- ConnectToServiceInsecurely(device::BluetoothUUID(kServiceId), _, _))
- .WillOnce(RunOnceCallback<1>(mock_bluetooth_socket_));
- adapter_->AllowConnectionsForUuid(device::BluetoothUUID(kServiceId));
- base::RunLoop run_loop;
- adapter_->ConnectToServiceInsecurely(
- kKnownDeviceAddress, device::BluetoothUUID(kServiceId),
- base::BindLambdaForTesting(
- [&](mojom::ConnectToServiceResultPtr connect_to_service_result) {
- EXPECT_TRUE(connect_to_service_result);
- run_loop.Quit();
- }));
- run_loop.Run();
- }
- TEST_F(AdapterTest, TestConnectToServiceInsecurely_KnownDevice_Error) {
- EXPECT_CALL(
- *mock_known_bluetooth_device_,
- ConnectToServiceInsecurely(device::BluetoothUUID(kServiceId), _, _))
- .WillOnce(RunOnceCallback<2>("Error"));
- adapter_->AllowConnectionsForUuid(device::BluetoothUUID(kServiceId));
- base::RunLoop run_loop;
- adapter_->ConnectToServiceInsecurely(
- kKnownDeviceAddress, device::BluetoothUUID(kServiceId),
- base::BindLambdaForTesting(
- [&](mojom::ConnectToServiceResultPtr connect_to_service_result) {
- EXPECT_FALSE(connect_to_service_result);
- run_loop.Quit();
- }));
- run_loop.Run();
- }
- #if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)
- TEST_F(
- AdapterTest,
- TestConnectToServiceInsecurely_UnknownDevice_Success_ServicesAlreadyResolved) {
- EXPECT_CALL(*mock_bluetooth_adapter_,
- ConnectDevice(kUnknownDeviceAddress, _, _, _))
- .WillOnce(RunOnceCallback<2>(mock_unknown_bluetooth_device_.get()));
- EXPECT_CALL(
- *mock_unknown_bluetooth_device_,
- ConnectToServiceInsecurely(device::BluetoothUUID(kServiceId), _, _))
- .WillOnce(RunOnceCallback<1>(mock_bluetooth_socket_));
- EXPECT_CALL(*mock_unknown_bluetooth_device_,
- IsGattServicesDiscoveryComplete())
- .WillOnce(Return(true));
- adapter_->AllowConnectionsForUuid(device::BluetoothUUID(kServiceId));
- base::RunLoop run_loop;
- adapter_->ConnectToServiceInsecurely(
- kUnknownDeviceAddress, device::BluetoothUUID(kServiceId),
- base::BindLambdaForTesting(
- [&](mojom::ConnectToServiceResultPtr connect_to_service_result) {
- EXPECT_TRUE(connect_to_service_result);
- run_loop.Quit();
- }));
- run_loop.Run();
- }
- TEST_F(
- AdapterTest,
- TestConnectToServiceInsecurely_UnknownDevice_Success_WaitForServicesToResolve) {
- EXPECT_CALL(*mock_bluetooth_adapter_,
- ConnectDevice(kUnknownDeviceAddress, _, _, _))
- .WillOnce(RunOnceCallback<2>(mock_unknown_bluetooth_device_.get()));
- EXPECT_CALL(
- *mock_unknown_bluetooth_device_,
- ConnectToServiceInsecurely(device::BluetoothUUID(kServiceId), _, _))
- .WillOnce(RunOnceCallback<1>(mock_bluetooth_socket_));
- // At first, return false to force |adapter_| to wait for the value to change,
- // but subsequently return true. On that first call, post a task to trigger
- // a notification that services are now resolved.
- EXPECT_CALL(*mock_unknown_bluetooth_device_,
- IsGattServicesDiscoveryComplete())
- .WillOnce(DoAll(InvokeWithoutArgs([this]() {
- base::SequencedTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindLambdaForTesting([&]() {
- adapter_->GattServicesDiscovered(
- mock_bluetooth_adapter_.get(),
- mock_unknown_bluetooth_device_.get());
- }));
- }),
- Return(false)))
- .WillRepeatedly(Return(true));
- adapter_->AllowConnectionsForUuid(device::BluetoothUUID(kServiceId));
- base::RunLoop run_loop;
- adapter_->ConnectToServiceInsecurely(
- kUnknownDeviceAddress, device::BluetoothUUID(kServiceId),
- base::BindLambdaForTesting(
- [&](mojom::ConnectToServiceResultPtr connect_to_service_result) {
- EXPECT_TRUE(connect_to_service_result);
- run_loop.Quit();
- }));
- run_loop.Run();
- }
- TEST_F(
- AdapterTest,
- TestConnectToServiceInsecurely_UnknownDevice_Failure_WaitForServicesToResolve_DeviceRemoved) {
- EXPECT_CALL(*mock_bluetooth_adapter_,
- ConnectDevice(kUnknownDeviceAddress, _, _, _))
- .WillOnce(RunOnceCallback<2>(mock_unknown_bluetooth_device_.get()));
- // Return false to force |adapter_| to wait for the value to change.
- EXPECT_CALL(*mock_unknown_bluetooth_device_,
- IsGattServicesDiscoveryComplete())
- .WillOnce(Return(false));
- adapter_->AllowConnectionsForUuid(device::BluetoothUUID(kServiceId));
- base::RunLoop run_loop;
- adapter_->ConnectToServiceInsecurely(
- kUnknownDeviceAddress, device::BluetoothUUID(kServiceId),
- base::BindLambdaForTesting(
- [&](mojom::ConnectToServiceResultPtr connect_to_service_result) {
- EXPECT_FALSE(connect_to_service_result);
- run_loop.Quit();
- }));
- // Device is removed before GATT service discovery is complete, resulting in a
- // failed connect-to-service result
- adapter_->DeviceRemoved(mock_bluetooth_adapter_.get(),
- mock_unknown_bluetooth_device_.get());
- run_loop.Run();
- }
- TEST_F(
- AdapterTest,
- TestConnectToServiceInsecurely_UnknownDevice_Failure_WaitForServicesToResolve_DeviceChangedWithNoRssi) {
- EXPECT_CALL(*mock_bluetooth_adapter_,
- ConnectDevice(kUnknownDeviceAddress, _, _, _))
- .WillOnce(RunOnceCallback<2>(mock_unknown_bluetooth_device_.get()));
- // Return false to force |adapter_| to wait for the value to change.
- EXPECT_CALL(*mock_unknown_bluetooth_device_,
- IsGattServicesDiscoveryComplete())
- .WillOnce(Return(false));
- adapter_->AllowConnectionsForUuid(device::BluetoothUUID(kServiceId));
- base::RunLoop run_loop;
- adapter_->ConnectToServiceInsecurely(
- kUnknownDeviceAddress, device::BluetoothUUID(kServiceId),
- base::BindLambdaForTesting(
- [&](mojom::ConnectToServiceResultPtr connect_to_service_result) {
- EXPECT_FALSE(connect_to_service_result);
- run_loop.Quit();
- }));
- // Before GATT service discovery is complete, we are notified of a device
- // change where the device has no RSSI. This will result in a failed
- // connect-to-service result.
- EXPECT_CALL(*mock_unknown_bluetooth_device_, GetInquiryRSSI())
- .WillRepeatedly(Return(absl::nullopt));
- adapter_->DeviceChanged(mock_bluetooth_adapter_.get(),
- mock_unknown_bluetooth_device_.get());
- run_loop.Run();
- }
- TEST_F(AdapterTest, TestConnectToServiceInsecurely_UnknownDevice_Error) {
- EXPECT_CALL(*mock_bluetooth_adapter_,
- ConnectDevice(kUnknownDeviceAddress, _, _, _))
- .WillOnce(RunOnceCallback<3>());
- adapter_->AllowConnectionsForUuid(device::BluetoothUUID(kServiceId));
- base::RunLoop run_loop;
- adapter_->ConnectToServiceInsecurely(
- kUnknownDeviceAddress, device::BluetoothUUID(kServiceId),
- base::BindLambdaForTesting(
- [&](mojom::ConnectToServiceResultPtr connect_to_service_result) {
- EXPECT_FALSE(connect_to_service_result);
- run_loop.Quit();
- }));
- run_loop.Run();
- }
- #else
- TEST_F(AdapterTest, TestConnectToServiceInsecurely_UnknownDevice) {
- adapter_->AllowConnectionsForUuid(device::BluetoothUUID(kServiceId));
- base::RunLoop run_loop;
- adapter_->ConnectToServiceInsecurely(
- kUnknownDeviceAddress, device::BluetoothUUID(kServiceId),
- base::BindLambdaForTesting(
- [&](mojom::ConnectToServiceResultPtr connect_to_service_result) {
- EXPECT_FALSE(connect_to_service_result);
- run_loop.Quit();
- }));
- run_loop.Run();
- }
- #endif
- TEST_F(AdapterTest, TestCreateRfcommServiceInsecurely_DisallowedUuid) {
- // Do not call Adapter::AllowConnectionsForUuid();
- base::RunLoop run_loop;
- adapter_->CreateRfcommServiceInsecurely(
- kServiceName, device::BluetoothUUID(kServiceId),
- base::BindLambdaForTesting(
- [&](mojo::PendingRemote<mojom::ServerSocket> pending_server_socket) {
- EXPECT_FALSE(pending_server_socket);
- run_loop.Quit();
- }));
- run_loop.Run();
- }
- TEST_F(AdapterTest, TestCreateRfcommServiceInsecurely_Error) {
- EXPECT_CALL(*mock_bluetooth_adapter_,
- CreateRfcommService(device::BluetoothUUID(kServiceId), _, _, _))
- .WillOnce(RunOnceCallback<3>("Error"));
- adapter_->AllowConnectionsForUuid(device::BluetoothUUID(kServiceId));
- base::RunLoop run_loop;
- adapter_->CreateRfcommServiceInsecurely(
- kServiceName, device::BluetoothUUID(kServiceId),
- base::BindLambdaForTesting(
- [&](mojo::PendingRemote<mojom::ServerSocket> pending_server_socket) {
- EXPECT_FALSE(pending_server_socket);
- run_loop.Quit();
- }));
- run_loop.Run();
- }
- TEST_F(AdapterTest, TestCreateRfcommServiceInsecurely_Success) {
- EXPECT_CALL(*mock_bluetooth_adapter_,
- CreateRfcommService(device::BluetoothUUID(kServiceId), _, _, _))
- .WillOnce(RunOnceCallback<2>(mock_bluetooth_socket_));
- adapter_->AllowConnectionsForUuid(device::BluetoothUUID(kServiceId));
- base::RunLoop run_loop;
- adapter_->CreateRfcommServiceInsecurely(
- kServiceName, device::BluetoothUUID(kServiceId),
- base::BindLambdaForTesting(
- [&](mojo::PendingRemote<mojom::ServerSocket> pending_server_socket) {
- EXPECT_TRUE(pending_server_socket);
- run_loop.Quit();
- }));
- run_loop.Run();
- }
- #if BUILDFLAG(IS_CHROMEOS)
- TEST_F(AdapterTest, TestMetricsOnShutdown_NoPendingConnects) {
- base::HistogramTester histogram_tester;
- adapter_.reset();
- EXPECT_EQ(0u,
- histogram_tester
- .GetAllSamples(
- "Bluetooth.Mojo.PendingConnectAtShutdown.DurationWaiting")
- .size());
- histogram_tester.ExpectUniqueSample(
- "Bluetooth.Mojo.PendingConnectAtShutdown."
- "NumberOfServiceDiscoveriesInProgress",
- /*sample=*/0, /*expected_bucket_count=*/1);
- }
- TEST_F(AdapterTest, TestMetricsOnShutdown_PendingConnects) {
- base::HistogramTester histogram_tester;
- EXPECT_CALL(*mock_bluetooth_adapter_,
- ConnectDevice(kUnknownDeviceAddress, _, _, _))
- .WillOnce(RunOnceCallback<2>(mock_unknown_bluetooth_device_.get()));
- EXPECT_CALL(*mock_unknown_bluetooth_device_,
- IsGattServicesDiscoveryComplete())
- .WillRepeatedly(Return(false));
- adapter_->AllowConnectionsForUuid(device::BluetoothUUID(kServiceId));
- adapter_->ConnectToServiceInsecurely(kUnknownDeviceAddress,
- device::BluetoothUUID(kServiceId),
- base::DoNothing());
- base::RunLoop().RunUntilIdle();
- adapter_.reset();
- histogram_tester.ExpectUniqueSample(
- "Bluetooth.Mojo.PendingConnectAtShutdown."
- "NumberOfServiceDiscoveriesInProgress",
- /*sample=*/1, /*expected_bucket_count=*/1);
- EXPECT_EQ(1u, histogram_tester
- .GetAllSamples("Bluetooth.Mojo.PendingConnectAtShutdown."
- "NumberOfServiceDiscoveriesInProgress")
- .size());
- }
- #endif
- } // namespace bluetooth
|