blob: 6a2e73d6287c99591d9fed383598193e8e0973ab [file] [log] [blame]
// Copyright 2017 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/fido/ble/fido_ble_discovery.h"
#include <utility>
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/time/time.h"
#include "components/device_event_log/device_event_log.h"
#include "device/bluetooth/bluetooth_adapter.h"
#include "device/bluetooth/bluetooth_common.h"
#include "device/bluetooth/bluetooth_discovery_session.h"
#include "device/bluetooth/public/cpp/bluetooth_uuid.h"
#include "device/fido/ble/fido_ble_device.h"
#include "device/fido/ble/fido_ble_uuids.h"
#include "device/fido/fido_authenticator.h"
#include "device/fido/fido_constants.h"
#include "device/fido/fido_device_authenticator.h"
namespace device {
FidoBleDiscovery::FidoBleDiscovery()
: FidoBleDiscoveryBase(FidoTransportProtocol::kBluetoothLowEnergy) {}
FidoBleDiscovery::~FidoBleDiscovery() = default;
// static
const BluetoothUUID& FidoBleDiscovery::FidoServiceUUID() {
static const BluetoothUUID service_uuid(kFidoServiceUUID);
return service_uuid;
}
void FidoBleDiscovery::OnSetPowered() {
DCHECK(adapter());
FIDO_LOG(DEBUG) << "Adapter " << adapter()->GetAddress() << " is powered on.";
for (BluetoothDevice* device : adapter()->GetDevices()) {
if (!CheckForExcludedDeviceAndCacheAddress(device) &&
base::Contains(device->GetUUIDs(), FidoServiceUUID())) {
const auto& device_address = device->GetAddress();
FIDO_LOG(DEBUG) << "FIDO BLE device: " << device_address;
AddDevice(std::make_unique<FidoBleDevice>(adapter(), device_address));
CheckAndRecordDevicePairingModeOnDiscovery(
FidoBleDevice::GetIdForAddress(device_address));
}
}
auto filter = std::make_unique<BluetoothDiscoveryFilter>(
BluetoothTransport::BLUETOOTH_TRANSPORT_LE);
filter->AddUUID(FidoServiceUUID());
adapter()->StartDiscoverySessionWithFilter(
std::move(filter),
base::AdaptCallbackForRepeating(
base::BindOnce(&FidoBleDiscovery::OnStartDiscoverySessionWithFilter,
weak_factory_.GetWeakPtr())),
base::AdaptCallbackForRepeating(
base::BindOnce(&FidoBleDiscovery::OnStartDiscoverySessionError,
weak_factory_.GetWeakPtr())));
}
void FidoBleDiscovery::DeviceAdded(BluetoothAdapter* adapter,
BluetoothDevice* device) {
if (!CheckForExcludedDeviceAndCacheAddress(device) &&
base::Contains(device->GetUUIDs(), FidoServiceUUID())) {
const auto& device_address = device->GetAddress();
FIDO_LOG(DEBUG) << "Discovered FIDO BLE device: " << device_address;
AddDevice(std::make_unique<FidoBleDevice>(adapter, device_address));
CheckAndRecordDevicePairingModeOnDiscovery(
FidoBleDevice::GetIdForAddress(device_address));
}
}
void FidoBleDiscovery::DeviceChanged(BluetoothAdapter* adapter,
BluetoothDevice* device) {
if (CheckForExcludedDeviceAndCacheAddress(device) ||
!base::Contains(device->GetUUIDs(), FidoServiceUUID())) {
return;
}
auto authenticator_id = FidoBleDevice::GetIdForAddress(device->GetAddress());
auto* authenticator = GetAuthenticator(authenticator_id);
if (!authenticator) {
FIDO_LOG(DEBUG) << "Discovered FIDO service on existing BLE device: "
<< device->GetAddress();
AddDevice(std::make_unique<FidoBleDevice>(adapter, device->GetAddress()));
CheckAndRecordDevicePairingModeOnDiscovery(std::move(authenticator_id));
return;
}
if (authenticator->device()->IsInPairingMode()) {
RecordDevicePairingStatus(std::move(authenticator_id),
PairingModeChangeType::kUnobserved);
}
}
void FidoBleDiscovery::DeviceRemoved(BluetoothAdapter* adapter,
BluetoothDevice* device) {
if (base::Contains(device->GetUUIDs(), FidoServiceUUID())) {
FIDO_LOG(DEBUG) << "FIDO BLE device removed: " << device->GetAddress();
auto device_id = FidoBleDevice::GetIdForAddress(device->GetAddress());
RemoveDevice(device_id);
RemoveDeviceFromPairingTracker(device_id);
}
}
void FidoBleDiscovery::AdapterPoweredChanged(BluetoothAdapter* adapter,
bool powered) {
// If Bluetooth adapter is powered on, resume scanning for nearby FIDO
// devices. Previously inactive discovery sessions would be terminated upon
// invocation of OnSetPowered().
if (powered)
OnSetPowered();
}
void FidoBleDiscovery::DeviceAddressChanged(BluetoothAdapter* adapter,
BluetoothDevice* device,
const std::string& old_address) {
auto previous_device_id = FidoBleDevice::GetIdForAddress(old_address);
auto new_device_id = FidoBleDevice::GetIdForAddress(device->GetAddress());
auto it = authenticators_.find(previous_device_id);
if (it == authenticators_.end())
return;
it = authenticators_.find(new_device_id);
// Don't proceed if new_device_id is already in the map, which indicates
// a collision in addresses.
if (it != authenticators_.end())
return;
FIDO_LOG(DEBUG)
<< "Discovered FIDO BLE device address change from old address : "
<< old_address << " to new address : " << device->GetAddress();
auto change_map_keys = [&](auto* map) {
auto it = map->find(previous_device_id);
if (it != map->end()) {
map->emplace(new_device_id, std::move(it->second));
map->erase(it);
}
};
change_map_keys(&authenticators_);
change_map_keys(&pairing_mode_device_tracker_);
if (observer()) {
observer()->AuthenticatorIdChanged(this, previous_device_id,
std::move(new_device_id));
}
}
bool FidoBleDiscovery::CheckForExcludedDeviceAndCacheAddress(
const BluetoothDevice* device) {
std::string device_address = device->GetAddress();
auto address_position =
excluded_cable_device_addresses_.lower_bound(device_address);
if (address_position != excluded_cable_device_addresses_.end() &&
*address_position == device_address) {
return true;
}
// IsCableDevice() is not stable, and can change throughout the lifetime. As
// so, cache device address for known Cable devices so that we do not attempt
// to connect to these devices.
if (IsCableDevice(device)) {
excluded_cable_device_addresses_.insert(address_position,
std::move(device_address));
return true;
}
return false;
}
void FidoBleDiscovery::CheckAndRecordDevicePairingModeOnDiscovery(
std::string authenticator_id) {
auto* authenticator = GetAuthenticator(authenticator_id);
DCHECK(authenticator);
if (authenticator->device()->IsInPairingMode()) {
RecordDevicePairingStatus(std::move(authenticator_id),
PairingModeChangeType::kObserved);
}
}
void FidoBleDiscovery::RecordDevicePairingStatus(std::string device_id,
PairingModeChangeType type) {
auto it = pairing_mode_device_tracker_.find(device_id);
if (it != pairing_mode_device_tracker_.end()) {
it->second->Reset();
return;
}
if (observer() && type == PairingModeChangeType::kUnobserved) {
observer()->AuthenticatorPairingModeChanged(this, device_id,
true /* is_in_pairing_mode */);
}
auto pairing_mode_timer = std::make_unique<base::OneShotTimer>();
pairing_mode_timer->Start(
FROM_HERE, kBleDevicePairingModeWaitingInterval,
base::BindOnce(&FidoBleDiscovery::RemoveDeviceFromPairingTracker,
weak_factory_.GetWeakPtr(), device_id));
pairing_mode_device_tracker_.emplace(std::move(device_id),
std::move(pairing_mode_timer));
}
void FidoBleDiscovery::RemoveDeviceFromPairingTracker(
const std::string& device_id) {
// Destroying the timer stops the timer scheduled task.
pairing_mode_device_tracker_.erase(device_id);
if (observer()) {
observer()->AuthenticatorPairingModeChanged(this, device_id,
false /* is_in_pairing_mode */);
}
}
} // namespace device