// Copyright 2016 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/test/bluetooth_test_win.h" #include "base/bind.h" #include "base/location.h" #include "base/run_loop.h" #include "base/strings/sys_string_conversions.h" #include "base/test/test_pending_task.h" #include "base/time/time.h" #include "device/bluetooth/bluetooth_adapter_win.h" #include "device/bluetooth/bluetooth_low_energy_win.h" #include "device/bluetooth/bluetooth_remote_gatt_characteristic_win.h" #include "device/bluetooth/bluetooth_remote_gatt_service_win.h" namespace { BLUETOOTH_ADDRESS CanonicalStringToBLUETOOTH_ADDRESS( std::string device_address) { BLUETOOTH_ADDRESS win_addr; unsigned int data[6]; int result = sscanf_s(device_address.c_str(), "%02X:%02X:%02X:%02X:%02X:%02X", &data[5], &data[4], &data[3], &data[2], &data[1], &data[0]); CHECK(result == 6); for (int i = 0; i < 6; i++) { win_addr.rgBytes[i] = data[i]; } return win_addr; } // The canonical UUID string format is device::BluetoothUUID.value(). BTH_LE_UUID CanonicalStringToBTH_LE_UUID(std::string uuid) { BTH_LE_UUID win_uuid = {0}; if (uuid.size() == 4) { win_uuid.IsShortUuid = TRUE; unsigned int data[1]; int result = sscanf_s(uuid.c_str(), "%04x", &data[0]); CHECK(result == 1); win_uuid.Value.ShortUuid = data[0]; } else if (uuid.size() == 36) { win_uuid.IsShortUuid = FALSE; unsigned int data[11]; int result = sscanf_s( uuid.c_str(), "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", &data[0], &data[1], &data[2], &data[3], &data[4], &data[5], &data[6], &data[7], &data[8], &data[9], &data[10]); CHECK(result == 11); win_uuid.Value.LongUuid.Data1 = data[0]; win_uuid.Value.LongUuid.Data2 = data[1]; win_uuid.Value.LongUuid.Data3 = data[2]; win_uuid.Value.LongUuid.Data4[0] = data[3]; win_uuid.Value.LongUuid.Data4[1] = data[4]; win_uuid.Value.LongUuid.Data4[2] = data[5]; win_uuid.Value.LongUuid.Data4[3] = data[6]; win_uuid.Value.LongUuid.Data4[4] = data[7]; win_uuid.Value.LongUuid.Data4[5] = data[8]; win_uuid.Value.LongUuid.Data4[6] = data[9]; win_uuid.Value.LongUuid.Data4[7] = data[10]; } else { CHECK(false); } return win_uuid; } } // namespace namespace device { BluetoothTestWin::BluetoothTestWin() : ui_task_runner_(new base::TestSimpleTaskRunner()), bluetooth_task_runner_(new base::TestSimpleTaskRunner()), adapter_win_(nullptr), fake_bt_classic_wrapper_(nullptr), fake_bt_le_wrapper_(nullptr) {} BluetoothTestWin::~BluetoothTestWin() {} bool BluetoothTestWin::PlatformSupportsLowEnergy() { if (fake_bt_le_wrapper_) return fake_bt_le_wrapper_->IsBluetoothLowEnergySupported(); return true; } void BluetoothTestWin::AdapterInitCallback() {} void BluetoothTestWin::InitWithDefaultAdapter() { adapter_ = new BluetoothAdapterWin(base::Bind( &BluetoothTestWin::AdapterInitCallback, base::Unretained(this))); adapter_win_ = static_cast(adapter_.get()); adapter_win_->Init(); } void BluetoothTestWin::InitWithoutDefaultAdapter() { adapter_ = new BluetoothAdapterWin(base::Bind( &BluetoothTestWin::AdapterInitCallback, base::Unretained(this))); adapter_win_ = static_cast(adapter_.get()); adapter_win_->InitForTest(ui_task_runner_, bluetooth_task_runner_); } void BluetoothTestWin::InitWithFakeAdapter() { fake_bt_classic_wrapper_ = new win::BluetoothClassicWrapperFake(); fake_bt_le_wrapper_ = new win::BluetoothLowEnergyWrapperFake(); fake_bt_le_wrapper_->AddObserver(this); win::BluetoothClassicWrapper::SetInstanceForTest(fake_bt_classic_wrapper_); win::BluetoothLowEnergyWrapper::SetInstanceForTest(fake_bt_le_wrapper_); fake_bt_classic_wrapper_->SimulateARadio( base::SysUTF8ToWide(kTestAdapterName), CanonicalStringToBLUETOOTH_ADDRESS(kTestAdapterAddress)); adapter_ = new BluetoothAdapterWin(base::Bind( &BluetoothTestWin::AdapterInitCallback, base::Unretained(this))); adapter_win_ = static_cast(adapter_.get()); adapter_win_->InitForTest(nullptr, bluetooth_task_runner_); adapter_win_->GetWinBluetoothTaskManager()->AddObserver(this); FinishPendingTasks(); } bool BluetoothTestWin::DenyPermission() { return false; } void BluetoothTestWin::StartLowEnergyDiscoverySession() { __super ::StartLowEnergyDiscoverySession(); FinishPendingTasks(); } BluetoothDevice* BluetoothTestWin::DiscoverLowEnergyDevice(int device_ordinal) { if (device_ordinal > 4 || device_ordinal < 1) return nullptr; std::string device_name = kTestDeviceName; std::string device_address = kTestDeviceAddress1; std::string service_uuid_1; std::string service_uuid_2; switch (device_ordinal) { case 1: { service_uuid_1 = kTestUUIDGenericAccess; service_uuid_2 = kTestUUIDGenericAttribute; } break; case 2: { service_uuid_1 = kTestUUIDImmediateAlert; service_uuid_2 = kTestUUIDLinkLoss; } break; case 3: { device_name = kTestDeviceNameEmpty; } break; case 4: { device_name = kTestDeviceNameEmpty; device_address = kTestDeviceAddress2; } break; } win::BLEDevice* simulated_device = fake_bt_le_wrapper_->SimulateBLEDevice( device_name, CanonicalStringToBLUETOOTH_ADDRESS(device_address)); if (simulated_device != nullptr) { if (!service_uuid_1.empty()) { fake_bt_le_wrapper_->SimulateGattService( simulated_device, nullptr, CanonicalStringToBTH_LE_UUID(service_uuid_1)); } if (!service_uuid_2.empty()) { fake_bt_le_wrapper_->SimulateGattService( simulated_device, nullptr, CanonicalStringToBTH_LE_UUID(service_uuid_2)); } } FinishPendingTasks(); std::vector devices = adapter_win_->GetDevices(); for (auto device : devices) { if (device->GetAddress() == device_address) return device; } return nullptr; } void BluetoothTestWin::SimulateGattConnection(BluetoothDevice* device) { FinishPendingTasks(); // Clear records caused by CreateGattConnection since we do not support it on // Windows. gatt_discovery_attempts_++; expected_success_callback_calls_--; unexpected_error_callback_ = false; } void BluetoothTestWin::SimulateGattServicesDiscovered( BluetoothDevice* device, const std::vector& uuids) { win::BLEDevice* simulated_device = fake_bt_le_wrapper_->GetSimulatedBLEDevice(device->GetAddress()); CHECK(simulated_device); for (auto uuid : uuids) { fake_bt_le_wrapper_->SimulateGattService( simulated_device, nullptr, CanonicalStringToBTH_LE_UUID(uuid)); } FinishPendingTasks(); } void BluetoothTestWin::SimulateGattServiceRemoved( BluetoothGattService* service) { std::string device_address = service->GetDevice()->GetAddress(); win::BLEDevice* target_device = fake_bt_le_wrapper_->GetSimulatedBLEDevice(device_address); CHECK(target_device); BluetoothRemoteGattServiceWin* win_service = static_cast(service); std::string service_att_handle = std::to_string(win_service->GetAttributeHandle()); fake_bt_le_wrapper_->SimulateGattServiceRemoved(target_device, nullptr, service_att_handle); ForceRefreshDevice(); } void BluetoothTestWin::SimulateGattCharacteristic(BluetoothGattService* service, const std::string& uuid, int properties) { std::string device_address = service->GetDevice()->GetAddress(); win::BLEDevice* target_device = fake_bt_le_wrapper_->GetSimulatedBLEDevice(device_address); CHECK(target_device); win::GattService* target_service = GetSimulatedService(target_device, service); CHECK(target_service); BTH_LE_GATT_CHARACTERISTIC win_characteristic_info; win_characteristic_info.CharacteristicUuid = CanonicalStringToBTH_LE_UUID(uuid); win_characteristic_info.IsBroadcastable = FALSE; win_characteristic_info.IsReadable = FALSE; win_characteristic_info.IsWritableWithoutResponse = FALSE; win_characteristic_info.IsWritable = FALSE; win_characteristic_info.IsNotifiable = FALSE; win_characteristic_info.IsIndicatable = FALSE; win_characteristic_info.IsSignedWritable = FALSE; win_characteristic_info.HasExtendedProperties = FALSE; if (properties & BluetoothGattCharacteristic::PROPERTY_BROADCAST) win_characteristic_info.IsBroadcastable = TRUE; if (properties & BluetoothGattCharacteristic::PROPERTY_READ) win_characteristic_info.IsReadable = TRUE; if (properties & BluetoothGattCharacteristic::PROPERTY_WRITE_WITHOUT_RESPONSE) win_characteristic_info.IsWritableWithoutResponse = TRUE; if (properties & BluetoothGattCharacteristic::PROPERTY_WRITE) win_characteristic_info.IsWritable = TRUE; if (properties & BluetoothGattCharacteristic::PROPERTY_NOTIFY) win_characteristic_info.IsNotifiable = TRUE; if (properties & BluetoothGattCharacteristic::PROPERTY_INDICATE) win_characteristic_info.IsIndicatable = TRUE; if (properties & BluetoothGattCharacteristic::PROPERTY_AUTHENTICATED_SIGNED_WRITES) { win_characteristic_info.IsSignedWritable = TRUE; } if (properties & BluetoothGattCharacteristic::PROPERTY_EXTENDED_PROPERTIES) win_characteristic_info.HasExtendedProperties = TRUE; fake_bt_le_wrapper_->SimulateGattCharacterisc(device_address, target_service, win_characteristic_info); ForceRefreshDevice(); } void BluetoothTestWin::SimulateGattCharacteristicRemoved( BluetoothGattService* service, BluetoothGattCharacteristic* characteristic) { CHECK(service); CHECK(characteristic); std::string device_address = service->GetDevice()->GetAddress(); win::GattService* target_service = GetSimulatedService( fake_bt_le_wrapper_->GetSimulatedBLEDevice(device_address), service); CHECK(target_service); std::string characteristic_att_handle = std::to_string( static_cast(characteristic) ->GetAttributeHandle()); fake_bt_le_wrapper_->SimulateGattCharacteriscRemove( target_service, characteristic_att_handle); ForceRefreshDevice(); } void BluetoothTestWin::RememberCharacteristicForSubsequentAction( BluetoothGattCharacteristic* characteristic) { remembered_characteristic_ = static_cast(characteristic); } void BluetoothTestWin::SimulateGattCharacteristicRead( BluetoothGattCharacteristic* characteristic, const std::vector& value) { BluetoothGattCharacteristic* target_characteristic = characteristic; if (target_characteristic == nullptr) target_characteristic = remembered_characteristic_; CHECK(target_characteristic); win::GattCharacteristic* target_simulated_characteristic = GetSimulatedCharacteristic(target_characteristic); if (target_simulated_characteristic == nullptr) return; fake_bt_le_wrapper_->SimulateGattCharacteristicValue( target_simulated_characteristic, value); RunPendingTasksUntilCallback(); } void BluetoothTestWin::SimulateGattCharacteristicReadError( BluetoothGattCharacteristic* characteristic, BluetoothGattService::GattErrorCode error_code) { win::GattCharacteristic* target_characteristic = GetSimulatedCharacteristic(characteristic); CHECK(target_characteristic); HRESULT hr = ERROR_SEM_TIMEOUT; if (error_code == BluetoothGattService::GATT_ERROR_INVALID_LENGTH) hr = E_BLUETOOTH_ATT_INVALID_ATTRIBUTE_VALUE_LENGTH; fake_bt_le_wrapper_->SimulateGattCharacteristicReadError( target_characteristic, hr); FinishPendingTasks(); } void BluetoothTestWin::SimulateGattCharacteristicWrite( BluetoothGattCharacteristic* characteristic) { RunPendingTasksUntilCallback(); } void BluetoothTestWin::SimulateGattCharacteristicWriteError( BluetoothGattCharacteristic* characteristic, BluetoothGattService::GattErrorCode error_code) { win::GattCharacteristic* target_characteristic = GetSimulatedCharacteristic(characteristic); CHECK(target_characteristic); HRESULT hr = ERROR_SEM_TIMEOUT; if (error_code == BluetoothGattService::GATT_ERROR_INVALID_LENGTH) hr = E_BLUETOOTH_ATT_INVALID_ATTRIBUTE_VALUE_LENGTH; fake_bt_le_wrapper_->SimulateGattCharacteristicWriteError( target_characteristic, hr); FinishPendingTasks(); } void BluetoothTestWin::DeleteDevice(BluetoothDevice* device) { CHECK(device); fake_bt_le_wrapper_->RemoveSimulatedBLEDevice(device->GetAddress()); } void BluetoothTestWin::SimulateGattDescriptor( BluetoothGattCharacteristic* characteristic, const std::string& uuid) { win::GattCharacteristic* target_characteristic = GetSimulatedCharacteristic(characteristic); CHECK(target_characteristic); fake_bt_le_wrapper_->SimulateGattDescriptor( characteristic->GetService()->GetDevice()->GetAddress(), target_characteristic, CanonicalStringToBTH_LE_UUID(uuid)); ForceRefreshDevice(); } void BluetoothTestWin::OnAttemptReadGattCharacteristic() { gatt_read_characteristic_attempts_++; } void BluetoothTestWin::OnAttemptWriteGattCharacteristic() { gatt_write_characteristic_attempts_++; } void BluetoothTestWin::onWriteGattCharacteristicValue( const PBTH_LE_GATT_CHARACTERISTIC_VALUE value) { last_write_value_.clear(); for (ULONG i = 0; i < value->DataSize; i++) last_write_value_.push_back(value->Data[i]); } win::GattService* BluetoothTestWin::GetSimulatedService( win::BLEDevice* device, BluetoothGattService* service) { CHECK(device); CHECK(service); std::vector chain_of_att_handles; BluetoothRemoteGattServiceWin* win_service = static_cast(service); chain_of_att_handles.insert( chain_of_att_handles.begin(), std::to_string(win_service->GetAttributeHandle())); win::GattService* simulated_service = fake_bt_le_wrapper_->GetSimulatedGattService(device, chain_of_att_handles); CHECK(simulated_service); return simulated_service; } win::GattCharacteristic* BluetoothTestWin::GetSimulatedCharacteristic( BluetoothGattCharacteristic* characteristic) { CHECK(characteristic); BluetoothRemoteGattCharacteristicWin* win_characteristic = static_cast(characteristic); std::string device_address = win_characteristic->GetService()->GetDevice()->GetAddress(); win::BLEDevice* target_device = fake_bt_le_wrapper_->GetSimulatedBLEDevice(device_address); if (target_device == nullptr) return nullptr; win::GattService* target_service = GetSimulatedService(target_device, win_characteristic->GetService()); if (target_service == nullptr) return nullptr; return fake_bt_le_wrapper_->GetSimulatedGattCharacteristic( target_service, std::to_string(win_characteristic->GetAttributeHandle())); } void BluetoothTestWin::RunPendingTasksUntilCallback() { std::deque tasks = bluetooth_task_runner_->GetPendingTasks(); bluetooth_task_runner_->ClearPendingTasks(); int original_callback_count = callback_count_; int original_error_callback_count = error_callback_count_; do { base::TestPendingTask task = tasks.front(); tasks.pop_front(); task.task.Run(); base::RunLoop().RunUntilIdle(); } while (tasks.size() && callback_count_ == original_callback_count && error_callback_count_ == original_error_callback_count); // Put the rest of pending tasks back to Bluetooth task runner. for (const auto& task : tasks) { if (task.delay.is_zero()) { bluetooth_task_runner_->PostTask(task.location, task.task); } else { bluetooth_task_runner_->PostDelayedTask(task.location, task.task, task.delay); } } } void BluetoothTestWin::ForceRefreshDevice() { adapter_win_->force_update_device_for_test_ = true; FinishPendingTasks(); } void BluetoothTestWin::FinishPendingTasks() { bluetooth_task_runner_->RunPendingTasks(); base::RunLoop().RunUntilIdle(); } }