blob: 861e4e1c8d982e03657da2c53c101b002402c5ac [file] [log] [blame]
// Copyright 2021 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.
use crate::config_management::{self};
use crate::util::historical_list::Timestamped;
use fuchsia_async::Time;
use wlan_common::bss::BssDescription;
use wlan_common::channel::Channel;
use wlan_common::security::SecurityAuthenticator;
use wlan_common::sequestered::Sequestered;
use wlan_metrics_registry::{
PolicyConnectionAttemptMigratedMetricDimensionReason,
PolicyDisconnectionMigratedMetricDimensionReason,
};
use {
fidl_fuchsia_wlan_internal as fidl_internal, fidl_fuchsia_wlan_policy as fidl_policy,
fidl_fuchsia_wlan_sme as fidl_sme, fuchsia_zircon as zx,
};
#[cfg(test)]
pub(crate) use crate::regulatory_manager::REGION_CODE_LEN;
pub type NetworkIdentifier = config_management::network_config::NetworkIdentifier;
pub type SecurityTypeDetailed = fidl_sme::Protection;
pub type SecurityType = config_management::network_config::SecurityType;
pub type ConnectionState = fidl_policy::ConnectionState;
pub type ClientState = fidl_policy::WlanClientState;
pub type DisconnectStatus = fidl_policy::DisconnectStatus;
pub type Compatibility = fidl_policy::Compatibility;
pub type WlanChan = wlan_common::channel::Channel;
pub type Cbw = wlan_common::channel::Cbw;
pub use ieee80211::{Bssid, Ssid};
pub type DisconnectReason = PolicyDisconnectionMigratedMetricDimensionReason;
pub type ConnectReason = PolicyConnectionAttemptMigratedMetricDimensionReason;
pub type ScanError = fidl_policy::ScanErrorCode;
pub fn convert_to_sme_disconnect_reason(
disconnect_reason: PolicyDisconnectionMigratedMetricDimensionReason,
) -> fidl_sme::UserDisconnectReason {
match disconnect_reason {
PolicyDisconnectionMigratedMetricDimensionReason::Unknown => {
fidl_sme::UserDisconnectReason::Unknown
}
PolicyDisconnectionMigratedMetricDimensionReason::FailedToConnect => {
fidl_sme::UserDisconnectReason::FailedToConnect
}
PolicyDisconnectionMigratedMetricDimensionReason::FidlConnectRequest => {
fidl_sme::UserDisconnectReason::FidlConnectRequest
}
PolicyDisconnectionMigratedMetricDimensionReason::FidlStopClientConnectionsRequest => {
fidl_sme::UserDisconnectReason::FidlStopClientConnectionsRequest
}
PolicyDisconnectionMigratedMetricDimensionReason::ProactiveNetworkSwitch => {
fidl_sme::UserDisconnectReason::ProactiveNetworkSwitch
}
PolicyDisconnectionMigratedMetricDimensionReason::DisconnectDetectedFromSme => {
fidl_sme::UserDisconnectReason::DisconnectDetectedFromSme
}
PolicyDisconnectionMigratedMetricDimensionReason::RegulatoryRegionChange => {
fidl_sme::UserDisconnectReason::RegulatoryRegionChange
}
PolicyDisconnectionMigratedMetricDimensionReason::Startup => {
fidl_sme::UserDisconnectReason::Startup
}
PolicyDisconnectionMigratedMetricDimensionReason::NetworkUnsaved => {
fidl_sme::UserDisconnectReason::NetworkUnsaved
}
PolicyDisconnectionMigratedMetricDimensionReason::NetworkConfigUpdated => {
fidl_sme::UserDisconnectReason::NetworkConfigUpdated
}
}
}
// An internal version of fidl_policy::ScanResult that can be cloned
// To avoid printing PII, only allow Debug in tests, runtime logging should use Display
#[cfg_attr(test, derive(Debug, PartialEq))]
#[derive(Clone)]
pub struct ScanResult {
/// Network properties used to distinguish between networks and to group
/// individual APs.
pub ssid: Ssid,
pub security_type_detailed: SecurityTypeDetailed,
/// Individual access points offering the specified network.
pub entries: Vec<Bss>,
/// Indication if the detected network is supported by the implementation.
pub compatibility: Compatibility,
}
// Only derive(Debug) in tests, we should never directly print this in non-test code
#[cfg_attr(test, derive(Debug, PartialOrd, Ord, Clone))]
#[derive(Hash, PartialEq, Eq)]
pub struct NetworkIdentifierDetailed {
pub ssid: Ssid,
pub security_type: SecurityTypeDetailed,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum ScanObservation {
Passive,
Active,
Unknown,
}
// An internal version of fidl_policy::Bss with extended information
// To avoid printing PII, only allow Debug in tests, runtime logging should use Display
#[cfg_attr(test, derive(Debug, PartialEq))]
#[derive(Clone)]
pub struct Bss {
/// MAC address for the AP interface.
pub bssid: Bssid,
/// Signal strength for the beacon/probe response.
pub signal: Signal,
/// Channel for this network.
pub channel: WlanChan,
/// Realtime timestamp for this scan result entry.
pub timestamp: zx::Time,
/// The scanning mode used to observe the BSS.
pub observation: ScanObservation,
/// Compatibility with this device's network stack.
pub compatibility: Option<wlan_common::scan::Compatibility>,
/// The BSS description with information that SME needs for connecting.
pub bss_description: Sequestered<fidl_internal::BssDescription>,
}
impl Bss {
pub fn is_compatible(&self) -> bool {
self.compatibility.is_some()
}
pub fn is_same_bssid_and_security(&self, other: &Bss) -> bool {
self.bssid == other.bssid && self.compatibility == other.compatibility
}
}
// TODO(https://fxbug.dev/42065250): Move this into `wlan_common::bss` and use it in place of signal fields
// in `BssDescription`.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Signal {
/// Calculated received signal strength for the beacon/probe response.
pub rssi_dbm: i8,
/// Signal to noise ratio for the beacon/probe response.
pub snr_db: i8,
}
impl From<fidl_internal::SignalReportIndication> for Signal {
fn from(ind: fidl_internal::SignalReportIndication) -> Signal {
Signal { rssi_dbm: ind.rssi_dbm, snr_db: ind.snr_db }
}
}
// For tracking the past signal reports
#[derive(Clone, Debug, PartialEq)]
pub struct TimestampedSignal {
pub signal: Signal,
pub time: Time,
}
impl Timestamped for TimestampedSignal {
fn time(&self) -> Time {
self.time
}
}
/// BSS information tracked by the client state machine.
///
/// While connected to an AP, some important BSS configuration may change, such as the channel and
/// signal quality statistics. `TrackedBss` provides fields for this configuration that are managed
/// by the client state machine.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct TrackedBss {
pub signal: Signal,
pub channel: Channel,
}
impl TrackedBss {
/// Snapshots a BSS description.
///
/// A snapshot copies configuration from the given BSS description into a `TrackedBss`.
pub fn snapshot(original: &BssDescription) -> Self {
TrackedBss {
signal: Signal { rssi_dbm: original.rssi_dbm, snr_db: original.snr_db },
channel: original.channel,
}
}
}
impl PartialEq<BssDescription> for TrackedBss {
fn eq(&self, bss: &BssDescription) -> bool {
// This implementation is robust in the face of changes to `BssDescription` and
// `TrackedBss`, but must copy fields. This could be deceptively expensive for an
// equivalence query if `TrackedBss` has many congruent fields with respect to
// `BssDescription`.
*self == TrackedBss::snapshot(bss)
}
}
impl PartialEq<TrackedBss> for BssDescription {
fn eq(&self, tracked: &TrackedBss) -> bool {
tracked == self
}
}
/// Candidate BSS observed in a scan.
#[derive(Debug, Clone)]
#[cfg_attr(test, derive(PartialEq))]
pub struct InternalSavedNetworkData {
pub has_ever_connected: bool,
pub recent_failures: Vec<config_management::ConnectFailure>,
pub past_connections:
config_management::HistoricalListsByBssid<config_management::PastConnectionData>,
}
#[derive(Clone)]
// To avoid printing PII, only allow Debug in tests, runtime logging should use Display
#[cfg_attr(test, derive(Debug, PartialEq))]
pub struct ScannedCandidate {
pub network: NetworkIdentifier,
pub security_type_detailed: SecurityTypeDetailed,
pub credential: config_management::Credential,
pub bss: Bss,
pub network_has_multiple_bss: bool,
pub authenticator: SecurityAuthenticator,
pub saved_network_info: InternalSavedNetworkData,
}
impl ScannedCandidate {
// Returns if the two candidates represent the same BSS, ignore scan time variables.
pub fn is_same_bss_security_and_credential(&self, other: &ScannedCandidate) -> bool {
self.network == other.network
&& self.security_type_detailed == other.security_type_detailed
&& self.credential == other.credential
&& self.bss.is_same_bssid_and_security(&other.bss)
}
}
/// Selected network candidate for a connection.
///
/// This type is a promotion of a scanned candidate and provides the necessary data required to
/// establish a connection.
#[derive(Clone)]
// To avoid printing PII, only allow Debug in tests, runtime logging should use Display
#[cfg_attr(test, derive(Debug))]
#[cfg_attr(test, derive(PartialEq))]
pub struct ConnectSelection {
pub target: ScannedCandidate,
pub reason: ConnectReason,
}
/// The state of a remote AP.
///
/// `ApState` describes the configuration of a BSS to which a client is connected. The state is
/// comprised of an initial BSS description as well as tracked configuration. The tracked
/// configuration may change while a client is connected and is managed by the client state
/// machine. The initial BSS description is immutable.
///
/// See `TrackedBss`.
#[derive(Clone, Debug, PartialEq)]
pub struct ApState {
/// The initial configuration of the BSS (e.g., as seen from a scan).
original: BssDescription,
/// Tracked BSS configuration.
///
/// This subset of the initial BSS description is managed by the client state machine and may
/// change while connected to an AP.
pub tracked: TrackedBss,
}
impl ApState {
/// Gets the initial BSS description for the AP to which a client is connected.
pub fn original(&self) -> &BssDescription {
&self.original
}
/// Returns `true` if the tracked BSS configuration differs from the initial BSS description.
pub fn has_changes(&self) -> bool {
self.original != self.tracked
}
}
impl From<BssDescription> for ApState {
fn from(original: BssDescription) -> Self {
let tracked = TrackedBss::snapshot(&original);
ApState { original, tracked }
}
}