chromium/chrome/browser/metrics/chromeos_system_profile_provider_unittest.cc

// Copyright 2022 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/chromeos_system_profile_provider.h"

#include <stdint.h>

#include <string>

#include "ash/constants/ash_features.h"
#include "base/functional/bind.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/ash/login/demo_mode/demo_session.h"
#include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
#include "chrome/browser/ash/multidevice_setup/multidevice_setup_client_factory.h"
#include "chrome/browser/ash/profiles/profile_helper.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile_manager.h"
#include "chromeos/ash/components/install_attributes/stub_install_attributes.h"
#include "chromeos/ash/components/login/login_state/login_state.h"
#include "chromeos/ash/components/multidevice/remote_device_test_util.h"
#include "chromeos/ash/components/system/fake_statistics_provider.h"
#include "chromeos/ash/components/system/statistics_provider.h"
#include "chromeos/ash/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.h"
#include "chromeos/ash/services/multidevice_setup/public/cpp/multidevice_setup_client_impl.h"
#include "chromeos/constants/chromeos_features.h"
#include "chromeos/dbus/power/power_manager_client.h"
#include "chromeos/dbus/tpm_manager/tpm_manager_client.h"
#include "components/user_manager/scoped_user_manager.h"
#include "components/user_manager/user_manager.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/metrics_proto/system_profile.pb.h"

using Hardware = metrics::SystemProfileProto::Hardware;

namespace {

class FakeMultiDeviceSetupClientImplFactory
    : public ash::multidevice_setup::MultiDeviceSetupClientImpl::Factory {
 public:
  explicit FakeMultiDeviceSetupClientImplFactory(
      std::unique_ptr<ash::multidevice_setup::FakeMultiDeviceSetupClient>
          fake_multidevice_setup_client)
      : fake_multidevice_setup_client_(
            std::move(fake_multidevice_setup_client)) {}

  ~FakeMultiDeviceSetupClientImplFactory() override = default;

  // ash::multidevice_setup::MultiDeviceSetupClientImpl::Factory:
  // NOTE: At most, one client should be created per-test.
  std::unique_ptr<ash::multidevice_setup::MultiDeviceSetupClient>
  CreateInstance(
      mojo::PendingRemote<ash::multidevice_setup::mojom::MultiDeviceSetup>)
      override {
    EXPECT_TRUE(fake_multidevice_setup_client_);
    return std::move(fake_multidevice_setup_client_);
  }

 private:
  std::unique_ptr<ash::multidevice_setup::FakeMultiDeviceSetupClient>
      fake_multidevice_setup_client_;
};

// Wrapper around ChromeOSSystemProfileProvider that initializes
// hardware class in the constructor.
class TestChromeOSSystemProfileProvider : public ChromeOSSystemProfileProvider {
 public:
  TestChromeOSSystemProfileProvider() {
    base::RunLoop run_loop;
    AsyncInit(run_loop.QuitWhenIdleClosure());
    run_loop.Run();
  }

  void GetIdleCallback() {
    ASSERT_TRUE(base::RunLoop::IsRunningOnCurrentThread());
    base::RunLoop().QuitWhenIdle();
  }
};

const AccountId account_id1(AccountId::FromUserEmail("[email protected]"));
const AccountId account_id2(AccountId::FromUserEmail("[email protected]"));
const AccountId account_id3(AccountId::FromUserEmail("[email protected]"));

}  // namespace

class ChromeOSSystemProfileProviderTest : public testing::Test {
 public:
  ChromeOSSystemProfileProviderTest() = default;

  ChromeOSSystemProfileProviderTest(const ChromeOSSystemProfileProviderTest&) =
      delete;
  ChromeOSSystemProfileProviderTest& operator=(
      const ChromeOSSystemProfileProviderTest&) = delete;

 protected:
  void SetUp() override {
    chromeos::TpmManagerClient::InitializeFake();

    ash::multidevice_setup::MultiDeviceSetupClientFactory::GetInstance()
        ->SetServiceIsNULLWhileTestingForTesting(false);
    auto fake_multidevice_setup_client =
        std::make_unique<ash::multidevice_setup::FakeMultiDeviceSetupClient>();
    fake_multidevice_setup_client_ = fake_multidevice_setup_client.get();
    fake_multidevice_setup_client_impl_factory_ =
        std::make_unique<FakeMultiDeviceSetupClientImplFactory>(
            std::move(fake_multidevice_setup_client));
    ash::multidevice_setup::MultiDeviceSetupClientImpl::Factory::
        SetFactoryForTesting(fake_multidevice_setup_client_impl_factory_.get());

    profile_manager_ = std::make_unique<TestingProfileManager>(
        TestingBrowserProcess::GetGlobal());
    ASSERT_TRUE(profile_manager_->SetUp());
    testing_profile_ = profile_manager_->CreateTestingProfile("test_name");

    // Set statistic provider for hardware class tests.
    ash::system::StatisticsProvider::SetTestProvider(
        &fake_statistics_provider_);

    // Initialize the login state trackers.
    if (!ash::LoginState::IsInitialized())
      ash::LoginState::Initialize();
  }

