chromium/ash/webui/diagnostics_ui/backend/connectivity/network_health_provider.cc

// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ash/webui/diagnostics_ui/backend/connectivity/network_health_provider.h"

#include <string>
#include <utility>

#include "ash/constants/ash_features.h"
#include "ash/system/diagnostics/diagnostics_log_controller.h"
#include "ash/system/diagnostics/networking_log.h"
#include "ash/webui/diagnostics_ui/backend/common/histogram_util.h"
#include "base/containers/contains.h"
#include "base/containers/fixed_flat_map.h"
#include "base/functional/bind.h"
#include "base/strings/string_util.h"
#include "base/uuid.h"
#include "chromeos/ash/services/network_config/in_process_instance.h"
#include "chromeos/services/network_config/public/cpp/cros_network_config_util.h"
#include "mojo/public/cpp/bindings/enum_traits.h"

namespace ash {
namespace diagnostics {

namespace {

namespace network_mojom = ::chromeos::network_config::mojom;

bool IsSupportedNetworkType(network_mojom::NetworkType type) {
  switch (type) {
    case network_mojom::NetworkType::kWiFi:
    case network_mojom::NetworkType::kCellular:
    case network_mojom::NetworkType::kEthernet:
      return true;
    case network_mojom::NetworkType::kMobile:
    case network_mojom::NetworkType::kTether:
    case network_mojom::NetworkType::kVPN:
    case network_mojom::NetworkType::kAll:
    case network_mojom::NetworkType::kWireless:
      return false;
  }
}

bool IsConnectedOrConnecting(
    network_mojom::ConnectionStateType connection_state) {
  switch (connection_state) {
    case network_mojom::ConnectionStateType::kOnline:
    case network_mojom::ConnectionStateType::kConnected:
    case network_mojom::ConnectionStateType::kPortal:
    case network_mojom::ConnectionStateType::kConnecting:
      return true;
    case network_mojom::ConnectionStateType::kNotConnected:
      return false;
  }
}

bool IsConnectedOrOnline(mojom::NetworkState state) {
  switch (state) {
    case mojom::NetworkState::kOnline:
    case mojom::NetworkState::kConnected:
    case mojom::NetworkState::kPortal:
      return true;
    case mojom::NetworkState::kNotConnected:
    case mojom::NetworkState::kConnecting:
    case mojom::NetworkState::kDisabled:
      return false;
  }
}

constexpr mojom::NetworkState ConnectionStateToNetworkState(
    network_mojom::ConnectionStateType connection_state) {
  switch (connection_state) {
    case network_mojom::ConnectionStateType::kOnline:
      return mojom::NetworkState::kOnline;
    case network_mojom::ConnectionStateType::kConnected:
      return mojom::NetworkState::kConnected;
    case network_mojom::ConnectionStateType::kPortal:
      return mojom::NetworkState::kPortal;
    case network_mojom::ConnectionStateType::kConnecting:
      return mojom::NetworkState::kConnecting;
    case network_mojom::ConnectionStateType::kNotConnected:
      return mojom::NetworkState::kNotConnected;
  }
}

bool DeviceIsDisabledOrDisabling(network_mojom::DeviceStateType device_state) {
  return device_state == network_mojom::DeviceStateType::kDisabled ||
         device_state == network_mojom::DeviceStateType::kDisabling;
}

constexpr mojom::NetworkState CombineNetworkStates(
    network_mojom::ConnectionStateType connection_state,
    network_mojom::DeviceStateType device_state) {
  if (DeviceIsDisabledOrDisabling(device_state)) {
    return mojom::NetworkState::kDisabled;
  }

  return ConnectionStateToNetworkState(connection_state);
}

constexpr mojom::NetworkType ConvertNetworkType(
    network_mojom::NetworkType type) {
  switch (type) {
    case network_mojom::NetworkType::kWiFi:
      return mojom::NetworkType::kWiFi;
    case network_mojom::NetworkType::kCellular:
      return mojom::NetworkType::kCellular;
    case network_mojom::NetworkType::kEthernet:
      return mojom::NetworkType::kEthernet;
    case network_mojom::NetworkType::kMobile:
    case network_mojom::NetworkType::kTether:
    case network_mojom::NetworkType::kVPN:
    case network_mojom::NetworkType::kAll:
    case network_mojom::NetworkType::kWireless:
      return mojom::NetworkType::kUnsupported;
  }
}

mojom::SecurityType ConvertSecurityType(network_mojom::SecurityType type) {
  return mojo::EnumTraits<mojom::SecurityType,
                          network_mojom::SecurityType>::ToMojom(type);
}

mojom::AuthenticationType ConvertAuthenticationType(
    network_mojom::AuthenticationType type) {
  return mojo::EnumTraits<mojom::AuthenticationType,
                          network_mojom::AuthenticationType>::ToMojom(type);
}

mojom::IPConfigPropertiesPtr CreateIPConfigProperties(
    const network_mojom::IPConfigPropertiesPtr& ip_config_props) {
  mojom::IPConfigPropertiesPtr ip_config = mojom::IPConfigProperties::New();
  ip_config->ip_address = ip_config_props->ip_address;
  ip_config->gateway = ip_config_props->gateway;
  ip_config->name_servers = ip_config_props->name_servers;
  // Default to 0 means an unset value.
  ip_config->routing_prefix = 0;
  auto routing_prefix = ip_config_props->routing_prefix;
  bool isIPv4 = ip_config_props->type == network_mojom::IPConfigType::kIPv4;
  if (isnan(routing_prefix)) {
    LOG(ERROR) << "routing_prefix is not a number.";
    EmitNetworkDataError(metrics::DataError::kNotANumber);
  } else if (isIPv4 && (routing_prefix < 0 || routing_prefix > 32)) {
    LOG(ERROR) << "IPv4 routing_prefix should be larger/equal to zero and "
                  "smaller/equal to 32. It is now: "
               << routing_prefix;
    EmitNetworkDataError(metrics::DataError::kExpectationNotMet);
  } else if (!isIPv4 && (routing_prefix < 0 || routing_prefix > 128)) {
    // TODO(wenyu): Add unit test to cover this scenario.
    LOG(ERROR) << "IPv6 routing_prefix should be larger/equal to zero and "
                  "smaller/equal to 128. It is now: "
               << routing_prefix;
    EmitNetworkDataError(metrics::DataError::kExpectationNotMet);
  } else {
    ip_config->routing_prefix = routing_prefix;
  }
  return ip_config;
}

mojom::WiFiStatePropertiesPtr CreateWiFiStateProperties(
    const network_mojom::NetworkTypeStateProperties& network_type_props) {
  auto wifi_props = mojom::WiFiStateProperties::New();
  wifi_props->signal_strength = network_type_props.get_wifi()->signal_strength;
  wifi_props->frequency = network_type_props.get_wifi()->frequency;
  wifi_props->ssid = network_type_props.get_wifi()->ssid;
  wifi_props->bssid = base::ToUpperASCII(network_type_props.get_wifi()->bssid);
  wifi_props->security =
      ConvertSecurityType(network_type_props.get_wifi()->security);
  return wifi_props;
}

mojom::EthernetStatePropertiesPtr CreateEthernetStateProperties(
    const network_mojom::NetworkTypeStateProperties& network_type_props) {
  auto ethernet_props = mojom::EthernetStateProperties::New();
  ethernet_props->authentication = ConvertAuthenticationType(
      network_type_props.get_ethernet()->authentication);
  return ethernet_props;
}

mojom::CellularStatePropertiesPtr CreateCellularStateProperties(
    const network_mojom::NetworkTypeStateProperties& network_type_props) {
  auto cellular_props = mojom::CellularStateProperties::New();
  cellular_props->iccid = network_type_props.get_cellular()->iccid;
  cellular_props->eid = network_type_props.get_cellular()->eid;
  cellular_props->network_technology =
      network_type_props.get_cellular()->network_technology;
  cellular_props->roaming = network_type_props.get_cellular()->roaming;
  cellular_props->signal_strength =
      network_type_props.get_cellular()->signal_strength;
  return cellular_props;
}

bool IsMatchingDevice(const network_mojom::DeviceStatePropertiesPtr& device,
                      const mojom::NetworkPtr& network) {
  return ConvertNetworkType(device->type) == network->type;
}

// When |must_match_existing_guid| is true a network will only match if the
// backend network guid matches. This is to perform state updates to already
// known networks. When false, the network will match to a device/interface
// with the same type which allows rebinding a new network to a
// device/interface.
bool IsMatchingNetwork(
    const network_mojom::NetworkStatePropertiesPtr& backend_network,
    const NetworkObserverInfo& network_info,
    bool must_match_existing_guid) {
  const bool types_match =
      ConvertNetworkType(backend_network->type) == network_info.network->type;
  if (!must_match_existing_guid) {
    return types_match;
  }

  const std::string& network_guid = network_info.network_guid;
  if (IsConnectedOrConnecting(backend_network->connection_state)) {
    return types_match &&
           (network_guid.empty() || (network_guid == backend_network->guid));
  }

  return types_match && (network_guid == backend_network->guid);
}

bool ClearDisconnectedNetwork(NetworkObserverInfo* network_info) {
  mojom::Network* network = network_info->network.get();
  network_info->network_guid.clear();
  network->ip_config = nullptr;
  network->type_properties = nullptr;
  return true;
}

mojom::RoamingState GetRoamingState(std::optional<std::string>& roaming_state) {
  if (!roaming_state.has_value()) {
    return mojom::RoamingState::kNone;
  }

  std::string state = roaming_state.value();
  // Possible values are 'Home' and 'Roaming'.
  DCHECK(state == "Home" || state == "Roaming");
  return state == "Home" ? mojom::RoamingState::kHome
                         : mojom::RoamingState::kRoaming;
}

constexpr mojom::LockType GetLockType(const std::string& lock_type) {
  // Possible values are 'sim-pin', 'sim-puk', 'network-pin' or empty.
  if (lock_type.empty()) {
    return mojom::LockType::kNone;
  }

  DCHECK(lock_type == "sim-pin" || lock_type == "sim-puk" ||
         lock_type == "network-pin");

  if (lock_type == "sim-pin") {
    return mojom::LockType::kSimPin;
  }
  if (lock_type == "sim-puk") {
    return mojom::LockType::kSimPuk;
  }
  if (lock_type == "network-pin") {
    return mojom::LockType::kNetworkPin;
  }
  return mojom::LockType::kNone;
}

void UpdateNetwork(
    const network_mojom::NetworkTypeStateProperties& network_type_props,
    mojom::Network* network) {
  switch (network->type) {
    case mojom::NetworkType::kWiFi:
      network->type_properties = mojom::NetworkTypeProperties::NewWifi(
          CreateWiFiStateProperties(network_type_props));
      break;
    case mojom::NetworkType::kEthernet:
      network->type_properties = mojom::NetworkTypeProperties::NewEthernet(
          CreateEthernetStateProperties(network_type_props));
      break;
    case mojom::NetworkType::kCellular: {
      auto cellular_props = CreateCellularStateProperties(network_type_props);
      // If we have existing Cellular type properties, i.e., Sim Lock Status
      // was present for this network, combine the newly created Cellular
      // properties with the existing ones.
      if (network->type_properties) {
        cellular_props->lock_type =
            network->type_properties->get_cellular()->lock_type;
        cellular_props->sim_locked =
            network->type_properties->get_cellular()->sim_locked;
        cellular_props->roaming_state =
            network->type_properties->get_cellular()->roaming_state;
      }
      network->type_properties =
          mojom::NetworkTypeProperties::NewCellular(std::move(cellular_props));
      break;
    }
    case mojom::NetworkType::kUnsupported:
      NOTREACHED();
  }
}

void UpdateNetwork(
    const network_mojom::NetworkStatePropertiesPtr& network_state,
    NetworkObserverInfo* network_info) {
  mojom::Network* network = network_info->network.get();
  network->name = network_state->name;
  network->state = CombineNetworkStates(network_state->connection_state,
                                        network_info->device_state);

  // Network type must be populated before calling UpdateNetwork.
  network->type = ConvertNetworkType(network_state->type);

  if (IsConnectedOrConnecting(network_state->connection_state)) {
    network_info->network_guid = network_state->guid;
    UpdateNetwork(*network_state->type_state, network);
    return;
  }

  ClearDisconnectedNetwork(network_info);
}

void CreateEmptyCellularPropertiesForNetwork(mojom::Network* network) {
  auto cellular_props = mojom::CellularStateProperties::New();
  network->type_properties =
      mojom::NetworkTypeProperties::NewCellular(std::move(cellular_props));
}

void UpdateNetwork(const network_mojom::DeviceStatePropertiesPtr& device,
                   NetworkObserverInfo* network_info) {
  mojom::Network* network = network_info->network.get();
  DCHECK(!network->observer_guid.empty());

  network->type = ConvertNetworkType(device->type);
  network->mac_address = device->mac_address;
  network_info->device_state = device->device_state;
  if (network->type == mojom::NetworkType::kCellular &&
      device->sim_lock_status) {
    // Create partially populated CellularStateProperties struct.
    // Remaining properties will be added in CreateCellularStateProperties.
    CreateEmptyCellularPropertiesForNetwork(network);
    DCHECK(network->type_properties->is_cellular());
    network->type_properties->get_cellular()->lock_type =
        GetLockType(device->sim_lock_status->lock_type);
    network->type_properties->get_cellular()->sim_locked =
        device->sim_lock_status->lock_enabled;
  }

  if (DeviceIsDisabledOrDisabling(device->device_state)) {
    network->state = mojom::NetworkState::kDisabled;
    ClearDisconnectedNetwork(network_info);
  }
}

void UpdateNetwork(
    const network_mojom::ManagedPropertiesPtr& managed_properties,
    NetworkObserverInfo* network_info) {
  DCHECK(network_info);
  mojom::Network* network = network_info->network.get();

  const bool has_ip_config =
      managed_properties && managed_properties->ip_configs.has_value();
  if (has_ip_config) {
    const int ip_configs_len = managed_properties->ip_configs.value().size();
    DCHECK(ip_configs_len >= 1);
    if (ip_configs_len > 1) {
      LOG(WARNING) << "More than one entry in ManagedProperties' ip_configs "
                      "array, selecting the first, IP config count is: "
                   << ip_configs_len;
    }

    // TODO(zentaro): Investigate IPV6.
    auto ip_config = managed_properties->ip_configs.value()[0].Clone();
    network->ip_config = CreateIPConfigProperties(ip_config);
  }

  if (network->type == mojom::NetworkType::kCellular && managed_properties) {
    auto roaming_state = GetRoamingState(
        managed_properties->type_properties->get_cellular()->roaming_state);
    if (!network->type_properties) {
      CreateEmptyCellularPropertiesForNetwork(network);
    }
    DCHECK(network->type_properties->is_cellular());
    network->type_properties->get_cellular()->roaming_state = roaming_state;
  }
}

// Calculate a score for a network based on its type and state.
// Network state takes precedence over network type. In the case
// of a tie (Ethernet Connected & WiFi Connected for ex), the network
// type considered to have the higher priority will take precedence.
int GetScoreForNetwork(const mojom::NetworkPtr& network) {
  static constexpr auto kNetworkStatePriorityMap =
      base::MakeFixedFlatMap<mojom::NetworkState, int>(
          {{mojom::NetworkState::kOnline, 300},
           {mojom::NetworkState::kPortal, 200},
           {mojom::NetworkState::kConnected, 100}});

  static constexpr auto kNetworkTypePriorityMap =
      base::MakeFixedFlatMap<mojom::NetworkType, int>(
          {{mojom::NetworkType::kEthernet, 3},
           {mojom::NetworkType::kWiFi, 2},
           {mojom::NetworkType::kCellular, 1}});

  int state_priority = 0;
  if (base::Contains(kNetworkStatePriorityMap, network->state)) {
    state_priority += kNetworkStatePriorityMap.at(network->state);
  }

  DCHECK(base::Contains(kNetworkTypePriorityMap, network->type));
  return kNetworkTypePriorityMap.at(network->type) + state_priority;
}

bool IsLoggingEnabled() {
  return diagnostics::DiagnosticsLogController::IsInitialized();
}

}  // namespace

NetworkObserverInfo::NetworkObserverInfo() = default;
NetworkObserverInfo::NetworkObserverInfo(NetworkObserverInfo&&) = default;
NetworkObserverInfo& NetworkObserverInfo::operator=(NetworkObserverInfo&&) =
    default;
NetworkObserverInfo::~NetworkObserverInfo() = default;

NetworkHealthProvider::NetworkHealthProvider() {
  network_config::BindToInProcessInstance(
      remote_cros_network_config_.BindNewPipeAndPassReceiver());
  remote_cros_network_config_->AddObserver(
      cros_network_config_observer_receiver_.BindNewPipeAndPassRemote());

  // Get initial device/interface state.
  GetDeviceState();
}

NetworkHealthProvider::~NetworkHealthProvider() = default;

void NetworkHealthProvider::ObserveNetworkList(
    mojo::PendingRemote<mojom::NetworkListObserver> observer) {
  // Add the observer, then fire it immediately.
  network_list_observers_.Add(std::move(observer));
  NotifyNetworkListObservers();
}

void NetworkHealthProvider::ObserveNetwork(
    mojo::PendingRemote<mojom::NetworkStateObserver> observer,
    const std::string& observer_guid) {
  auto iter = networks_.find(observer_guid);
  if (iter == networks_.end()) {
    LOG(WARNING)
        << "Ignoring request to observe network that does not exist. guid="
        << observer_guid;
    return;
  }

  // Add the observer, then fire it immediately.
  iter->second.observer =
      mojo::Remote<mojom::NetworkStateObserver>(std::move(observer));
  NotifyNetworkStateObserver(iter->second);
}

void NetworkHealthProvider::OnDeviceStateListChanged() {
  GetDeviceState();
}

void NetworkHealthProvider::OnActiveNetworksChanged(
    std::vector<network_mojom::NetworkStatePropertiesPtr> active_networks) {
  OnActiveNetworkStateListReceived(std::move(active_networks));
}

void NetworkHealthProvider::OnNetworkStateChanged(
    network_mojom::NetworkStatePropertiesPtr network) {
  UpdateMatchingNetwork(std::move(network), /*must_match_existing_guid=*/true);
}

void NetworkHealthProvider::OnActiveNetworkStateListReceived(
    std::vector<network_mojom::NetworkStatePropertiesPtr> networks) {
  for (auto& network : networks) {
    UpdateMatchingNetwork(std::move(network),
                          /*must_match_existing_guid=*/false);
  }
}

void NetworkHealthProvider::UpdateMatchingNetwork(
    network_mojom::NetworkStatePropertiesPtr network,
    bool must_match_existing_guid) {
  NetworkObserverInfo* network_info =
      LookupNetwork(network, must_match_existing_guid);
  if (!network_info) {
    if (!must_match_existing_guid) {
      LOG(WARNING) << "Ignoring network " << network->guid
                   << " without matching interface. Network count is "
                   << networks_.size();
    }

    return;
  }

  UpdateNetwork(network, network_info);

  // Get the managed properties using the network guid, and pass the observer
  // guid.
  GetManagedPropertiesForNetwork(network->guid,
                                 network_info->network->observer_guid);
}

void NetworkHealthProvider::OnDeviceStateListReceived(
    std::vector<network_mojom::DeviceStatePropertiesPtr> devices) {
  base::flat_set<std::string> networks_seen;

  // Iterate all devices. If the device is already known, then update it's
  // state, otherwise add the new device to networks_. Any device no longer
  // present in devices will be removed from networks_.
  for (auto& device : devices) {
    if (!IsSupportedNetworkType(device->type)) {
      continue;
    }

    // Find a matching entry in networks_ for device and update it if present.
    bool matched = false;
    for (auto& pair : networks_) {
      const std::string& observer_guid = pair.first;
      NetworkObserverInfo& info = pair.second;
      mojom::NetworkPtr& network = info.network;

      // If this device is already known, update it's state.
      if (IsMatchingDevice(device, network)) {
        networks_seen.insert(observer_guid);
        matched = true;
        UpdateNetwork(device, &info);
        break;
      }
    }

    // If a matching entry could not be found, add a new entry to |networks_|.
    if (!matched) {
      std::string observer_guid = AddNewNetwork(device);
      networks_seen.insert(std::move(observer_guid));
    }
  }

  // Remove any entry in |networks_| that doesn't match a device.
  for (auto it = networks_.begin(); it != networks_.end();) {
    const std::string& observer_guid = it->first;
    if (!base::Contains(networks_seen, observer_guid)) {
      it = networks_.erase(it);
      continue;
    }

    ++it;
  }

  // Trigger a request for the list of active networks to get state updates
  // to the current list of devices/interfaces.
  GetActiveNetworkState();

  // Update the currently active network and fire the network list observer.
  NotifyNetworkListObservers();
}

std::string NetworkHealthProvider::AddNewNetwork(
    const network_mojom::DeviceStatePropertiesPtr& device) {
  std::string observer_guid =
      base::Uuid::GenerateRandomV4().AsLowercaseString();
  auto network = mojom::Network::New();
  network->observer_guid = observer_guid;

  // Initial state set to kNotConnected.
  network->state = mojom::NetworkState::kNotConnected;

  NetworkObserverInfo info;
  info.network = std::move(network);
  UpdateNetwork(device, &info);

  networks_.emplace(observer_guid, std::move(info));
  return observer_guid;
}

NetworkObserverInfo* NetworkHealthProvider::LookupNetwork(
    const network_mojom::NetworkStatePropertiesPtr& network,
    bool must_match_existing_guid) {
  for (auto& pair : networks_) {
    NetworkObserverInfo& info = pair.second;
    if (IsMatchingNetwork(network, info, must_match_existing_guid)) {
      return &info;
    }
  }

  return nullptr;
}

std::vector<std::string>
NetworkHealthProvider::GetObserverGuidsAndUpdateActiveGuid() {
  std::vector<std::string> observer_guids;
  observer_guids.reserve(networks_.size());
  for (auto& pair : networks_) {
    observer_guids.push_back(pair.first);
  }

  // Sort list of observer guids in descending order based on score.
  sort(observer_guids.begin(), observer_guids.end(),
       [&](const std::string& lhs, const std::string& rhs) -> bool {
         mojom::NetworkPtr& network1 = networks_.at(lhs).network;
         mojom::NetworkPtr& network2 = networks_.at(rhs).network;
         return GetScoreForNetwork(network1) > GetScoreForNetwork(network2);
       });

  // Update |active_guid_| if the observer guid with the highest score
  // corresponds to a network interface that is either online or connected.
  std::string new_active_guid;
  if (observer_guids.size() > 0) {
    const std::string& guid = observer_guids[0];
    if (IsConnectedOrOnline(GetNetworkStateForGuid(guid))) {
      new_active_guid = guid;
    }
  }

  active_guid_ = std::move(new_active_guid);
  return observer_guids;
}

void NetworkHealthProvider::GetManagedPropertiesForNetwork(
    const std::string& network_guid,
    const std::string& observer_guid) {
  remote_cros_network_config_->GetManagedProperties(
      network_guid,
      base::BindOnce(&NetworkHealthProvider::OnManagedPropertiesReceived,
                     base::Unretained(this), observer_guid));
}

void NetworkHealthProvider::OnManagedPropertiesReceived(
    const std::string& observer_guid,
    network_mojom::ManagedPropertiesPtr managed_properties) {
  auto iter = networks_.find(observer_guid);
  if (iter == networks_.end()) {
    LOG(WARNING) << "Ignoring network that no longer exists guid="
                 << observer_guid;
    return;
  }

  UpdateNetwork(managed_properties, &iter->second);

  // In case the new update to the network changed which network is the primary
  // then update the network list observers as well. This can happen if for
  // example, a WiFi network is currently the primary and then an ethernet
  // network becomes connected.
  NotifyNetworkListObservers();
  NotifyNetworkStateObserver(iter->second);
}

void NetworkHealthProvider::BindInterface(
    mojo::PendingReceiver<mojom::NetworkHealthProvider> pending_receiver) {
  receiver_.reset();
  receiver_.Bind(std::move(pending_receiver));
}

void NetworkHealthProvider::NotifyNetworkListObservers() {
  std::vector<std::string> observer_guids =
      GetObserverGuidsAndUpdateActiveGuid();
  for (auto& observer : network_list_observers_) {
    observer->OnNetworkListChanged(mojo::Clone(observer_guids), active_guid_);
  }

  if (IsLoggingEnabled() && !active_guid_.empty()) {
    DiagnosticsLogController::Get()->GetNetworkingLog().UpdateNetworkList(
        observer_guids, active_guid_);
  }
}

void NetworkHealthProvider::NotifyNetworkStateObserver(
    const NetworkObserverInfo& network_info) {
  if (!network_info.observer) {
    return;
  }

  network_info.observer->OnNetworkStateChanged(
      mojo::Clone(network_info.network));

  if (IsLoggingEnabled()) {
    DiagnosticsLogController::Get()->GetNetworkingLog().UpdateNetworkState(
        network_info.network.Clone());
  }
}

void NetworkHealthProvider::GetActiveNetworkState() {
  remote_cros_network_config_->GetNetworkStateList(
      network_mojom::NetworkFilter::New(network_mojom::FilterType::kActive,
                                        network_mojom::NetworkType::kAll,
                                        network_mojom::kNoLimit),
      base::BindOnce(&NetworkHealthProvider::OnActiveNetworkStateListReceived,
                     base::Unretained(this)));
}

void NetworkHealthProvider::GetDeviceState() {
  remote_cros_network_config_->GetDeviceStateList(
      base::BindOnce(&NetworkHealthProvider::OnDeviceStateListReceived,
                     base::Unretained(this)));
}

mojom::NetworkState NetworkHealthProvider::GetNetworkStateForGuid(
    const std::string& guid) {
  auto it = networks_.find(guid);
  DCHECK(it != networks_.end());
  return it->second.network->state;
}

}  // namespace diagnostics
}  // namespace ash