blob: 93d324eddd4da1d8aa2d373e74285433fa3cb394 [file] [log] [blame]
// 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 "chrome/browser/ash/policy/reporting/metrics_reporting/network/network_telemetry_sampler.h"
#include <string>
#include <utility>
#include <vector>
#include "base/containers/contains.h"
#include "base/containers/queue.h"
#include "base/logging.h"
#include "base/ranges/algorithm.h"
#include "base/task/bind_post_task.h"
#include "base/task/sequenced_task_runner.h"
#include "chrome/browser/ash/policy/reporting/metrics_reporting/network/wifi_signal_strength_rssi_fetcher.h"
#include "chromeos/ash/components/network/device_state.h"
#include "chromeos/ash/components/network/network_state.h"
#include "chromeos/ash/components/network/network_state_handler.h"
#include "chromeos/ash/components/network/network_type_pattern.h"
#include "chromeos/ash/services/cros_healthd/public/cpp/service_connection.h"
#include "components/reporting/proto/synced/metric_data.pb.h"
namespace reporting {
namespace {
using ::ash::cros_healthd::mojom::NetworkInterfaceInfoPtr;
::ash::NetworkStateHandler::NetworkStateList GetNetworkStateList() {
::ash::NetworkStateHandler::NetworkStateList network_state_list;
::ash::NetworkHandler::Get()->network_state_handler()->GetNetworkListByType(
::ash::NetworkTypePattern::Default(),
/*configured_only=*/true,
/*visible_only=*/false,
/*limit=*/0, // no limit to number of results
&network_state_list);
return network_state_list;
}
NetworkInterfaceInfoPtr GetWifiNetworkInterfaceInfo(
const std::string& device_path,
const ash::cros_healthd::mojom::TelemetryInfoPtr& cros_healthd_telemetry) {
if (device_path.empty() || cros_healthd_telemetry.is_null() ||
cros_healthd_telemetry->network_interface_result.is_null() ||
!cros_healthd_telemetry->network_interface_result
->is_network_interface_info()) {
return nullptr;
}
auto* const device_state =
::ash::NetworkHandler::Get()->network_state_handler()->GetDeviceState(
device_path);
if (!device_state) {
return nullptr;
}
const std::string& interface_name = device_state->interface();
const auto& interface_info_list =
cros_healthd_telemetry->network_interface_result
->get_network_interface_info();
const auto& interface_info_it = base::ranges::find_if(
interface_info_list,
[&interface_name](const NetworkInterfaceInfoPtr& interface_info) {
return !interface_info.is_null() &&
interface_info->is_wireless_interface_info() &&
!interface_info->get_wireless_interface_info().is_null() &&
interface_info->get_wireless_interface_info()->interface_name ==
interface_name;
});
if (interface_info_it == interface_info_list.end()) {
return nullptr;
}
return interface_info_it->Clone();
}
NetworkConnectionState GetNetworkConnectionState(
const ash::NetworkState* network) {
if (network->IsConnectedState()) {
auto portal_state = network->GetPortalState();
switch (portal_state) {
case ash::NetworkState::PortalState::kUnknown:
return NetworkConnectionState::CONNECTED;
case ash::NetworkState::PortalState::kOnline:
return NetworkConnectionState::ONLINE;
case ash::NetworkState::PortalState::kPortalSuspected:
case ash::NetworkState::PortalState::kPortal:
case ash::NetworkState::PortalState::kProxyAuthRequired:
case ash::NetworkState::PortalState::kNoInternet:
return NetworkConnectionState::PORTAL;
}
}
if (network->IsConnectingState()) {
return NetworkConnectionState::CONNECTING;
}
return NetworkConnectionState::NOT_CONNECTED;
}
NetworkType GetNetworkType(const ash::NetworkTypePattern& type) {
if (type.Equals(ash::NetworkTypePattern::Cellular())) {
return NetworkType::CELLULAR;
}
if (type.MatchesPattern(ash::NetworkTypePattern::EthernetOrEthernetEAP())) {
return NetworkType::ETHERNET;
}
if (type.Equals(ash::NetworkTypePattern::Tether())) {
return NetworkType::TETHER;
}
if (type.Equals(ash::NetworkTypePattern::VPN())) {
return NetworkType::VPN;
}
if (type.Equals(ash::NetworkTypePattern::WiFi())) {
return NetworkType::WIFI;
}
NOTREACHED() << "Unsupported network type: " << type.ToDebugString();
return NetworkType::NETWORK_TYPE_UNSPECIFIED; // Unsupported
}
} // namespace
NetworkTelemetrySampler::NetworkTelemetrySampler() = default;
NetworkTelemetrySampler::~NetworkTelemetrySampler() = default;
void NetworkTelemetrySampler::MaybeCollect(OptionalMetricCallback callback) {
auto handle_probe_result_cb =
base::BindOnce(&NetworkTelemetrySampler::CollectWifiSignalStrengthRssi,
weak_ptr_factory_.GetWeakPtr(), std::move(callback));
ash::cros_healthd::ServiceConnection::GetInstance()
->GetProbeService()
->ProbeTelemetryInfo(
std::vector<ash::cros_healthd::mojom::ProbeCategoryEnum>{
ash::cros_healthd::mojom::ProbeCategoryEnum::kNetworkInterface},
base::BindPostTaskToCurrentDefault(
std::move(handle_probe_result_cb)));
}
void NetworkTelemetrySampler::CollectWifiSignalStrengthRssi(
OptionalMetricCallback callback,
ash::cros_healthd::mojom::TelemetryInfoPtr cros_healthd_telemetry) {
base::queue<std::string> service_paths;
::ash::NetworkStateHandler::NetworkStateList network_state_list =
GetNetworkStateList();
for (const auto* network : network_state_list) {
::ash::NetworkTypePattern type =
::ash::NetworkTypePattern::Primitive(network->type());
if (!type.Equals(::ash::NetworkTypePattern::WiFi()) ||
network->signal_strength() == 0) {
continue;
}
service_paths.push(network->path());
}
if (service_paths.empty()) {
CollectNetworksStates(std::move(callback),
std::move(cros_healthd_telemetry),
/*service_path_rssi_map=*/{});
return;
}
auto wifi_signal_rssi_cb =
base::BindOnce(&NetworkTelemetrySampler::CollectNetworksStates,
weak_ptr_factory_.GetWeakPtr(), std::move(callback),
std::move(cros_healthd_telemetry));
FetchWifiSignalStrengthRssi(
std::move(service_paths),
base::BindPostTaskToCurrentDefault(std::move(wifi_signal_rssi_cb)));
}
void NetworkTelemetrySampler::CollectNetworksStates(
OptionalMetricCallback callback,
ash::cros_healthd::mojom::TelemetryInfoPtr cros_healthd_telemetry,
base::flat_map<std::string, int> service_path_rssi_map) {
if (cros_healthd_telemetry.is_null() ||
cros_healthd_telemetry->network_interface_result.is_null()) {
DVLOG(1) << "cros_healthd: Error getting network result, result is null.";
} else if (cros_healthd_telemetry->network_interface_result->is_error()) {
DVLOG(1)
<< "cros_healthd: Error getting network result: "
<< cros_healthd_telemetry->network_interface_result->get_error()->msg;
}
MetricData metric_data;
::ash::NetworkStateHandler::NetworkStateList network_state_list =
GetNetworkStateList();
if (network_state_list.empty()) {
std::move(callback).Run(absl::nullopt);
return;
}
bool should_report = false;
for (const auto* network : network_state_list) {
::ash::NetworkTypePattern type =
::ash::NetworkTypePattern::Primitive(network->type());
// Only collect and report networks of any types that are connected, or wifi
// networks that have signal strength regardless of their connection states.
if (!network->IsConnectedState() &&
(!type.Equals(::ash::NetworkTypePattern::WiFi()) ||
network->signal_strength() == 0)) {
continue;
}
should_report = true;
NetworkTelemetry* const network_telemetry =
metric_data.mutable_telemetry_data()
->mutable_networks_telemetry()
->add_network_telemetry();
network_telemetry->set_guid(network->guid());
network_telemetry->set_type(GetNetworkType(type));
network_telemetry->set_connection_state(GetNetworkConnectionState(network));
if (!network->device_path().empty()) {
network_telemetry->set_device_path(network->device_path());
}
if (!network->GetIpAddress().empty()) {
network_telemetry->set_ip_address(network->GetIpAddress());
}
if (!network->GetGateway().empty()) {
network_telemetry->set_gateway(network->GetGateway());
}
if (type.Equals(::ash::NetworkTypePattern::WiFi())) {
network_telemetry->set_signal_strength(network->signal_strength());
if (base::Contains(service_path_rssi_map, network->path())) {
network_telemetry->set_signal_strength_dbm(
service_path_rssi_map.at(network->path()));
} else {
DVLOG(1) << "Wifi signal RSSI not found in the service to signal "
"map for service: "
<< network->path();
}
const auto& network_interface_info = GetWifiNetworkInterfaceInfo(
network->device_path(), cros_healthd_telemetry);
if (!network_interface_info.is_null() &&
!network_interface_info->get_wireless_interface_info().is_null()) {
const auto& wireless_info =
network_interface_info->get_wireless_interface_info();
// Power management can be set even if the device is not connected to an
// access point.
network_telemetry->set_power_management_enabled(
wireless_info->power_management_on);
// wireless link info is only available when the device is
// connected to the access point.
if (!wireless_info->wireless_link_info.is_null() &&
network->IsConnectedState()) {
const auto& wireless_link_info = wireless_info->wireless_link_info;
network_telemetry->set_tx_bit_rate_mbps(
wireless_link_info->tx_bit_rate_mbps);
network_telemetry->set_rx_bit_rate_mbps(
wireless_link_info->rx_bit_rate_mbps);
network_telemetry->set_tx_power_dbm(wireless_link_info->tx_power_dBm);
network_telemetry->set_encryption_on(
wireless_link_info->encyption_on);
network_telemetry->set_link_quality(wireless_link_info->link_quality);
}
}
}
}
if (should_report) {
std::move(callback).Run(std::move(metric_data));
return;
}
std::move(callback).Run(absl::nullopt);
}
} // namespace reporting