#include "device/bluetooth/adapter.h"
#include "base/containers/contains.h"
#include "base/functional/callback_helpers.h"
#include "base/run_loop.h"
#include "base/task/sequenced_task_runner.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 "base/test/test_future.h"
#include "build/build_config.h"
#include "device/bluetooth/bluetooth_advertisement.h"
#include "device/bluetooth/gatt_service.h"
#include "device/bluetooth/public/mojom/adapter.mojom.h"
#include "device/bluetooth/test/fake_local_gatt_service.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"
RunOnceCallback;
_;
DoAll;
InvokeWithoutArgs;
NiceMock;
Return;
namespace {
const char kKnownDeviceAddress[] = …;
const char kUnknownDeviceAddress[] = …;
const char kServiceName[] = …;
const char kServiceId[] = …;
const char kDeviceServiceDataStr[] = …;
std::vector<uint8_t> GetByteVector(const std::string& str) { … }
class MockBluetoothAdapterWithAdvertisements
: public device::MockBluetoothAdapter { … };
}
namespace bluetooth {
class AdapterTest : public testing::Test, public mojom::GattServiceObserver { … };
TEST_F(AdapterTest, TestRegisterAdvertisement_Success) { … }
TEST_F(AdapterTest, TestRegisterAdvertisement_Error) { … }
TEST_F(AdapterTest, TestRegisterAdvertisement_ScanResponseData) { … }
TEST_F(AdapterTest, TestRegisterAdvertisement_NotConnectable) { … }
TEST_F(AdapterTest, TestRegisterAdvertisement_Connectable) { … }
TEST_F(AdapterTest, TestConnectToServiceInsecurely_DisallowedUuid) { … }
TEST_F(AdapterTest, TestConnectToServiceInsecurely_KnownDevice_Success) { … }
TEST_F(AdapterTest, TestConnectToServiceInsecurely_KnownDevice_Error) { … }
#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)
TEST_F(
AdapterTest,
TestConnectToServiceInsecurely_UnknownDevice_Success_ServicesAlreadyResolved) { … }
TEST_F(
AdapterTest,
TestConnectToServiceInsecurely_UnknownDevice_Success_WaitForServicesToResolve) { … }
TEST_F(
AdapterTest,
TestConnectToServiceInsecurely_UnknownDevice_Failure_WaitForServicesToResolve_DeviceRemoved) { … }
TEST_F(
AdapterTest,
TestConnectToServiceInsecurely_UnknownDevice_Failure_WaitForServicesToResolve_DeviceChangedWithNoRssi) { … }
TEST_F(AdapterTest, TestConnectToServiceInsecurely_UnknownDevice_Error) { … }
#else
TEST_F(AdapterTest, TestConnectToServiceInsecurely_UnknownDevice) {
adapter_->AllowConnectionsForUuid(device::BluetoothUUID(kServiceId));
base::RunLoop run_loop;
adapter_->ConnectToServiceInsecurely(
kUnknownDeviceAddress, device::BluetoothUUID(kServiceId),
false,
base::BindLambdaForTesting(
[&](mojom::ConnectToServiceResultPtr connect_to_service_result) {
EXPECT_FALSE(connect_to_service_result);
run_loop.Quit();
}));
run_loop.Run();
}
#endif
#if BUILDFLAG(IS_CHROMEOS)
TEST_F(AdapterTest, TestConnectToServiceInsecurely_HalfPaired) {
EXPECT_CALL(*mock_known_bluetooth_device_, IsBonded).WillOnce(Return(true));
EXPECT_CALL(*mock_known_bluetooth_device_,
ConnectToServiceInsecurely(_, _, _))
.WillOnce(RunOnceCallback<2>("br-connection-canceled"));
EXPECT_CALL(*mock_known_bluetooth_device_, Forget).Times(1);
adapter_->AllowConnectionsForUuid(device::BluetoothUUID(kServiceId));
adapter_->ConnectToServiceInsecurely(
kKnownDeviceAddress, device::BluetoothUUID(kServiceId),
true,
base::BindLambdaForTesting(
[&](mojom::ConnectToServiceResultPtr connect_to_service_result) {}));
}
TEST_F(AdapterTest, CreateLocalGattService) {
mojo::PendingRemote<mojom::GattServiceObserver> observer =
observer_.BindNewPipeAndPassRemote();
base::test::TestFuture<mojo::PendingRemote<mojom::GattService>> future;
adapter_->CreateLocalGattService(bluetooth_service_id_, std::move(observer),
future.GetCallback());
EXPECT_TRUE(future.Take());
}
TEST_F(AdapterTest, MemoryCleanUp_ResetGattServiceMojoRemote) {
mojo::Remote<mojom::GattService> gatt_service_remote;
{
mojo::PendingRemote<mojom::GattServiceObserver> observer =
observer_.BindNewPipeAndPassRemote();
base::test::TestFuture<mojo::PendingRemote<mojom::GattService>> future;
adapter_->CreateLocalGattService(bluetooth_service_id_, std::move(observer),
future.GetCallback());
gatt_service_remote.Bind(future.Take());
}
ON_CALL(*mock_bluetooth_adapter_, GetGattService)
.WillByDefault(testing::Return(fake_local_gatt_service_.get()));
{
base::RunLoop run_loop;
fake_local_gatt_service_->set_on_deleted_callback(run_loop.QuitClosure());
gatt_service_remote.reset();
run_loop.Run();
}
ON_CALL(*mock_bluetooth_adapter_, GetGattService)
.WillByDefault(testing::Return(nullptr));
observer_.reset();
{
mojo::PendingRemote<mojom::GattServiceObserver> observer =
observer_.BindNewPipeAndPassRemote();
base::test::TestFuture<mojo::PendingRemote<mojom::GattService>> future;
adapter_->CreateLocalGattService(bluetooth_service_id_, std::move(observer),
future.GetCallback());
EXPECT_TRUE(future.Take());
}
}
#endif
TEST_F(AdapterTest, TestCreateRfcommServiceInsecurely_DisallowedUuid) { … }
TEST_F(AdapterTest, TestCreateRfcommServiceInsecurely_Error) { … }
TEST_F(AdapterTest, TestCreateRfcommServiceInsecurely_Success) { … }
#if BUILDFLAG(IS_CHROMEOS)
TEST_F(AdapterTest, TestIsLeScatternetDualRoleSupported_Suppported) {
std::vector<device::BluetoothAdapter::BluetoothRole> roles{
device::BluetoothAdapter::BluetoothRole::kCentralPeripheral};
ON_CALL(*mock_bluetooth_adapter_, GetSupportedRoles())
.WillByDefault(Return(roles));
base::test::TestFuture<bool> future;
adapter_->IsLeScatternetDualRoleSupported(future.GetCallback());
EXPECT_TRUE(future.Get());
}
TEST_F(AdapterTest, TestIsLeScatternetDualRoleSupported_NotSupported) {
std::vector<device::BluetoothAdapter::BluetoothRole> roles{
device::BluetoothAdapter::BluetoothRole::kCentral,
device::BluetoothAdapter::BluetoothRole::kPeripheral};
ON_CALL(*mock_bluetooth_adapter_, GetSupportedRoles())
.WillByDefault(Return(roles));
base::test::TestFuture<bool> future;
adapter_->IsLeScatternetDualRoleSupported(future.GetCallback());
EXPECT_FALSE(future.Get());
}
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",
0, 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),
false, base::DoNothing());
base::RunLoop().RunUntilIdle();
adapter_.reset();
histogram_tester.ExpectUniqueSample(
"Bluetooth.Mojo.PendingConnectAtShutdown."
"NumberOfServiceDiscoveriesInProgress",
1, 1);
EXPECT_EQ(1u, histogram_tester
.GetAllSamples("Bluetooth.Mojo.PendingConnectAtShutdown."
"NumberOfServiceDiscoveriesInProgress")
.size());
}
TEST_F(AdapterTest,
TestConnectToServiceInsecurely_UnknownDevice_Error_DeviceRemoved) {
EXPECT_CALL(*mock_bluetooth_adapter_,
ConnectDevice(kUnknownDeviceAddress, _, _, _))
.WillOnce(
[&](const std::string& address,
const std::optional<device::BluetoothDevice::AddressType>&
address_type,
MockBluetoothAdapterWithAdvertisements::ConnectDeviceCallback
callback,
MockBluetoothAdapterWithAdvertisements::ConnectDeviceErrorCallback
error_callback) {
adapter_->DeviceRemoved(mock_bluetooth_adapter_.get(),
mock_unknown_bluetooth_device_.get());
std::move(error_callback).Run("error_message");
});
adapter_->AllowConnectionsForUuid(device::BluetoothUUID(kServiceId));
base::test::TestFuture<mojom::ConnectToServiceResultPtr> future;
adapter_->ConnectToServiceInsecurely(
kUnknownDeviceAddress, device::BluetoothUUID(kServiceId),
false, future.GetCallback());
EXPECT_FALSE(future.Take());
}
#endif
}