#include "ash/system/human_presence/snooping_protection_controller.h"
#include <memory>
#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
#include "ash/constants/ash_switches.h"
#include "ash/session/session_controller_impl.h"
#include "ash/session/test_session_controller_client.h"
#include "ash/shell.h"
#include "ash/system/human_presence/human_presence_metrics.h"
#include "ash/test/ash_test_base.h"
#include "base/memory/raw_ptr.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_command_line.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "base/time/time.h"
#include "chromeos/ash/components/dbus/hps/hps_service.pb.h"
#include "chromeos/ash/components/dbus/human_presence/fake_human_presence_dbus_client.h"
#include "chromeos/ash/components/dbus/human_presence/human_presence_dbus_client.h"
#include "components/account_id/account_id.h"
#include "components/user_manager/user_type.h"
namespace ash {
namespace {
namespace metrics = ash::snooping_protection_metrics;
constexpr base::TimeDelta kShortTime = base::Milliseconds(30);
constexpr base::TimeDelta kLongTime = base::Seconds(30);
class SnoopingProtectionControllerTestBase : public NoSessionAshTestBase {
public:
SnoopingProtectionControllerTestBase(
bool service_available,
bool service_state,
const std::map<std::string, std::string>& params)
: NoSessionAshTestBase(
base::test::TaskEnvironment::TimeSource::MOCK_TIME),
service_available_(service_available),
service_state_(service_state),
params_(params) {
scoped_feature_list_.InitAndEnableFeatureWithParameters(
ash::features::kSnoopingProtection, params_);
scoped_command_line_.GetProcessCommandLine()->AppendSwitch(
switches::kHasHps);
}
SnoopingProtectionControllerTestBase(
const SnoopingProtectionControllerTestBase&) = delete;
SnoopingProtectionControllerTestBase& operator=(
const SnoopingProtectionControllerTestBase&) = delete;
~SnoopingProtectionControllerTestBase() override = default;
void SetUp() override {
HumanPresenceDBusClient::InitializeFake();
dbus_client_ = FakeHumanPresenceDBusClient::Get();
dbus_client_->set_hps_service_is_available(service_available_);
hps::HpsResultProto state;
state.set_value(service_state_ ? hps::HpsResult::POSITIVE
: hps::HpsResult::NEGATIVE);
dbus_client_->set_hps_notify_result(state);
AshTestBase::SetUp();
controller_ = Shell::Get()->snooping_protection_controller();
task_environment()->FastForwardBy(kShortTime);
}
void TearDown() override {
AshTestBase::TearDown();
HumanPresenceDBusClient::Shutdown();
}
protected:
base::test::ScopedFeatureList scoped_feature_list_;
base::test::ScopedCommandLine scoped_command_line_;
const bool service_available_;
const bool service_state_;
const std::map<std::string, std::string> params_;
raw_ptr<FakeHumanPresenceDBusClient, DanglingUntriaged> dbus_client_ =
nullptr;
raw_ptr<SnoopingProtectionController, DanglingUntriaged> controller_ =
nullptr;
void SimulateLogin() {
SimulateUserLogin({"testuser@gmail.com"});
task_environment()->FastForwardBy(kShortTime);
}
void SetEnabledPref(bool enabled) {
Shell::Get()->session_controller()->GetActivePrefService()->SetBoolean(
prefs::kSnoopingProtectionEnabled, enabled);
task_environment()->FastForwardBy(kShortTime);
}
};
class SnoopingProtectionControllerTestAbsent
: public SnoopingProtectionControllerTestBase {
public:
SnoopingProtectionControllerTestAbsent()
: SnoopingProtectionControllerTestBase(
true,
false,
{{"SnoopingProtection_filter_config_case", "1"}}) {}
};
TEST_F(SnoopingProtectionControllerTestAbsent, Hidden) {
SimulateLogin();
SetEnabledPref(false);
EXPECT_EQ(dbus_client_->hps_notify_count(), 0);
EXPECT_FALSE(controller_->SnooperPresent());
}
TEST_F(SnoopingProtectionControllerTestAbsent, PresenceChange) {
SimulateLogin();
SetEnabledPref(true);
EXPECT_EQ(dbus_client_->hps_notify_count(), 1);
EXPECT_FALSE(controller_->SnooperPresent());
hps::HpsResultProto state;
state.set_value(hps::HpsResult::POSITIVE);
controller_->OnHpsNotifyChanged(state);
task_environment()->FastForwardBy(kLongTime);
EXPECT_TRUE(controller_->SnooperPresent());
state.set_value(hps::HpsResult::NEGATIVE);
controller_->OnHpsNotifyChanged(state);
task_environment()->FastForwardBy(kLongTime);
EXPECT_FALSE(controller_->SnooperPresent());
}
TEST_F(SnoopingProtectionControllerTestAbsent, ReconfigureOnPrefs) {
EXPECT_EQ(dbus_client_->enable_hps_notify_count(), 0);
EXPECT_EQ(dbus_client_->disable_hps_notify_count(), 1);
EXPECT_EQ(dbus_client_->hps_notify_count(), 0);
SimulateLogin();
EXPECT_EQ(dbus_client_->enable_hps_notify_count(), 0);
EXPECT_EQ(dbus_client_->disable_hps_notify_count(), 1);
EXPECT_EQ(dbus_client_->hps_notify_count(), 0);
SetEnabledPref(true);
EXPECT_EQ(dbus_client_->enable_hps_notify_count(), 1);
EXPECT_EQ(dbus_client_->disable_hps_notify_count(), 1);
EXPECT_EQ(dbus_client_->hps_notify_count(), 1);
SetEnabledPref(false);
EXPECT_EQ(dbus_client_->enable_hps_notify_count(), 1);
EXPECT_EQ(dbus_client_->disable_hps_notify_count(), 2);
EXPECT_EQ(dbus_client_->hps_notify_count(), 1);
SetEnabledPref(true);
EXPECT_EQ(dbus_client_->enable_hps_notify_count(), 2);
EXPECT_EQ(dbus_client_->disable_hps_notify_count(), 2);
EXPECT_EQ(dbus_client_->hps_notify_count(), 2);
}
TEST_F(SnoopingProtectionControllerTestAbsent, ReconfigureOnRestarts) {
SimulateLogin();
SetEnabledPref(true);
EXPECT_EQ(dbus_client_->enable_hps_notify_count(), 1);
EXPECT_EQ(dbus_client_->disable_hps_notify_count(), 1);
EXPECT_EQ(dbus_client_->hps_notify_count(), 1);
controller_->OnShutdown();
EXPECT_EQ(dbus_client_->enable_hps_notify_count(), 1);
EXPECT_EQ(dbus_client_->disable_hps_notify_count(), 1);
EXPECT_EQ(dbus_client_->hps_notify_count(), 1);
controller_->OnRestart();
task_environment()->FastForwardBy(kLongTime);
EXPECT_EQ(dbus_client_->enable_hps_notify_count(), 2);
EXPECT_EQ(dbus_client_->disable_hps_notify_count(), 1);
EXPECT_EQ(dbus_client_->hps_notify_count(), 2);
}
TEST_F(SnoopingProtectionControllerTestAbsent, ReconfigureOnlyIfNecessary) {
EXPECT_EQ(dbus_client_->enable_hps_notify_count(), 0);
EXPECT_EQ(dbus_client_->disable_hps_notify_count(), 1);
EXPECT_EQ(dbus_client_->hps_notify_count(), 0);
SetEnabledPref(true);
EXPECT_EQ(dbus_client_->enable_hps_notify_count(), 0);
EXPECT_EQ(dbus_client_->disable_hps_notify_count(), 1);
EXPECT_EQ(dbus_client_->hps_notify_count(), 0);
SimulateLogin();
SetEnabledPref(true);
EXPECT_EQ(dbus_client_->enable_hps_notify_count(), 1);
EXPECT_EQ(dbus_client_->disable_hps_notify_count(), 1);
EXPECT_EQ(dbus_client_->hps_notify_count(), 1);
}
class SnoopingProtectionControllerTestPresent
: public SnoopingProtectionControllerTestBase {
public:
SnoopingProtectionControllerTestPresent()
: SnoopingProtectionControllerTestBase(
true,
true,
{{"SnoopingProtection_filter_config_case", "1"}}) {}
};
TEST_F(SnoopingProtectionControllerTestPresent, PresenceState) {
SimulateLogin();
SetEnabledPref(true);
EXPECT_EQ(dbus_client_->hps_notify_count(), 1);
EXPECT_TRUE(controller_->SnooperPresent());
}
TEST_F(SnoopingProtectionControllerTestPresent, PrefChanged) {
SimulateLogin();
SetEnabledPref(false);
EXPECT_EQ(dbus_client_->hps_notify_count(), 0);
EXPECT_FALSE(controller_->SnooperPresent());
SetEnabledPref(true);
EXPECT_EQ(dbus_client_->hps_notify_count(), 1);
EXPECT_TRUE(controller_->SnooperPresent());
}
TEST_F(SnoopingProtectionControllerTestPresent, Oobe) {
TestSessionControllerClient* session = GetSessionControllerClient();
SimulateUserLogin({.display_email = "testuser@gmail.com",
.is_new_profile = true,
.activate_session = false});
session->SetSessionState(session_manager::SessionState::OOBE);
SetEnabledPref(true);
EXPECT_EQ(dbus_client_->hps_notify_count(), 0);
EXPECT_FALSE(controller_->SnooperPresent());
session->SetSessionState(session_manager::SessionState::ACTIVE);
task_environment()->FastForwardBy(kLongTime);
EXPECT_EQ(dbus_client_->hps_notify_count(), 1);
EXPECT_TRUE(controller_->SnooperPresent());
}
TEST_F(SnoopingProtectionControllerTestPresent, Login) {
SetEnabledPref(true);
EXPECT_EQ(dbus_client_->hps_notify_count(), 0);
EXPECT_FALSE(controller_->SnooperPresent());
SimulateLogin();
EXPECT_FALSE(controller_->SnooperPresent());
SetEnabledPref(true);
EXPECT_TRUE(controller_->SnooperPresent());
}
TEST_F(SnoopingProtectionControllerTestPresent, Restarts) {
SimulateLogin();
SetEnabledPref(true);
EXPECT_EQ(dbus_client_->hps_notify_count(), 1);
EXPECT_TRUE(controller_->SnooperPresent());
dbus_client_->set_hps_service_is_available(false);
controller_->OnShutdown();
task_environment()->FastForwardBy(kLongTime);
EXPECT_FALSE(controller_->SnooperPresent());
dbus_client_->set_hps_service_is_available(true);
controller_->OnRestart();
task_environment()->FastForwardBy(kLongTime);
EXPECT_EQ(dbus_client_->hps_notify_count(), 2);
EXPECT_TRUE(controller_->SnooperPresent());
}
TEST_F(SnoopingProtectionControllerTestPresent, ClearPresenceState) {
SimulateLogin();
SetEnabledPref(true);
EXPECT_EQ(controller_->SnooperPresent(), true);
SetEnabledPref(false);
EXPECT_EQ(controller_->SnooperPresent(), false);
Shell::Get()->session_controller()->GetActivePrefService()->SetBoolean(
prefs::kSnoopingProtectionEnabled, true);
EXPECT_EQ(controller_->SnooperPresent(), false);
}
TEST_F(SnoopingProtectionControllerTestPresent, Orientation) {
SimulateLogin();
SetEnabledPref(true);
EXPECT_EQ(dbus_client_->enable_hps_notify_count(), 1);
EXPECT_EQ(dbus_client_->disable_hps_notify_count(), 1);
EXPECT_EQ(dbus_client_->hps_notify_count(), 1);
EXPECT_TRUE(controller_->SnooperPresent());
controller_->OnOrientationChanged(false);
task_environment()->FastForwardBy(kLongTime);
EXPECT_EQ(dbus_client_->enable_hps_notify_count(), 1);
EXPECT_EQ(dbus_client_->disable_hps_notify_count(), 2);
EXPECT_EQ(dbus_client_->hps_notify_count(), 1);
EXPECT_FALSE(controller_->SnooperPresent());
controller_->OnOrientationChanged(true);
task_environment()->FastForwardBy(kLongTime);
EXPECT_EQ(dbus_client_->enable_hps_notify_count(), 2);
EXPECT_EQ(dbus_client_->disable_hps_notify_count(), 2);
EXPECT_EQ(dbus_client_->hps_notify_count(), 2);
EXPECT_TRUE(controller_->SnooperPresent());
}
TEST_F(SnoopingProtectionControllerTestPresent, PositiveWindow) {
SimulateLogin();
SetEnabledPref(true);
EXPECT_EQ(dbus_client_->hps_notify_count(), 1);
EXPECT_TRUE(controller_->SnooperPresent());
hps::HpsResultProto state;
state.set_value(hps::HpsResult::NEGATIVE);
controller_->OnHpsNotifyChanged(state);
task_environment()->FastForwardBy(kShortTime);
EXPECT_TRUE(controller_->SnooperPresent());
task_environment()->FastForwardBy(kLongTime);
EXPECT_FALSE(controller_->SnooperPresent());
state.set_value(hps::HpsResult::POSITIVE);
controller_->OnHpsNotifyChanged(state);
EXPECT_TRUE(controller_->SnooperPresent());
task_environment()->FastForwardBy(kLongTime);
EXPECT_TRUE(controller_->SnooperPresent());
controller_->OnShutdown();
task_environment()->FastForwardBy(kShortTime);
EXPECT_FALSE(controller_->SnooperPresent());
}
class SnoopingProtectionControllerTestUnavailable
: public SnoopingProtectionControllerTestBase {
public:
SnoopingProtectionControllerTestUnavailable()
: SnoopingProtectionControllerTestBase(
false,
true,
{{"SnoopingProtection_filter_config_case", "1"}}) {}
};
TEST_F(SnoopingProtectionControllerTestUnavailable, WaitForService) {
SimulateLogin();
SetEnabledPref(true);
EXPECT_EQ(dbus_client_->enable_hps_notify_count(), 0);
EXPECT_EQ(dbus_client_->disable_hps_notify_count(), 0);
EXPECT_EQ(dbus_client_->hps_notify_count(), 0);
EXPECT_FALSE(controller_->SnooperPresent());
dbus_client_->set_hps_service_is_available(true);
controller_->OnRestart();
task_environment()->FastForwardBy(kLongTime);
EXPECT_EQ(dbus_client_->enable_hps_notify_count(), 1);
EXPECT_EQ(dbus_client_->disable_hps_notify_count(), 0);
EXPECT_EQ(dbus_client_->hps_notify_count(), 1);
EXPECT_TRUE(controller_->SnooperPresent());
}
class SnoopingProtectionControllerTestBadParams
: public SnoopingProtectionControllerTestBase {
public:
SnoopingProtectionControllerTestBadParams()
: SnoopingProtectionControllerTestBase(
true,
true,
{{"SnoopingProtection_filter_config_case", "0"}}) {}
};
TEST_F(SnoopingProtectionControllerTestBadParams, BadParams) {
SimulateLogin();
SetEnabledPref(true);
EXPECT_EQ(dbus_client_->enable_hps_notify_count(), 0);
EXPECT_EQ(dbus_client_->disable_hps_notify_count(), 1);
EXPECT_EQ(dbus_client_->hps_notify_count(), 0);
}
class SnoopingProtectionControllerTestMetrics
: public SnoopingProtectionControllerTestAbsent {};
TEST_F(SnoopingProtectionControllerTestMetrics, EnableDisablePref) {
base::HistogramTester tester;
SimulateLogin();
tester.ExpectTotalCount(metrics::kEnabledHistogramName, 0);
SetEnabledPref(true);
tester.ExpectBucketCount(metrics::kEnabledHistogramName, 1, 1);
tester.ExpectTotalCount(metrics::kEnabledHistogramName, 1);
SetEnabledPref(false);
tester.ExpectBucketCount(metrics::kEnabledHistogramName, 0, 1);
tester.ExpectTotalCount(metrics::kEnabledHistogramName, 2);
}
TEST_F(SnoopingProtectionControllerTestMetrics, Duration) {
base::HistogramTester tester;
SimulateLogin();
SetEnabledPref(true);
hps::HpsResultProto state;
state.set_value(hps::HpsResult::POSITIVE);
controller_->OnHpsNotifyChanged(state);
tester.ExpectTotalCount(metrics::kPositiveDurationHistogramName, 0);
tester.ExpectTotalCount(metrics::kNegativeDurationHistogramName, 0);
task_environment()->FastForwardBy(kLongTime);
state.set_value(hps::HpsResult::UNKNOWN);
controller_->OnHpsNotifyChanged(state);
tester.ExpectTimeBucketCount(metrics::kPositiveDurationHistogramName,
kLongTime, 1);
tester.ExpectTotalCount(metrics::kPositiveDurationHistogramName, 1);
tester.ExpectTotalCount(metrics::kNegativeDurationHistogramName, 0);
task_environment()->FastForwardBy(kLongTime);
state.set_value(hps::HpsResult::NEGATIVE);
controller_->OnHpsNotifyChanged(state);
tester.ExpectTotalCount(metrics::kPositiveDurationHistogramName, 1);
tester.ExpectTotalCount(metrics::kNegativeDurationHistogramName, 0);
state.set_value(hps::HpsResult::POSITIVE);
controller_->OnHpsNotifyChanged(state);
tester.ExpectTimeBucketCount(metrics::kNegativeDurationHistogramName,
kLongTime, 1);
tester.ExpectTotalCount(metrics::kPositiveDurationHistogramName, 1);
tester.ExpectTotalCount(metrics::kNegativeDurationHistogramName, 1);
}
TEST_F(SnoopingProtectionControllerTestMetrics, ShutDownTest) {
base::HistogramTester tester;
SimulateLogin();
SetEnabledPref(true);
hps::HpsResultProto state;
state.set_value(hps::HpsResult::POSITIVE);
controller_->OnHpsNotifyChanged(state);
tester.ExpectTotalCount(metrics::kPositiveDurationHistogramName, 0);
tester.ExpectTotalCount(metrics::kNegativeDurationHistogramName, 0);
task_environment()->FastForwardBy(kLongTime);
dbus_client_->Shutdown();
tester.ExpectTimeBucketCount(metrics::kPositiveDurationHistogramName,
kLongTime, 1);
tester.ExpectTotalCount(metrics::kPositiveDurationHistogramName, 1);
tester.ExpectTotalCount(metrics::kNegativeDurationHistogramName, 0);
dbus_client_->Restart();
state.set_value(hps::HpsResult::NEGATIVE);
controller_->OnHpsNotifyChanged(state);
tester.ExpectTotalCount(metrics::kPositiveDurationHistogramName, 1);
tester.ExpectTotalCount(metrics::kNegativeDurationHistogramName, 0);
task_environment()->FastForwardBy(kLongTime);
state.set_value(hps::HpsResult::POSITIVE);
controller_->OnHpsNotifyChanged(state);
tester.ExpectTimeBucketCount(metrics::kNegativeDurationHistogramName,
kLongTime, 1);
tester.ExpectTotalCount(metrics::kPositiveDurationHistogramName, 1);
tester.ExpectTotalCount(metrics::kNegativeDurationHistogramName, 1);
}
TEST_F(SnoopingProtectionControllerTestMetrics, FlakeyDetection) {
base::HistogramTester tester;
SimulateLogin();
SetEnabledPref(true);
hps::HpsResultProto state;
state.set_value(hps::HpsResult::POSITIVE);
controller_->OnHpsNotifyChanged(state);
tester.ExpectTotalCount(metrics::kPositiveDurationHistogramName, 0);
tester.ExpectTotalCount(metrics::kNegativeDurationHistogramName, 0);
tester.ExpectTotalCount(metrics::kFlakeyHistogramName, 0);
EXPECT_TRUE(controller_->SnooperPresent());
task_environment()->FastForwardBy(kShortTime);
state.set_value(hps::HpsResult::NEGATIVE);
controller_->OnHpsNotifyChanged(state);
tester.ExpectTotalCount(metrics::kPositiveDurationHistogramName, 0);
tester.ExpectTotalCount(metrics::kNegativeDurationHistogramName, 0);
tester.ExpectTotalCount(metrics::kFlakeyHistogramName, 1);
EXPECT_TRUE(controller_->SnooperPresent());
task_environment()->FastForwardBy(kShortTime);
state.set_value(hps::HpsResult::NEGATIVE);
controller_->OnHpsNotifyChanged(state);
tester.ExpectTotalCount(metrics::kPositiveDurationHistogramName, 0);
tester.ExpectTotalCount(metrics::kNegativeDurationHistogramName, 0);
tester.ExpectTotalCount(metrics::kFlakeyHistogramName, 2);
EXPECT_TRUE(controller_->SnooperPresent());
task_environment()->FastForwardBy(kLongTime);
state.set_value(hps::HpsResult::NEGATIVE);
controller_->OnHpsNotifyChanged(state);
tester.ExpectTotalCount(metrics::kPositiveDurationHistogramName, 1);
tester.ExpectTotalCount(metrics::kNegativeDurationHistogramName, 0);
tester.ExpectTotalCount(metrics::kFlakeyHistogramName, 2);
EXPECT_FALSE(controller_->SnooperPresent());
task_environment()->FastForwardBy(kShortTime);
state.set_value(hps::HpsResult::POSITIVE);
controller_->OnHpsNotifyChanged(state);
tester.ExpectTotalCount(metrics::kPositiveDurationHistogramName, 1);
tester.ExpectTotalCount(metrics::kNegativeDurationHistogramName, 1);
tester.ExpectTotalCount(metrics::kFlakeyHistogramName, 2);
EXPECT_TRUE(controller_->SnooperPresent());
}
TEST_F(SnoopingProtectionControllerTestMetrics,
FlakeyDetectionWithOtherSignals) {
base::HistogramTester tester;
SimulateLogin();
SetEnabledPref(true);
hps::HpsResultProto state;
state.set_value(hps::HpsResult::POSITIVE);
controller_->OnHpsNotifyChanged(state);
tester.ExpectTotalCount(metrics::kFlakeyHistogramName, 0);
task_environment()->FastForwardBy(kShortTime);
controller_->OnOrientationChanged(false);
controller_->OnOrientationChanged(true);
tester.ExpectTotalCount(metrics::kFlakeyHistogramName, 0);
EXPECT_FALSE(controller_->SnooperPresent());
state.set_value(hps::HpsResult::NEGATIVE);
controller_->OnHpsNotifyChanged(state);
tester.ExpectTotalCount(metrics::kFlakeyHistogramName, 0);
}
}
}