blob: 028b8b3afef585e300d87962f06ac388e87d701d [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_events_observer.h"
#include <string>
#include <utility>
#include "base/check.h"
#include "base/containers/contains.h"
#include "base/containers/queue.h"
#include "base/feature_list.h"
#include "base/logging.h"
#include "base/task/bind_post_task.h"
#include "chrome/browser/ash/net/network_health/network_health_manager.h"
#include "chrome/browser/ash/policy/reporting/metrics_reporting/network/wifi_signal_strength_rssi_fetcher.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/services/network_health/public/mojom/network_health_types.mojom.h"
#include "components/reporting/proto/synced/metric_data.pb.h"
namespace reporting {
namespace {
constexpr int kSignalThresholdDbm = -70;
bool IsConnectedWifiNetwork(const ash::NetworkState* network_state) {
const auto network_type =
::ash::NetworkTypePattern::Primitive(network_state->type());
return network_state->IsConnectedState() &&
network_type.Equals(ash::NetworkTypePattern::WiFi());
}
} // namespace
BASE_FEATURE(kEnableWifiSignalEventsReporting,
"EnableWifiSignalEventsReporting",
base::FEATURE_ENABLED_BY_DEFAULT);
BASE_FEATURE(kEnableNetworkConnectionStateEventsReporting,
"EnableNetworkConnectionStateEventsReporting",
base::FEATURE_ENABLED_BY_DEFAULT);
BASE_FEATURE(kEnableVpnConnectionStateEventsReporting,
"EnableVpnConnectionStateEventsReporting",
base::FEATURE_ENABLED_BY_DEFAULT);
NetworkEventsObserver::NetworkEventsObserver()
: MojoServiceEventsObserverBase<
chromeos::network_health::mojom::NetworkEventsObserver>(this) {}
NetworkEventsObserver::~NetworkEventsObserver() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
void NetworkEventsObserver::OnConnectionStateChanged(
const std::string& guid,
chromeos::network_health::mojom::NetworkState state) {
using NetworkStateMojom = chromeos::network_health::mojom::NetworkState;
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
const auto* network_state = ::ash::NetworkHandler::Get()
->network_state_handler()
->GetNetworkStateFromGuid(guid);
if (!network_state) {
return;
}
MetricData metric_data;
const auto network_type =
::ash::NetworkTypePattern::Primitive(network_state->type());
if (network_type.MatchesPattern(ash::NetworkTypePattern::Physical()) &&
base::FeatureList::IsEnabled(
kEnableNetworkConnectionStateEventsReporting)) {
metric_data.mutable_event_data()->set_type(
MetricEventType::NETWORK_STATE_CHANGE);
} else if (network_type.Equals(ash::NetworkTypePattern::VPN()) &&
base::FeatureList::IsEnabled(
kEnableVpnConnectionStateEventsReporting)) {
metric_data.mutable_event_data()->set_type(
MetricEventType::VPN_CONNECTION_STATE_CHANGE);
} else {
return;
}
if (base::Contains(connection_state_map_, guid) &&
connection_state_map_.at(guid) == state) {
DVLOG(1) << "Connection state already reported";
return;
}
connection_state_map_[guid] = state;
auto* const connection_change_data =
metric_data.mutable_telemetry_data()
->mutable_networks_telemetry()
->mutable_network_connection_change_event_data();
connection_change_data->set_guid(guid);
switch (state) {
case NetworkStateMojom::kOnline:
connection_change_data->set_connection_state(
NetworkConnectionState::ONLINE);
break;
case NetworkStateMojom::kConnected:
connection_change_data->set_connection_state(
NetworkConnectionState::CONNECTED);
break;
case NetworkStateMojom::kPortal:
connection_change_data->set_connection_state(
NetworkConnectionState::PORTAL);
break;
case NetworkStateMojom::kConnecting:
connection_change_data->set_connection_state(
NetworkConnectionState::CONNECTING);
break;
case NetworkStateMojom::kNotConnected:
connection_change_data->set_connection_state(
NetworkConnectionState::NOT_CONNECTED);
break;
default:
NOTREACHED();
}
OnEventObserved(std::move(metric_data));
}
void NetworkEventsObserver::OnSignalStrengthChanged(
const std::string& guid,
chromeos::network_health::mojom::UInt32ValuePtr signal_strength) {
DCHECK(signal_strength) << "Signal strength should have a value.";
const auto* network_state = ::ash::NetworkHandler::Get()
->network_state_handler()
->GetNetworkStateFromGuid(guid);
if (!network_state) {
DVLOG(1) << "Could not find network state with guid " << guid;
return;
}
if (IsConnectedWifiNetwork(network_state)) {
CheckForSignalStrengthEvent(network_state);
}
}
void NetworkEventsObserver::OnNetworkListChanged(
std::vector<::chromeos::network_health::mojom::NetworkPtr> networks) {}
void NetworkEventsObserver::AddObserver() {
ash::network_health::NetworkHealthManager::GetInstance()->AddObserver(
BindNewPipeAndPassRemote());
}
void NetworkEventsObserver::SetReportingEnabled(bool is_enabled) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
MojoServiceEventsObserverBase<
::chromeos::network_health::mojom::NetworkEventsObserver>::
SetReportingEnabled(is_enabled);
if (!is_enabled) {
// Reset connection state fields.
connection_state_map_.clear();
return;
}
// Get signal strength.
low_signal_reported_ = false;
const ash::NetworkState* network_state =
::ash::NetworkHandler::Get()
->network_state_handler()
->ActiveNetworkByType(ash::NetworkTypePattern::WiFi());
if (!network_state || !network_state->IsConnectedState()) {
return;
}
DCHECK(IsConnectedWifiNetwork(network_state));
CheckForSignalStrengthEvent(network_state);
}
void NetworkEventsObserver::CheckForSignalStrengthEvent(
const ash::NetworkState* network_state) {
if (!base::FeatureList::IsEnabled(kEnableWifiSignalEventsReporting)) {
return;
}
auto wifi_signal_rssi_cb = base::BindOnce(
&NetworkEventsObserver::OnSignalStrengthChangedRssiValueReceived,
weak_ptr_factory_.GetWeakPtr(), network_state->guid(),
network_state->path());
FetchWifiSignalStrengthRssi(
base::queue<std::string>({network_state->path()}),
base::BindPostTaskToCurrentDefault(std::move(wifi_signal_rssi_cb)));
}
void NetworkEventsObserver::OnSignalStrengthChangedRssiValueReceived(
const std::string& guid,
const std::string& service_path,
base::flat_map<std::string, int> service_path_rssi_map) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!base::Contains(service_path_rssi_map, service_path)) {
DVLOG(1) << "Wifi signal RSSI not found in the service to signal "
"map for service: "
<< service_path << " with guid: " << guid;
return;
}
const int signal_strength_dbm = service_path_rssi_map.at(service_path);
const bool low_signal = (signal_strength_dbm < kSignalThresholdDbm);
if (low_signal == low_signal_reported_) {
return; // No change in low signal state.
}
// State changed, report metrics.
low_signal_reported_ = low_signal;
MetricData metric_data;
metric_data.mutable_event_data()->set_type(
signal_strength_dbm < kSignalThresholdDbm
? MetricEventType::WIFI_SIGNAL_STRENGTH_LOW
: MetricEventType::WIFI_SIGNAL_STRENGTH_RECOVERED);
auto* const networks_telemetry =
metric_data.mutable_telemetry_data()->mutable_networks_telemetry();
networks_telemetry->mutable_signal_strength_event_data()->set_guid(guid);
networks_telemetry->mutable_signal_strength_event_data()
->set_signal_strength_dbm(signal_strength_dbm);
OnEventObserved(std::move(metric_data));
}
} // namespace reporting