// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "device/bluetooth/test/bluetooth_test_mac.h"
#import <CoreBluetooth/CoreBluetooth.h>
#include <stdint.h>
#include <memory>
#import "base/apple/foundation_util.h"
#include "base/functional/bind.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/sys_string_conversions.h"
#include "build/build_config.h"
#import "device/bluetooth/bluetooth_device_mac.h"
#include "device/bluetooth/bluetooth_low_energy_adapter_apple.h"
#include "device/bluetooth/bluetooth_low_energy_central_manager_delegate.h"
#import "device/bluetooth/bluetooth_remote_gatt_characteristic_mac.h"
#import "device/bluetooth/bluetooth_remote_gatt_descriptor_mac.h"
#import "device/bluetooth/bluetooth_remote_gatt_service_mac.h"
#import "device/bluetooth/test/mock_bluetooth_cbcharacteristic_mac.h"
#import "device/bluetooth/test/mock_bluetooth_cbdescriptor_mac.h"
#import "device/bluetooth/test/mock_bluetooth_cbperipheral_mac.h"
#import "device/bluetooth/test/mock_bluetooth_cbservice_mac.h"
#import "device/bluetooth/test/mock_bluetooth_central_manager_mac.h"
#import "device/bluetooth/test/test_bluetooth_adapter_observer.h"
#if BUILDFLAG(IS_IOS)
#import "device/bluetooth/bluetooth_adapter_ios.h"
#else
#import "device/bluetooth/bluetooth_adapter_mac.h"
#endif
using base::apple::ObjCCast;
namespace device {
// This class hides Objective-C from bluetooth_test_mac.h.
class BluetoothTestMac::ScopedMockCentralManager {
public:
explicit ScopedMockCentralManager(MockCentralManager* mock_central_manager) {
mock_central_manager_ = mock_central_manager;
}
ScopedMockCentralManager(const ScopedMockCentralManager&) = delete;
ScopedMockCentralManager& operator=(const ScopedMockCentralManager&) = delete;
// Returns MockCentralManager instance.
MockCentralManager* get() { return mock_central_manager_; }
private:
MockCentralManager* __strong mock_central_manager_;
};
namespace {
NSDictionary* AdvertisementData(NSString* name,
NSArray* uuids,
NSDictionary* service_data,
NSData* manufacturer_data,
NSNumber* tx_power) {
NSMutableDictionary* advertisement_data =
[NSMutableDictionary dictionaryWithDictionary:@{
CBAdvertisementDataIsConnectable : @YES
}];
if (name) {
advertisement_data[CBAdvertisementDataLocalNameKey] = name;
}
if (uuids) {
advertisement_data[CBAdvertisementDataServiceUUIDsKey] = uuids;
}
if (service_data) {
advertisement_data[CBAdvertisementDataServiceDataKey] = service_data;
}
if (service_data) {
advertisement_data[CBAdvertisementDataManufacturerDataKey] =
manufacturer_data;
}
if (tx_power) {
advertisement_data[CBAdvertisementDataTxPowerLevelKey] = tx_power;
}
return advertisement_data;
}
} // namespace
// UUID1 hashes to kTestDeviceAddress1, and UUID2 to kTestDeviceAddress2.
const char BluetoothTestMac::kTestPeripheralUUID1[] =
"34045B00-0000-0000-0000-000000000000";
const char BluetoothTestMac::kTestPeripheralUUID2[] =
"EC1B8F00-0000-0000-0000-000000000000";
// Fake error domain for testing error metrics
static NSString* const kDisconnectErrorDomain = @"FakeDisconnectErrorDomain";
BluetoothTestMac::BluetoothTestMac() = default;
BluetoothTestMac::~BluetoothTestMac() = default;
void BluetoothTestMac::SetUp() {}
bool BluetoothTestMac::PlatformSupportsLowEnergy() {
return true;
}
void BluetoothTestMac::InitWithDefaultAdapter() {
auto adapter = BluetoothAdapter::CreateAdapter();
adapter_low_energy_ =
static_cast<BluetoothLowEnergyAdapterApple*>(adapter.get());
adapter_ = std::move(adapter);
base::RunLoop run_loop;
adapter_->Initialize(run_loop.QuitClosure());
run_loop.Run();
}
void BluetoothTestMac::InitWithoutDefaultAdapter() {
#if BUILDFLAG(IS_IOS)
auto adapter = BluetoothAdapterIOS::CreateAdapterForTest(
"", "", task_environment_.GetMainThreadTaskRunner());
#else
auto adapter = BluetoothAdapterMac::CreateAdapterForTest(
"", "", task_environment_.GetMainThreadTaskRunner());
#endif
adapter_low_energy_ = adapter.get();
adapter_ = std::move(adapter);
mock_central_manager_ = std::make_unique<ScopedMockCentralManager>(
[[MockCentralManager alloc] init]);
[mock_central_manager_->get() setBluetoothTestMac:this];
[mock_central_manager_->get() setState:CBManagerStateUnsupported];
adapter_low_energy_->SetCentralManagerForTesting(
(id)mock_central_manager_->get());
}
void BluetoothTestMac::InitWithFakeAdapter() {
#if BUILDFLAG(IS_IOS)
auto adapter = BluetoothAdapterIOS::CreateAdapterForTest(
kTestAdapterName, kTestAdapterAddress,
task_environment_.GetMainThreadTaskRunner());
#else
auto adapter = BluetoothAdapterMac::CreateAdapterForTest(
kTestAdapterName, kTestAdapterAddress,
task_environment_.GetMainThreadTaskRunner());
#endif
adapter_low_energy_ = adapter.get();
adapter_ = std::move(adapter);
mock_central_manager_ = std::make_unique<ScopedMockCentralManager>(
[[MockCentralManager alloc] init]);
mock_central_manager_->get().bluetoothTestMac = this;
[mock_central_manager_->get() setState:CBManagerStatePoweredOn];
adapter_low_energy_->SetCentralManagerForTesting(
(id)mock_central_manager_->get());
#if BUILDFLAG(IS_MAC)
static_cast<BluetoothAdapterMac*>(adapter_low_energy_)
->SetPowerStateFunctionForTesting(
base::BindRepeating(&BluetoothTestMac::SetMockControllerPowerState,
base::Unretained(this)));
static_cast<BluetoothAdapterMac*>(adapter_low_energy_)
->SetPresentForTesting(true);
#endif
}
void BluetoothTestMac::ResetEventCounts() {
BluetoothTestBase::ResetEventCounts();
last_notify_value_ = false;
}
void BluetoothTestMac::SimulateAdapterPoweredOff() {
[mock_central_manager_->get() setState:CBManagerStatePoweredOff];
for (BluetoothDevice* device : adapter_->GetDevices()) {
MockCBPeripheral* peripheral_mock = GetMockCBPeripheral(device);
[peripheral_mock setState:CBPeripheralStateDisconnected];
}
BluetoothLowEnergyCentralManagerDelegate* central_manager_delegate =
adapter_low_energy_->low_energy_central_manager_delegate_;
CBCentralManager* central_manager =
adapter_low_energy_->low_energy_central_manager_;
[central_manager_delegate centralManagerDidUpdateState:central_manager];
}
BluetoothDevice* BluetoothTestMac::SimulateLowEnergyDevice(int device_ordinal) {
TestBluetoothAdapterObserver observer(adapter_);
CBCentralManager* central_manager =
adapter_low_energy_->low_energy_central_manager_;
BluetoothLowEnergyCentralManagerDelegate* central_manager_delegate =
adapter_low_energy_->low_energy_central_manager_delegate_;
const char* identifier;
NSString* name;
NSArray* uuids;
NSNumber* rssi;
NSDictionary* service_data;
NSData* manufacturer_data;
NSNumber* tx_power;
switch (device_ordinal) {
case 1:
identifier = kTestPeripheralUUID1;
name = @(kTestDeviceName);
rssi = @(static_cast<int8_t>(TestRSSI::LOWEST));
uuids = @[
[CBUUID UUIDWithString:@(kTestUUIDGenericAccess)],
[CBUUID UUIDWithString:@(kTestUUIDGenericAttribute)]
];
service_data = @{
[CBUUID UUIDWithString:@(kTestUUIDHeartRate)] :
[NSData dataWithBytes:(unsigned char[]){1} length:1]
};
manufacturer_data =
[NSData dataWithBytes:(unsigned char[]){0xE0, 0x00, 1, 2, 3, 4}
length:6];
tx_power = @(static_cast<int8_t>(TestTxPower::LOWEST));
break;
case 2:
identifier = kTestPeripheralUUID1;
name = @(kTestDeviceName);
rssi = @(static_cast<int8_t>(TestRSSI::LOWER));
uuids = @[
[CBUUID UUIDWithString:@(kTestUUIDImmediateAlert)],
[CBUUID UUIDWithString:@(kTestUUIDLinkLoss)]
];
service_data = @{
[CBUUID UUIDWithString:@(kTestUUIDHeartRate)] :
[NSData dataWithBytes:(unsigned char[]){} length:0],
[CBUUID UUIDWithString:@(kTestUUIDImmediateAlert)] :
[NSData dataWithBytes:(unsigned char[]){0, 2} length:2]
};
manufacturer_data =
[NSData dataWithBytes:(unsigned char[]){0xE0, 0x00} length:2];
tx_power = @(static_cast<int8_t>(TestTxPower::LOWER));
break;
case 3:
identifier = kTestPeripheralUUID1;
name = @(kTestDeviceNameEmpty);
rssi = @(static_cast<int8_t>(TestRSSI::LOW));
uuids = nil;
service_data = nil;
manufacturer_data = nil;
tx_power = nil;
break;
case 4:
identifier = kTestPeripheralUUID2;
name = @(kTestDeviceNameEmpty);
rssi = @(static_cast<int8_t>(TestRSSI::MEDIUM));
uuids = nil;
service_data = nil;
manufacturer_data = nil;
tx_power = nil;
break;
case 5:
identifier = kTestPeripheralUUID1;
name = nil;
rssi = @(static_cast<int8_t>(TestRSSI::HIGH));
uuids = nil;
service_data = nil;
manufacturer_data = nil;
tx_power = nil;
break;
default:
NOTREACHED_IN_MIGRATION()
<< "SimulateLowEnergyDevice not implemented for " << device_ordinal;
identifier = nil;
name = nil;
rssi = nil;
uuids = nil;
service_data = nil;
manufacturer_data = nil;
tx_power = nil;
}
MockCBPeripheral* mock_peripheral =
[[MockCBPeripheral alloc] initWithUTF8StringIdentifier:identifier
name:name];
[mock_peripheral setBluetoothTestMac:this];
[central_manager_delegate
centralManager:central_manager
didDiscoverPeripheral:[mock_peripheral peripheral]
advertisementData:AdvertisementData(name, uuids, service_data,
manufacturer_data, tx_power)
RSSI:rssi];
return observer.last_device();
}
void BluetoothTestMac::SimulateGattConnection(BluetoothDevice* device) {
MockCBPeripheral* peripheral_mock = GetMockCBPeripheral(device);
[peripheral_mock setState:CBPeripheralStateConnected];
CBCentralManager* centralManager =
ObjCCast<CBCentralManager>(mock_central_manager_->get());
[centralManager.delegate centralManager:centralManager
didConnectPeripheral:peripheral_mock.peripheral];
}
void BluetoothTestMac::SimulateConnectedLowEnergyDevice(
ConnectedDeviceType device_ordinal) {
const char* identifier = nullptr;
NSString* name = nil;
NSMutableSet* cbUUIDs = [[NSMutableSet alloc] init];
switch (device_ordinal) {
case ConnectedDeviceType::GENERIC_DEVICE:
name = @(kTestDeviceName);
identifier = kTestPeripheralUUID1;
[cbUUIDs addObject:[CBUUID UUIDWithString:@(kTestUUIDGenericAccess)]];
break;
case ConnectedDeviceType::HEART_RATE_DEVICE:
name = @(kTestDeviceName);
identifier = kTestPeripheralUUID2;
[cbUUIDs addObject:[CBUUID UUIDWithString:@(kTestUUIDGenericAccess)]];
[cbUUIDs addObject:[CBUUID UUIDWithString:@(kTestUUIDHeartRate)]];
break;
}
DCHECK(name);
DCHECK(identifier);
DCHECK([cbUUIDs count] > 0);
MockCBPeripheral* mock_peripheral =
[[MockCBPeripheral alloc] initWithUTF8StringIdentifier:identifier
name:name];
[mock_peripheral setBluetoothTestMac:this];
[mock_central_manager_->get()
setConnectedMockPeripheral:[mock_peripheral peripheral]
withServiceUUIDs:cbUUIDs];
}
void BluetoothTestMac::SimulateGattConnectionError(
BluetoothDevice* device,
BluetoothDevice::ConnectErrorCode errorCode) {
MockCBPeripheral* peripheral_mock = GetMockCBPeripheral(device);
[peripheral_mock setState:CBPeripheralStateDisconnected];
CBCentralManager* centralManager =
ObjCCast<CBCentralManager>(mock_central_manager_->get());
NSError* error =
BluetoothDeviceMac::GetNSErrorFromConnectErrorCode(errorCode);
[centralManager.delegate centralManager:centralManager
didFailToConnectPeripheral:peripheral_mock.peripheral
error:error];
}
void BluetoothTestMac::SimulateGattDisconnection(BluetoothDevice* device) {
MockCBPeripheral* peripheral_mock = GetMockCBPeripheral(device);
[peripheral_mock setState:CBPeripheralStateDisconnected];
CBCentralManager* central_manager =
ObjCCast<CBCentralManager>(mock_central_manager_->get());
[central_manager.delegate centralManager:central_manager
didDisconnectPeripheral:peripheral_mock.peripheral
error:nil];
}
void BluetoothTestMac::SimulateGattDisconnectionError(BluetoothDevice* device) {
MockCBPeripheral* peripheral_mock = GetMockCBPeripheral(device);
[peripheral_mock setState:CBPeripheralStateDisconnected];
NSError* error = [NSError errorWithDomain:kDisconnectErrorDomain
code:CBErrorUnknown
userInfo:nil];
CBCentralManager* central_manager =
ObjCCast<CBCentralManager>(mock_central_manager_->get());
[central_manager.delegate centralManager:central_manager
didDisconnectPeripheral:peripheral_mock.peripheral
error:error];
}
void BluetoothTestMac::SimulateGattServicesDiscovered(
BluetoothDevice* device,
const std::vector<std::string>& uuids,
const std::vector<std::string>& blocked_uuids) {
DCHECK(blocked_uuids.empty()) << "Setting blocked_uuids unsupported.";
AddServicesToDeviceMac(device, uuids);
[GetMockCBPeripheral(device) mockDidDiscoverEvents];
}
void BluetoothTestMac::SimulateGattServicesChanged(BluetoothDevice* device) {
[GetMockCBPeripheral(device) didModifyServices:@[]];
}
void BluetoothTestMac::SimulateGattServiceRemoved(
BluetoothRemoteGattService* service) {
BluetoothUUID bluetooth_service_uuid = service->GetUUID();
std::string service_uuid_string = bluetooth_service_uuid.canonical_value();
BluetoothRemoteGattServiceMac* mac_gatt_service =
static_cast<BluetoothRemoteGattServiceMac*>(service);
BluetoothDevice* device = service->GetDevice();
MockCBPeripheral* peripheral_mock = GetMockCBPeripheral(device);
[peripheral_mock removeService:mac_gatt_service->GetService()];
// After -[MockCBPeripheral removeService:], BluetoothLowEnergyDeviceMac is
// expected to call -[CBPeripheral discoverServices:]
[peripheral_mock mockDidDiscoverEvents];
}
void BluetoothTestMac::SimulateGattCharacteristic(
BluetoothRemoteGattService* service,
const std::string& uuid,
int properties) {
BluetoothRemoteGattServiceMac* mac_gatt_service =
static_cast<BluetoothRemoteGattServiceMac*>(service);
CBService* cb_service = mac_gatt_service->GetService();
MockCBService* service_mock = ObjCCast<MockCBService>(cb_service);
CBUUID* cb_uuid = [CBUUID UUIDWithString:@(uuid.c_str())];
[service_mock addCharacteristicWithUUID:cb_uuid properties:properties];
MockCBPeripheral* peripheral_mock = GetMockCBPeripheral(service);
[peripheral_mock didModifyServices:@[]];
// After -[MockCBPeripheral didModifyServices:], BluetoothLowEnergyDeviceMac
// is expected to call -[CBPeripheral discoverServices:]
[peripheral_mock mockDidDiscoverEvents];
}
void BluetoothTestMac::SimulateGattCharacteristicRead(
BluetoothRemoteGattCharacteristic* characteristic,
const std::vector<uint8_t>& value) {
MockCBCharacteristic* characteristic_mock =
GetCBMockCharacteristic(characteristic);
NSData* data = [[NSData alloc] initWithBytes:value.data()
length:value.size()];
[characteristic_mock simulateReadWithValue:data error:nil];
}
void BluetoothTestMac::SimulateGattCharacteristicReadError(
BluetoothRemoteGattCharacteristic* characteristic,
BluetoothGattService::GattErrorCode error_code) {
MockCBCharacteristic* characteristic_mock =
GetCBMockCharacteristic(characteristic);
NSError* error = BluetoothDeviceMac::GetNSErrorFromGattErrorCode(error_code);
[characteristic_mock simulateReadWithValue:nil error:error];
}
void BluetoothTestMac::SimulateGattCharacteristicWrite(
BluetoothRemoteGattCharacteristic* characteristic) {
MockCBCharacteristic* characteristic_mock =
GetCBMockCharacteristic(characteristic);
[characteristic_mock simulateWriteWithError:nil];
}
void BluetoothTestMac::SimulateGattCharacteristicWriteError(
BluetoothRemoteGattCharacteristic* characteristic,
BluetoothGattService::GattErrorCode error_code) {
MockCBCharacteristic* characteristic_mock =
GetCBMockCharacteristic(characteristic);
NSError* error = BluetoothDeviceMac::GetNSErrorFromGattErrorCode(error_code);
[characteristic_mock simulateWriteWithError:error];
}
void BluetoothTestMac::SimulateGattDescriptor(
BluetoothRemoteGattCharacteristic* characteristic,
const std::string& uuid) {
MockCBCharacteristic* characteristic_mock =
GetCBMockCharacteristic(characteristic);
CBUUID* cb_uuid = [CBUUID UUIDWithString:@(uuid.c_str())];
[characteristic_mock addDescriptorWithUUID:cb_uuid];
MockCBPeripheral* peripheral_mock = GetMockCBPeripheral(characteristic);
[peripheral_mock didModifyServices:@[]];
// After -[MockCBPeripheral didModifyServices:], BluetoothLowEnergyDeviceMac
// is expected to call -[CBPeripheral discoverServices:]
[peripheral_mock mockDidDiscoverEvents];
}
void BluetoothTestMac::SimulateGattNotifySessionStarted(
BluetoothRemoteGattCharacteristic* characteristic) {
MockCBCharacteristic* characteristic_mock =
GetCBMockCharacteristic(characteristic);
[characteristic_mock simulateGattNotifySessionStarted];
}
void BluetoothTestMac::SimulateGattNotifySessionStartError(
BluetoothRemoteGattCharacteristic* characteristic,
BluetoothGattService::GattErrorCode error_code) {
MockCBCharacteristic* characteristic_mock =
GetCBMockCharacteristic(characteristic);
NSError* error = BluetoothDeviceMac::GetNSErrorFromGattErrorCode(error_code);
[characteristic_mock simulateGattNotifySessionFailedWithError:error];
}
void BluetoothTestMac::SimulateGattNotifySessionStopped(
BluetoothRemoteGattCharacteristic* characteristic) {
MockCBCharacteristic* characteristic_mock =
GetCBMockCharacteristic(characteristic);
[characteristic_mock simulateGattNotifySessionStopped];
}
void BluetoothTestMac::SimulateGattNotifySessionStopError(
BluetoothRemoteGattCharacteristic* characteristic,
BluetoothGattService::GattErrorCode error_code) {
MockCBCharacteristic* characteristic_mock =
GetCBMockCharacteristic(characteristic);
NSError* error = BluetoothDeviceMac::GetNSErrorFromGattErrorCode(error_code);
[characteristic_mock simulateGattNotifySessionStoppedWithError:error];
}
void BluetoothTestMac::SimulateGattCharacteristicChanged(
BluetoothRemoteGattCharacteristic* characteristic,
const std::vector<uint8_t>& value) {
MockCBCharacteristic* characteristic_mock =
GetCBMockCharacteristic(characteristic);
NSData* data = [[NSData alloc] initWithBytes:value.data()
length:value.size()];
[characteristic_mock simulateGattCharacteristicChangedWithValue:data];
}
void BluetoothTestMac::SimulateGattCharacteristicRemoved(
BluetoothRemoteGattService* service,
BluetoothRemoteGattCharacteristic* characteristic) {
MockCBPeripheral* peripheral_mock = GetMockCBPeripheral(service);
BluetoothRemoteGattServiceMac* mac_gatt_service =
static_cast<BluetoothRemoteGattServiceMac*>(service);
CBService* cb_service = mac_gatt_service->GetService();
MockCBService* service_mock = ObjCCast<MockCBService>(cb_service);
BluetoothRemoteGattCharacteristicMac* characteristic_mac =
static_cast<BluetoothRemoteGattCharacteristicMac*>(characteristic);
CBCharacteristic* cb_characteristic =
characteristic_mac->GetCBCharacteristic();
MockCBCharacteristic* characteristic_mock =
ObjCCast<MockCBCharacteristic>(cb_characteristic);
[service_mock removeCharacteristicMock:characteristic_mock];
[peripheral_mock didModifyServices:@[]];
[peripheral_mock mockDidDiscoverEvents];
}
void BluetoothTestMac::SimulateGattDescriptorRead(
BluetoothRemoteGattDescriptor* descriptor,
const std::vector<uint8_t>& value) {
SimulateGattDescriptorReadNSDataMac(descriptor, value);
}
void BluetoothTestMac::SimulateGattDescriptorReadError(
BluetoothRemoteGattDescriptor* descriptor,
BluetoothGattService::GattErrorCode error_code) {
NSError* error = BluetoothDeviceMac::GetNSErrorFromGattErrorCode(error_code);
[GetCBMockDescriptor(descriptor) simulateReadWithValue:nil error:error];
}
void BluetoothTestMac::SimulateGattDescriptorWrite(
BluetoothRemoteGattDescriptor* descriptor) {
[GetCBMockDescriptor(descriptor) simulateWriteWithError:nil];
}
void BluetoothTestMac::SimulateGattDescriptorWriteError(
BluetoothRemoteGattDescriptor* descriptor,
BluetoothGattService::GattErrorCode error_code) {
NSError* error = BluetoothDeviceMac::GetNSErrorFromGattErrorCode(error_code);
[GetCBMockDescriptor(descriptor) simulateWriteWithError:error];
}
void BluetoothTestMac::SimulateGattDescriptorUpdateError(
BluetoothRemoteGattDescriptor* descriptor,
BluetoothGattService::GattErrorCode error_code) {
NSError* error = BluetoothDeviceMac::GetNSErrorFromGattErrorCode(error_code);
[GetCBMockDescriptor(descriptor) simulateUpdateWithError:error];
}
void BluetoothTestMac::ExpectedChangeNotifyValueAttempts(int attempts) {
EXPECT_EQ(attempts, gatt_notify_characteristic_attempts_);
}
void BluetoothTestMac::ExpectedNotifyValue(
NotifyValueState expected_value_state) {
switch (expected_value_state) {
case NotifyValueState::NONE:
EXPECT_FALSE(last_notify_value_);
break;
case NotifyValueState::NOTIFY:
case NotifyValueState::INDICATE:
EXPECT_TRUE(last_notify_value_);
break;
}
}
void BluetoothTestMac::SimulateDidDiscoverServicesMac(BluetoothDevice* device) {
[GetMockCBPeripheral(device) mockDidDiscoverServices];
}
void BluetoothTestMac::SimulateDidDiscoverServicesMacWithError(
BluetoothDevice* device) {
NSError* error = [NSError errorWithDomain:CBErrorDomain
code:CBErrorUnknown
userInfo:nil];
[GetMockCBPeripheral(device) mockDidDiscoverServicesWithError:error];
}
void BluetoothTestMac::SimulateDidDiscoverCharacteristicsMac(
BluetoothRemoteGattService* service) {
BluetoothRemoteGattServiceMac* mac_gatt_service =
static_cast<BluetoothRemoteGattServiceMac*>(service);
CBService* cb_service = mac_gatt_service->GetService();
[GetMockCBPeripheral(service)
mockDidDiscoverCharacteristicsForService:cb_service];
}
void BluetoothTestMac::SimulateDidDiscoverCharacteristicsWithErrorMac(
BluetoothRemoteGattService* service) {
auto* mac_gatt_service = static_cast<BluetoothRemoteGattServiceMac*>(service);
CBService* cb_service = mac_gatt_service->GetService();
NSError* error = [NSError errorWithDomain:CBErrorDomain
code:CBErrorUnknown
userInfo:nil];
[GetMockCBPeripheral(service)
mockDidDiscoverCharacteristicsForService:cb_service
WithError:error];
}
void BluetoothTestMac::SimulateDidDiscoverDescriptorsMac(
BluetoothRemoteGattCharacteristic* characteristic) {
auto* mac_gatt_characteristic =
static_cast<BluetoothRemoteGattCharacteristicMac*>(characteristic);
CBCharacteristic* cb_characteristic =
mac_gatt_characteristic->GetCBCharacteristic();
[GetMockCBPeripheral(characteristic)
mockDidDiscoverDescriptorsForCharacteristic:cb_characteristic];
}
void BluetoothTestMac::SimulateDidDiscoverDescriptorsWithErrorMac(
BluetoothRemoteGattCharacteristic* characteristic) {
auto* mac_gatt_characteristic =
static_cast<BluetoothRemoteGattCharacteristicMac*>(characteristic);
CBCharacteristic* cb_characteristic =
mac_gatt_characteristic->GetCBCharacteristic();
NSError* error = [NSError errorWithDomain:CBErrorDomain
code:CBErrorUnknown
userInfo:nil];
[GetMockCBPeripheral(characteristic)
mockDidDiscoverDescriptorsForCharacteristic:cb_characteristic
WithError:error];
}
void BluetoothTestMac::SimulateGattDescriptorReadNSDataMac(
BluetoothRemoteGattDescriptor* descriptor,
const std::vector<uint8_t>& value) {
NSData* data = [[NSData alloc] initWithBytes:value.data()
length:value.size()];
[GetCBMockDescriptor(descriptor) simulateReadWithValue:data error:nil];
}
void BluetoothTestMac::SimulateGattDescriptorReadNSStringMac(
BluetoothRemoteGattDescriptor* descriptor,
const std::string& value) {
NSString* string = base::SysUTF8ToNSString(value);
[GetCBMockDescriptor(descriptor) simulateReadWithValue:string error:nil];
}
void BluetoothTestMac::SimulateGattDescriptorReadNSNumberMac(
BluetoothRemoteGattDescriptor* descriptor,
short value) {
NSNumber* number = @(value);
[GetCBMockDescriptor(descriptor) simulateReadWithValue:number error:nil];
}
#if BUILDFLAG(IS_MAC)
void BluetoothTestMac::SetMockControllerPowerState(int powered) {
// We are posting a task so that the state only gets updated in the next cycle
// and pending callbacks are not executed immediately.
adapter_low_energy_->ui_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
[](base::WeakPtr<BluetoothAdapterMac> adapter_mac, int powered) {
// Guard against deletion of the adapter.
if (!adapter_mac)
return;
auto* mock_central_manager =
base::apple::ObjCCastStrict<MockCentralManager>(
adapter_mac->GetCentralManager());
[mock_central_manager setState:powered ? CBManagerStatePoweredOn
: CBManagerStatePoweredOff];
[mock_central_manager.delegate
centralManagerDidUpdateState:adapter_mac->GetCentralManager()];
// On real devices, the Bluetooth classic code will call
// RunPendingPowerCallbacks() and dispatch the AdapterPoweredChanged
// event. Test code does not fake Bluetooth classic behavior so we
// call the method and dispatch the event directly.
// BT classic code related to powering the adapter is tested in
// BluetoothAdapterMacTest.PollAndChangePower.
adapter_mac->RunPendingPowerCallbacks();
adapter_mac->NotifyAdapterPoweredChanged(powered);
},
static_cast<BluetoothAdapterMac*>(adapter_low_energy_)
->weak_ptr_factory_.GetWeakPtr(),
powered));
}
#endif
void BluetoothTest::AddServicesToDeviceMac(
BluetoothDevice* device,
const std::vector<std::string>& uuids) {
NSMutableArray* services = [[NSMutableArray alloc] init];
for (auto uuid : uuids) {
CBUUID* cb_service_uuid = [CBUUID UUIDWithString:@(uuid.c_str())];
[services addObject:cb_service_uuid];
}
[GetMockCBPeripheral(device) addServices:services];
}
void BluetoothTestMac::AddCharacteristicToServiceMac(
BluetoothRemoteGattService* service,
const std::string& characteristic_uuid,
int properties) {
auto* mac_gatt_service = static_cast<BluetoothRemoteGattServiceMac*>(service);
CBService* cb_service = mac_gatt_service->GetService();
MockCBService* service_mock = ObjCCast<MockCBService>(cb_service);
CBUUID* cb_uuid = [CBUUID UUIDWithString:@(characteristic_uuid.c_str())];
[service_mock addCharacteristicWithUUID:cb_uuid properties:properties];
}
void BluetoothTestMac::AddDescriptorToCharacteristicMac(
BluetoothRemoteGattCharacteristic* characteristic,
const std::string& uuid) {
auto* mac_gatt_characteristic =
static_cast<BluetoothRemoteGattCharacteristicMac*>(characteristic);
CBCharacteristic* cb_characteristic =
mac_gatt_characteristic->GetCBCharacteristic();
MockCBCharacteristic* characteristic_mock =
ObjCCast<MockCBCharacteristic>(cb_characteristic);
CBUUID* cb_uuid = [CBUUID UUIDWithString:@(uuid.c_str())];
[characteristic_mock addDescriptorWithUUID:cb_uuid];
}
void BluetoothTestMac::OnFakeBluetoothDeviceConnectGattCalled() {
gatt_connection_attempts_++;
}
void BluetoothTestMac::OnFakeBluetoothGattDisconnect() {
gatt_disconnection_attempts_++;
}
void BluetoothTestMac::OnFakeBluetoothServiceDiscovery() {
gatt_discovery_attempts_++;
}
void BluetoothTestMac::OnFakeBluetoothCharacteristicDiscovery() {
gatt_characteristic_discovery_attempts_++;
}
void BluetoothTest::OnFakeBluetoothCharacteristicReadValue() {
gatt_read_characteristic_attempts_++;
}
void BluetoothTest::OnFakeBluetoothCharacteristicWriteValue(
std::vector<uint8_t> value) {
last_write_value_ = value;
gatt_write_characteristic_attempts_++;
}
void BluetoothTest::OnFakeBluetoothGattSetCharacteristicNotification(
bool notify_value) {
last_notify_value_ = notify_value;
gatt_notify_characteristic_attempts_++;
}
void BluetoothTest::OnFakeBluetoothDescriptorReadValue() {
gatt_read_descriptor_attempts_++;
}
void BluetoothTest::OnFakeBluetoothDescriptorWriteValue(
std::vector<uint8_t> value) {
last_write_value_ = value;
gatt_write_descriptor_attempts_++;
}
BluetoothDevice::UUIDSet
BluetoothTestMac::RetrieveConnectedPeripheralServiceUUIDs() {
BluetoothDevice::UUIDSet service_uuids;
for (CBUUID* uuid in
[mock_central_manager_->get() retrieveConnectedPeripheralServiceUUIDs]) {
service_uuids.insert(
BluetoothLowEnergyAdapterApple::BluetoothUUIDWithCBUUID(uuid));
}
return service_uuids;
}
void BluetoothTestMac::ResetRetrieveConnectedPeripheralServiceUUIDs() {
[mock_central_manager_->get() resetRetrieveConnectedPeripheralServiceUUIDs];
}
MockCBPeripheral* BluetoothTestMac::GetMockCBPeripheral(
BluetoothDevice* device) const {
auto* device_mac = static_cast<BluetoothLowEnergyDeviceMac*>(device);
CBPeripheral* peripheral = device_mac->GetPeripheral();
MockCBPeripheral* peripheral_mock = ObjCCast<MockCBPeripheral>(peripheral);
DCHECK(peripheral_mock);
return peripheral_mock;
}
MockCBPeripheral* BluetoothTestMac::GetMockCBPeripheral(
BluetoothRemoteGattService* service) const {
BluetoothDevice* device = service->GetDevice();
auto* device_mac = static_cast<BluetoothLowEnergyDeviceMac*>(device);
CBPeripheral* cb_peripheral = device_mac->GetPeripheral();
return ObjCCast<MockCBPeripheral>(cb_peripheral);
}
MockCBPeripheral* BluetoothTestMac::GetMockCBPeripheral(
BluetoothRemoteGattCharacteristic* characteristic) const {
return GetMockCBPeripheral(characteristic->GetService());
}
MockCBPeripheral* BluetoothTestMac::GetMockCBPeripheral(
BluetoothRemoteGattDescriptor* descriptor) const {
return GetMockCBPeripheral(descriptor->GetCharacteristic());
}
MockCBCharacteristic* BluetoothTest::GetCBMockCharacteristic(
BluetoothRemoteGattCharacteristic* characteristic) const {
device::BluetoothRemoteGattCharacteristicMac* mac_gatt_characteristic =
static_cast<device::BluetoothRemoteGattCharacteristicMac*>(
characteristic);
CBCharacteristic* cb_characteristic =
mac_gatt_characteristic->GetCBCharacteristic();
return ObjCCast<MockCBCharacteristic>(cb_characteristic);
}
MockCBDescriptor* BluetoothTest::GetCBMockDescriptor(
BluetoothRemoteGattDescriptor* descriptor) const {
device::BluetoothRemoteGattDescriptorMac* mac_gatt_descriptor =
static_cast<device::BluetoothRemoteGattDescriptorMac*>(descriptor);
CBDescriptor* cb_descriptor = mac_gatt_descriptor->GetCBDescriptor();
return ObjCCast<MockCBDescriptor>(cb_descriptor);
}
// Utility function for generating new (CBUUID, address) pairs where CBUUID
// hashes to address. For use when adding a new device address to the testing
// suite because CoreBluetooth peripherals have CBUUIDs in place of addresses,
// and we construct fake addresses for them by hashing the CBUUID. By changing
// |target| the user can generate sequentially numbered test addresses.
//
// std::string BluetoothTestMac::FindCBUUIDForHashTarget() {
// // The desired first 6 digits of the hash. For example 0100000, 020000,
// // 030000, ...
// const std::string target = "010000";
// // 128 bit buffer to be encoded as a hex string.
// int64_t input[2] = {0};
// // There are 2^24 ~ 10^7 possible configurations for the first 6 digits,
// // ie. each input has probability 10^-7 of succeeding, under the dubious
// // assumption that traversing inputs sequentially is as good as traversing
// // them randomly. After 10^8 iterations then the probability of never
// // succeeding is ((10^7-1)/10^7)^(10^8) ~= 10^-5.
// while (input[0] < LLONG_MAX) {
// // Encode as a hexidecimal number. Note that on x86 input[0] is stored
// // as a little-endian number, and so read backwards by HexEncode.
// std::string input_str = base::HexEncode(&input, sizeof(input));
// input_str.insert(20, "-");
// input_str.insert(16, "-");
// input_str.insert(12, "-");
// input_str.insert(8, "-");
// char raw[3];
// crypto::SHA256HashString(input_str, raw, sizeof(raw));
// if (base::HexEncode(raw, sizeof(raw)) == target) {
// return input_str;
// }
// ++input[0];
// }
// return "";
// }
} // namespace device