blob: 44629ddca3936d908ec1f57b3950b58c8d541e84 [file] [log] [blame]
// Copyright 2014 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/bluetooth_device.h"
#include <stddef.h>
#include "base/macros.h"
#include "base/run_loop.h"
#include "base/stl_util.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "device/bluetooth/bluetooth_remote_gatt_service.h"
#include "device/bluetooth/test/test_bluetooth_adapter_observer.h"
#include "device/bluetooth/test/test_pairing_delegate.h"
#include "testing/gtest/include/gtest/gtest.h"
#if defined(OS_ANDROID)
#include "device/bluetooth/test/bluetooth_test_android.h"
#elif defined(OS_MACOSX)
#include "device/bluetooth/test/bluetooth_test_mac.h"
#elif defined(OS_WIN)
#include "device/bluetooth/test/bluetooth_test_win.h"
#elif defined(USE_CAST_BLUETOOTH_ADAPTER)
#include "device/bluetooth/test/bluetooth_test_cast.h"
#elif defined(OS_CHROMEOS) || defined(OS_LINUX)
#include "device/bluetooth/test/bluetooth_test_bluez.h"
#elif defined(OS_FUCHSIA)
#include "device/bluetooth/test/bluetooth_test_fuchsia.h"
#endif
namespace device {
namespace {
int8_t ToInt8(BluetoothTest::TestRSSI rssi) {
return static_cast<int8_t>(rssi);
}
int8_t ToInt8(BluetoothTest::TestTxPower tx_power) {
return static_cast<int8_t>(tx_power);
}
} // namespace
using UUIDSet = BluetoothDevice::UUIDSet;
using ServiceDataMap = BluetoothDevice::ServiceDataMap;
using ManufacturerDataMap = BluetoothDevice::ManufacturerDataMap;
TEST(BluetoothDeviceTest, CanonicalizeAddressFormat_AcceptsAllValidFormats) {
// There are three valid separators (':', '-', and none).
// Case shouldn't matter.
const char* const kValidFormats[] = {
"1A:2B:3C:4D:5E:6F",
"1a:2B:3c:4D:5e:6F",
"1a:2b:3c:4d:5e:6f",
"1A-2B-3C-4D-5E-6F",
"1a-2B-3c-4D-5e-6F",
"1a-2b-3c-4d-5e-6f",
"1A2B3C4D5E6F",
"1a2B3c4D5e6F",
"1a2b3c4d5e6f",
};
for (size_t i = 0; i < arraysize(kValidFormats); ++i) {
SCOPED_TRACE(std::string("Input format: '") + kValidFormats[i] + "'");
EXPECT_EQ("1A:2B:3C:4D:5E:6F",
BluetoothDevice::CanonicalizeAddress(kValidFormats[i]));
}
}
TEST(BluetoothDeviceTest, CanonicalizeAddressFormat_RejectsInvalidFormats) {
const char* const kValidFormats[] = {
// Empty string.
"",
// Too short.
"1A:2B:3C:4D:5E",
// Too long.
"1A:2B:3C:4D:5E:6F:70",
// Missing a separator.
"1A:2B:3C:4D:5E6F",
// Mixed separators.
"1A:2B-3C:4D-5E:6F",
// Invalid characters.
"1A:2B-3C:4D-5E:6X",
// Separators in the wrong place.
"1:A2:B3:C4:D5:E6F",
};
for (size_t i = 0; i < arraysize(kValidFormats); ++i) {
SCOPED_TRACE(std::string("Input format: '") + kValidFormats[i] + "'");
EXPECT_EQ(std::string(),
BluetoothDevice::CanonicalizeAddress(kValidFormats[i]));
}
}
#if defined(OS_WIN)
TEST_P(BluetoothTestWinrtOnly, DeviceIsPaired) {
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
InitWithFakeAdapter();
StartLowEnergyDiscoverySession();
BluetoothDevice* device = SimulateLowEnergyDevice(1);
// By default a device should not be paired.
EXPECT_FALSE(device->IsPaired());
// Connect to the device and simulate a paired state.
device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
GetConnectErrorCallback(Call::NOT_EXPECTED));
SimulateGattConnection(device);
base::RunLoop().RunUntilIdle();
SimulateDevicePaired(device, true);
EXPECT_TRUE(device->IsPaired());
SimulateDevicePaired(device, false);
EXPECT_FALSE(device->IsPaired());
}
// Tests that providing a correct pin code results in a paired device.
TEST_P(BluetoothTestWinrtOnly, DevicePairRequestPinCodeCorrect) {
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
InitWithFakeAdapter();
StartLowEnergyDiscoverySession();
BluetoothDevice* device = SimulateLowEnergyDevice(1);
device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
GetConnectErrorCallback(Call::NOT_EXPECTED));
SimulateGattConnection(device);
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(device->IsPaired());
EXPECT_FALSE(device->ExpectingPinCode());
SimulatePairingPinCode(device, "123456");
TestPairingDelegate pairing_delegate;
device->Pair(&pairing_delegate, GetCallback(Call::EXPECTED),
GetConnectErrorCallback(Call::NOT_EXPECTED));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, pairing_delegate.call_count_);
EXPECT_EQ(1, pairing_delegate.request_pincode_count_);
EXPECT_TRUE(device->ExpectingPinCode());
device->SetPinCode("123456");
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(device->IsPaired());
EXPECT_FALSE(device->ExpectingPinCode());
}
// Tests that providing a wrong pin code does not result in a paired device.
TEST_P(BluetoothTestWinrtOnly, DevicePairRequestPinCodeWrong) {
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
InitWithFakeAdapter();
StartLowEnergyDiscoverySession();
BluetoothDevice* device = SimulateLowEnergyDevice(1);
device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
GetConnectErrorCallback(Call::NOT_EXPECTED));
SimulateGattConnection(device);
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(device->IsPaired());
EXPECT_FALSE(device->ExpectingPinCode());
SimulatePairingPinCode(device, "123456");
TestPairingDelegate pairing_delegate;
device->Pair(&pairing_delegate, GetCallback(Call::NOT_EXPECTED),
GetConnectErrorCallback(Call::EXPECTED));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, pairing_delegate.call_count_);
EXPECT_EQ(1, pairing_delegate.request_pincode_count_);
EXPECT_TRUE(device->ExpectingPinCode());
device->SetPinCode("000000");
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(device->IsPaired());
EXPECT_FALSE(device->ExpectingPinCode());
EXPECT_EQ(BluetoothDevice::ERROR_FAILED, last_connect_error_code_);
}
// Tests that rejecting the pairing does not result in a paired device.
TEST_P(BluetoothTestWinrtOnly, DevicePairRequestPinCodeRejectPairing) {
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
InitWithFakeAdapter();
StartLowEnergyDiscoverySession();
BluetoothDevice* device = SimulateLowEnergyDevice(1);
device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
GetConnectErrorCallback(Call::NOT_EXPECTED));
SimulateGattConnection(device);
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(device->IsPaired());
EXPECT_FALSE(device->ExpectingPinCode());
SimulatePairingPinCode(device, "123456");
TestPairingDelegate pairing_delegate;
device->Pair(&pairing_delegate, GetCallback(Call::NOT_EXPECTED),
GetConnectErrorCallback(Call::EXPECTED));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, pairing_delegate.call_count_);
EXPECT_EQ(1, pairing_delegate.request_pincode_count_);
EXPECT_TRUE(device->ExpectingPinCode());
device->RejectPairing();
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(device->IsPaired());
EXPECT_FALSE(device->ExpectingPinCode());
EXPECT_EQ(BluetoothDevice::ERROR_AUTH_REJECTED, last_connect_error_code_);
}
// Tests that cancelling the pairing does not result in a paired device.
TEST_P(BluetoothTestWinrtOnly, DevicePairRequestPinCodeCancelPairing) {
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
InitWithFakeAdapter();
StartLowEnergyDiscoverySession();
BluetoothDevice* device = SimulateLowEnergyDevice(1);
device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
GetConnectErrorCallback(Call::NOT_EXPECTED));
SimulateGattConnection(device);
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(device->IsPaired());
EXPECT_FALSE(device->ExpectingPinCode());
SimulatePairingPinCode(device, "123456");
TestPairingDelegate pairing_delegate;
device->Pair(&pairing_delegate, GetCallback(Call::NOT_EXPECTED),
GetConnectErrorCallback(Call::EXPECTED));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, pairing_delegate.call_count_);
EXPECT_EQ(1, pairing_delegate.request_pincode_count_);
EXPECT_TRUE(device->ExpectingPinCode());
device->CancelPairing();
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(device->IsPaired());
EXPECT_FALSE(device->ExpectingPinCode());
EXPECT_EQ(BluetoothDevice::ERROR_AUTH_CANCELED, last_connect_error_code_);
}
#endif
// Verifies basic device properties, e.g. GetAddress, GetName, ...
#if defined(OS_WIN)
TEST_P(BluetoothTestWinrt, LowEnergyDeviceProperties) {
#else
TEST_F(BluetoothTest, LowEnergyDeviceProperties) {
#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
InitWithFakeAdapter();
StartLowEnergyDiscoverySession();
BluetoothDevice* device = SimulateLowEnergyDevice(1);
ASSERT_TRUE(device);
// Bluetooth class information for BLE device is not available on Windows.
#ifndef OS_WIN
EXPECT_EQ(0x1F00u, device->GetBluetoothClass());
#endif
EXPECT_EQ(kTestDeviceAddress1, device->GetAddress());
EXPECT_EQ(BluetoothDevice::VENDOR_ID_UNKNOWN, device->GetVendorIDSource());
EXPECT_EQ(0, device->GetVendorID());
EXPECT_EQ(0, device->GetProductID());
EXPECT_EQ(0, device->GetDeviceID());
EXPECT_EQ(base::UTF8ToUTF16(kTestDeviceName), device->GetNameForDisplay());
EXPECT_FALSE(device->IsPaired());
UUIDSet uuids = device->GetUUIDs();
EXPECT_TRUE(base::ContainsKey(uuids, BluetoothUUID(kTestUUIDGenericAccess)));
EXPECT_TRUE(
base::ContainsKey(uuids, BluetoothUUID(kTestUUIDGenericAttribute)));
}
// Device with no advertised Service UUIDs.
#if defined(OS_WIN)
TEST_P(BluetoothTestWinrt, LowEnergyDeviceNoUUIDs) {
#else
TEST_F(BluetoothTest, LowEnergyDeviceNoUUIDs) {
#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
InitWithFakeAdapter();
StartLowEnergyDiscoverySession();
BluetoothDevice* device = SimulateLowEnergyDevice(3);
ASSERT_TRUE(device);
UUIDSet uuids = device->GetUUIDs();
EXPECT_EQ(0u, uuids.size());
}
#if defined(OS_MACOSX) || defined(OS_CHROMEOS) || defined(OS_LINUX) || \
defined(OS_ANDROID)
#define MAYBE_GetServiceDataUUIDs_GetServiceDataForUUID \
GetServiceDataUUIDs_GetServiceDataForUUID
#else
#define MAYBE_GetServiceDataUUIDs_GetServiceDataForUUID \
DISABLED_GetServiceDataUUIDs_GetServiceDataForUUID
#endif
#if defined(OS_WIN)
TEST_P(BluetoothTestWinrtOnly, GetServiceDataUUIDs_GetServiceDataForUUID) {
#else
TEST_F(BluetoothTest, MAYBE_GetServiceDataUUIDs_GetServiceDataForUUID) {
#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
InitWithFakeAdapter();
#if !defined(OS_LINUX) && !defined(OS_CHROMEOS)
// TODO(crbug.com/706043): Remove #if once StartLowEnergyDiscoverySession is
// implemented for bluez.
StartLowEnergyDiscoverySession();
#endif // !defined(OS_LINUX) && !defined(OS_CHROMEOS)
// Receive Advertisement with empty service data.
BluetoothDevice* device1 = SimulateLowEnergyDevice(4);
EXPECT_FALSE(device1->GetAdvertisingDataFlags().has_value());
EXPECT_TRUE(device1->GetServiceData().empty());
EXPECT_TRUE(device1->GetServiceDataUUIDs().empty());
EXPECT_TRUE(device1->GetManufacturerData().empty());
// Receive Advertisement with service data.
BluetoothDevice* device2 = SimulateLowEnergyDevice(1);
#if defined(OS_WIN)
EXPECT_TRUE(device2->GetAdvertisingDataFlags().has_value());
EXPECT_EQ(0x04, device2->GetAdvertisingDataFlags().value());
#endif
EXPECT_EQ(ServiceDataMap({{BluetoothUUID(kTestUUIDHeartRate), {1}}}),
device2->GetServiceData());
EXPECT_EQ(UUIDSet({BluetoothUUID(kTestUUIDHeartRate)}),
device2->GetServiceDataUUIDs());
EXPECT_EQ(std::vector<uint8_t>({1}),
*device2->GetServiceDataForUUID(BluetoothUUID(kTestUUIDHeartRate)));
EXPECT_EQ(std::vector<uint8_t>({1, 2, 3, 4}),
*device2->GetManufacturerDataForID(kTestManufacturerId));
// Receive Advertisement with no flags and no service and manufacturer data.
SimulateLowEnergyDevice(3);
// TODO(crbug.com/707039): Remove #if once the BlueZ caching behavior is
// changed.
#if (defined(OS_CHROMEOS) || defined(OS_LINUX)) && \
!defined(USE_CAST_BLUETOOTH_ADAPTER)
// On ChromeOS and Linux, BlueZ persists all service data meaning if
// a device stops advertising service data for a UUID, BlueZ will
// still return the cached value for that UUID.
EXPECT_EQ(ServiceDataMap({{BluetoothUUID(kTestUUIDHeartRate), {1}}}),
device2->GetServiceData());
EXPECT_EQ(UUIDSet({BluetoothUUID(kTestUUIDHeartRate)}),
device2->GetServiceDataUUIDs());
EXPECT_EQ(std::vector<uint8_t>({1}),
*device2->GetServiceDataForUUID(BluetoothUUID(kTestUUIDHeartRate)));
#else
EXPECT_FALSE(device2->GetAdvertisingDataFlags().has_value());
EXPECT_TRUE(device2->GetServiceData().empty());
EXPECT_TRUE(device2->GetServiceDataUUIDs().empty());
EXPECT_TRUE(device2->GetManufacturerData().empty());
EXPECT_EQ(nullptr,
device2->GetServiceDataForUUID(BluetoothUUID(kTestUUIDHeartRate)));
#endif
// Receive Advertisement with new service data and empty manufacturer data.
SimulateLowEnergyDevice(2);
#if defined(OS_WIN)
EXPECT_TRUE(device2->GetAdvertisingDataFlags().has_value());
EXPECT_EQ(0x05, device2->GetAdvertisingDataFlags().value());
#endif
EXPECT_EQ(ServiceDataMap(
{{BluetoothUUID(kTestUUIDHeartRate), std::vector<uint8_t>({})},
{BluetoothUUID(kTestUUIDImmediateAlert), {0, 2}}}),
device2->GetServiceData());
EXPECT_EQ(UUIDSet({BluetoothUUID(kTestUUIDHeartRate),
BluetoothUUID(kTestUUIDImmediateAlert)}),
device2->GetServiceDataUUIDs());
EXPECT_EQ(std::vector<uint8_t>({}),
*device2->GetServiceDataForUUID(BluetoothUUID(kTestUUIDHeartRate)));
EXPECT_EQ(std::vector<uint8_t>({}),
*device2->GetManufacturerDataForID(kTestManufacturerId));
EXPECT_EQ(
std::vector<uint8_t>({0, 2}),
*device2->GetServiceDataForUUID(BluetoothUUID(kTestUUIDImmediateAlert)));
#if !defined(OS_LINUX) && !defined(OS_CHROMEOS)
// TODO(crbug.com/706043): Remove #if once StartLowEnergyDiscoverySession is
// implemented for bluez.
// Stop discovery.
discovery_sessions_[0]->Stop(GetCallback(Call::EXPECTED),
GetErrorCallback(Call::NOT_EXPECTED));
base::RunLoop().RunUntilIdle();
ASSERT_FALSE(adapter_->IsDiscovering());
ASSERT_FALSE(discovery_sessions_[0]->IsActive());
EXPECT_FALSE(device2->GetAdvertisingDataFlags().has_value());
EXPECT_TRUE(device2->GetServiceData().empty());
EXPECT_TRUE(device2->GetServiceDataUUIDs().empty());
EXPECT_EQ(nullptr,
device2->GetServiceDataForUUID(BluetoothUUID(kTestUUIDHeartRate)));
EXPECT_EQ(nullptr, device2->GetServiceDataForUUID(
BluetoothUUID(kTestUUIDImmediateAlert)));
#endif // !defined(OS_LINUX) && !defined(OS_CHROMEOS)
}
#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_AdvertisementData_Discovery AdvertisementData_Discovery
#else
#define MAYBE_AdvertisementData_Discovery DISABLED_AdvertisementData_Discovery
#endif
// Tests that the Advertisement Data fields are correctly updated during
// discovery.
#if defined(OS_WIN)
TEST_P(BluetoothTestWinrtOnly, AdvertisementData_Discovery) {
#else
TEST_F(BluetoothTest, MAYBE_AdvertisementData_Discovery) {
#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
InitWithFakeAdapter();
TestBluetoothAdapterObserver observer(adapter_);
// Start Discovery Session and receive Advertisement, should
// not notify of device changed because the device is new.
// - GetInquiryRSSI: Should return the packet's rssi.
// - GetAdvertisingDataFlags: Should return advertised flags.
// - GetUUIDs: Should return Advertised UUIDs.
// - GetServiceData: Should return advertised Service Data.
// - GetInquiryTxPower: Should return the packet's advertised Tx Power.
StartLowEnergyDiscoverySession();
BluetoothDevice* device = SimulateLowEnergyDevice(1);
EXPECT_EQ(0, observer.device_changed_count());
EXPECT_EQ(ToInt8(TestRSSI::LOWEST), device->GetInquiryRSSI().value());
#if defined(OS_WIN)
EXPECT_TRUE(device->GetAdvertisingDataFlags().has_value());
EXPECT_EQ(0x04, device->GetAdvertisingDataFlags().value());
#endif
EXPECT_EQ(UUIDSet({BluetoothUUID(kTestUUIDGenericAccess),
BluetoothUUID(kTestUUIDGenericAttribute)}),
device->GetUUIDs());
EXPECT_EQ(ServiceDataMap({{BluetoothUUID(kTestUUIDHeartRate), {1}}}),
device->GetServiceData());
EXPECT_EQ(ManufacturerDataMap({{kTestManufacturerId, {1, 2, 3, 4}}}),
device->GetManufacturerData());
EXPECT_EQ(ToInt8(TestTxPower::LOWEST), device->GetInquiryTxPower().value());
// Receive Advertisement with no flags, no UUIDs, Service Data, or Tx Power,
// should notify device changed.
// - GetInquiryRSSI: Should return packet's rssi.
// - GetAdvertisingDataFlags: Should return nullopt because of no flags.
// - GetUUIDs: Should return no UUIDs.
// - GetServiceData: Should return empty map.
// - GetInquiryTxPower: Should return nullopt because of no Tx Power.
SimulateLowEnergyDevice(3);
EXPECT_EQ(1, observer.device_changed_count());
EXPECT_EQ(ToInt8(TestRSSI::LOW), device->GetInquiryRSSI().value());
EXPECT_FALSE(device->GetAdvertisingDataFlags().has_value());
EXPECT_TRUE(device->GetUUIDs().empty());
EXPECT_TRUE(device->GetServiceData().empty());
EXPECT_TRUE(device->GetManufacturerData().empty());
EXPECT_FALSE(device->GetInquiryTxPower());
// Receive Advertisement with different UUIDs, Service Data, and Tx Power,
// should notify device changed.
// - GetInquiryRSSI: Should return last packet's rssi.
// - GetAdvertisingDataFlags: Should return last advertised flags.
// - GetUUIDs: Should return latest Advertised UUIDs.
// - GetServiceData: Should return last advertised Service Data.
// - GetInquiryTxPower: Should return last advertised Tx Power.
SimulateLowEnergyDevice(2);
EXPECT_EQ(2, observer.device_changed_count());
EXPECT_EQ(ToInt8(TestRSSI::LOWER), device->GetInquiryRSSI().value());
#if defined(OS_WIN)
EXPECT_TRUE(device->GetAdvertisingDataFlags().has_value());
EXPECT_EQ(0x05, device->GetAdvertisingDataFlags().value());
#endif
EXPECT_EQ(UUIDSet({BluetoothUUID(kTestUUIDImmediateAlert),
BluetoothUUID(kTestUUIDLinkLoss)}),
device->GetUUIDs());
EXPECT_EQ(ServiceDataMap(
{{BluetoothUUID(kTestUUIDHeartRate), std::vector<uint8_t>({})},
{BluetoothUUID(kTestUUIDImmediateAlert), {0, 2}}}),
device->GetServiceData());
EXPECT_EQ(ManufacturerDataMap({{kTestManufacturerId, {}}}),
device->GetManufacturerData());
EXPECT_EQ(ToInt8(TestTxPower::LOWER), device->GetInquiryTxPower().value());
// Stop discovery session, should notify of device changed.
// - GetInquiryRSSI: Should return nullopt because we are no longer
// discovering.
// - GetAdvertisingDataFlags: Should return no flags.
// - GetUUIDs: Should not return any UUIDs.
// - GetServiceData: Should return empty map.
// - GetMAnufacturerData: Should return empty map.
// - GetInquiryTxPower: Should return nullopt because we are no longer
// discovering.
discovery_sessions_[0]->Stop(GetCallback(Call::EXPECTED),
GetErrorCallback(Call::NOT_EXPECTED));
base::RunLoop().RunUntilIdle();
ASSERT_FALSE(adapter_->IsDiscovering());
ASSERT_FALSE(discovery_sessions_[0]->IsActive());
EXPECT_EQ(3, observer.device_changed_count());
EXPECT_FALSE(device->GetInquiryRSSI());
EXPECT_FALSE(device->GetAdvertisingDataFlags().has_value());
EXPECT_TRUE(device->GetUUIDs().empty());
EXPECT_TRUE(device->GetServiceData().empty());
EXPECT_TRUE(device->GetManufacturerData().empty());
EXPECT_FALSE(device->GetInquiryTxPower());
// Discover the device again with different UUIDs, should notify of device
// changed.
// - GetInquiryRSSI: Should return last packet's rssi.
// - GetAdvertisingDataFlags: Should return last advertised flags.
// - GetUUIDs: Should return only the latest Advertised UUIDs.
// - GetServiceData: Should return last advertise Service Data.
// - GetInquiryTxPower: Should return last advertised Tx Power.
StartLowEnergyDiscoverySession();
device = SimulateLowEnergyDevice(1);
EXPECT_EQ(4, observer.device_changed_count());
EXPECT_EQ(ToInt8(TestRSSI::LOWEST), device->GetInquiryRSSI().value());
#if defined(OS_WIN)
EXPECT_TRUE(device->GetAdvertisingDataFlags().has_value());
EXPECT_EQ(0x04, device->GetAdvertisingDataFlags().value());
#endif
EXPECT_EQ(UUIDSet({BluetoothUUID(kTestUUIDGenericAccess),
BluetoothUUID(kTestUUIDGenericAttribute)}),
device->GetUUIDs());
EXPECT_EQ(ServiceDataMap({{BluetoothUUID(kTestUUIDHeartRate), {1}}}),
device->GetServiceData());
EXPECT_EQ(ManufacturerDataMap({{kTestManufacturerId, {1, 2, 3, 4}}}),
device->GetManufacturerData());
EXPECT_EQ(ToInt8(TestTxPower::LOWEST), device->GetInquiryTxPower().value());
}
#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_GetUUIDs_Connection GetUUIDs_Connection
#else
#define MAYBE_GetUUIDs_Connection DISABLED_GetUUIDs_Connection
#endif
// Tests Advertisement Data is updated correctly during a connection.
#if defined(OS_WIN)
TEST_P(BluetoothTestWinrtOnly, GetUUIDs_Connection) {
#else
TEST_F(BluetoothTest, MAYBE_GetUUIDs_Connection) {
#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
InitWithFakeAdapter();
TestBluetoothAdapterObserver observer(adapter_);
StartLowEnergyDiscoverySession();
BluetoothDevice* device = SimulateLowEnergyDevice(1);
discovery_sessions_[0]->Stop(GetCallback(Call::EXPECTED),
GetErrorCallback(Call::NOT_EXPECTED));
// Connect to the device.
// - GetUUIDs: Should return no UUIDs because Services have not been
// discovered.
device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
GetConnectErrorCallback(Call::NOT_EXPECTED));
SimulateGattConnection(device);
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(device->IsConnected());
EXPECT_TRUE(device->GetUUIDs().empty());
observer.Reset();
// Discover services, should notify of device changed.
// - GetUUIDs: Should return the device's services' UUIDs.
std::vector<std::string> services;
services.push_back(kTestUUIDGenericAccess);
SimulateGattServicesDiscovered(device, services);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, observer.device_changed_count());
EXPECT_EQ(UUIDSet({BluetoothUUID(kTestUUIDGenericAccess)}),
device->GetUUIDs());
#if defined(OS_MACOSX) || defined(OS_WIN)
// TODO(ortuno): Enable in Android and classic Windows.
// Android and Windows don't yet support service changed events.
// http://crbug.com/548280
// http://crbug.com/579202
observer.Reset();
// Notify of services changed, should notify of device changed.
// - GetUUIDs: Should return no UUIDs because we no longer know what services
// the device has.
SimulateGattServicesChanged(device);
ASSERT_FALSE(device->IsGattServicesDiscoveryComplete());
EXPECT_EQ(1, observer.device_changed_count());
EXPECT_TRUE(device->GetUUIDs().empty());
// Services discovered again, should notify of device changed.
// - GetUUIDs: Should return Service UUIDs.
SimulateGattServicesDiscovered(device, {} /* services */);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(2, observer.device_changed_count());
EXPECT_EQ(UUIDSet({BluetoothUUID(kTestUUIDGenericAccess)}),
device->GetUUIDs());
#endif // defined(OS_MACOSX) || defined(OS_WIN)
observer.Reset();
// Disconnect, should notify device changed.
// - GetUUIDs: Should return no UUIDs since we no longer know what services
// the device holds and notify of device changed.
gatt_connections_[0]->Disconnect();
SimulateGattDisconnection(device);
base::RunLoop().RunUntilIdle();
ASSERT_FALSE(device->IsGattConnected());
EXPECT_EQ(1, observer.device_changed_count());
EXPECT_TRUE(device->GetUUIDs().empty());
}
#if defined(OS_MACOSX)
// Tests that receiving 2 notifications in a row from macOS that services has
// changed is handled correctly. Each notification should generate a
// notification that the gatt device has changed, and each notification should
// ask to macOS to scan for services. Only after the second service scan is
// received, the device changed notification should be sent and the
// characteristic discovery procedure should be started.
// Android: This test doesn't apply to Android because there is no services
// changed event that could arrive during a discovery procedure.
TEST_F(BluetoothTest, TwoPendingServiceDiscoveryRequests) {
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
InitWithFakeAdapter();
TestBluetoothAdapterObserver observer(adapter_);
BluetoothDevice* device = SimulateLowEnergyDevice(1);
device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
GetConnectErrorCallback(Call::NOT_EXPECTED));
SimulateGattConnection(device);
EXPECT_EQ(1, observer.device_changed_count());
EXPECT_FALSE(device->IsGattServicesDiscoveryComplete());
observer.Reset();
SimulateGattServicesChanged(device);
EXPECT_EQ(1, observer.device_changed_count());
EXPECT_FALSE(device->IsGattServicesDiscoveryComplete());
// First system call to
// -[id<CBPeripheralDelegate> peripheral:didDiscoverServices:] using
// SimulateDidDiscoverServicesMac().
observer.Reset();
AddServicesToDeviceMac(device, {kTestUUIDHeartRate});
SimulateDidDiscoverServicesMac(device);
EXPECT_EQ(0, observer.device_changed_count());
EXPECT_FALSE(device->IsGattServicesDiscoveryComplete());
EXPECT_EQ(gatt_characteristic_discovery_attempts_, 0);
// Second system call to
// -[id<CBPeripheralDelegate> peripheral:didDiscoverServices:] using the
// generic call to SimulateGattServicesDiscovered(). This method triggers
// the full discovery cycles (services, characteristics and descriptors),
// which includes -[id<CBPeripheralDelegate> peripheral:didDiscoverServices:].
SimulateGattServicesDiscovered(
device, std::vector<std::string>({kTestUUIDImmediateAlert}));
EXPECT_EQ(1, observer.device_changed_count());
EXPECT_TRUE(device->IsGattServicesDiscoveryComplete());
// Characteristics are discovered once for each service.
EXPECT_EQ(gatt_characteristic_discovery_attempts_, 2);
EXPECT_EQ(2u, device->GetGattServices().size());
}
// Simulate an unexpected call to -[id<CBPeripheralDelegate>
// peripheral:didDiscoverServices:]. This should not happen, but if it does
// (buggy device?), a discovery cycle should be done.
TEST_F(BluetoothTest, ExtraDidDiscoverServicesCall) {
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
InitWithFakeAdapter();
TestBluetoothAdapterObserver observer(adapter_);
BluetoothDevice* device = SimulateLowEnergyDevice(1);
device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
GetConnectErrorCallback(Call::NOT_EXPECTED));
SimulateGattConnection(device);
EXPECT_EQ(1, observer.device_changed_count());
EXPECT_FALSE(device->IsGattServicesDiscoveryComplete());
// Legitimate system call to
// -[id<CBPeripheralDelegate> peripheral:didDiscoverServices:].
observer.Reset();
SimulateGattServicesDiscovered(
device, std::vector<std::string>({kTestUUIDHeartRate}));
EXPECT_EQ(1, observer.device_changed_count());
EXPECT_TRUE(device->IsGattServicesDiscoveryComplete());
EXPECT_EQ(gatt_characteristic_discovery_attempts_, 1);
EXPECT_EQ(1u, device->GetGattServices().size());
// Unexpected system call to
// -[id<CBPeripheralDelegate> peripheral:didDiscoverServices:]:
// This system call is expected only once after -[CBCentralManager
// discoverServices:]. The call to -[CBCentralManager discoverServices:] and
// its answer with -[id<CBPeripheralDelegate> peripheral:didDiscoverServices:]
// is done with SimulateGattServicesDiscovered(). So a second system call to
// -[id<CBPeripheralDelegate> peripheral:didDiscoverServices:] is not expected
// and should be ignored.
AddServicesToDeviceMac(device, {kTestUUIDImmediateAlert});
SimulateDidDiscoverServicesMac(device);
EXPECT_EQ(1, observer.device_changed_count());
EXPECT_TRUE(device->IsGattServicesDiscoveryComplete());
EXPECT_EQ(1u, device->GetGattServices().size());
}
#endif
#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_AdvertisementData_DiscoveryDuringConnection \
AdvertisementData_DiscoveryDuringConnection
#else
#define MAYBE_AdvertisementData_DiscoveryDuringConnection \
DISABLED_AdvertisementData_DiscoveryDuringConnection
#endif
// Tests Advertisement Data is updated correctly when we start discovery
// during a connection.
#if defined(OS_WIN)
TEST_P(BluetoothTestWinrtOnly, AdvertisementData_DiscoveryDuringConnection) {
#else
TEST_F(BluetoothTest, MAYBE_AdvertisementData_DiscoveryDuringConnection) {
#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
InitWithFakeAdapter();
TestBluetoothAdapterObserver observer(adapter_);
StartLowEnergyDiscoverySession();
BluetoothDevice* device = SimulateLowEnergyDevice(1);
EXPECT_EQ(UUIDSet({BluetoothUUID(kTestUUIDGenericAccess),
BluetoothUUID(kTestUUIDGenericAttribute)}),
device->GetUUIDs());
discovery_sessions_[0]->Stop(GetCallback(Call::EXPECTED),
GetErrorCallback(Call::NOT_EXPECTED));
base::RunLoop().RunUntilIdle();
ASSERT_FALSE(adapter_->IsDiscovering());
ASSERT_FALSE(discovery_sessions_[0]->IsActive());
ASSERT_EQ(0u, device->GetUUIDs().size());
discovery_sessions_.clear();
// Connect.
device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
GetConnectErrorCallback(Call::NOT_EXPECTED));
SimulateGattConnection(device);
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(device->IsConnected());
observer.Reset();
// Start Discovery and receive advertisement during connection,
// should notify of device changed.
// - GetInquiryRSSI: Should return the packet's rssi.
// - GetAdvertisingDataFlags: Should return last advertised flags.
// - GetUUIDs: Should return only Advertised UUIDs since services haven't
// been discovered yet.
// - GetServiceData: Should return last advertised Service Data.
// - GetInquiryTxPower: Should return the packet's advertised Tx Power.
StartLowEnergyDiscoverySession();
ASSERT_TRUE(adapter_->IsDiscovering());
ASSERT_TRUE(discovery_sessions_[0]->IsActive());
device = SimulateLowEnergyDevice(1);
EXPECT_EQ(1, observer.device_changed_count());
EXPECT_EQ(ToInt8(TestRSSI::LOWEST), device->GetInquiryRSSI().value());
EXPECT_EQ(UUIDSet({BluetoothUUID(kTestUUIDGenericAccess),
BluetoothUUID(kTestUUIDGenericAttribute)}),
device->GetUUIDs());
#if defined(OS_WIN)
EXPECT_TRUE(device->GetAdvertisingDataFlags().has_value());
EXPECT_EQ(0x04, device->GetAdvertisingDataFlags().value());
#endif
EXPECT_EQ(ServiceDataMap({{BluetoothUUID(kTestUUIDHeartRate), {1}}}),
device->GetServiceData());
EXPECT_EQ(ToInt8(TestTxPower::LOWEST), device->GetInquiryTxPower().value());
// Discover services, should notify of device changed.
// - GetUUIDs: Should return both Advertised UUIDs and Service UUIDs.
std::vector<std::string> services;
services.push_back(kTestUUIDHeartRate);
SimulateGattServicesDiscovered(device, services);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(2, observer.device_changed_count());
EXPECT_EQ(UUIDSet({BluetoothUUID(kTestUUIDGenericAccess),
BluetoothUUID(kTestUUIDGenericAttribute),
BluetoothUUID(kTestUUIDHeartRate)}),
device->GetUUIDs());
// Receive advertisement again, notify of device changed.
// - GetInquiryRSSI: Should return last packet's rssi.
// - GetAdvertisingDataFlags: Should return last advertised flags.
// - GetUUIDs: Should return only new Advertised UUIDs and Service UUIDs.
// - GetServiceData: Should return last advertised Service Data.
// - GetInquiryTxPower: Should return the last packet's advertised Tx Power.
device = SimulateLowEnergyDevice(2);
EXPECT_EQ(3, observer.device_changed_count());
EXPECT_EQ(ToInt8(TestRSSI::LOWER), device->GetInquiryRSSI().value());
#if defined(OS_WIN)
EXPECT_TRUE(device->GetAdvertisingDataFlags().has_value());
EXPECT_EQ(0x05, device->GetAdvertisingDataFlags().value());
#endif
EXPECT_EQ(UUIDSet({BluetoothUUID(kTestUUIDLinkLoss),
BluetoothUUID(kTestUUIDImmediateAlert),
BluetoothUUID(kTestUUIDHeartRate)}),
device->GetUUIDs());
EXPECT_EQ(ServiceDataMap(
{{BluetoothUUID(kTestUUIDHeartRate), std::vector<uint8_t>({})},
{BluetoothUUID(kTestUUIDImmediateAlert), {0, 2}}}),
device->GetServiceData());
EXPECT_EQ(ToInt8(TestTxPower::LOWER), device->GetInquiryTxPower().value());
// Stop discovery session, should notify of device changed.
// - GetInquiryRSSI: Should return nullopt because we are no longer
// discovering.
// - GetAdvertisingDataFlags: Should return no flags since we are no longer
// discovering.
// - GetUUIDs: Should only return Service UUIDs.
// - GetServiceData: Should return an empty map since we are no longer
// discovering.
// - GetInquiryTxPower: Should return nullopt because we are no longer
// discovering.
discovery_sessions_[0]->Stop(GetCallback(Call::EXPECTED),
GetErrorCallback(Call::NOT_EXPECTED));
base::RunLoop().RunUntilIdle();
ASSERT_FALSE(adapter_->IsDiscovering());
ASSERT_FALSE(discovery_sessions_[0]->IsActive());
EXPECT_EQ(4, observer.device_changed_count());
EXPECT_FALSE(device->GetInquiryRSSI());
EXPECT_FALSE(device->GetAdvertisingDataFlags().has_value());
EXPECT_EQ(UUIDSet({BluetoothUUID(kTestUUIDHeartRate)}), device->GetUUIDs());
EXPECT_EQ(ServiceDataMap(), device->GetServiceData());
EXPECT_FALSE(device->GetInquiryTxPower());
// Disconnect device, should notify of device changed.
// - GetUUIDs: Should return no UUIDs.
gatt_connections_[0]->Disconnect();
SimulateGattDisconnection(device);
base::RunLoop().RunUntilIdle();
ASSERT_FALSE(device->IsGattConnected());
EXPECT_EQ(5, observer.device_changed_count());
EXPECT_TRUE(device->GetUUIDs().empty());
}
#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_AdvertisementData_ConnectionDuringDiscovery \
AdvertisementData_ConnectionDuringDiscovery
#else
#define MAYBE_AdvertisementData_ConnectionDuringDiscovery \
DISABLED_AdvertisementData_ConnectionDuringDiscovery
#endif
#if defined(OS_WIN)
TEST_P(BluetoothTestWinrtOnly, AdvertisementData_ConnectionDuringDiscovery) {
#else
TEST_F(BluetoothTest, MAYBE_AdvertisementData_ConnectionDuringDiscovery) {
#endif
// Tests that the Advertisement Data is correctly updated when
// the device connects during discovery.
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
InitWithFakeAdapter();
TestBluetoothAdapterObserver observer(adapter_);
// Start discovery session and receive and advertisement. No device changed
// notification because it's a new device.
// - GetInquiryRSSI: Should return the packet's rssi.
// - GetAdvertisingDataFlags: Should return advertised flags.
// - GetUUIDs: Should return Advertised UUIDs.
// - GetServiceData: Should return advertised Service Data.
// - GetInquiryTxPower: Should return the packet's advertised Tx Power.
StartLowEnergyDiscoverySession();
ASSERT_TRUE(adapter_->IsDiscovering());
ASSERT_TRUE(discovery_sessions_[0]->IsActive());
BluetoothDevice* device = SimulateLowEnergyDevice(1);
EXPECT_EQ(0, observer.device_changed_count());
EXPECT_EQ(ToInt8(TestRSSI::LOWEST), device->GetInquiryRSSI().value());
#if defined(OS_WIN)
EXPECT_TRUE(device->GetAdvertisingDataFlags().has_value());
EXPECT_EQ(0x04, device->GetAdvertisingDataFlags().value());
#endif
EXPECT_EQ(UUIDSet({BluetoothUUID(kTestUUIDGenericAccess),
BluetoothUUID(kTestUUIDGenericAttribute)}),
device->GetUUIDs());
EXPECT_EQ(ServiceDataMap({{BluetoothUUID(kTestUUIDHeartRate), {1}}}),
device->GetServiceData());
EXPECT_EQ(ToInt8(TestTxPower::LOWEST), device->GetInquiryTxPower().value());
// Connect, should notify of device changed.
// - GetUUIDs: Should return Advertised UUIDs even before GATT Discovery.
device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
GetConnectErrorCallback(Call::NOT_EXPECTED));
SimulateGattConnection(device);
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(device->IsConnected());
observer.Reset();
EXPECT_EQ(UUIDSet({BluetoothUUID(kTestUUIDGenericAccess),
BluetoothUUID(kTestUUIDGenericAttribute)}),
device->GetUUIDs());
// Receive Advertisement with new UUIDs, should notify of device changed.
// - GetInquiryRSSI: Should return the packet's rssi.
// - GetAdvertisingDataFlags: Should return advertised flags.
// - GetUUIDs: Should return new Advertised UUIDs.
// - GetServiceData: Should return new advertised Service Data.
// - GetInquiryTxPower: Should return the packet's advertised Tx Power.
device = SimulateLowEnergyDevice(2);
EXPECT_EQ(1, observer.device_changed_count());
EXPECT_EQ(ToInt8(TestRSSI::LOWER), device->GetInquiryRSSI().value());
#if defined(OS_WIN)
EXPECT_TRUE(device->GetAdvertisingDataFlags().has_value());
EXPECT_EQ(0x05, device->GetAdvertisingDataFlags().value());
#endif
EXPECT_EQ(UUIDSet({BluetoothUUID(kTestUUIDLinkLoss),
BluetoothUUID(kTestUUIDImmediateAlert)}),
device->GetUUIDs());
EXPECT_EQ(ServiceDataMap(
{{BluetoothUUID(kTestUUIDHeartRate), std::vector<uint8_t>({})},
{BluetoothUUID(kTestUUIDImmediateAlert), {0, 2}}}),
device->GetServiceData());
EXPECT_EQ(ToInt8(TestTxPower::LOWER), device->GetInquiryTxPower().value());
// Discover Services, should notify of device changed.
// - GetUUIDs: Should return Advertised UUIDs and Service UUIDs.
std::vector<std::string> services;
services.push_back(kTestUUIDHeartRate);
SimulateGattServicesDiscovered(device, services);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(2, observer.device_changed_count());
EXPECT_EQ(UUIDSet({BluetoothUUID(kTestUUIDLinkLoss),
BluetoothUUID(kTestUUIDImmediateAlert),
BluetoothUUID(kTestUUIDHeartRate)}),
device->GetUUIDs());
// Disconnect, should notify of device changed.
// - GetInquiryRSSI: Should return last packet's rssi.
// - GetAdvertisingDataFlags: Should return same advertised flags.
// - GetUUIDs: Should return only Advertised UUIDs.
// - GetServiceData: Should still return same advertised Service Data.
// - GetInquiryTxPower: Should return the last packet's advertised Tx Power.
gatt_connections_[0]->Disconnect();
SimulateGattDisconnection(device);
base::RunLoop().RunUntilIdle();
ASSERT_FALSE(device->IsGattConnected());
EXPECT_EQ(3, observer.device_changed_count());
EXPECT_EQ(ToInt8(TestRSSI::LOWER), device->GetInquiryRSSI().value());
#if defined(OS_WIN)
EXPECT_TRUE(device->GetAdvertisingDataFlags().has_value());
EXPECT_EQ(0x05, device->GetAdvertisingDataFlags().value());
#endif
EXPECT_EQ(UUIDSet({BluetoothUUID(kTestUUIDLinkLoss),
BluetoothUUID(kTestUUIDImmediateAlert)}),
device->GetUUIDs());
EXPECT_EQ(ServiceDataMap(
{{BluetoothUUID(kTestUUIDHeartRate), std::vector<uint8_t>({})},
{BluetoothUUID(kTestUUIDImmediateAlert), {0, 2}}}),
device->GetServiceData());
EXPECT_EQ(ToInt8(TestTxPower::LOWER), device->GetInquiryTxPower().value());
// Receive Advertisement with new UUIDs, should notify of device changed.
// - GetInquiryRSSI: Should return last packet's rssi.
// - GetAdvertisingDataFlags: Should return the new advertised flags.
// - GetUUIDs: Should return only new Advertised UUIDs.
// - GetServiceData: Should return only new advertised Service Data.
// - GetInquiryTxPower: Should return the last packet's advertised Tx Power.
device = SimulateLowEnergyDevice(1);
EXPECT_EQ(4, observer.device_changed_count());
EXPECT_EQ(ToInt8(TestRSSI::LOWEST), device->GetInquiryRSSI().value());
#if defined(OS_WIN)
EXPECT_TRUE(device->GetAdvertisingDataFlags().has_value());
EXPECT_EQ(0x04, device->GetAdvertisingDataFlags().value());
#endif
EXPECT_EQ(UUIDSet({BluetoothUUID(kTestUUIDGenericAccess),
BluetoothUUID(kTestUUIDGenericAttribute)}),
device->GetUUIDs());
EXPECT_EQ(ServiceDataMap({{BluetoothUUID(kTestUUIDHeartRate), {1}}}),
device->GetServiceData());
EXPECT_EQ(ToInt8(TestTxPower::LOWEST), device->GetInquiryTxPower().value());
// Stop discovery session, should notify of device changed.
// - GetInquiryRSSI: Should return nullopt because we are no longer
// discovering.
// - GetAdvertisingDataFlags: Should return no advertised flags since we are
// no longer discovering.
// - GetUUIDs: Should return no UUIDs.
// - GetServiceData: Should return no UUIDs since we are no longer
// discovering.
// - GetInquiryTxPower: Should return nullopt because we are no longer
// discovering.
discovery_sessions_[0]->Stop(GetCallback(Call::EXPECTED),
GetErrorCallback(Call::NOT_EXPECTED));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(5, observer.device_changed_count());
EXPECT_FALSE(device->GetInquiryRSSI());
EXPECT_FALSE(device->GetAdvertisingDataFlags().has_value());
EXPECT_TRUE(device->GetUUIDs().empty());
EXPECT_TRUE(device->GetServiceData().empty());
EXPECT_FALSE(device->GetInquiryTxPower());
}
#if defined(OS_ANDROID) || defined(OS_CHROMEOS) || defined(OS_MACOSX) || \
defined(OS_LINUX)
#define MAYBE_GetName_NullName GetName_NullName
#else
#define MAYBE_GetName_NullName DISABLED_GetName_NullName
#endif
// GetName for Device with no name.
TEST_F(BluetoothTest, MAYBE_GetName_NullName) {
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
InitWithFakeAdapter();
// StartLowEnergyDiscoverySession is not yet implemented on ChromeOS|bluez,
// and is non trivial to implement. On ChromeOS, it is not essential for
// this test to operate, and so it is simply skipped. Android at least
// does require this step.
#if !defined(OS_CHROMEOS)
StartLowEnergyDiscoverySession();
#endif
BluetoothDevice* device = SimulateLowEnergyDevice(5);
EXPECT_FALSE(device->GetName());
}
// TODO(506415): Test GetNameForDisplay with a device with no name.
// BluetoothDevice::GetAddressWithLocalizedDeviceTypeName() will run, which
// requires string resources to be loaded. For that, something like
// InitSharedInstance must be run. See unittest files that call that. It will
// also require build configuration to generate string resources into a .pak
// file.
#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_CreateGattConnection CreateGattConnection
#else
#define MAYBE_CreateGattConnection DISABLED_CreateGattConnection
#endif
// Basic CreateGattConnection test.
#if defined(OS_WIN)
TEST_P(BluetoothTestWinrtOnly, CreateGattConnection) {
#else
TEST_F(BluetoothTest, MAYBE_CreateGattConnection) {
#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
InitWithFakeAdapter();
StartLowEnergyDiscoverySession();
BluetoothDevice* device = SimulateLowEnergyDevice(3);
ResetEventCounts();
device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
GetConnectErrorCallback(Call::NOT_EXPECTED));
SimulateGattConnection(device);
base::RunLoop().RunUntilIdle();
ASSERT_EQ(1u, gatt_connections_.size());
EXPECT_TRUE(device->IsGattConnected());
EXPECT_TRUE(gatt_connections_[0]->IsConnected());
}
#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_DisconnectionNotifiesDeviceChanged \
DisconnectionNotifiesDeviceChanged
#else
#define MAYBE_DisconnectionNotifiesDeviceChanged \
DISABLED_DisconnectionNotifiesDeviceChanged
#endif
#if defined(OS_WIN)
TEST_P(BluetoothTestWinrtOnly, DisconnectionNotifiesDeviceChanged) {
#else
TEST_F(BluetoothTest, MAYBE_DisconnectionNotifiesDeviceChanged) {
#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
InitWithFakeAdapter();
TestBluetoothAdapterObserver observer(adapter_);
StartLowEnergyDiscoverySession();
BluetoothDevice* device = SimulateLowEnergyDevice(3);
device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
GetConnectErrorCallback(Call::NOT_EXPECTED));
SimulateGattConnection(device);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, observer.device_changed_count());
EXPECT_TRUE(device->IsConnected());
EXPECT_TRUE(device->IsGattConnected());
SimulateDeviceBreaksConnection(device);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(2, observer.device_changed_count());
EXPECT_FALSE(device->IsConnected());
EXPECT_FALSE(device->IsGattConnected());
}
#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_BluetoothGattConnection BluetoothGattConnection
#else
#define MAYBE_BluetoothGattConnection DISABLED_BluetoothGattConnection
#endif
// Creates BluetoothGattConnection instances and tests that the interface
// functions even when some Disconnect and the BluetoothDevice is destroyed.
#if defined(OS_WIN)
TEST_P(BluetoothTestWinrtOnly, BluetoothGattConnection) {
#else
TEST_F(BluetoothTest, MAYBE_BluetoothGattConnection) {
#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
InitWithFakeAdapter();
StartLowEnergyDiscoverySession();
BluetoothDevice* device = SimulateLowEnergyDevice(3);
std::string device_address = device->GetAddress();
// CreateGattConnection
ResetEventCounts();
device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
GetConnectErrorCallback(Call::NOT_EXPECTED));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, gatt_connection_attempts_);
SimulateGattConnection(device);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, callback_count_);
EXPECT_EQ(0, error_callback_count_);
ASSERT_EQ(1u, gatt_connections_.size());
EXPECT_TRUE(device->IsGattConnected());
EXPECT_TRUE(gatt_connections_[0]->IsConnected());
// Connect again once already connected.
ResetEventCounts();
device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
GetConnectErrorCallback(Call::NOT_EXPECTED));
device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
GetConnectErrorCallback(Call::NOT_EXPECTED));
EXPECT_EQ(0, gatt_connection_attempts_);
EXPECT_EQ(2, callback_count_);
EXPECT_EQ(0, error_callback_count_);
ASSERT_EQ(3u, gatt_connections_.size());
// Test GetDeviceAddress
EXPECT_EQ(device_address, gatt_connections_[0]->GetDeviceAddress());
// Test IsConnected
EXPECT_TRUE(gatt_connections_[0]->IsConnected());
EXPECT_TRUE(gatt_connections_[1]->IsConnected());
EXPECT_TRUE(gatt_connections_[2]->IsConnected());
// Disconnect & Delete connection objects. Device stays connected.
gatt_connections_[0]->Disconnect(); // Disconnect first.
gatt_connections_.pop_back(); // Delete last.
EXPECT_FALSE(gatt_connections_[0]->IsConnected());
EXPECT_TRUE(gatt_connections_[1]->IsConnected());
EXPECT_TRUE(device->IsGattConnected());
EXPECT_EQ(0, gatt_disconnection_attempts_);
// Delete device, connection objects should all be disconnected.
gatt_disconnection_attempts_ = 0;
DeleteDevice(device);
EXPECT_EQ(1, gatt_disconnection_attempts_);
EXPECT_FALSE(gatt_connections_[0]->IsConnected());
EXPECT_FALSE(gatt_connections_[1]->IsConnected());
// Test GetDeviceAddress after device deleted.
EXPECT_EQ(device_address, gatt_connections_[0]->GetDeviceAddress());
EXPECT_EQ(device_address, gatt_connections_[1]->GetDeviceAddress());
}
#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_BluetoothGattConnection_ConnectWithMultipleOSConnections \
BluetoothGattConnection_ConnectWithMultipleOSConnections
#else
#define MAYBE_BluetoothGattConnection_ConnectWithMultipleOSConnections \
DISABLED_BluetoothGattConnection_ConnectWithMultipleOSConnections
#endif
// Calls CreateGattConnection then simulates multiple connections from platform.
TEST_F(BluetoothTest,
MAYBE_BluetoothGattConnection_ConnectWithMultipleOSConnections) {
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
InitWithFakeAdapter();
TestBluetoothAdapterObserver observer(adapter_);
StartLowEnergyDiscoverySession();
BluetoothDevice* device = SimulateLowEnergyDevice(3);
// CreateGattConnection, & multiple connections from platform only invoke
// callbacks once:
ResetEventCounts();
device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
GetConnectErrorCallback(Call::NOT_EXPECTED));
SimulateGattConnection(device);
SimulateGattConnection(device);
base::RunLoop().RunUntilIdle();
#if defined(OS_ANDROID)
// Android incorrectly starts second discovery for devices that are already
// connected.
// TODO(crbug.com/718168): Remove once Android is fixed.
EXPECT_EQ(2, gatt_discovery_attempts_);
EXPECT_EQ(2, observer.device_changed_count());
#else // !defined(OS_ANDROID)
EXPECT_EQ(1, gatt_discovery_attempts_);
EXPECT_EQ(1, observer.device_changed_count());
#endif // defined(OS_ANDROID)
EXPECT_EQ(1, gatt_connection_attempts_);
EXPECT_EQ(1, callback_count_);
EXPECT_EQ(0, error_callback_count_);
EXPECT_TRUE(gatt_connections_[0]->IsConnected());
// Become disconnected:
SimulateGattDisconnection(device);
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(gatt_connections_[0]->IsConnected());
}
#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_BluetoothGattConnection_AlreadyConnected \
BluetoothGattConnection_AlreadyConnected
#else
#define MAYBE_BluetoothGattConnection_AlreadyConnected \
DISABLED_BluetoothGattConnection_AlreadyConnected
#endif
// Calls CreateGattConnection after already connected.
#if defined(OS_WIN)
TEST_P(BluetoothTestWinrtOnly, BluetoothGattConnection_AlreadyConnected) {
#else
TEST_F(BluetoothTest, MAYBE_BluetoothGattConnection_AlreadyConnected) {
#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
InitWithFakeAdapter();
StartLowEnergyDiscoverySession();
BluetoothDevice* device = SimulateLowEnergyDevice(3);
// Be already connected:
device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
GetConnectErrorCallback(Call::NOT_EXPECTED));
SimulateGattConnection(device);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(gatt_connections_[0]->IsConnected());
// Then CreateGattConnection:
ResetEventCounts();
device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
GetConnectErrorCallback(Call::NOT_EXPECTED));
EXPECT_EQ(0, gatt_connection_attempts_);
EXPECT_TRUE(gatt_connections_[1]->IsConnected());
}
#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_BluetoothGattConnection_NewConnectionLeavesPreviousDisconnected \
BluetoothGattConnection_NewConnectionLeavesPreviousDisconnected
#else
#define MAYBE_BluetoothGattConnection_NewConnectionLeavesPreviousDisconnected \
DISABLED_BluetoothGattConnection_NewConnectionLeavesPreviousDisconnected
#endif
// Creates BluetoothGattConnection after one exists that has disconnected.
#if defined(OS_WIN)
TEST_P(BluetoothTestWinrtOnly,
BluetoothGattConnection_NewConnectionLeavesPreviousDisconnected) {
#else
TEST_F(BluetoothTest,
MAYBE_BluetoothGattConnection_NewConnectionLeavesPreviousDisconnected) {
#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
InitWithFakeAdapter();
StartLowEnergyDiscoverySession();
BluetoothDevice* device = SimulateLowEnergyDevice(3);
// Create connection:
device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
GetConnectErrorCallback(Call::NOT_EXPECTED));
SimulateGattConnection(device);
base::RunLoop().RunUntilIdle();
// Disconnect connection:
gatt_connections_[0]->Disconnect();
SimulateGattDisconnection(device);
base::RunLoop().RunUntilIdle();
// Create 2nd connection:
device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
GetConnectErrorCallback(Call::NOT_EXPECTED));
SimulateGattConnection(device);
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(gatt_connections_[0]->IsConnected())
<< "The disconnected connection shouldn't become connected when another "
"connection is created.";
EXPECT_TRUE(gatt_connections_[1]->IsConnected());
}
#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_BluetoothGattConnection_DisconnectWhenObjectsDestroyed \
BluetoothGattConnection_DisconnectWhenObjectsDestroyed
#else
#define MAYBE_BluetoothGattConnection_DisconnectWhenObjectsDestroyed \
DISABLED_BluetoothGattConnection_DisconnectWhenObjectsDestroyed
#endif
// Deletes BluetoothGattConnection causing disconnection.
#if defined(OS_WIN)
TEST_P(BluetoothTestWinrtOnly,
BluetoothGattConnection_DisconnectWhenObjectsDestroyed) {
#else
TEST_F(BluetoothTest,
MAYBE_BluetoothGattConnection_DisconnectWhenObjectsDestroyed) {
#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
InitWithFakeAdapter();
StartLowEnergyDiscoverySession();
BluetoothDevice* device = SimulateLowEnergyDevice(3);
// Create multiple connections and simulate connection complete:
device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
GetConnectErrorCallback(Call::NOT_EXPECTED));
device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
GetConnectErrorCallback(Call::NOT_EXPECTED));
SimulateGattConnection(device);
base::RunLoop().RunUntilIdle();
// Delete all CreateGattConnection objects, observe disconnection:
ResetEventCounts();
gatt_connections_.clear();
EXPECT_EQ(1, gatt_disconnection_attempts_);
}
#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_BluetoothGattConnection_DisconnectInProgress \
BluetoothGattConnection_DisconnectInProgress
#else
#define MAYBE_BluetoothGattConnection_DisconnectInProgress \
DISABLED_BluetoothGattConnection_DisconnectInProgress
#endif
// Starts process of disconnecting and then calls BluetoothGattConnection.
#if defined(OS_WIN)
TEST_P(BluetoothTestWinrtOnly, BluetoothGattConnection_DisconnectInProgress) {
#else
TEST_F(BluetoothTest, MAYBE_BluetoothGattConnection_DisconnectInProgress) {
#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
InitWithFakeAdapter();
StartLowEnergyDiscoverySession();
BluetoothDevice* device = SimulateLowEnergyDevice(3);
// Create multiple connections and simulate connection complete:
device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
GetConnectErrorCallback(Call::NOT_EXPECTED));
device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
GetConnectErrorCallback(Call::NOT_EXPECTED));
SimulateGattConnection(device);
base::RunLoop().RunUntilIdle();
// Disconnect all CreateGattConnection objects & create a new connection.
// But, don't yet simulate the device disconnecting:
ResetEventCounts();
for (const auto& connection : gatt_connections_)
connection->Disconnect();
EXPECT_EQ(1, gatt_disconnection_attempts_);
// Create a connection.
device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
GetConnectErrorCallback(Call::NOT_EXPECTED));
EXPECT_EQ(0, gatt_connection_attempts_); // No connection attempt.
EXPECT_EQ(1, callback_count_); // Device is assumed still connected.
EXPECT_EQ(0, error_callback_count_);
EXPECT_FALSE(gatt_connections_.front()->IsConnected());
EXPECT_TRUE(gatt_connections_.back()->IsConnected());
// Actually disconnect:
SimulateGattDisconnection(device);
base::RunLoop().RunUntilIdle();
for (const auto& connection : gatt_connections_)
EXPECT_FALSE(connection->IsConnected());
}
#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_BluetoothGattConnection_SimulateDisconnect \
BluetoothGattConnection_SimulateDisconnect
#else
#define MAYBE_BluetoothGattConnection_SimulateDisconnect \
DISABLED_BluetoothGattConnection_SimulateDisconnect
#endif
// Calls CreateGattConnection but receives notice that the device disconnected
// before it ever connects.
#if defined(OS_WIN)
TEST_P(BluetoothTestWinrtOnly, BluetoothGattConnection_SimulateDisconnect) {
#else
TEST_F(BluetoothTest, MAYBE_BluetoothGattConnection_SimulateDisconnect) {
#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
InitWithFakeAdapter();
StartLowEnergyDiscoverySession();
BluetoothDevice* device = SimulateLowEnergyDevice(3);
ResetEventCounts();
device->CreateGattConnection(GetGattConnectionCallback(Call::NOT_EXPECTED),
GetConnectErrorCallback(Call::EXPECTED));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, gatt_connection_attempts_);
SimulateGattDisconnection(device);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(BluetoothDevice::ERROR_FAILED, last_connect_error_code_);
for (const auto& connection : gatt_connections_)
EXPECT_FALSE(connection->IsConnected());
}
#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_BluetoothGattConnection_DisconnectGatt_SimulateConnect \
BluetoothGattConnection_DisconnectGatt_SimulateConnect
#else
#define MAYBE_BluetoothGattConnection_DisconnectGatt_SimulateConnect \
DISABLED_BluetoothGattConnection_DisconnectGatt_SimulateConnect
#endif
// Calls CreateGattConnection & DisconnectGatt, then simulates connection.
#if defined(OS_WIN)
TEST_P(BluetoothTestWinrtOnly,
BluetoothGattConnection_DisconnectGatt_SimulateConnect) {
#else
TEST_F(BluetoothTest,
MAYBE_BluetoothGattConnection_DisconnectGatt_SimulateConnect) {
#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
InitWithFakeAdapter();
StartLowEnergyDiscoverySession();
BluetoothDevice* device = SimulateLowEnergyDevice(3);
ResetEventCounts();
device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
GetConnectErrorCallback(Call::NOT_EXPECTED));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, gatt_connection_attempts_);
#if !defined(OS_WIN)
// On Windows there is currently no way to cancel a pending GATT connection
// from the callers site.
device->DisconnectGatt();
EXPECT_EQ(1, gatt_disconnection_attempts_);
#endif
SimulateGattConnection(device);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, callback_count_);
EXPECT_EQ(0, error_callback_count_);
EXPECT_TRUE(gatt_connections_.back()->IsConnected());
ResetEventCounts();
SimulateGattDisconnection(device);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(0, callback_count_);
EXPECT_EQ(0, error_callback_count_);
}
#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_BluetoothGattConnection_DisconnectGatt_SimulateDisconnect \
BluetoothGattConnection_DisconnectGatt_SimulateDisconnect
#else
#define MAYBE_BluetoothGattConnection_DisconnectGatt_SimulateDisconnect \
DISABLED_BluetoothGattConnection_DisconnectGatt_SimulateDisconnect
#endif
// Calls CreateGattConnection & DisconnectGatt, then simulates disconnection.
#if defined(OS_WIN)
TEST_P(BluetoothTestWinrtOnly,
BluetoothGattConnection_DisconnectGatt_SimulateDisconnect) {
#else
TEST_F(BluetoothTest,
MAYBE_BluetoothGattConnection_DisconnectGatt_SimulateDisconnect) {
#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
InitWithFakeAdapter();
StartLowEnergyDiscoverySession();
BluetoothDevice* device = SimulateLowEnergyDevice(3);
ResetEventCounts();
device->CreateGattConnection(GetGattConnectionCallback(Call::NOT_EXPECTED),
GetConnectErrorCallback(Call::EXPECTED));
base::RunLoop().RunUntilIdle();
device->DisconnectGatt();
EXPECT_EQ(1, gatt_connection_attempts_);
EXPECT_EQ(1, gatt_disconnection_attempts_);
SimulateGattDisconnection(device);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(BluetoothDevice::ERROR_FAILED, last_connect_error_code_);
for (const auto& connection : gatt_connections_)
EXPECT_FALSE(connection->IsConnected());
}
#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_BluetoothGattConnection_DisconnectGatt_Cleanup \
BluetoothGattConnection_DisconnectGatt_Cleanup
#else
#define MAYBE_BluetoothGattConnection_DisconnectGatt_Cleanup \
DISABLED_BluetoothGattConnection_DisconnectGatt_Cleanup
#endif
// Calls CreateGattConnection & DisconnectGatt, then checks that gatt services
// have been cleaned up.
#if defined(OS_WIN)
TEST_P(BluetoothTestWinrtOnly, BluetoothGattConnection_DisconnectGatt_Cleanup) {
#else
TEST_F(BluetoothTest, MAYBE_BluetoothGattConnection_DisconnectGatt_Cleanup) {
#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
InitWithFakeAdapter();
StartLowEnergyDiscoverySession();
BluetoothDevice* device = SimulateLowEnergyDevice(3);
EXPECT_FALSE(device->IsConnected());
// Connect to the device
ResetEventCounts();
device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
GetConnectErrorCallback(Call::NOT_EXPECTED));
TestBluetoothAdapterObserver observer(adapter_);
SimulateGattConnection(device);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(device->IsConnected());
// Discover services
SimulateGattServicesDiscovered(
device,
std::vector<std::string>({kTestUUIDGenericAccess, kTestUUIDHeartRate}));
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(device->IsGattServicesDiscoveryComplete());
EXPECT_EQ(2u, device->GetGattServices().size());
EXPECT_EQ(1, observer.gatt_services_discovered_count());
// Disconnect from the device
device->DisconnectGatt();
SimulateGattDisconnection(device);
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(device->IsConnected());
EXPECT_FALSE(device->IsGattServicesDiscoveryComplete());
EXPECT_EQ(0u, device->GetGattServices().size());
// Verify that the device can be connected to again
device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
GetConnectErrorCallback(Call::NOT_EXPECTED));
SimulateGattConnection(device);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(device->IsConnected());
// Verify that service discovery can be done again
SimulateGattServicesDiscovered(
device, std::vector<std::string>({kTestUUIDGenericAttribute}));
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(device->IsGattServicesDiscoveryComplete());
EXPECT_EQ(1u, device->GetGattServices().size());
EXPECT_EQ(2, observer.gatt_services_discovered_count());
}
#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_BluetoothGattConnection_ErrorAfterConnection \
BluetoothGattConnection_ErrorAfterConnection
#else
#define MAYBE_BluetoothGattConnection_ErrorAfterConnection \
DISABLED_BluetoothGattConnection_ErrorAfterConnection
#endif
// Calls CreateGattConnection, but simulate errors connecting. Also, verifies
// multiple errors should only invoke callbacks once.
#if defined(OS_WIN)
TEST_P(BluetoothTestWinrtOnly, BluetoothGattConnection_ErrorAfterConnection) {
#else
TEST_F(BluetoothTest, MAYBE_BluetoothGattConnection_ErrorAfterConnection) {
#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
InitWithFakeAdapter();
StartLowEnergyDiscoverySession();
BluetoothDevice* device = SimulateLowEnergyDevice(3);
ResetEventCounts();
device->CreateGattConnection(GetGattConnectionCallback(Call::NOT_EXPECTED),
GetConnectErrorCallback(Call::EXPECTED));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, gatt_connection_attempts_);
SimulateGattConnectionError(device, BluetoothDevice::ERROR_AUTH_FAILED);
SimulateGattConnectionError(device, BluetoothDevice::ERROR_FAILED);
base::RunLoop().RunUntilIdle();
#if defined(OS_ANDROID) || defined(OS_WIN)
// TODO: Change to ERROR_AUTH_FAILED. We should be getting a callback
// only with the first error, but our android framework doesn't yet
// support sending different errors.
// http://crbug.com/578191
// On Windows, any GattConnectioError will result in ERROR_FAILED.
EXPECT_EQ(BluetoothDevice::ERROR_FAILED, last_connect_error_code_);
#else
EXPECT_EQ(BluetoothDevice::ERROR_AUTH_FAILED, last_connect_error_code_);
#endif
for (const auto& connection : gatt_connections_)
EXPECT_FALSE(connection->IsConnected());
}
#if defined(OS_ANDROID) || defined(OS_WIN) || defined(OS_MACOSX)
#define MAYBE_GattServices_ObserversCalls GattServices_ObserversCalls
#else
#define MAYBE_GattServices_ObserversCalls DISABLED_GattServices_ObserversCalls
#endif
#if defined(OS_WIN)
TEST_P(BluetoothTestWinrtOnly, GattServices_ObserversCalls) {
#else
TEST_F(BluetoothTest, MAYBE_GattServices_ObserversCalls) {
#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
InitWithFakeAdapter();
StartLowEnergyDiscoverySession();
BluetoothDevice* device = SimulateLowEnergyDevice(3);
device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
GetConnectErrorCallback(Call::NOT_EXPECTED));
TestBluetoothAdapterObserver observer(adapter_);
ResetEventCounts();
SimulateGattConnection(device);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, gatt_discovery_attempts_);
SimulateGattServicesDiscovered(
device,
std::vector<std::string>({kTestUUIDGenericAccess, kTestUUIDHeartRate}));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, observer.gatt_services_discovered_count());
}
#if defined(OS_ANDROID) || defined(OS_WIN) || defined(OS_MACOSX)
#define MAYBE_GattServicesDiscovered_Success GattServicesDiscovered_Success
#else
#define MAYBE_GattServicesDiscovered_Success \
DISABLED_GattServicesDiscovered_Success
#endif
#if defined(OS_WIN)
TEST_P(BluetoothTestWinrtOnly, GattServicesDiscovered_Success) {
#else
TEST_F(BluetoothTest, MAYBE_GattServicesDiscovered_Success) {
#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
InitWithFakeAdapter();
StartLowEnergyDiscoverySession();
TestBluetoothAdapterObserver observer(adapter_);
BluetoothDevice* device = SimulateLowEnergyDevice(3);
device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
GetConnectErrorCallback(Call::NOT_EXPECTED));
ResetEventCounts();
SimulateGattConnection(device);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, gatt_discovery_attempts_);
EXPECT_EQ(0, observer.gatt_services_discovered_count());
SimulateGattServicesDiscovered(
device,
std::vector<std::string>({kTestUUIDGenericAccess, kTestUUIDHeartRate}));
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(device->IsGattServicesDiscoveryComplete());
EXPECT_EQ(1, observer.gatt_services_discovered_count());
EXPECT_EQ(2u, device->GetGattServices().size());
}
#if defined(OS_ANDROID) || defined(OS_WIN)
#define MAYBE_GattServicesDiscovered_AfterDeleted \
GattServicesDiscovered_AfterDeleted
#else
#define MAYBE_GattServicesDiscovered_AfterDeleted \
DISABLED_GattServicesDiscovered_AfterDeleted
#endif
// macOS: Not applicable: This can never happen because when
// the device gets destroyed the CBPeripheralDelegate is also destroyed
// and no more events are dispatched.
TEST_F(BluetoothTest, MAYBE_GattServicesDiscovered_AfterDeleted) {
// Tests that we don't crash if services are discovered after
// the device object is deleted.
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
InitWithFakeAdapter();
StartLowEnergyDiscoverySession();
BluetoothDevice* device = SimulateLowEnergyDevice(3);
device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
GetConnectErrorCallback(Call::NOT_EXPECTED));
ResetEventCounts();
SimulateGattConnection(device);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, gatt_discovery_attempts_);
RememberDeviceForSubsequentAction(device);
DeleteDevice(device);
SimulateGattServicesDiscovered(
nullptr /* use remembered device */,
std::vector<std::string>({kTestUUIDGenericAccess, kTestUUIDHeartRate}));
base::RunLoop().RunUntilIdle();
}
#if defined(OS_ANDROID) || defined(OS_WIN)
#define MAYBE_GattServicesDiscoveredError_AfterDeleted \
GattServicesDiscoveredError_AfterDeleted
#else
#define MAYBE_GattServicesDiscoveredError_AfterDeleted \
DISABLED_GattServicesDiscoveredError_AfterDeleted
#endif
// macOS: Not applicable: This can never happen because when
// the device gets destroyed the CBPeripheralDelegate is also destroyed
// and no more events are dispatched.
TEST_F(BluetoothTest, MAYBE_GattServicesDiscoveredError_AfterDeleted) {
// Tests that we don't crash if there was an error discoverying services
// after the device object is deleted.
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
InitWithFakeAdapter();
StartLowEnergyDiscoverySession();
BluetoothDevice* device = SimulateLowEnergyDevice(3);
device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
GetConnectErrorCallback(Call::NOT_EXPECTED));
ResetEventCounts();
SimulateGattConnection(device);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, gatt_discovery_attempts_);
RememberDeviceForSubsequentAction(device);
DeleteDevice(device);
SimulateGattServicesDiscoveryError(nullptr /* use remembered device */);
base::RunLoop().RunUntilIdle();
}
#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_GattServicesDiscovered_AfterDisconnection \
GattServicesDiscovered_AfterDisconnection
#else
#define MAYBE_GattServicesDiscovered_AfterDisconnection \
DISABLED_GattServicesDiscovered_AfterDisconnection
#endif
// Classic Windows does not support disconnection.
#if defined(OS_WIN)
TEST_P(BluetoothTestWinrtOnly, GattServicesDiscovered_AfterDisconnection) {
#else
TEST_F(BluetoothTest, MAYBE_GattServicesDiscovered_AfterDisconnection) {
#endif
// Tests that we don't crash if there was an error discovering services after
// the device disconnects.
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
InitWithFakeAdapter();
StartLowEnergyDiscoverySession();
BluetoothDevice* device = SimulateLowEnergyDevice(3);
device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
GetConnectErrorCallback(Call::NOT_EXPECTED));
ResetEventCounts();
SimulateGattConnection(device);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, gatt_discovery_attempts_);
SimulateDeviceBreaksConnection(device);
base::RunLoop().RunUntilIdle();
SimulateGattServicesDiscovered(
device,
std::vector<std::string>({kTestUUIDGenericAccess, kTestUUIDHeartRate}));
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(device->IsGattServicesDiscoveryComplete());
EXPECT_EQ(0u, device->GetGattServices().size());
}
#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_GattServicesDiscoveredError_AfterDisconnection \
GattServicesDiscoveredError_AfterDisconnection
#else
#define MAYBE_GattServicesDiscoveredError_AfterDisconnection \
DISABLED_GattServicesDiscoveredError_AfterDisconnection
#endif
// Windows does not support disconnecting.
#if defined(OS_WIN)
TEST_P(BluetoothTestWinrtOnly, GattServicesDiscoveredError_AfterDisconnection) {
#else
TEST_F(BluetoothTest, MAYBE_GattServicesDiscoveredError_AfterDisconnection) {
#endif
// Tests that we don't crash if services are discovered after
// the device disconnects.
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
InitWithFakeAdapter();
StartLowEnergyDiscoverySession();
BluetoothDevice* device = SimulateLowEnergyDevice(3);
device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
GetConnectErrorCallback(Call::NOT_EXPECTED));
ResetEventCounts();
SimulateGattConnection(device);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, gatt_discovery_attempts_);
SimulateGattDisconnection(device);
base::RunLoop().RunUntilIdle();
SimulateGattServicesDiscoveryError(device);
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(device->IsGattServicesDiscoveryComplete());
EXPECT_EQ(0u, device->GetGattServices().size());
}
#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_GetGattServices_and_GetGattService \
GetGattServices_and_GetGattService
#else
#define MAYBE_GetGattServices_and_GetGattService \
DISABLED_GetGattServices_and_GetGattService
#endif
#if defined(OS_WIN)
TEST_P(BluetoothTestWinrt, GetGattServices_and_GetGattService) {
#else
TEST_F(BluetoothTest, MAYBE_GetGattServices_and_GetGattService) {
#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
InitWithFakeAdapter();
StartLowEnergyDiscoverySession();
BluetoothDevice* device = SimulateLowEnergyDevice(3);
device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
GetConnectErrorCallback(Call::NOT_EXPECTED));
ResetEventCounts();
SimulateGattConnection(device);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, gatt_discovery_attempts_);
// 2 duplicate UUIDs creating 2 instances.
SimulateGattServicesDiscovered(
device,
std::vector<std::string>(
{kTestUUIDGenericAccess, kTestUUIDHeartRate, kTestUUIDHeartRate}));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(3u, device->GetGattServices().size());
// Test GetGattService:
std::string service_id1 = device->GetGattServices()[0]->GetIdentifier();
std::string service_id2 = device->GetGattServices()[1]->GetIdentifier();
std::string service_id3 = device->GetGattServices()[2]->GetIdentifier();
EXPECT_TRUE(device->GetGattService(service_id1));
EXPECT_TRUE(device->GetGattService(service_id2));
EXPECT_TRUE(device->GetGattService(service_id3));
}
#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_GetGattServices_FindNone GetGattServices_FindNone
#else
#define MAYBE_GetGattServices_FindNone DISABLED_GetGattServices_FindNone
#endif
#if defined(OS_WIN)
TEST_P(BluetoothTestWinrtOnly, GetGattServices_FindNone) {
#else
TEST_F(BluetoothTest, MAYBE_GetGattServices_FindNone) {
#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
InitWithFakeAdapter();
StartLowEnergyDiscoverySession();
BluetoothDevice* device = SimulateLowEnergyDevice(3);
device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
GetConnectErrorCallback(Call::NOT_EXPECTED));
ResetEventCounts();
SimulateGattConnection(device);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, gatt_discovery_attempts_);
// Simulate an empty set of discovered services.
SimulateGattServicesDiscovered(device, {} /* uuids */);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(0u, device->GetGattServices().size());
}
#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_GetGattServices_DiscoveryError GetGattServices_DiscoveryError
#else
#define MAYBE_GetGattServices_DiscoveryError \
DISABLED_GetGattServices_DiscoveryError
#endif
#if defined(OS_WIN)
TEST_P(BluetoothTestWinrtOnly, GetGattServices_DiscoveryError) {
#else
TEST_F(BluetoothTest, MAYBE_GetGattServices_DiscoveryError) {
#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
InitWithFakeAdapter();
StartLowEnergyDiscoverySession();
BluetoothDevice* device = SimulateLowEnergyDevice(3);
device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
GetConnectErrorCallback(Call::NOT_EXPECTED));
ResetEventCounts();
SimulateGattConnection(device);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, gatt_discovery_attempts_);
SimulateGattServicesDiscoveryError(device);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(0u, device->GetGattServices().size());
}
#if defined(OS_CHROMEOS) || defined(OS_LINUX)
TEST_F(BluetoothTest, GetDeviceTransportType) {
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
InitWithFakeAdapter();
BluetoothDevice* device = SimulateLowEnergyDevice(1);
EXPECT_EQ(BLUETOOTH_TRANSPORT_LE, device->GetType());
#if !defined(USE_CAST_BLUETOOTH_ADAPTER)
BluetoothDevice* device2 = SimulateLowEnergyDevice(6);
EXPECT_EQ(BLUETOOTH_TRANSPORT_DUAL, device2->GetType());
BluetoothDevice* device3 = SimulateClassicDevice();
EXPECT_EQ(BLUETOOTH_TRANSPORT_CLASSIC, device3->GetType());
#endif // !defined(USE_CAST_BLUETOOTH_ADAPTER)
}
#endif // defined(OS_CHROMEOS) || defined(OS_LINUX)
#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_GetPrimaryServices GetPrimaryServices
#else
#define MAYBE_GetPrimaryServices DISABLED_GetPrimaryServices
#endif
#if defined(OS_WIN)
TEST_P(BluetoothTestWinrt, GetPrimaryServices) {
#else
TEST_F(BluetoothTest, MAYBE_GetPrimaryServices) {
#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
InitWithFakeAdapter();
StartLowEnergyDiscoverySession();
BluetoothDevice* device = SimulateLowEnergyDevice(3);
EXPECT_FALSE(device->IsConnected());
// Connect to the device.
ResetEventCounts();
device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
GetConnectErrorCallback(Call::NOT_EXPECTED));
SimulateGattConnection(device);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(device->IsGattConnected());
// Discover services: Two unique UUIDs, of which the second is duplicated.
SimulateGattServicesDiscovered(
device, {kTestUUIDGenericAccess, kTestUUIDHeartRate, kTestUUIDHeartRate});
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(device->IsGattServicesDiscoveryComplete());
EXPECT_EQ(3u, device->GetPrimaryServices().size());
}
#if defined(OS_ANDROID) || defined(OS_MACOSX)
#define MAYBE_GetPrimaryServicesByUUID GetPrimaryServicesByUUID
#else
#define MAYBE_GetPrimaryServicesByUUID DISABLED_GetPrimaryServicesByUUID
#endif
#if defined(OS_WIN)
TEST_P(BluetoothTestWinrt, GetPrimaryServicesByUUID) {
#else
TEST_F(BluetoothTest, MAYBE_GetPrimaryServicesByUUID) {
#endif
if (!PlatformSupportsLowEnergy()) {
LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
return;
}
InitWithFakeAdapter();
StartLowEnergyDiscoverySession();
BluetoothDevice* device = SimulateLowEnergyDevice(3);
EXPECT_FALSE(device->IsConnected());
// Connect to the device.
ResetEventCounts();
device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
GetConnectErrorCallback(Call::NOT_EXPECTED));
SimulateGattConnection(device);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(device->IsGattConnected());
// Discover services: Two unique UUIDs, of which the second is duplicated.
SimulateGattServicesDiscovered(
device, {kTestUUIDGenericAccess, kTestUUIDHeartRate, kTestUUIDHeartRate});
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(device->IsGattServicesDiscoveryComplete());
{
const BluetoothUUID unique_service_uuid(kTestUUIDGenericAccess);
std::vector<BluetoothRemoteGattService*> services =
device->GetPrimaryServicesByUUID(unique_service_uuid);
EXPECT_EQ(1u, services.size());
EXPECT_EQ(unique_service_uuid, services[0]->GetUUID());
}
{
const BluetoothUUID duplicate_service_uuid(kTestUUIDHeartRate);
std::vector<BluetoothRemoteGattService*> services =
device->GetPrimaryServicesByUUID(duplicate_service_uuid);
EXPECT_EQ(2u, services.size());
EXPECT_EQ(duplicate_service_uuid, services[0]->GetUUID());
EXPECT_EQ(duplicate_service_uuid, services[1]->GetUUID());
EXPECT_TRUE(
device
->GetPrimaryServicesByUUID(BluetoothUUID(kTestUUIDGenericAttribute))
.empty());
EXPECT_NE(services[0]->GetIdentifier(), services[1]->GetIdentifier());
}
}
} // namespace device