|  | // 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/metrics/per_user_state_manager_chromeos.h" | 
|  |  | 
|  | #include "chrome/browser/ash/login/login_pref_names.h" | 
|  | #include "chrome/browser/ash/login/startup_utils.h" | 
|  | #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h" | 
|  | #include "chrome/browser/ash/profiles/profile_helper.h" | 
|  | #include "chrome/browser/metrics/profile_pref_names.h" | 
|  | #include "chrome/browser/prefs/browser_prefs.h" | 
|  | #include "chrome/test/base/testing_profile.h" | 
|  | #include "components/metrics/metrics_pref_names.h" | 
|  | #include "components/metrics/unsent_log_store.h" | 
|  | #include "components/pref_registry/pref_registry_syncable.h" | 
|  | #include "components/prefs/testing_pref_service.h" | 
|  | #include "components/sync_preferences/pref_service_mock_factory.h" | 
|  | #include "components/sync_preferences/pref_service_syncable.h" | 
|  | #include "content/public/test/browser_task_environment.h" | 
|  | #include "testing/gmock/include/gmock/gmock.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  |  | 
|  | namespace metrics { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | using ::testing::Eq; | 
|  | using ::testing::Ne; | 
|  |  | 
|  | // Testing version of PerUserStateManager that decouples external dependencies | 
|  | // used for unit testing purposes. | 
|  | class TestPerUserStateManager : public PerUserStateManagerChromeOS { | 
|  | public: | 
|  | TestPerUserStateManager(user_manager::UserManager* user_manager, | 
|  | PrefService* local_state, | 
|  | const MetricsLogStore::StorageLimits& storage_limits, | 
|  | const std::string& signing_key) | 
|  | : PerUserStateManagerChromeOS(/*metrics_service_client=*/nullptr, | 
|  | user_manager, | 
|  | local_state, | 
|  | storage_limits, | 
|  | signing_key) {} | 
|  | ~TestPerUserStateManager() override = default; | 
|  |  | 
|  | void SetUserLogStore(std::unique_ptr<UnsentLogStore> log_store) override { | 
|  | is_log_store_set_ = true; | 
|  | } | 
|  | void SetIsManaged(bool is_managed) { is_managed_ = is_managed; } | 
|  |  | 
|  | void SetDeviceMetricsConsent(bool metrics_consent) { | 
|  | device_metrics_consent_ = metrics_consent; | 
|  | } | 
|  |  | 
|  | void SetIsDeviceOwned(bool is_device_owned) { | 
|  | is_device_owned_ = is_device_owned; | 
|  | } | 
|  |  | 
|  | bool is_log_store_set() const { return is_log_store_set_; } | 
|  | bool is_client_id_reset() const { return is_client_id_reset_; } | 
|  |  | 
|  | protected: | 
|  | void UnsetUserLogStore() override { is_log_store_set_ = false; } | 
|  |  | 
|  | void ForceClientIdReset() override { is_client_id_reset_ = true; } | 
|  |  | 
|  | bool IsReportingPolicyManaged() const override { return is_managed_; } | 
|  |  | 
|  | bool GetDeviceMetricsConsent() const override { | 
|  | return device_metrics_consent_; | 
|  | } | 
|  |  | 
|  | bool HasUserLogStore() const override { return is_log_store_set_; } | 
|  |  | 
|  | bool IsDeviceOwned() const override { return is_device_owned_; } | 
|  |  | 
|  | void WaitForOwnershipStatus() override { | 
|  | InitializeProfileMetricsState( | 
|  | is_device_owned_ ? ash::DeviceSettingsService::OWNERSHIP_TAKEN | 
|  | : ash::DeviceSettingsService::OWNERSHIP_NONE); | 
|  | } | 
|  |  | 
|  | private: | 
|  | bool is_log_store_set_ = false; | 
|  | bool is_client_id_reset_ = false; | 
|  | bool is_managed_ = false; | 
|  | bool device_metrics_consent_ = true; | 
|  | bool is_device_owned_ = true; | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | class PerUserStateManagerChromeOSTest : public testing::Test { | 
|  | public: | 
|  | PerUserStateManagerChromeOSTest() : signing_key_("signing_key") {} | 
|  | // Profiles must be destructed on the UI thread. | 
|  | ~PerUserStateManagerChromeOSTest() override { | 
|  | // Destruct before user manager because of dependency. | 
|  | per_user_state_manager_.reset(); | 
|  |  | 
|  | // Profiles must be destructed on the UI thread. | 
|  | profile_.reset(); | 
|  | } | 
|  |  | 
|  | user_manager::User* RegisterUser(const AccountId& account_id) { | 
|  | // Create profile. | 
|  | TestingProfile::Builder profile_builder; | 
|  | sync_preferences::PrefServiceMockFactory factory; | 
|  | auto registry = base::MakeRefCounted<user_prefs::PrefRegistrySyncable>(); | 
|  |  | 
|  | RegisterUserProfilePrefs(registry.get()); | 
|  |  | 
|  | std::unique_ptr<sync_preferences::PrefServiceSyncable> prefs( | 
|  | factory.CreateSyncable(registry.get())); | 
|  | profile_builder.SetPrefService(std::move(prefs)); | 
|  | profile_ = profile_builder.Build(); | 
|  |  | 
|  | return test_user_manager_->AddUserWithAffiliationAndTypeAndProfile( | 
|  | account_id, false, user_manager::USER_TYPE_REGULAR, profile_.get()); | 
|  | } | 
|  |  | 
|  | user_manager::User* RegisterGuestUser() { | 
|  | // Create profile. | 
|  | TestingProfile::Builder profile_builder; | 
|  | sync_preferences::PrefServiceMockFactory factory; | 
|  | auto registry = base::MakeRefCounted<user_prefs::PrefRegistrySyncable>(); | 
|  |  | 
|  | RegisterUserProfilePrefs(registry.get()); | 
|  |  | 
|  | std::unique_ptr<sync_preferences::PrefServiceSyncable> prefs( | 
|  | factory.CreateSyncable(registry.get())); | 
|  | profile_builder.SetPrefService(std::move(prefs)); | 
|  | profile_ = profile_builder.Build(); | 
|  |  | 
|  | auto* user = test_user_manager_->AddGuestUser(); | 
|  | ash::ProfileHelper::Get()->SetUserToProfileMappingForTesting( | 
|  | user, profile_.get()); | 
|  | return user; | 
|  | } | 
|  |  | 
|  | void LoginRegularUser(user_manager::User* user) { | 
|  | test_user_manager_->LoginUser(user->GetAccountId()); | 
|  | test_user_manager_->SwitchActiveUser(user->GetAccountId()); | 
|  | test_user_manager_->SimulateUserProfileLoad(user->GetAccountId()); | 
|  | } | 
|  |  | 
|  | void LoginGuestUser(user_manager::User* user) { | 
|  | test_user_manager_->LoginUser(user->GetAccountId()); | 
|  | test_user_manager_->set_current_user_ephemeral(true); | 
|  | test_user_manager_->SwitchActiveUser(user->GetAccountId()); | 
|  | test_user_manager_->SimulateUserProfileLoad(user->GetAccountId()); | 
|  | } | 
|  |  | 
|  | void InitializeProfileState(const std::string& user_id, | 
|  | bool metrics_consent, | 
|  | bool has_consented_to_metrics) { | 
|  | profile_->GetPrefs()->SetString(prefs::kMetricsUserId, user_id); | 
|  | profile_->GetPrefs()->SetBoolean(prefs::kMetricsUserConsent, | 
|  | metrics_consent); | 
|  | profile_->GetPrefs()->SetBoolean( | 
|  | prefs::kMetricsRequiresClientIdResetOnConsent, | 
|  | has_consented_to_metrics); | 
|  | profile_->GetPrefs()->SetBoolean(prefs::kMetricsUserInheritOwnerConsent, | 
|  | false); | 
|  | } | 
|  |  | 
|  | void SetShouldInheritOwnerConsent(bool should_inherit) { | 
|  | profile_->GetPrefs()->SetBoolean(prefs::kMetricsUserInheritOwnerConsent, | 
|  | should_inherit); | 
|  | } | 
|  |  | 
|  | void SetGuestOobeMetricsConsent(bool metrics_consent) { | 
|  | GetLocalState()->SetBoolean(ash::prefs::kOobeGuestMetricsEnabled, | 
|  | metrics_consent); | 
|  | } | 
|  |  | 
|  | void RunUntilIdle() { task_environment_.RunUntilIdle(); } | 
|  |  | 
|  | PrefService* GetLocalState() { return &pref_service_; } | 
|  | Profile* GetTestProfile() { return profile_.get(); } | 
|  | TestPerUserStateManager* GetPerUserStateManager() { | 
|  | return per_user_state_manager_.get(); | 
|  | } | 
|  |  | 
|  | protected: | 
|  | void SetUp() override { | 
|  | storage_limits_.min_ongoing_log_queue_count = 5; | 
|  | storage_limits_.min_ongoing_log_queue_size = 10000; | 
|  | storage_limits_.max_ongoing_log_size = 0; | 
|  |  | 
|  | test_user_manager_ = std::make_unique<ash::FakeChromeUserManager>(); | 
|  |  | 
|  | per_user_state_manager_ = std::make_unique<TestPerUserStateManager>( | 
|  | test_user_manager_.get(), &pref_service_, storage_limits_, | 
|  | signing_key_); | 
|  |  | 
|  | ash::StartupUtils::RegisterPrefs(pref_service_.registry()); | 
|  | PerUserStateManagerChromeOS::RegisterPrefs(pref_service_.registry()); | 
|  | } | 
|  |  | 
|  | std::unique_ptr<TestPerUserStateManager> per_user_state_manager_; | 
|  | std::unique_ptr<ash::FakeChromeUserManager> test_user_manager_; | 
|  | std::unique_ptr<TestingProfile> profile_; | 
|  |  | 
|  | TestingPrefServiceSimple pref_service_; | 
|  |  | 
|  | // Profiles must be created in browser threads. | 
|  | content::BrowserTaskEnvironment task_environment_; | 
|  |  | 
|  | MetricsLogStore::StorageLimits storage_limits_; | 
|  | std::string signing_key_; | 
|  | }; | 
|  |  | 
|  | TEST_F(PerUserStateManagerChromeOSTest, UserIdErasedWhenConsentTurnedOff) { | 
|  | auto* test_user = | 
|  | RegisterUser(AccountId::FromUserEmailGaiaId("test@example.com", "1")); | 
|  | InitializeProfileState(/*user_id=*/"user_id", | 
|  | /*metrics_consent=*/true, | 
|  | /*has_consented_to_metrics=*/true); | 
|  | GetPerUserStateManager()->SetIsManaged(false); | 
|  |  | 
|  | LoginRegularUser(test_user); | 
|  |  | 
|  | // User log store is created async. Ensure that the log store loading | 
|  | // finishes. | 
|  | RunUntilIdle(); | 
|  |  | 
|  | GetPerUserStateManager()->SetCurrentUserMetricsConsent(false); | 
|  |  | 
|  | EXPECT_FALSE( | 
|  | GetTestProfile()->GetPrefs()->GetBoolean(prefs::kMetricsUserConsent)); | 
|  | EXPECT_TRUE(GetTestProfile()->GetPrefs()->GetBoolean( | 
|  | prefs::kMetricsRequiresClientIdResetOnConsent)); | 
|  |  | 
|  | // Client ID should only be reset when going from off->on. | 
|  | EXPECT_FALSE(GetPerUserStateManager()->is_client_id_reset()); | 
|  |  | 
|  | // Ensure that state is clean. | 
|  | EXPECT_THAT(GetTestProfile()->GetPrefs()->GetString(prefs::kMetricsUserId), | 
|  | Eq("")); | 
|  | EXPECT_THAT(GetLocalState()->GetString(prefs::kMetricsCurrentUserId), Eq("")); | 
|  | } | 
|  |  | 
|  | TEST_F(PerUserStateManagerChromeOSTest, | 
|  | ClientIdReset_WhenConsentTurnedOn_AndUserSentMetrics) { | 
|  | auto* test_user = | 
|  | RegisterUser(AccountId::FromUserEmailGaiaId("test@example.com", "1")); | 
|  | InitializeProfileState(/*user_id=*/"", /*metrics_consent=*/false, | 
|  | /*has_consented_to_metrics=*/true); | 
|  | GetPerUserStateManager()->SetIsManaged(false); | 
|  |  | 
|  | // Simulate user login. | 
|  | LoginRegularUser(test_user); | 
|  |  | 
|  | // User log store is created async. Ensure that the log store loading | 
|  | // finishes. | 
|  | RunUntilIdle(); | 
|  |  | 
|  | GetPerUserStateManager()->SetCurrentUserMetricsConsent(true); | 
|  |  | 
|  | EXPECT_TRUE( | 
|  | GetTestProfile()->GetPrefs()->GetBoolean(prefs::kMetricsUserConsent)); | 
|  | EXPECT_TRUE(GetTestProfile()->GetPrefs()->GetBoolean( | 
|  | prefs::kMetricsRequiresClientIdResetOnConsent)); | 
|  |  | 
|  | // Client ID should be reset when going from off->on and user has sent | 
|  | // metrics. | 
|  | EXPECT_TRUE(GetPerUserStateManager()->is_client_id_reset()); | 
|  | } | 
|  |  | 
|  | TEST_F(PerUserStateManagerChromeOSTest, | 
|  | ClientIdNotResetForUserWhoNeverReportedMetrics) { | 
|  | auto* test_user = | 
|  | RegisterUser(AccountId::FromUserEmailGaiaId("test@example.com", "1")); | 
|  | InitializeProfileState(/*user_id=*/"", /*metrics_consent=*/false, | 
|  | /*has_consented_to_metrics=*/false); | 
|  | GetPerUserStateManager()->SetIsManaged(false); | 
|  |  | 
|  | // Simulate user login. | 
|  | LoginRegularUser(test_user); | 
|  |  | 
|  | // User log store is created async. Ensure that the log store loading | 
|  | // finishes. | 
|  | RunUntilIdle(); | 
|  |  | 
|  | GetPerUserStateManager()->SetCurrentUserMetricsConsent(true); | 
|  |  | 
|  | EXPECT_TRUE( | 
|  | GetTestProfile()->GetPrefs()->GetBoolean(prefs::kMetricsUserConsent)); | 
|  | EXPECT_TRUE(GetTestProfile()->GetPrefs()->GetBoolean( | 
|  | prefs::kMetricsRequiresClientIdResetOnConsent)); | 
|  |  | 
|  | // Client ID should not be reset when going from off->on and user had not sent | 
|  | // metrics. | 
|  | EXPECT_FALSE(GetPerUserStateManager()->is_client_id_reset()); | 
|  | } | 
|  |  | 
|  | TEST_F(PerUserStateManagerChromeOSTest, | 
|  | EphemeralLogStoreUsedForGuestSessionWithDisabledPolicy) { | 
|  | GetPerUserStateManager()->SetIsManaged(false); | 
|  | GetPerUserStateManager()->SetDeviceMetricsConsent(false); | 
|  |  | 
|  | // Simulate ephemeral user login. | 
|  | LoginGuestUser(RegisterGuestUser()); | 
|  |  | 
|  | // User log store is created async. Ensure that the log store loading | 
|  | // finishes. | 
|  | RunUntilIdle(); | 
|  |  | 
|  | // Log store should be set to use temporary cryptohome for when device metrics | 
|  | // consent is off. | 
|  | EXPECT_TRUE(GetPerUserStateManager()->is_log_store_set()); | 
|  | } | 
|  |  | 
|  | TEST_F(PerUserStateManagerChromeOSTest, | 
|  | LocalStateLogStoreUsedForGuestWithEnabledPolicy) { | 
|  | GetPerUserStateManager()->SetIsManaged(false); | 
|  | GetPerUserStateManager()->SetDeviceMetricsConsent(true); | 
|  |  | 
|  | // Simulate ephemeral user login. | 
|  | LoginGuestUser(RegisterGuestUser()); | 
|  |  | 
|  | // User log store is created async. Ensure that the log store loading | 
|  | // finishes. | 
|  | RunUntilIdle(); | 
|  |  | 
|  | // Log store should not be loaded yet to store logs in local state for when | 
|  | // device metrics consent is on. | 
|  | EXPECT_FALSE(GetPerUserStateManager()->is_log_store_set()); | 
|  | } | 
|  |  | 
|  | TEST_F(PerUserStateManagerChromeOSTest, | 
|  | GuestWithNoDeviceOwnerLoadsConsentSetOnOobe) { | 
|  | GetPerUserStateManager()->SetIsManaged(false); | 
|  | GetPerUserStateManager()->SetIsDeviceOwned(false); | 
|  |  | 
|  | // Guest user went through oobe. | 
|  | SetGuestOobeMetricsConsent(true); | 
|  |  | 
|  | // Simulate ephemeral user login. | 
|  | LoginGuestUser(RegisterGuestUser()); | 
|  |  | 
|  | // User log store is created async. Ensure that the log store loading | 
|  | // finishes. | 
|  | RunUntilIdle(); | 
|  |  | 
|  | // Consent set by guest during OOBE. | 
|  | EXPECT_TRUE( | 
|  | *GetPerUserStateManager()->GetCurrentUserReportingConsentIfApplicable()); | 
|  |  | 
|  | // Ensure state has been reset. | 
|  | EXPECT_FALSE( | 
|  | GetLocalState()->GetBoolean(ash::prefs::kOobeGuestMetricsEnabled)); | 
|  |  | 
|  | // Check to ensure that metrics consent is stored in profile pref. | 
|  | EXPECT_TRUE( | 
|  | GetTestProfile()->GetPrefs()->GetBoolean(prefs::kMetricsUserConsent)); | 
|  |  | 
|  | // Log store should be set to use ephemeral partition in the absence of a | 
|  | // device owner. | 
|  | EXPECT_TRUE(GetPerUserStateManager()->is_log_store_set()); | 
|  | } | 
|  |  | 
|  | TEST_F(PerUserStateManagerChromeOSTest, OwnerCannotUsePerUser) { | 
|  | // Create device owner. | 
|  | const AccountId account_id = | 
|  | AccountId::FromUserEmailGaiaId("test@example.com", "1"); | 
|  | auto* test_user = RegisterUser(account_id); | 
|  | test_user_manager_->SetOwnerId(account_id); | 
|  |  | 
|  | // Simulate user login. | 
|  | LoginRegularUser(test_user); | 
|  |  | 
|  | // User log store is created async. Ensure that the log store loading | 
|  | // finishes. | 
|  | RunUntilIdle(); | 
|  |  | 
|  | // Owner should not have a consent. | 
|  | EXPECT_FALSE( | 
|  | GetPerUserStateManager()->GetCurrentUserReportingConsentIfApplicable()); | 
|  |  | 
|  | // User logs should still be persisted in the owner's cryptohome. | 
|  | EXPECT_TRUE(GetPerUserStateManager()->is_log_store_set()); | 
|  | } | 
|  |  | 
|  | TEST_F(PerUserStateManagerChromeOSTest, | 
|  | NewOrMigratingUserInheritsOwnerConsent) { | 
|  | auto* test_user = | 
|  | RegisterUser(AccountId::FromUserEmailGaiaId("test@example.com", "1")); | 
|  | InitializeProfileState(/*user_id=*/"", /*metrics_consent=*/false, | 
|  | /*has_consented_to_metrics=*/false); | 
|  |  | 
|  | // User should inherit owner consent if migrating or new user. | 
|  | SetShouldInheritOwnerConsent(true); | 
|  |  | 
|  | GetPerUserStateManager()->SetIsManaged(false); | 
|  | GetPerUserStateManager()->SetDeviceMetricsConsent(true); | 
|  |  | 
|  | // Simulate user login. | 
|  | LoginRegularUser(test_user); | 
|  |  | 
|  | // User log store is created async. Ensure that the log store loading | 
|  | // finishes. | 
|  | RunUntilIdle(); | 
|  |  | 
|  | // User consent should be set to true since pref is true and device metrics | 
|  | // consent is also true. | 
|  | EXPECT_TRUE( | 
|  | GetPerUserStateManager()->GetCurrentUserReportingConsentIfApplicable()); | 
|  |  | 
|  | EXPECT_TRUE(GetPerUserStateManager()->is_log_store_set()); | 
|  | } | 
|  |  | 
|  | // Multi-user sessions are deprecated, but still need to be supported. This test | 
|  | // ensures that the primary user (user originally used to login) is used and all | 
|  | // other users are ignored. | 
|  | TEST_F(PerUserStateManagerChromeOSTest, MultiUserUsesPrimaryUser) { | 
|  | auto* test_user1 = | 
|  | RegisterUser(AccountId::FromUserEmailGaiaId("test1@example.com", "1")); | 
|  | InitializeProfileState(/*user_id=*/"", /*metrics_consent=*/false, | 
|  | /*has_consented_to_metrics=*/true); | 
|  | GetPerUserStateManager()->SetIsManaged(false); | 
|  | GetPerUserStateManager()->SetDeviceMetricsConsent(true); | 
|  |  | 
|  | // Simulate user login. | 
|  | LoginRegularUser(test_user1); | 
|  |  | 
|  | // User log store is created async. Ensure that the log store loading | 
|  | // finishes. | 
|  | RunUntilIdle(); | 
|  |  | 
|  | GetPerUserStateManager()->SetCurrentUserMetricsConsent(true); | 
|  |  | 
|  | // User consent should be set to true since pref is true and device metrics | 
|  | // consent is also true. | 
|  | EXPECT_TRUE( | 
|  | *GetPerUserStateManager()->GetCurrentUserReportingConsentIfApplicable()); | 
|  |  | 
|  | EXPECT_TRUE(GetPerUserStateManager()->is_log_store_set()); | 
|  |  | 
|  | // Create secondary user. | 
|  | TestingProfile::Builder profile_builder; | 
|  | sync_preferences::PrefServiceMockFactory factory; | 
|  | auto registry = base::MakeRefCounted<user_prefs::PrefRegistrySyncable>(); | 
|  | RegisterUserProfilePrefs(registry.get()); | 
|  | std::unique_ptr<sync_preferences::PrefServiceSyncable> prefs( | 
|  | factory.CreateSyncable(registry.get())); | 
|  | profile_builder.SetPrefService(std::move(prefs)); | 
|  | auto test_user2_profile = profile_builder.Build(); | 
|  | AccountId test_user2_account_id = | 
|  | AccountId::FromUserEmailGaiaId("test2@example.com", "2"); | 
|  |  | 
|  | // Add user. | 
|  | user_manager::User* test_user2 = | 
|  | test_user_manager_->AddUserWithAffiliationAndTypeAndProfile( | 
|  | test_user2_account_id, false, user_manager::USER_TYPE_REGULAR, | 
|  | test_user2_profile.get()); | 
|  |  | 
|  | // Explicitly set the user consent to false. | 
|  | test_user2_profile->GetPrefs()->SetBoolean(prefs::kMetricsUserConsent, false); | 
|  |  | 
|  | // Simulate user login. | 
|  | LoginRegularUser(test_user2); | 
|  |  | 
|  | // User log store is created async. Ensure that the log store loading | 
|  | // finishes. | 
|  | RunUntilIdle(); | 
|  |  | 
|  | // User consent should still be true since that's the value of the primary | 
|  | // user. | 
|  | EXPECT_TRUE( | 
|  | *GetPerUserStateManager()->GetCurrentUserReportingConsentIfApplicable()); | 
|  | EXPECT_TRUE(GetPerUserStateManager()->is_log_store_set()); | 
|  |  | 
|  | // Enable user2's metrics consent and disable it. | 
|  | test_user2_profile->GetPrefs()->SetBoolean(prefs::kMetricsUserConsent, true); | 
|  | test_user2_profile->GetPrefs()->SetBoolean(prefs::kMetricsUserConsent, false); | 
|  |  | 
|  | // User consent should still be true since that's the value of the primary | 
|  | // user. | 
|  | EXPECT_TRUE( | 
|  | *GetPerUserStateManager()->GetCurrentUserReportingConsentIfApplicable()); | 
|  |  | 
|  | // Profiles must be destructed on the UI thread. | 
|  | test_user2_profile.reset(); | 
|  | } | 
|  |  | 
|  | }  // namespace metrics |