blob: 16afcb088d8292ed10ee02ed79eb39fb17334b92 [file] [log] [blame]
// Copyright 2017 The Fuchsia 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 "remote_device_cache.h"
#include <fbl/function.h>
#include "garnet/drivers/bluetooth/lib/gap/remote_device.h"
#include "garnet/drivers/bluetooth/lib/hci/connection.h"
#include "garnet/drivers/bluetooth/lib/hci/low_energy_scanner.h"
#include "lib/async/default.h"
#include "lib/fxl/random/uuid.h"
#include "lib/zx/time.h"
namespace {
constexpr auto kCacheTimeout = zx::sec(60);
}
namespace btlib {
namespace gap {
RemoteDevice* RemoteDeviceCache::NewDevice(const common::DeviceAddress& address,
bool connectable) {
if (address_map_.find(address) != address_map_.end())
return nullptr;
auto* device = new RemoteDevice(
fit::bind_member(this, &RemoteDeviceCache::NotifyDeviceUpdated),
fit::bind_member(this, &RemoteDeviceCache::UpdateExpiry),
fxl::GenerateUUID(), address, connectable);
devices_.emplace(
std::piecewise_construct, std::forward_as_tuple(device->identifier()),
std::forward_as_tuple(std::unique_ptr<RemoteDevice>(device),
[this, device] { RemoveDevice(device); }));
address_map_[device->address()] = device->identifier();
UpdateExpiry(*device);
NotifyDeviceUpdated(*device);
return device;
}
bool RemoteDeviceCache::AddBondedDevice(std::string identifier,
const common::DeviceAddress& address,
const sm::LTK& key) {
if (FindDeviceById(identifier) || FindDeviceByAddress(address)) {
FXL_VLOG(1) << "RemoteDeviceCache: A device with the same identifier or "
"address is already present in the device cache";
return false;
}
auto* device = new RemoteDevice(
fit::bind_member(this, &RemoteDeviceCache::NotifyDeviceUpdated),
fit::bind_member(this, &RemoteDeviceCache::UpdateExpiry),
identifier, address, true);
devices_.emplace(
std::piecewise_construct, std::forward_as_tuple(device->identifier()),
std::forward_as_tuple(std::unique_ptr<RemoteDevice>(device),
[this, device] { RemoveDevice(device); }));
address_map_[device->address()] = device->identifier();
device->set_ltk(key);
NotifyDeviceUpdated(*device);
return true;
}
bool RemoteDeviceCache::StoreLTK(std::string device_id, const sm::LTK& key) {
FXL_VLOG(1) << "RemoteDeviceCache: StoreLTK";
auto device = FindDeviceById(device_id);
if (!device)
return false;
device->set_ltk(key);
NotifyDeviceBonded(*device);
return true;
}
RemoteDevice* RemoteDeviceCache::FindDeviceById(
const std::string& identifier) const {
auto iter = devices_.find(identifier);
return iter != devices_.end() ? iter->second.device() : nullptr;
}
RemoteDevice* RemoteDeviceCache::FindDeviceByAddress(
const common::DeviceAddress& address) const {
auto iter = address_map_.find(address);
if (iter == address_map_.end())
return nullptr;
auto* dev = FindDeviceById(iter->second);
FXL_DCHECK(dev);
return dev;
}
// Private methods below.
void RemoteDeviceCache::NotifyDeviceBonded(const RemoteDevice& device) {
FXL_DCHECK(devices_.find(device.identifier()) != devices_.end());
FXL_DCHECK(devices_.at(device.identifier()).device() == &device);
FXL_VLOG(1) << "RemoteDeviceCache: Bonding a device";
if (device_bonded_callback_)
device_bonded_callback_(device);
}
void RemoteDeviceCache::NotifyDeviceUpdated(const RemoteDevice& device) {
FXL_DCHECK(devices_.find(device.identifier()) != devices_.end());
FXL_DCHECK(devices_.at(device.identifier()).device() == &device);
if (device_updated_callback_)
device_updated_callback_(device);
}
void RemoteDeviceCache::UpdateExpiry(const RemoteDevice& device) {
auto device_record_iter = devices_.find(device.identifier());
FXL_DCHECK(device_record_iter != devices_.end());
auto& device_record = device_record_iter->second;
FXL_DCHECK(device_record.device() == &device);
const auto cancel_res = device_record.removal_task()->Cancel();
FXL_DCHECK(cancel_res == ZX_OK || cancel_res == ZX_ERR_NOT_FOUND);
if (!device.temporary() ||
device.le_connection_state() ==
RemoteDevice::ConnectionState::kConnected ||
device.bredr_connection_state() ==
RemoteDevice::ConnectionState::kConnected) {
return;
}
const auto schedule_res = device_record.removal_task()->PostDelayed(
async_get_default_dispatcher(), kCacheTimeout);
FXL_DCHECK(schedule_res == ZX_OK || schedule_res == ZX_ERR_BAD_STATE);
}
void RemoteDeviceCache::RemoveDevice(RemoteDevice* device) {
FXL_DCHECK(device);
auto device_record_it = devices_.find(device->identifier());
FXL_DCHECK(device_record_it != devices_.end());
FXL_DCHECK(device_record_it->second.device() == device);
const std::string identifier_copy = device->identifier();
address_map_.erase(device->address());
devices_.erase(device_record_it); // Destroys |device|.
if (device_removed_callback_) {
device_removed_callback_(identifier_copy);
}
}
} // namespace gap
} // namespace btlib