  void TearDown() override {
    // Destroy the login state tracker if it was initialized.
    ash::LoginState::Shutdown();
    chromeos::TpmManagerClient::Shutdown();
    chromeos::PowerManagerClient::Shutdown();
    ash::multidevice_setup::MultiDeviceSetupClientImpl::Factory::
        SetFactoryForTesting(nullptr);
    profile_manager_.reset();
  }

 protected:
  raw_ptr<ash::multidevice_setup::FakeMultiDeviceSetupClient, DanglingUntriaged>
      fake_multidevice_setup_client_;
  base::test::ScopedFeatureList scoped_feature_list_;
  ash::system::ScopedFakeStatisticsProvider fake_statistics_provider_;
  std::unique_ptr<TestingProfileManager> profile_manager_;
  raw_ptr<TestingProfile, DanglingUntriaged> testing_profile_ = nullptr;
  std::unique_ptr<FakeMultiDeviceSetupClientImplFactory>
      fake_multidevice_setup_client_impl_factory_;

 private:
  content::BrowserTaskEnvironment task_environment_;
};

TEST_F(ChromeOSSystemProfileProviderTest, MultiProfileUserCount) {
  // |scoped_enabler| takes over the lifetime of |user_manager|.
  auto* user_manager = new ash::FakeChromeUserManager();
  // TODO(crbug.com/40735060): Overload operator-> in ScopedUserManager.
  user_manager::ScopedUserManager scoped_enabler(
      base::WrapUnique(user_manager));
  user_manager->AddKioskAppUser(account_id1);
  user_manager->AddKioskAppUser(account_id2);
  user_manager->AddKioskAppUser(account_id3);

  user_manager->LoginUser(account_id1);
  user_manager->LoginUser(account_id3);

  TestChromeOSSystemProfileProvider provider;
  provider.OnDidCreateMetricsLog();
  metrics::SystemProfileProto system_profile;
  provider.ProvideSystemProfileMetrics(&system_profile);
  EXPECT_EQ(2u, system_profile.multi_profile_user_count());
}

TEST_F(ChromeOSSystemProfileProviderTest, MultiProfileCountInvalidated) {
  // |scoped_enabler| takes over the lifetime of |user_manager|.
  auto* user_manager = new ash::FakeChromeUserManager();
  // TODO(crbug.com/40735060): Overload operator-> in ScopedUserManager.
  user_manager::ScopedUserManager scoped_enabler(
      base::WrapUnique(user_manager));
  user_manager->AddKioskAppUser(account_id1);
  user_manager->AddKioskAppUser(account_id2);
  user_manager->AddKioskAppUser(account_id3);

  user_manager->LoginUser(account_id1);

  TestChromeOSSystemProfileProvider provider;
  provider.OnDidCreateMetricsLog();

  metrics::SystemProfileProto system_profile;
  provider.ProvideSystemProfileMetrics(&system_profile);
  EXPECT_EQ(1u, system_profile.multi_profile_user_count());

  user_manager->LoginUser(account_id2);
  provider.ProvideSystemProfileMetrics(&system_profile);
  EXPECT_EQ(0u, system_profile.multi_profile_user_count());
}

TEST_F(ChromeOSSystemProfileProviderTest,
       HasLinkedAndroidPhoneAndEnabledFeatures) {
  fake_multidevice_setup_client_->SetHostStatusWithDevice(
      std::make_pair(ash::multidevice_setup::mojom::HostStatus::kHostVerified,
                     ash::multidevice::CreateRemoteDeviceRefForTest()));
  fake_multidevice_setup_client_->SetFeatureState(
      ash::multidevice_setup::mojom::Feature::kInstantTethering,
      ash::multidevice_setup::mojom::FeatureState::kEnabledByUser);
  fake_multidevice_setup_client_->SetFeatureState(
      ash::multidevice_setup::mojom::Feature::kSmartLock,
      ash::multidevice_setup::mojom::FeatureState::kEnabledByUser);

  // |scoped_enabler| takes over the lifetime of |user_manager|.
  auto* user_manager = new ash::FakeChromeUserManager();
  // TODO(crbug.com/40735060): Overload operator-> in ScopedUserManager.
  user_manager::ScopedUserManager scoped_enabler(
      base::WrapUnique(user_manager));
  user_manager->AddKioskAppUser(account_id1);
  user_manager->LoginUser(account_id1);
  const user_manager::User* primary_user = user_manager->GetPrimaryUser();
  ash::ProfileHelper::Get()->SetUserToProfileMappingForTesting(
      primary_user, testing_profile_);

  TestChromeOSSystemProfileProvider provider;
  metrics::SystemProfileProto system_profile;
  provider.ProvideSystemProfileMetrics(&system_profile);

  EXPECT_TRUE(system_profile.has_linked_android_phone_data());
  EXPECT_TRUE(
      system_profile.linked_android_phone_data().has_phone_model_name_hash());
  EXPECT_TRUE(system_profile.linked_android_phone_data()
                  .is_instant_tethering_enabled());
  EXPECT_TRUE(
      system_profile.linked_android_phone_data().is_smartlock_enabled());
  EXPECT_FALSE(
      system_profile.linked_android_phone_data().is_messages_enabled());
}

