blob: f2e096bdada0cfd042d6dca6d4e50b538d550bbc [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.
#ifndef SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_GAP_PEER_CACHE_H_
#define SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_GAP_PEER_CACHE_H_
#include <fbl/function.h>
#include <fbl/macros.h>
#include <lib/async/cpp/task.h>
#include <unordered_map>
#include "src/connectivity/bluetooth/core/bt-host/common/device_address.h"
#include "src/connectivity/bluetooth/core/bt-host/gap/bonding_data.h"
#include "src/connectivity/bluetooth/core/bt-host/gap/identity_resolving_list.h"
#include "src/connectivity/bluetooth/core/bt-host/gap/peer.h"
#include "src/connectivity/bluetooth/core/bt-host/hci/connection.h"
#include "src/connectivity/bluetooth/core/bt-host/sm/types.h"
#include "src/lib/fxl/synchronization/thread_checker.h"
namespace bt {
class ByteBuffer;
namespace hci {
struct LowEnergyScanResult;
} // namespace hci
namespace gap {
// A PeerCache provides access to remote Bluetooth devices that are
// known to the system.
class PeerCache final {
public:
using PeerCallback = fit::function<void(const Peer& peer)>;
using PeerIdCallback = fit::function<void(PeerId identifier)>;
PeerCache() = default;
// Creates a new peer entry using the given parameters, and returns a
// (non-owning) pointer to that peer. The caller must not retain the pointer
// beyond the current dispatcher task, as the underlying Peer is owned
// by |this| PeerCache, and may be invalidated spontaneously.
//
// Returns nullptr if an entry matching |address| already exists in the cache,
// including as a public identity of a peer with a different technology.
Peer* NewPeer(const DeviceAddress& address, bool connectable);
// Iterates over all current peers in the map, running |f| on each entry
// synchronously. This is intended for IPC methods that request a list of
// peers.
//
// Clients should use the FindBy*() methods below to interact with
// Peer objects.
void ForEach(PeerCallback f);
// Creates a new non-temporary peer entry using the given |bd.identifier| and
// identity |bd.address|. This is intended to initialize this PeerCache
// with previously bonded peers while bootstrapping a bt-host peer. The
// "peer bonded" callback will not be invoked.
//
// This method is not intended for updating the bonding data of a peer that
// already exists the cache and returns false if a mapping for
// |bd. identifier| or |bd.address| is already present. Use Store*Bond()
// methods to update pairing information of an existing peer.
//
// If a peer already exists that has the same public identity address with a
// different technology, this method will return false. The existing peer
// should be instead updated with new bond information to create a dual-mode
// peer.
//
bool AddBondedPeer(BondingData bd);
// Update the peer with the given identifier with new LE bonding
// information. The peer will be considered "bonded" and the bonded callback
// will be notified. If the peer is already bonded then bonding data will be
// updated.
//
// If |bond_data| contains an |identity_address|, the peer cache will be
// updated with a new mapping from that address to this peer identifier. If
// the identity address already maps to an existing peer, this method will
// return false. TODO(armansito): Merge the peers instead of failing? What
// happens if we obtain a LE identity address from a dual-mode peer that
// matches the BD_ADDR previously obtained from it over BR/EDR?
bool StoreLowEnergyBond(PeerId identifier, const sm::PairingData& bond_data);
// Update a peer identified by BD_ADDR |address| with a new BR/EDR link key.
// The peer will be considered "bonded" and the bonded callback notified. If
// the peer is already bonded then the link key will be updated. Returns
// false if the address does not match that of a known peer.
bool StoreBrEdrBond(const DeviceAddress& address, const sm::LTK& link_key);
// Resets a peer |peer_id| to an unbonded state by removing secrets for its
// transports. The peer will become temporary and may expire. This does not
// otherwise disconnect the peer or remove its ID or address from the cache.
// Returns true if bonds were deleted from a known peer.
//
// TODO(BT-824): Delete the peer immediately after disconnecting. Will return
// true if a known peer was deleted or marked for deletion.
bool ForgetPeer(PeerId peer_id);
// Returns the remote peer with identifier |peer_id|. Returns nullptr if
// |peer_id| is not recognized.
Peer* FindById(PeerId peer_id) const;
// Finds and returns a Peer with address |address| if it exists,
// returns nullptr otherwise. Tries to resolve |address| if it is resolvable.
// If |address| is of type kBREDR or kLEPublic, then this searches for peers
// that have either type of address.
Peer* FindByAddress(const DeviceAddress& address) const;
// When set, |callback| will be invoked whenever a peer is added or updated.
void set_peer_updated_callback(PeerCallback callback) {
ZX_DEBUG_ASSERT(thread_checker_.IsCreationThreadCurrent());
peer_updated_callback_ = std::move(callback);
}
// When set, |callback| will be invoked whenever a peer is removed.
void set_peer_removed_callback(PeerIdCallback callback) {
ZX_DEBUG_ASSERT(thread_checker_.IsCreationThreadCurrent());
peer_removed_callback_ = std::move(callback);
}
// When this callback is set, |callback| will be invoked whenever the bonding
// data of a peer is updated and should be persisted. The caller must ensure
// that |callback| outlives |this|.
void set_peer_bonded_callback(PeerCallback callback) {
ZX_DEBUG_ASSERT(thread_checker_.IsCreationThreadCurrent());
peer_bonded_callback_ = std::move(callback);
}
// Returns the number of peers that are currently in the peer cache.
size_t count() const { return peers_.size(); }
private:
class PeerRecord final {
public:
PeerRecord(std::unique_ptr<Peer> peer, fbl::Closure remove_peer_callback)
: peer_(std::move(peer)),
removal_task_(std::move(remove_peer_callback)) {}
// The copy and move ctors cannot be implicitly defined, since
// async::TaskClosure does not support those operations. Nor is any
// meaningful explicit definition possible.
PeerRecord(const PeerRecord&) = delete;
PeerRecord(PeerRecord&&) = delete;
Peer* peer() const { return peer_.get(); }
// Returns a pointer to removal_task_, which can be used to (re-)schedule or
// cancel |remove_peer_callback|.
async::TaskClosure* removal_task() { return &removal_task_; }
private:
std::unique_ptr<Peer> peer_;
async::TaskClosure removal_task_;
};
// Create and track a record of a remote peer with a given |identifier|,
// |address|, and connectability (|connectable|). Returns a pointer to the
// inserted peer or nullptr if |identifier| or |address| already exists in
// the cache.
Peer* InsertPeerRecord(PeerId identifier, const DeviceAddress& address,
bool connectable);
// Notifies interested parties that |peer| has bonded
// |peer| must already exist in the cache.
void NotifyPeerBonded(const Peer& peer);
// Notifies interested parties that |peer| has seen a significant change.
// |peer| must already exist in the cache.
void NotifyPeerUpdated(const Peer& peer);
// Updates the expiration time for |peer|, if a temporary. Cancels expiry,
// if a non-temporary. Pre-conditions:
// - |peer| must already exist in the cache
// - can only be called from the thread that created |peer|
void UpdateExpiry(const Peer& peer);
// Updates the cache when an existing peer is found to be dual-mode. Also
// notifies listeners of the "peer updated" callback.
// |peer| must already exist in the cache.
void MakeDualMode(const Peer& peer);
// Removes |peer| from this cache, and notifies listeners of the removal.
void RemovePeer(Peer* peer);
// Search for an unique peer ID by its peer address |address|, by both
// technologies if it is a public address. |address| should be already
// resolved, if it is resolvable. If found, returns a valid peer ID;
// otherwise returns std::nullopt.
std::optional<PeerId> FindIdByAddress(const DeviceAddress& address) const;
// Mapping from unique peer IDs to PeerRecords.
// Owns the corresponding Peers.
std::unordered_map<PeerId, PeerRecord> peers_;
// Mapping from peer addresses to unique peer identifiers for all known
// peers. This is used to look-up and update existing cached data for a
// particular scan result so as to avoid creating duplicate entries for the
// same peer.
//
// Dual-mode peers shall have identity addresses of both technologies
// mapped to the same ID, if the addresses have the same value.
std::unordered_map<DeviceAddress, PeerId> address_map_;
// The LE identity resolving list used to resolve RPAs.
IdentityResolvingList le_resolving_list_;
PeerCallback peer_updated_callback_;
PeerIdCallback peer_removed_callback_;
PeerCallback peer_bonded_callback_;
fxl::ThreadChecker thread_checker_;
DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(PeerCache);
};
} // namespace gap
} // namespace bt
#endif // SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_GAP_PEER_CACHE_H_