blob: abed801799ff37a36c8f00b3b048f744f4e78ad9 [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.
#pragma once
#include <string>
#include <type_traits>
#include <unordered_map>
#include "garnet/drivers/bluetooth/lib/common/byte_buffer.h"
#include "garnet/drivers/bluetooth/lib/common/device_address.h"
#include "garnet/drivers/bluetooth/lib/common/optional.h"
#include "garnet/drivers/bluetooth/lib/gap/gap.h"
#include "garnet/drivers/bluetooth/lib/hci/connection.h"
#include "garnet/drivers/bluetooth/lib/hci/lmp_feature_set.h"
#include "garnet/drivers/bluetooth/lib/sm/pairing_state.h"
#include "lib/fxl/macros.h"
namespace btlib {
namespace gap {
class RemoteDeviceCache;
// Represents a remote Bluetooth device that is known to the current system due
// to discovery and/or connection and bonding procedures. These devices can be
// LE-only, Classic-only, or dual-mode.
//
// Instances should not be created directly and must be obtained via a
// RemoteDeviceCache.
class RemoteDevice final {
public:
// TODO(armansito): Probably keep separate states for LE and BR/EDR.
enum class ConnectionState {
// No link exists between the local adapter and this device.
kNotConnected,
// The device is currently establishing a link or performing service
// discovery or encryption setup. In this state, a link may have been
// established but it is not ready to use yet.
kInitializing,
// Link setup, service discovery, and any encryption setup has completed
kConnected,
// Bonding procedures are in progress
kBonding,
// Bonded
kBonded
};
// 128-bit UUID that uniquely identifies this device on this system.
const std::string& identifier() const { return identifier_; }
// The Bluetooth technologies that are supported by this device.
TechnologyType technology() const { return technology_; }
// The known device address of this device.
// TODO(armansito):
// - For paired devices this should return the identity address.
// - For temporary devices this is the address that was seen in the
// advertisement.
// - For classic devices this the BD_ADDR.
const common::DeviceAddress& address() const { return address_; }
// Returns true if this is a connectable device.
bool connectable() const { return connectable_; }
// Returns the advertising data for this device (including any scan response
// data).
const common::BufferView advertising_data() const {
return advertising_data_buffer_.view(0, advertising_data_length_);
}
// Returns the most recently observed RSSI for this remote device. Returns
// hci::kRSSIInvalid if the value is unknown.
int8_t rssi() const { return rssi_; }
// Updates the advertising and scan response data for this device.
// |rssi| corresponds to the most recent advertisement RSSI.
// |advertising_data| should include any scan response data.
void SetLEAdvertisingData(int8_t rssi,
const common::ByteBuffer& advertising_data);
// Updates the device based on |inquiry_result|. Notifies listeners if |this|
// has changed in a significant way.
template <typename T>
typename std::enable_if<
std::is_same<T, hci::InquiryResult>::value ||
std::is_same<T, hci::InquiryResultRSSI>::value ||
std::is_same<T, hci::ExtendedInquiryResultEventParams>::value>::type
SetInquiryData(const T& inquiry_result);
// Updates the name of this device.
// If Advertising Data has been set, this must match any local name advertised
// in that data. (Bluetooth 5.0, Vol 2 E 6.23)
void SetName(const std::string& name);
// Gets the user-friendly name of the device, if it's known.
// This can be set by LE Advertising data as well as by SetName.
const common::Optional<std::string>& name() const { return name_; }
// Returns the most recently used connection parameters for this device.
// Returns nullptr if these values are unknown.
const hci::LEConnectionParameters* le_connection_params() const {
return le_conn_params_.value();
}
void set_le_connection_params(const hci::LEConnectionParameters& params) {
le_conn_params_ = params;
}
void set_ltk(const sm::LTK& key) { ltk_ = key; }
const common::Optional<sm::LTK> ltk() const { return ltk_; }
// Returns this device's preferred connection parameters, if known. LE
// peripherals report their preferred connection parameters using one of the
// GAP Connection Parameter Update procedures (e.g. L2CAP, Advertising, LL).
const hci::LEPreferredConnectionParameters* le_preferred_connection_params()
const {
return le_preferred_conn_params_.value();
}
void set_le_preferred_connection_params(
const hci::LEPreferredConnectionParameters& params) {
le_preferred_conn_params_ = params;
}
// The current LE connection state of this RemoteDevice.
ConnectionState le_connection_state() const { return le_connection_state_; }
void SetLEConnectionState(ConnectionState state);
// The current BR/EDR connection state of this RemoteDevice.
ConnectionState bredr_connection_state() const {
return bredr_connection_state_;
}
void SetBREDRConnectionState(ConnectionState state);
// A temporary device is one that is never persisted, such as
//
// 1. A device that has never been connected to;
// 2. A device that was connected but uses a Non-resolvable Private Address.
// 3. A device that was connected, uses a Resolvable Private Address, but
// the local host has no Identity Resolving Key for it.
//
// All other devices can be considered bonded.
bool temporary() const { return temporary_; }
// Marks this device as non-temporary. This operation may fail due to one of
// the conditions described above the |temporary()| method.
//
// TODO(armansito): Replace this with something more sophisticated when we
// implement bonding procedures. This method is here to remind us that these
// conditions are subtle and not fully supported yet.
bool TryMakeNonTemporary();
// Returns a string representation of this device.
std::string ToString() const;
// Returns the device class of this device, if it is known.
const common::Optional<common::DeviceClass>& device_class() const {
return device_class_;
}
// Returns the page scan repettion mode of this device, if known.
const common::Optional<hci::PageScanRepetitionMode>&
page_scan_repetition_mode() const {
return page_scan_repetition_mode_;
}
// Returns the clock offset reported by the device, if known and valid.
// The clock offset will have the highest-order bit set, and the rest
// represent bits 16-2 of CLKNslave-CLK.
const common::Optional<uint16_t>& clock_offset() const {
return clock_offset_;
}
// Returns the set of features of this device.
const hci::LMPFeatureSet& features() const { return lmp_features_; }
void SetFeaturePage(size_t page, uint64_t features) {
lmp_features_.SetPage(page, features);
}
void set_version(hci::HCIVersion version, uint16_t manufacturer,
uint16_t subversion) {
lmp_version_ = version;
lmp_manufacturer_ = manufacturer;
lmp_subversion_ = subversion;
}
const common::Optional<hci::HCIVersion>& version() const {
return lmp_version_;
}
private:
friend class RemoteDeviceCache;
using DeviceCallback = fit::function<void(const RemoteDevice&)>;
// TODO(armansito): Add constructor from persistent storage format.
// Caller must ensure that both callbacks are non-empty.
// Note that the ctor is only intended for use by RemoteDeviceCache.
// Expanding access would a) violate the constraint that all RemoteDevices
// are created through a RemoteDeviceCache, and b) introduce lifetime issues
// (do the callbacks outlive |this|?).
RemoteDevice(DeviceCallback notify_listeners_callback,
DeviceCallback update_expiry_callback,
const std::string& identifier,
const common::DeviceAddress& address, bool connectable);
// Updates the device based on extended inquiry response data.
// |bytes| contains the data from an ExtendedInquiryResponse event.
// Returns true if |this| was modified in a way that warrants notification to
// listeners.
bool SetExtendedInquiryResponse(const common::ByteBuffer& bytes);
// Updates the device based on type-specific inquiry data. Returns true if
// |this| was modified in a way that warrants notification to listeners.
bool SetSpecificInquiryData(const hci::InquiryResult& result);
bool SetSpecificInquiryData(const hci::InquiryResultRSSI& result);
bool SetSpecificInquiryData(
const hci::ExtendedInquiryResultEventParams& result);
DeviceCallback notify_listeners_callback_;
DeviceCallback update_expiry_callback_;
const std::string identifier_;
const common::DeviceAddress address_;
const TechnologyType technology_;
ConnectionState le_connection_state_;
ConnectionState bredr_connection_state_;
common::Optional<sm::LTK> ltk_;
common::Optional<std::string> name_;
bool connectable_;
bool temporary_;
int8_t rssi_;
common::Optional<common::DeviceClass> device_class_;
common::Optional<hci::PageScanRepetitionMode> page_scan_repetition_mode_;
common::Optional<uint16_t> clock_offset_;
common::Optional<hci::HCIVersion> lmp_version_;
uint16_t lmp_manufacturer_;
uint16_t lmp_subversion_;
hci::LMPFeatureSet lmp_features_;
// TODO(armansito): Store device name and remote features.
// TODO(armansito): Store discovered service UUIDs.
// TODO(armansito): Store an AdvertisingData structure rather than the raw
// payload.
size_t advertising_data_length_;
common::DynamicByteBuffer advertising_data_buffer_;
// TODO(jamuraa): Parse more of the Extended Inquiry Response fields
common::DynamicByteBuffer extended_inquiry_response_;
// Most recently used LE connection parameters. Has no value if this device
// has never been connected.
common::Optional<hci::LEConnectionParameters> le_conn_params_;
// Preferred LE connection parameters as reported by this device. Has no value
// if this parameter is unknown.
// TODO(armansito): Add a method for storing the preferred parameters.
common::Optional<hci::LEPreferredConnectionParameters>
le_preferred_conn_params_;
FXL_DISALLOW_COPY_AND_ASSIGN(RemoteDevice);
};
} // namespace gap
} // namespace btlib