blob: 37f6d9086e4dfdf7ff27ae083935d71be8cfe883 [file] [log] [blame]
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "device/bluetooth/floss/fake_floss_adapter_client.h"
#include <string>
#include <vector>
#include "base/containers/flat_map.h"
#include "base/logging.h"
#include "base/observer_list.h"
#include "base/task/single_thread_task_runner.h"
#include "device/bluetooth/floss/floss_dbus_client.h"
namespace floss {
namespace {
const int kDelayedTaskMs = 100;
FlossDeviceId ConvertAddressToDevice(const std::string& address) {
static const base::flat_map<std::string, FlossDeviceId> address_to_device{
{FakeFlossAdapterClient::kClassicAddress,
FlossDeviceId{FakeFlossAdapterClient::kClassicAddress,
FakeFlossAdapterClient::kClassicName}},
{FakeFlossAdapterClient::kPinCodeDisplayAddress,
FlossDeviceId{FakeFlossAdapterClient::kPinCodeDisplayAddress,
FakeFlossAdapterClient::kPinCodeDisplayName}},
{FakeFlossAdapterClient::kPasskeyDisplayAddress,
FlossDeviceId{FakeFlossAdapterClient::kPasskeyDisplayAddress,
FakeFlossAdapterClient::kPasskeyDisplayName}},
{FakeFlossAdapterClient::kPinCodeRequestAddress,
FlossDeviceId{FakeFlossAdapterClient::kPinCodeRequestAddress,
FakeFlossAdapterClient::kPinCodeRequestName}},
{FakeFlossAdapterClient::kPhoneAddress,
FlossDeviceId{FakeFlossAdapterClient::kPhoneAddress,
FakeFlossAdapterClient::kPhoneName}},
{FakeFlossAdapterClient::kPasskeyRequestAddress,
FlossDeviceId{FakeFlossAdapterClient::kPasskeyRequestAddress,
FakeFlossAdapterClient::kPasskeyRequestName}},
{FakeFlossAdapterClient::kJustWorksAddress,
FlossDeviceId{FakeFlossAdapterClient::kJustWorksAddress,
FakeFlossAdapterClient::kJustWorksName}}};
auto iter = address_to_device.find(address);
if (iter != address_to_device.end()) {
return iter->second;
}
return FlossDeviceId{address, ""};
}
uint32_t ConvertAddressToClassOfDevice(const std::string& address) {
static const base::flat_map<std::string, uint32_t> address_to_cod{
{FakeFlossAdapterClient::kClassicAddress,
FakeFlossAdapterClient::kClassicClassOfDevice},
{FakeFlossAdapterClient::kPinCodeDisplayAddress,
FakeFlossAdapterClient::kPinCodeDisplayClassOfDevice},
{FakeFlossAdapterClient::kPasskeyDisplayAddress,
FakeFlossAdapterClient::kPasskeyDisplayClassOfDevice},
{FakeFlossAdapterClient::kPinCodeRequestAddress,
FakeFlossAdapterClient::kPinCodeRequestClassOfDevice},
{FakeFlossAdapterClient::kPhoneAddress,
FakeFlossAdapterClient::kPhoneClassOfDevice},
{FakeFlossAdapterClient::kPasskeyRequestAddress,
FakeFlossAdapterClient::kPasskeyRequestClassOfDevice},
{FakeFlossAdapterClient::kJustWorksAddress,
FakeFlossAdapterClient::kJustWorksClassOfDevice}};
auto iter = address_to_cod.find(address);
if (iter != address_to_cod.end()) {
return iter->second;
}
return FakeFlossAdapterClient::kDefaultClassOfDevice;
}
} // namespace
FakeFlossAdapterClient::FakeFlossAdapterClient()
: bonded_addresses_({kBondedAddress1, kBondedAddress2}),
connected_addresses_({}) {}
FakeFlossAdapterClient::~FakeFlossAdapterClient() = default;
const char FakeFlossAdapterClient::kBondedAddress1[] = "11:11:11:11:11:01";
const char FakeFlossAdapterClient::kBondedAddress2[] = "11:11:11:11:11:02";
const char FakeFlossAdapterClient::kPairedAddressBrEdr[] = "11:11:11:11:11:03";
const char FakeFlossAdapterClient::kPairedAddressLE[] = "11:11:11:11:11:04";
const uint32_t FakeFlossAdapterClient::kDefaultClassOfDevice = 0x240418;
const char FakeFlossAdapterClient::kClassicName[] = "Bluetooth 2.0 Mouse";
const char FakeFlossAdapterClient::kClassicAddress[] = "11:35:11:35:00:01";
const uint32_t FakeFlossAdapterClient::kClassicClassOfDevice = 0x002580;
const char FakeFlossAdapterClient::kPinCodeDisplayName[] =
"Bluetooth 2.0 Keyboard";
const char FakeFlossAdapterClient::kPinCodeDisplayAddress[] =
"11:35:11:35:00:02";
const uint32_t FakeFlossAdapterClient::kPinCodeDisplayClassOfDevice = 0x002540;
const char FakeFlossAdapterClient::kPasskeyDisplayName[] =
"Bluetooth 2.1+ Keyboard";
const char FakeFlossAdapterClient::kPasskeyDisplayAddress[] =
"11:35:11:35:00:03";
const uint32_t FakeFlossAdapterClient::kPasskeyDisplayClassOfDevice = 0x002540;
const char FakeFlossAdapterClient::kPinCodeRequestName[] = "PIN Device";
const char FakeFlossAdapterClient::kPinCodeRequestAddress[] =
"11:35:11:35:00:04";
const uint32_t FakeFlossAdapterClient::kPinCodeRequestClassOfDevice = 0x240408;
const char FakeFlossAdapterClient::kPhoneName[] = "Phone";
const char FakeFlossAdapterClient::kPhoneAddress[] = "11:35:11:35:00:05";
const uint32_t FakeFlossAdapterClient::kPhoneClassOfDevice = 0x7a020c;
const char FakeFlossAdapterClient::kPasskeyRequestName[] = "Passkey Device";
const char FakeFlossAdapterClient::kPasskeyRequestAddress[] =
"11:35:11:35:00:06";
const uint32_t FakeFlossAdapterClient::kPasskeyRequestClassOfDevice = 0x7a020c;
const char FakeFlossAdapterClient::kJustWorksName[] = "Just-Works Device";
const char FakeFlossAdapterClient::kJustWorksAddress[] = "11:35:11:35:00:07";
const uint32_t FakeFlossAdapterClient::kJustWorksClassOfDevice = 0x240428;
const uint32_t FakeFlossAdapterClient::kPasskey = 123456;
const char FakeFlossAdapterClient::kPinCode[] = "012345";
void FakeFlossAdapterClient::Init(dbus::Bus* bus,
const std::string& service_name,
const int adapter_index,
base::Version version,
base::OnceClosure on_ready) {
bus_ = bus;
adapter_path_ = GenerateAdapterPath(adapter_index);
service_name_ = service_name;
version_ = version;
std::move(on_ready).Run();
}
void FakeFlossAdapterClient::SetName(ResponseCallback<Void> callback,
const std::string& name) {
PostDelayedTask(base::BindOnce(std::move(callback), Void{}));
}
void FakeFlossAdapterClient::StartDiscovery(ResponseCallback<Void> callback) {
// Fail fast if we're meant to fail discovery
if (fail_discovery_) {
fail_discovery_ = std::nullopt;
std::move(callback).Run(base::unexpected(
Error("org.chromium.bluetooth.Bluetooth.FooError", "Foo error")));
return;
}
// Simulate devices being discovered.
const auto discoverable_devices = std::vector{
// Report empty name first to simulate this device sends its name later.
FlossDeviceId{kClassicAddress, ""},
FlossDeviceId{kClassicAddress, kClassicName},
FlossDeviceId{kPinCodeDisplayAddress, kPinCodeDisplayName},
FlossDeviceId{kPasskeyDisplayAddress, kPasskeyDisplayName},
FlossDeviceId{kPinCodeRequestAddress, kPinCodeRequestName},
FlossDeviceId{kPhoneAddress, kPhoneName},
FlossDeviceId{kPasskeyRequestAddress, kPasskeyRequestName},
FlossDeviceId{kJustWorksAddress, kJustWorksName}};
for (const auto& device : discoverable_devices) {
if (base::Contains(connected_addresses_, device.address)) {
// Skip connected devices.
continue;
}
for (auto& observer : observers_) {
observer.AdapterFoundDevice(device);
observer.AdapterDevicePropertyChanged(
FlossAdapterClient::BtPropertyType::kBdName, device);
observer.AdapterDevicePropertyChanged(
FlossAdapterClient::BtPropertyType::kClassOfDevice, device);
observer.AdapterDevicePropertyChanged(
FlossAdapterClient::BtPropertyType::kTypeOfDevice, device);
}
}
PostDelayedTask(base::BindOnce(std::move(callback), Void{}));
}
void FakeFlossAdapterClient::CancelDiscovery(ResponseCallback<Void> callback) {
// Will need to stop simulated discovery once the simulation grows.
PostDelayedTask(base::BindOnce(std::move(callback), Void{}));
}
void FakeFlossAdapterClient::CreateBond(ResponseCallback<bool> callback,
FlossDeviceId device,
BluetoothTransport transport) {
if (fail_bonding_) {
fail_bonding_ = std::nullopt;
std::move(callback).Run(base::unexpected(
Error("org.chromium.Error.Failed", "Bonding failed by request")));
return;
}
for (auto& observer : observers_) {
observer.DeviceBondStateChanged(
device, /*status=*/0,
FlossAdapterClient::BondState::kBondingInProgress);
}
if (device.address == kJustWorksAddress ||
device.address == kClassicAddress) {
for (auto& observer : observers_) {
observer.DeviceBondStateChanged(device, /*status=*/0,
FlossAdapterClient::BondState::kBonded);
}
bonded_addresses_.insert(device.address);
SetConnected(device.address, true);
PostDelayedTask(base::BindOnce(std::move(callback), true));
} else if (device.address == kPasskeyDisplayAddress) {
for (auto& observer : observers_) {
observer.AdapterSspRequest(device, /*cod=*/0,
BluetoothSspVariant::kPasskeyNotification,
kPasskey);
}
PostDelayedTask(base::BindOnce(std::move(callback), true));
} else if (device.address == kPhoneAddress) {
for (auto& observer : observers_) {
observer.AdapterSspRequest(device, /*cod=*/0,
BluetoothSspVariant::kPasskeyConfirmation,
kPasskey);
}
PostDelayedTask(base::BindOnce(std::move(callback), true));
} else if (device.address == kPasskeyRequestAddress) {
for (auto& observer : observers_) {
observer.AdapterSspRequest(device, /*cod=*/0,
BluetoothSspVariant::kPasskeyEntry, 0);
}
PostDelayedTask(base::BindOnce(std::move(callback), true));
} else if (device.address == kPinCodeDisplayAddress) {
for (auto& observer : observers_) {
observer.AdapterPinDisplay(device, std::string(kPinCode));
}
PostDelayedTask(base::BindOnce(std::move(callback), true));
} else if (device.address == kPinCodeRequestAddress) {
for (auto& observer : observers_) {
observer.AdapterPinRequest(device, 0, false);
}
PostDelayedTask(base::BindOnce(std::move(callback), true));
} else {
for (auto& observer : observers_) {
observer.DeviceBondStateChanged(
device, static_cast<uint32_t>(BtifStatus::kFail),
FlossAdapterClient::BondState::kNotBonded);
}
PostDelayedTask(base::BindOnce(
std::move(callback),
base::unexpected(Error("org.chromium.bluetooth.UnknownDevice", ""))));
}
}
void FakeFlossAdapterClient::CreateBond(
ResponseCallback<FlossDBusClient::BtifStatus> callback,
FlossDeviceId device,
BluetoothTransport transport) {
if (fail_bonding_) {
fail_bonding_ = std::nullopt;
std::move(callback).Run(base::unexpected(
Error("org.chromium.Error.Failed", "Bonding failed by request")));
return;
}
for (auto& observer : observers_) {
observer.DeviceBondStateChanged(
device, /*status=*/0,
FlossAdapterClient::BondState::kBondingInProgress);
}
if (device.address == kJustWorksAddress ||
device.address == kClassicAddress) {
for (auto& observer : observers_) {
observer.DeviceBondStateChanged(device, /*status=*/0,
FlossAdapterClient::BondState::kBonded);
}
bonded_addresses_.insert(device.address);
SetConnected(device.address, true);
PostDelayedTask(base::BindOnce(std::move(callback),
FlossDBusClient::BtifStatus::kSuccess));
} else if (device.address == kPasskeyDisplayAddress) {
for (auto& observer : observers_) {
observer.AdapterSspRequest(device, /*cod=*/0,
BluetoothSspVariant::kPasskeyNotification,
kPasskey);
}
PostDelayedTask(base::BindOnce(std::move(callback),
FlossDBusClient::BtifStatus::kSuccess));
} else if (device.address == kPhoneAddress) {
for (auto& observer : observers_) {
observer.AdapterSspRequest(device, /*cod=*/0,
BluetoothSspVariant::kPasskeyConfirmation,
kPasskey);
}
PostDelayedTask(base::BindOnce(std::move(callback),
FlossDBusClient::BtifStatus::kSuccess));
} else if (device.address == kPasskeyRequestAddress) {
for (auto& observer : observers_) {
observer.AdapterSspRequest(device, /*cod=*/0,
BluetoothSspVariant::kPasskeyEntry, 0);
}
PostDelayedTask(base::BindOnce(std::move(callback),
FlossDBusClient::BtifStatus::kSuccess));
} else if (device.address == kPinCodeDisplayAddress) {
for (auto& observer : observers_) {
observer.AdapterPinDisplay(device, std::string(kPinCode));
}
PostDelayedTask(base::BindOnce(std::move(callback),
FlossDBusClient::BtifStatus::kSuccess));
} else if (device.address == kPinCodeRequestAddress) {
for (auto& observer : observers_) {
observer.AdapterPinRequest(device, 0, false);
}
PostDelayedTask(base::BindOnce(std::move(callback),
FlossDBusClient::BtifStatus::kSuccess));
} else {
for (auto& observer : observers_) {
observer.DeviceBondStateChanged(
device, static_cast<uint32_t>(BtifStatus::kFail),
FlossAdapterClient::BondState::kNotBonded);
}
PostDelayedTask(base::BindOnce(
std::move(callback),
base::unexpected(Error("org.chromium.bluetooth.UnknownDevice", ""))));
}
}
void FakeFlossAdapterClient::CancelBondProcess(ResponseCallback<bool> callback,
FlossDeviceId device) {
for (auto& observer : observers_) {
observer.DeviceBondStateChanged(device, /*status=*/0,
FlossAdapterClient::BondState::kNotBonded);
}
PostDelayedTask(base::BindOnce(std::move(callback), true));
}
void FakeFlossAdapterClient::RemoveBond(ResponseCallback<bool> callback,
FlossDeviceId device) {
if (!base::Contains(bonded_addresses_, device.address)) {
PostDelayedTask(base::BindOnce(std::move(callback), false));
return;
}
for (auto& observer : observers_) {
observer.DeviceBondStateChanged(device, /*status=*/0,
FlossAdapterClient::BondState::kNotBonded);
}
bonded_addresses_.erase(device.address);
SetConnected(device.address, false);
PostDelayedTask(base::BindOnce(std::move(callback), true));
}
void FakeFlossAdapterClient::GetRemoteType(
ResponseCallback<BluetoothDeviceType> callback,
FlossDeviceId device) {
auto remote_type = BluetoothDeviceType::kBredr;
if (device.address == kBondedAddress1 || device.address == kBondedAddress2 ||
device.address == kPairedAddressLE) {
remote_type = BluetoothDeviceType::kBle;
}
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), remote_type));
}
void FakeFlossAdapterClient::GetRemoteClass(ResponseCallback<uint32_t> callback,
FlossDeviceId device) {
uint32_t cod = ConvertAddressToClassOfDevice(device.address);
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), cod));
}
void FakeFlossAdapterClient::GetRemoteAppearance(
ResponseCallback<uint16_t> callback,
FlossDeviceId device) {
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), 0));
}
void FakeFlossAdapterClient::GetConnectionState(
ResponseCallback<uint32_t> callback,
const FlossDeviceId& device) {
FlossAdapterClient::ConnectionState conn_state =
FlossAdapterClient::ConnectionState::kDisconnected;
if (base::Contains(connected_addresses_, device.address)) {
if (device.address == kPairedAddressBrEdr) {
conn_state = FlossAdapterClient::ConnectionState::kPairedBREDROnly;
} else if (device.address == kPairedAddressLE) {
conn_state = FlossAdapterClient::ConnectionState::kPairedLEOnly;
} else {
conn_state = FlossAdapterClient::ConnectionState::kConnectedOnly;
}
}
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback), static_cast<uint32_t>(conn_state)));
}
void FakeFlossAdapterClient::GetRemoteUuids(
ResponseCallback<device::BluetoothDevice::UUIDList> callback,
FlossDeviceId device) {
device::BluetoothDevice::UUIDList uuid_list;
PostDelayedTask(base::BindOnce(std::move(callback), std::move(uuid_list)));
}
void FakeFlossAdapterClient::GetRemoteVendorProductInfo(
ResponseCallback<FlossAdapterClient::VendorProductInfo> callback,
FlossDeviceId device) {
FlossAdapterClient::VendorProductInfo info;
PostDelayedTask(base::BindOnce(std::move(callback), std::move(info)));
}
void FakeFlossAdapterClient::GetRemoteAddressType(
ResponseCallback<FlossAdapterClient::BtAddressType> callback,
FlossDeviceId device) {
PostDelayedTask(base::BindOnce(std::move(callback),
FlossAdapterClient::BtAddressType::kPublic));
}
void FakeFlossAdapterClient::GetBondState(ResponseCallback<uint32_t> callback,
const FlossDeviceId& device) {
FlossAdapterClient::BondState bond_state =
base::Contains(bonded_addresses_, device.address)
? floss::FlossAdapterClient::BondState::kBonded
: floss::FlossAdapterClient::BondState::kNotBonded;
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback), static_cast<uint32_t>(bond_state)));
}
void FakeFlossAdapterClient::ConnectAllEnabledProfiles(
ResponseCallback<Void> callback,
const FlossDeviceId& device) {
SetConnected(device.address, true);
PostDelayedTask(base::BindOnce(std::move(callback), Void{}));
}
void FakeFlossAdapterClient::ConnectAllEnabledProfiles(
ResponseCallback<FlossDBusClient::BtifStatus> callback,
const FlossDeviceId& device) {
SetConnected(device.address, true);
PostDelayedTask(base::BindOnce(std::move(callback),
FlossDBusClient::BtifStatus::kSuccess));
}
void FakeFlossAdapterClient::DisconnectAllEnabledProfiles(
ResponseCallback<Void> callback,
const FlossDeviceId& device) {
SetConnected(device.address, false);
PostDelayedTask(base::BindOnce(std::move(callback), Void{}));
}
void FakeFlossAdapterClient::PostDelayedTask(base::OnceClosure callback) {
base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE, std::move(callback), base::Milliseconds(kDelayedTaskMs));
}
void FakeFlossAdapterClient::SetConnected(const std::string& address,
bool connected) {
if (base::Contains(connected_addresses_, address) == connected) {
return;
}
const auto device = ConvertAddressToDevice(address);
for (auto& observer : observers_) {
if (connected) {
connected_addresses_.insert(address);
observer.AdapterDeviceConnected(device);
} else {
connected_addresses_.erase(address);
observer.AdapterDeviceDisconnected(device);
}
}
}
void FakeFlossAdapterClient::NotifyObservers(
const base::RepeatingCallback<void(Observer*)>& notify) const {
for (auto& observer : observers_) {
notify.Run(&observer);
}
}
void FakeFlossAdapterClient::FailNextDiscovery() {
fail_discovery_ = std::make_optional(true);
}
void FakeFlossAdapterClient::FailNextBonding() {
fail_bonding_ = std::make_optional(true);
}
void FakeFlossAdapterClient::SetPairingConfirmation(
ResponseCallback<Void> callback,
const FlossDeviceId& device,
bool accept) {
for (auto& observer : observers_) {
observer.DeviceBondStateChanged(device, /*status=*/0,
FlossAdapterClient::BondState::kBonded);
}
bonded_addresses_.insert(device.address);
SetConnected(device.address, true);
PostDelayedTask(base::BindOnce(std::move(callback), Void{}));
}
void FakeFlossAdapterClient::SetPin(ResponseCallback<Void> callback,
const FlossDeviceId& device,
bool accept,
const std::vector<uint8_t>& pin) {
for (auto& observer : observers_) {
observer.DeviceBondStateChanged(device, /*status=*/0,
FlossAdapterClient::BondState::kBonded);
}
bonded_addresses_.insert(device.address);
SetConnected(device.address, true);
PostDelayedTask(base::BindOnce(std::move(callback), Void{}));
}
void FakeFlossAdapterClient::GetBondedDevices() {
for (const auto& addr : bonded_addresses_) {
const auto device_id = ConvertAddressToDevice(addr);
for (auto& observer : observers_) {
observer.AdapterFoundDevice(device_id);
}
}
}
void FakeFlossAdapterClient::GetConnectedDevices() {
for (const auto& addr : connected_addresses_) {
const auto device_id = ConvertAddressToDevice(addr);
for (auto& observer : observers_) {
observer.AdapterFoundDevice(device_id);
observer.AdapterDeviceConnected(device_id);
}
}
}
} // namespace floss