TEST_F(ChromeOSSystemProfileProviderTest, FullHardwareClass) {
  const std::string expected_full_hw_class = "feature_enabled";
  fake_statistics_provider_.SetMachineStatistic("hardware_class",
                                                expected_full_hw_class);

  TestChromeOSSystemProfileProvider provider;
  provider.OnDidCreateMetricsLog();
  metrics::SystemProfileProto system_profile;
  provider.ProvideSystemProfileMetrics(&system_profile);

  ASSERT_TRUE(system_profile.has_hardware());
  std::string proto_full_hw_class =
      system_profile.hardware().full_hardware_class();

  EXPECT_EQ(expected_full_hw_class, proto_full_hw_class);
}

TEST_F(ChromeOSSystemProfileProviderTest, DemoModeDimensions) {
  testing_profile_->ScopedCrosSettingsTestHelper()
      ->InstallAttributes()
      ->SetDemoMode();
  const std::string expected_country = "CA";
  const std::string expected_retailer_id = "ABC";
  const std::string expected_store_id = "12345";
  const std::string app_expected_version = "0.0.0.0";
  const std::string resources_expected_version = "0.0.0.1";
  scoped_feature_list_.InitWithFeatures(
      {chromeos::features::kCloudGamingDevice,
       ash::features::kFeatureManagementFeatureAwareDeviceDemoMode},
      {});
  g_browser_process->local_state()->SetString("demo_mode.country",
                                              expected_country);
  g_browser_process->local_state()->SetString("demo_mode.retailer_id",
                                              expected_retailer_id);
  g_browser_process->local_state()->SetString("demo_mode.store_id",
                                              expected_store_id);
  g_browser_process->local_state()->SetString("demo_mode.app_version",
                                              app_expected_version);
  g_browser_process->local_state()->SetString("demo_mode.resources_version",
                                              resources_expected_version);

  TestChromeOSSystemProfileProvider provider;
  provider.OnDidCreateMetricsLog();
  metrics::SystemProfileProto system_profile;
  provider.ProvideSystemProfileMetrics(&system_profile);

  ASSERT_TRUE(system_profile.has_demo_mode_dimensions());
  ASSERT_TRUE(system_profile.demo_mode_dimensions().has_country());
  ASSERT_TRUE(system_profile.demo_mode_dimensions().has_retailer());
  ASSERT_TRUE(
      system_profile.demo_mode_dimensions().retailer().has_retailer_id());
  ASSERT_TRUE(system_profile.demo_mode_dimensions().retailer().has_store_id());
  EXPECT_EQ(system_profile.demo_mode_dimensions().customization_facet_size(),
            2);
  ASSERT_EQ(
      system_profile.demo_mode_dimensions().customization_facet().at(0),
      metrics::
          SystemProfileProto_DemoModeDimensions_CustomizationFacet_CLOUD_GAMING_DEVICE);
  ASSERT_EQ(
      system_profile.demo_mode_dimensions().customization_facet().at(1),
      metrics::
          SystemProfileProto_DemoModeDimensions_CustomizationFacet_FEATURE_AWARE_DEVICE);
  std::string country = system_profile.demo_mode_dimensions().country();
  std::string retailer_id =
      system_profile.demo_mode_dimensions().retailer().retailer_id();
  std::string store_id =
      system_profile.demo_mode_dimensions().retailer().store_id();
  std::string app_version = system_profile.demo_mode_dimensions().app_version();
  std::string resources_version =
      system_profile.demo_mode_dimensions().resources_version();

  EXPECT_EQ(country, expected_country);
  EXPECT_EQ(retailer_id, expected_retailer_id);
  EXPECT_EQ(store_id, expected_store_id);
  EXPECT_EQ(app_version, app_expected_version);
  EXPECT_EQ(resources_version, resources_expected_version);
}

TEST_F(ChromeOSSystemProfileProviderTest, TpmRwFirmwareVersion) {
  const std::string expected_rw_firmware_version = "0.5.190";
  chromeos::TpmManagerClient::Get()
      ->GetTestInterface()
      ->mutable_version_info_reply()
      ->set_rw_version(expected_rw_firmware_version);

  TestChromeOSSystemProfileProvider provider;
  provider.OnDidCreateMetricsLog();
  metrics::SystemProfileProto system_profile;
  provider.ProvideSystemProfileMetrics(&system_profile);

  ASSERT_TRUE(system_profile.has_hardware());
  ASSERT_TRUE(system_profile.hardware().has_tpm_rw_firmware_version());

  EXPECT_EQ(system_profile.hardware().tpm_rw_firmware_version(),
            expected_rw_firmware_version);
}