chromium/chromeos/ash/components/system/statistics_provider_impl.h

// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROMEOS_ASH_COMPONENTS_SYSTEM_STATISTICS_PROVIDER_IMPL_H_
#define CHROMEOS_ASH_COMPONENTS_SYSTEM_STATISTICS_PROVIDER_IMPL_H_

#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <utility>
#include <vector>

#include "base/command_line.h"
#include "base/containers/flat_map.h"
#include "base/files/file_path.h"
#include "base/functional/callback_forward.h"
#include "base/memory/scoped_refptr.h"
#include "base/synchronization/atomic_flag.h"
#include "base/synchronization/lock.h"
#include "base/synchronization/waitable_event.h"
#include "base/task/sequenced_task_runner.h"
#include "chromeos/ash/components/system/name_value_pairs_parser.h"
#include "chromeos/ash/components/system/statistics_provider.h"

namespace ash::system {

class COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_SYSTEM) StatisticsProviderImpl
    : public StatisticsProvider {
 public:
  struct StatisticsSources {
    StatisticsSources();
    ~StatisticsSources();

    StatisticsSources(const StatisticsSources& other);
    StatisticsSources& operator=(const StatisticsSources& other);

    StatisticsSources(StatisticsSources&& other);
    StatisticsSources& operator=(StatisticsSources&& other);

    // Command line for retrieving a filtered list of VPD key/value pairs. (Or,
    // a fake tool.)
    base::CommandLine vpd_tool{base::CommandLine::NO_PROGRAM};
    // Binary to fake crossystem tool with arguments. E.g. echo.
    base::CommandLine crossystem_tool{base::CommandLine::NO_PROGRAM};

    base::FilePath machine_info_filepath;
    base::FilePath oem_manifest_filepath;
    base::FilePath cros_regions_filepath;
  };

  // Constructs a provider with given `testing_sources` for testing purposes.
  static std::unique_ptr<StatisticsProviderImpl> CreateProviderForTesting(
      StatisticsSources testing_sources);

  // Constructs a provider with default source paths.
  StatisticsProviderImpl();
  ~StatisticsProviderImpl() override;

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

  // StatisticsProvider implementation:
  void StartLoadingMachineStatistics(bool load_oem_manifest) override;
  void ScheduleOnMachineStatisticsLoaded(base::OnceClosure callback) override;

  // If `ash::switches::kCrosRegion` switch is set, looks for the requested
  // statistic in the region file and ignores any other sources. Otherwise
  // returns the statistic from the first matching source.
  std::optional<std::string_view> GetMachineStatistic(
      std::string_view name) override;
  FlagValue GetMachineFlag(std::string_view name) override;

  void Shutdown() override;

  // Returns true when Chrome OS is running in a VM. NOTE: if crossystem is not
  // installed it will return false even if Chrome OS is running in a VM.
  bool IsRunningOnVm() override;

  // Returns true when ChromeOS is running in debug mode. NOTE: if crossystem
  // is not installed it will return false even if ChromeOS is running in debug
  // mode.
  bool IsCrosDebugMode() override;

  VpdStatus GetVpdStatus() const override;

 private:
  using MachineFlags = base::flat_map<std::string, bool>;

  explicit StatisticsProviderImpl(StatisticsSources sources);

  // Called when statistics have finished loading. Unblocks pending calls to
  // `WaitForStatisticsLoaded()` and schedules callbacks passed to
  // `ScheduleOnMachineStatisticsLoaded()`.
  void SignalStatisticsLoaded();

  // Waits up to `kTimeoutSecs` for statistics to be loaded. Returns true if
  // they were loaded successfully.
  bool WaitForStatisticsLoaded(std::string_view statistic_name);

  // Loads the machine statistics off of disk. Runs on the file thread.
  void LoadMachineStatistics(bool load_oem_manifest);

  // Loads calls the crossystem tool and loads statistics from its output.
  void LoadCrossystemTool();

  // Loads the machine info statistics off of disk. Runs on the file thread.
  void LoadMachineInfoFile();

  // Loads the VPD statistics. Runs on the file thread.
  void LoadVpd();

  // Loads the OEM statistics off of disk. Runs on the file thread.
  void LoadOemManifestFromFile(const base::FilePath& file);

  // Loads regional data off of disk. Runs on the file thread.
  void LoadRegionsFile(const base::FilePath& filename, std::string_view region);

  // Extracts known data from `regional_data_`.
  std::optional<std::string_view> GetRegionalInformation(
      std::string_view name) const;

  StatisticsSources sources_;

  bool load_statistics_started_;
  NameValuePairsParser::NameValueMap machine_info_;
  MachineFlags machine_flags_;
  // Statistics extracted from region file and associated with `kRegionKey`
  // region.
  base::flat_map<std::string, std::string> region_info_;
  base::AtomicFlag cancellation_flag_;
  bool oem_manifest_loaded_;

  // Stores VPD partitions status.
  // VPD partition or partitions are considered in invalid state if:
  // 1. The VPD dump program encounters an error.
  // 2. The region in question (RO or RW) is reported invalid (e.g., erased or
  //    corrupted).
  VpdStatus vpd_status_{VpdStatus::kUnknown};

  // Lock held when `statistics_loaded_` is signaled and when
  // `statistics_loaded_callbacks_` is accessed.
  base::Lock statistics_loaded_lock_;

  // Signaled once machine statistics are loaded. It is guaranteed that
  // `machine_info_` and `machine_flags_` don't change once this is signaled.
  base::WaitableEvent statistics_loaded_;

  // Callbacks to schedule once machine statistics are loaded.
  std::vector<
      std::pair<base::OnceClosure, scoped_refptr<base::SequencedTaskRunner>>>
      statistics_loaded_callbacks_;
};

}  // namespace ash::system

#endif  // CHROMEOS_ASH_COMPONENTS_SYSTEM_STATISTICS_PROVIDER_IMPL_H_