blob: 477429c7d4efcae17570661668841d831759e992 [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 <memory>
#include <string>
#include <utility>
#include "base/run_loop.h"
#include "base/strings/string_piece.h"
#include "base/strings/stringprintf.h"
#include "base/test/bind.h"
#include "base/test/repeating_test_future.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "chromeos/ash/components/dbus/debug_daemon/debug_daemon_client.h"
#include "chromeos/ash/components/login/login_state/login_state.h"
#include "chromeos/ash/components/network/network_handler_test_helper.h"
#include "chromeos/ash/components/network/tether_constants.h"
#include "chromeos/services/network_health/public/mojom/network_health_types.mojom.h"
#include "components/reporting/proto/synced/metric_data.pb.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/cros_system_api/dbus/shill/dbus-constants.h"
using chromeos::network_health::mojom::NetworkState;
using testing::Eq;
namespace reporting {
namespace {
constexpr int kSignalStrength = 10;
constexpr int kGoodSignalStrengthRssi = -50;
constexpr int kLowSignalStrengthRssi = -75;
constexpr int kVeryLowSignalStrengthRssi = -85;
constexpr char kWifiConfig[] =
R"({"GUID": "%s", "Type": "wifi", "State": "%s",
"WiFi.SignalStrengthRssi": %d})";
// Guids.
constexpr char kWifiGuid[] = "wifi-guid";
constexpr char kWifiIdleGuid[] = "wifi-idle-guid";
constexpr char kCellularGuid[] = "cellular-guid";
constexpr char kVpnGuid[] = "vpn-guid";
constexpr char kTetherGuid[] = "tether-guid";
// Service paths.
constexpr char kWifiServicePath[] = "/service/wlan";
constexpr char kWifiIdleServicePath[] = "/service/wifi-idle";
constexpr char kCellularServicePath[] = "/service/cellular";
constexpr char kVpnServicePath[] = "/service/vpn";
constexpr char kTetherServicePath[] = "/service/tether";
class NetworkEventsObserverTestHelper {
public:
NetworkEventsObserverTestHelper() = default;
NetworkEventsObserverTestHelper(const NetworkEventsObserverTestHelper&) =
delete;
NetworkEventsObserverTestHelper& operator=(
const NetworkEventsObserverTestHelper&) = delete;
~NetworkEventsObserverTestHelper() = default;
void SetUp() {
ash::DebugDaemonClient::InitializeFake();
ash::LoginState::Initialize();
ash::LoginState::Get()->SetLoggedInStateAndPrimaryUser(
ash::LoginState::LOGGED_IN_ACTIVE,
ash::LoginState::LOGGED_IN_USER_REGULAR,
network_handler_test_helper_.UserHash());
network_handler_test_helper_.AddDefaultProfiles();
network_handler_test_helper_.ResetDevicesAndServices();
network_handler_test_helper_.manager_test()->AddTechnology(
::ash::kTypeTether, true);
auto* const service_client = network_handler_test_helper_.service_test();
service_client->AddService(kWifiServicePath, kWifiGuid, "wifi-name",
shill::kTypeWifi, shill::kStateReady,
/*visible=*/true);
service_client->AddService(kWifiIdleServicePath, kWifiIdleGuid,
"wifi-idle-name", shill::kTypeWifi,
shill::kStateIdle, /*visible=*/true);
service_client->AddService(kVpnServicePath, kVpnGuid, "vpn-name",
shill::kTypeVPN, shill::kStateReady,
/*visible=*/true);
service_client->AddService(kTetherServicePath, kTetherGuid, "tether-name",
ash::kTypeTether, shill::kStateReady,
/*visible=*/true);
service_client->AddService(kCellularServicePath, kCellularGuid,
"cellular-network-name", shill::kTypeCellular,
shill::kStateReady, /*visible=*/true);
service_client->SetServiceProperty(
kCellularServicePath, shill::kIccidProperty, base::Value("test_iccid"));
task_environment_.RunUntilIdle();
}
void TearDown() {
ash::LoginState::Shutdown();
ash::DebugDaemonClient::Shutdown();
}
ash::NetworkHandlerTestHelper* network_handler_test_helper() {
return &network_handler_test_helper_;
}
private:
base::test::TaskEnvironment task_environment_;
ash::NetworkHandlerTestHelper network_handler_test_helper_;
};
class NetworkEventsObserverSignalStrengthTest : public ::testing::Test {
protected:
void SetUp() override { network_events_observer_test_helper_.SetUp(); }
void TearDown() override { network_events_observer_test_helper_.TearDown(); }
ash::NetworkHandlerTestHelper* network_handler_test_helper() {
return network_events_observer_test_helper_.network_handler_test_helper();
}
void SetFeatureEnabled(bool enabled) {
scoped_feature_list_.InitWithFeatureState(kEnableWifiSignalEventsReporting,
enabled);
}
private:
NetworkEventsObserverTestHelper network_events_observer_test_helper_;
base::test::ScopedFeatureList scoped_feature_list_;
};
TEST_F(NetworkEventsObserverSignalStrengthTest, InitiallyLowSignal) {
SetFeatureEnabled(true);
const std::string service_config_low_signal = base::StringPrintf(
kWifiConfig, kWifiGuid, shill::kStateReady, kLowSignalStrengthRssi);
std::string service_path = network_handler_test_helper()->ConfigureService(
service_config_low_signal);
ASSERT_THAT(service_path, Eq(kWifiServicePath));
NetworkEventsObserver network_events_observer;
MetricData result_metric_data;
base::test::RepeatingTestFuture<MetricData> test_future;
network_events_observer.SetOnEventObservedCallback(test_future.GetCallback());
network_events_observer.SetReportingEnabled(/*is_enabled=*/true);
result_metric_data = test_future.Take();
ASSERT_TRUE(result_metric_data.has_event_data());
EXPECT_THAT(result_metric_data.event_data().type(),
Eq(MetricEventType::WIFI_SIGNAL_STRENGTH_LOW));
ASSERT_TRUE(result_metric_data.has_telemetry_data());
ASSERT_TRUE(result_metric_data.telemetry_data().has_networks_telemetry());
ASSERT_TRUE(result_metric_data.telemetry_data()
.networks_telemetry()
.has_signal_strength_event_data());
EXPECT_THAT(result_metric_data.telemetry_data()
.networks_telemetry()
.signal_strength_event_data()
.guid(),
Eq(kWifiGuid));
EXPECT_THAT(result_metric_data.telemetry_data()
.networks_telemetry()
.signal_strength_event_data()
.signal_strength_dbm(),
Eq(kLowSignalStrengthRssi));
std::string service_config_very_low_signal = base::StringPrintf(
kWifiConfig, kWifiGuid, shill::kStateReady, kVeryLowSignalStrengthRssi);
service_path = network_handler_test_helper()->ConfigureService(
service_config_very_low_signal);
ASSERT_THAT(service_path, Eq(kWifiServicePath));
network_events_observer.OnSignalStrengthChanged(
kWifiGuid,
::chromeos::network_health::mojom::UInt32Value::New(kSignalStrength));
base::RunLoop().RunUntilIdle();
// Low signal strength event already reported.
ASSERT_TRUE(test_future.IsEmpty());
std::string service_config_good_signal = base::StringPrintf(
kWifiConfig, kWifiGuid, shill::kStateReady, kGoodSignalStrengthRssi);
service_path = network_handler_test_helper()->ConfigureService(
service_config_good_signal);
ASSERT_THAT(service_path, Eq(kWifiServicePath));
network_events_observer.OnSignalStrengthChanged(
kWifiGuid,
::chromeos::network_health::mojom::UInt32Value::New(kSignalStrength));
result_metric_data = test_future.Take();
ASSERT_TRUE(result_metric_data.has_event_data());
EXPECT_THAT(result_metric_data.event_data().type(),
Eq(MetricEventType::WIFI_SIGNAL_STRENGTH_RECOVERED));
ASSERT_TRUE(result_metric_data.has_telemetry_data());
ASSERT_TRUE(result_metric_data.telemetry_data().has_networks_telemetry());
ASSERT_TRUE(result_metric_data.telemetry_data()
.networks_telemetry()
.has_signal_strength_event_data());
EXPECT_THAT(result_metric_data.telemetry_data()
.networks_telemetry()
.signal_strength_event_data()
.guid(),
Eq(kWifiGuid));
EXPECT_THAT(result_metric_data.telemetry_data()
.networks_telemetry()
.signal_strength_event_data()
.signal_strength_dbm(),
Eq(kGoodSignalStrengthRssi));
}
TEST_F(NetworkEventsObserverSignalStrengthTest, WifiNotConnected) {
SetFeatureEnabled(true);
network_handler_test_helper()->ResetDevicesAndServices();
auto* const service_client = network_handler_test_helper()->service_test();
service_client->AddService(kWifiIdleServicePath, kWifiIdleGuid,
"wifi-idle-name", shill::kTypeWifi,
shill::kStateIdle, /*visible=*/true);
base::RunLoop().RunUntilIdle();
std::string idle_service_config = base::StringPrintf(
kWifiConfig, kWifiIdleGuid, shill::kStateIdle, kLowSignalStrengthRssi);
std::string idle_service_path =
network_handler_test_helper()->ConfigureService(idle_service_config);
ASSERT_THAT(idle_service_path, Eq(kWifiIdleServicePath));
NetworkEventsObserver network_events_observer;
base::test::RepeatingTestFuture<MetricData> test_future;
network_events_observer.SetOnEventObservedCallback(test_future.GetCallback());
network_events_observer.SetReportingEnabled(/*is_enabled=*/true);
base::RunLoop().RunUntilIdle();
network_events_observer.OnSignalStrengthChanged(
kWifiIdleGuid,
::chromeos::network_health::mojom::UInt32Value::New(kSignalStrength));
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(test_future.IsEmpty());
}
TEST_F(NetworkEventsObserverSignalStrengthTest, WifiConnecting) {
SetFeatureEnabled(true);
network_handler_test_helper()->ResetDevicesAndServices();
auto* const service_client = network_handler_test_helper()->service_test();
service_client->AddService(kWifiServicePath, kWifiGuid, "wifi-name",
shill::kTypeWifi, shill::kStateAssociation,
/*visible=*/true);
base::RunLoop().RunUntilIdle();
const std::string service_config_low_signal = base::StringPrintf(
kWifiConfig, kWifiGuid, shill::kStateAssociation, kLowSignalStrengthRssi);
std::string service_path = network_handler_test_helper()->ConfigureService(
service_config_low_signal);
ASSERT_THAT(service_path, Eq(kWifiServicePath));
NetworkEventsObserver network_events_observer;
base::test::RepeatingTestFuture<MetricData> test_future;
network_events_observer.SetOnEventObservedCallback(test_future.GetCallback());
network_events_observer.SetReportingEnabled(/*is_enabled=*/true);
base::RunLoop().RunUntilIdle();
network_events_observer.OnSignalStrengthChanged(
kWifiGuid,
::chromeos::network_health::mojom::UInt32Value::New(kSignalStrength));
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(test_future.IsEmpty());
}
TEST_F(NetworkEventsObserverSignalStrengthTest, Cellular) {
SetFeatureEnabled(true);
std::string service_config_good_signal = base::StringPrintf(
kWifiConfig, kWifiGuid, shill::kStateReady, kGoodSignalStrengthRssi);
std::string service_path = network_handler_test_helper()->ConfigureService(
service_config_good_signal);
ASSERT_THAT(service_path, Eq(kWifiServicePath));
NetworkEventsObserver network_events_observer;
base::test::RepeatingTestFuture<MetricData> test_future;
network_events_observer.SetOnEventObservedCallback(test_future.GetCallback());
network_events_observer.SetReportingEnabled(/*is_enabled=*/true);
base::RunLoop().RunUntilIdle();
network_events_observer.OnSignalStrengthChanged(
kCellularGuid,
::chromeos::network_health::mojom::UInt32Value::New(kSignalStrength));
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(test_future.IsEmpty());
}
TEST_F(NetworkEventsObserverSignalStrengthTest, InvalidGuid) {
SetFeatureEnabled(true);
NetworkEventsObserver network_events_observer;
bool event_reported = false;
auto cb =
base::BindLambdaForTesting([&](MetricData) { event_reported = true; });
network_events_observer.SetOnEventObservedCallback(std::move(cb));
network_events_observer.SetReportingEnabled(/*is_enabled=*/true);
network_events_observer.OnSignalStrengthChanged(
"invalid_guid",
::chromeos::network_health::mojom::UInt32Value::New(kSignalStrength));
base::RunLoop().RunUntilIdle();
ASSERT_FALSE(event_reported);
}
TEST_F(NetworkEventsObserverSignalStrengthTest, FeatureDisabled) {
SetFeatureEnabled(false);
const std::string service_config_low_signal = base::StringPrintf(
kWifiConfig, kWifiGuid, shill::kStateReady, kLowSignalStrengthRssi);
std::string service_path = network_handler_test_helper()->ConfigureService(
service_config_low_signal);
ASSERT_THAT(service_path, Eq(kWifiServicePath));
NetworkEventsObserver network_events_observer;
bool event_reported = false;
auto cb =
base::BindLambdaForTesting([&](MetricData) { event_reported = true; });
network_events_observer.SetOnEventObservedCallback(std::move(cb));
network_events_observer.SetReportingEnabled(/*is_enabled=*/true);
network_events_observer.OnSignalStrengthChanged(
kWifiGuid,
::chromeos::network_health::mojom::UInt32Value::New(kSignalStrength));
base::RunLoop().RunUntilIdle();
ASSERT_FALSE(event_reported);
}
struct NetworkConnectionStateTestCase {
std::string test_name;
std::string guid;
NetworkState input_state;
NetworkState other_state = NetworkState::kNotConnected;
MetricEventType expected_event_type;
NetworkConnectionState expected_state;
};
class NetworkEventsObserverConnectionStateTest
: public ::testing::TestWithParam<NetworkConnectionStateTestCase> {
protected:
void SetUp() override { network_events_observer_test_helper_.SetUp(); }
void TearDown() override { network_events_observer_test_helper_.TearDown(); }
NetworkEventsObserverTestHelper network_events_observer_test_helper_;
base::test::ScopedFeatureList scoped_feature_list_;
};
TEST_F(NetworkEventsObserverConnectionStateTest, PhysicalFeatureDisabled) {
scoped_feature_list_.InitWithFeatures(
/*enabled_features=*/{kEnableVpnConnectionStateEventsReporting},
/*disabled_features=*/{kEnableNetworkConnectionStateEventsReporting});
bool event_reported = false;
NetworkEventsObserver network_events_observer;
MetricData result_metric_data;
auto cb =
base::BindLambdaForTesting([&](MetricData) { event_reported = true; });
network_events_observer.SetOnEventObservedCallback(std::move(cb));
network_events_observer.OnConnectionStateChanged(kWifiGuid,
NetworkState::kNotConnected);
ASSERT_FALSE(event_reported);
network_events_observer.OnConnectionStateChanged(kVpnGuid,
NetworkState::kNotConnected);
EXPECT_TRUE(event_reported);
}
TEST_F(NetworkEventsObserverConnectionStateTest, VpnFeatureDisabled) {
scoped_feature_list_.InitWithFeatures(
/*enabled_features=*/
{kEnableNetworkConnectionStateEventsReporting},
/*disabled_features=*/
{kEnableVpnConnectionStateEventsReporting});
bool event_reported = false;
NetworkEventsObserver network_events_observer;
MetricData result_metric_data;
auto cb =
base::BindLambdaForTesting([&](MetricData) { event_reported = true; });
network_events_observer.SetOnEventObservedCallback(std::move(cb));
network_events_observer.OnConnectionStateChanged(kVpnGuid,
NetworkState::kNotConnected);
ASSERT_FALSE(event_reported);
network_events_observer.OnConnectionStateChanged(kWifiGuid,
NetworkState::kNotConnected);
EXPECT_TRUE(event_reported);
}
TEST_F(NetworkEventsObserverConnectionStateTest, TetherConnection) {
scoped_feature_list_.InitWithFeatures(
/*enabled_features=*/
{kEnableNetworkConnectionStateEventsReporting,
kEnableVpnConnectionStateEventsReporting},
/*disabled_features=*/
{});
bool event_reported = false;
NetworkEventsObserver network_events_observer;
MetricData result_metric_data;
auto cb =
base::BindLambdaForTesting([&](MetricData) { event_reported = true; });
network_events_observer.SetOnEventObservedCallback(std::move(cb));
network_events_observer.OnConnectionStateChanged(kTetherGuid,
NetworkState::kConnected);
EXPECT_FALSE(event_reported);
}
TEST_F(NetworkEventsObserverConnectionStateTest, InvalidGuid) {
scoped_feature_list_.InitWithFeatures(
/*enabled_features=*/{kEnableNetworkConnectionStateEventsReporting,
kEnableVpnConnectionStateEventsReporting},
/*disabled_features=*/{});
NetworkEventsObserver network_events_observer;
bool event_reported = false;
auto cb =
base::BindLambdaForTesting([&](MetricData) { event_reported = true; });
network_events_observer.SetOnEventObservedCallback(std::move(cb));
network_events_observer.SetReportingEnabled(/*is_enabled=*/true);
network_events_observer.OnConnectionStateChanged("invalid_guid",
NetworkState::kOnline);
ASSERT_FALSE(event_reported);
}
TEST_P(NetworkEventsObserverConnectionStateTest, Default) {
const NetworkConnectionStateTestCase& test_case = GetParam();
bool event_reported = false;
scoped_feature_list_.InitWithFeatures(
/*enabled_features=*/{kEnableNetworkConnectionStateEventsReporting,
kEnableVpnConnectionStateEventsReporting},
/*disabled_features=*/{});
NetworkEventsObserver network_events_observer;
MetricData result_metric_data;
auto cb = base::BindLambdaForTesting([&](MetricData metric_data) {
event_reported = true;
result_metric_data = std::move(metric_data);
});
network_events_observer.SetOnEventObservedCallback(std::move(cb));
network_events_observer.OnConnectionStateChanged(test_case.guid,
test_case.input_state);
ASSERT_TRUE(event_reported);
ASSERT_TRUE(result_metric_data.has_event_data());
EXPECT_THAT(result_metric_data.event_data().type(),
Eq(test_case.expected_event_type));
ASSERT_TRUE(result_metric_data.has_telemetry_data());
ASSERT_TRUE(result_metric_data.telemetry_data().has_networks_telemetry());
ASSERT_TRUE(result_metric_data.telemetry_data()
.networks_telemetry()
.has_network_connection_change_event_data());
EXPECT_THAT(result_metric_data.telemetry_data()
.networks_telemetry()
.network_connection_change_event_data()
.guid(),
Eq(test_case.guid));
EXPECT_THAT(result_metric_data.telemetry_data()
.networks_telemetry()
.network_connection_change_event_data()
.connection_state(),
Eq(test_case.expected_state));
// Same event with different guid should be reported.
event_reported = false;
network_events_observer.OnConnectionStateChanged(kWifiIdleGuid,
test_case.input_state);
ASSERT_TRUE(event_reported);
// Duplicate events should not be reported
event_reported = false;
network_events_observer.OnConnectionStateChanged(test_case.guid,
test_case.input_state);
ASSERT_FALSE(event_reported);
// Different event for same network should be reported.
network_events_observer.OnConnectionStateChanged(test_case.guid,
test_case.other_state);
ASSERT_TRUE(event_reported);
// Same event with same guid should be reported if reporting state changed
// from disabled to enabled.
network_events_observer.SetReportingEnabled(/*is_enabled=*/false);
network_events_observer.SetReportingEnabled(/*is_enabled=*/true);
event_reported = false;
network_events_observer.OnConnectionStateChanged(test_case.guid,
test_case.other_state);
ASSERT_TRUE(event_reported);
}
INSTANTIATE_TEST_SUITE_P(
NetworkEventsObserverConnectionStateTest,
NetworkEventsObserverConnectionStateTest,
::testing::ValuesIn<NetworkConnectionStateTestCase>(
{{.test_name = "WifiOnline",
.guid = kWifiGuid,
.input_state = NetworkState::kOnline,
.other_state = NetworkState::kNotConnected,
.expected_event_type = MetricEventType::NETWORK_STATE_CHANGE,
.expected_state = NetworkConnectionState::ONLINE},
{.test_name = "CellularConnected",
.guid = kCellularGuid,
.input_state = NetworkState::kConnected,
.expected_event_type = MetricEventType::NETWORK_STATE_CHANGE,
.expected_state = NetworkConnectionState::CONNECTED},
{.test_name = "WifiPortal",
.guid = kWifiGuid,
.input_state = NetworkState::kPortal,
.expected_event_type = MetricEventType::NETWORK_STATE_CHANGE,
.expected_state = NetworkConnectionState::PORTAL},
{.test_name = "VpnConnecting",
.guid = kVpnGuid,
.input_state = NetworkState::kConnecting,
.expected_event_type = MetricEventType::VPN_CONNECTION_STATE_CHANGE,
.expected_state = NetworkConnectionState::CONNECTING},
{.test_name = "VpnNotConnected",
.guid = kVpnGuid,
.input_state = NetworkState::kNotConnected,
.other_state = NetworkState::kConnecting,
.expected_event_type = MetricEventType::VPN_CONNECTION_STATE_CHANGE,
.expected_state = NetworkConnectionState::NOT_CONNECTED}}),
[](const testing::TestParamInfo<
NetworkEventsObserverConnectionStateTest::ParamType>& info) {
return info.param.test_name;
});
} // namespace
} // namespace reporting