chromium/chrome/browser/ash/policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer_uploaded_crash_info_manager.h

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

#ifndef CHROME_BROWSER_ASH_POLICY_REPORTING_METRICS_REPORTING_FATAL_CRASH_FATAL_CRASH_EVENTS_OBSERVER_UPLOADED_CRASH_INFO_MANAGER_H_
#define CHROME_BROWSER_ASH_POLICY_REPORTING_METRICS_REPORTING_FATAL_CRASH_FATAL_CRASH_EVENTS_OBSERVER_UPLOADED_CRASH_INFO_MANAGER_H_

#include <atomic>
#include <cstddef>
#include <memory>
#include <string_view>

#include "base/memory/scoped_refptr.h"
#include "base/sequence_checker.h"
#include "base/task/sequenced_task_runner.h"
#include "base/time/time.h"
#include "chrome/browser/ash/policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer.h"
#include "chromeos/ash/services/cros_healthd/public/mojom/cros_healthd_events.mojom.h"
#include "components/reporting/util/status.h"
#include "components/reporting/util/statusor.h"

namespace reporting {

class FatalCrashEventsObserver::UploadedCrashInfoManager {
 public:
  // Callback type once the save file is loaded.
  using SaveFileLoadedCallback = base::OnceCallback<void()>;

  // Create a `UploadedCrashInfoManager` instance.
  //
  // Params:
  //
  // - save_file_path: Path to the save file.
  // - save_file_loaded_callback: The value of `save_file_loaded_callback_`. See
  //   its document.
  // - io_task_runner: The task runner to run IO tasks on. If nullptr, the
  //                   constructor would create a default task runner.
  static std::unique_ptr<UploadedCrashInfoManager> Create(
      base::FilePath save_file_path,
      SaveFileLoadedCallback save_file_loaded_callback,
      scoped_refptr<base::SequencedTaskRunner> io_task_runner);
  UploadedCrashInfoManager(const UploadedCrashInfoManager&) = delete;
  UploadedCrashInfoManager& operator=(const UploadedCrashInfoManager&) = delete;
  virtual ~UploadedCrashInfoManager();

  // Tells whether a given crash event should be reported.
  bool ShouldReport(
      const ash::cros_healthd::mojom::CrashUploadInfoPtr& upload_info) const;

  // Updates uploaded crash info if the given info is newer.
  void Update(base::Time uploads_log_creation_time,
              uint64_t uploads_log_offset);

  // Indicates whether the save file has been loaded.
  bool IsSaveFileLoaded() const;

 private:
  // Give `TestEnvironment` the access to the JSON key strings.
  friend class FatalCrashEventsObserver::TestEnvironment;

  struct ParseResult {
    int64_t uploads_log_creation_timestamp_ms;
    uint64_t uploads_log_offset;
  };

  // Keys of the fields in the save file.
  static constexpr std::string_view kCreationTimestampMsJsonKey =
      "creation_timestamp_ms";
  static constexpr std::string_view kOffsetJsonKey = "offset";

  explicit UploadedCrashInfoManager(
      base::FilePath save_file_path,
      SaveFileLoadedCallback save_file_loaded_callback,
      scoped_refptr<base::SequencedTaskRunner> io_task_runner);

  // Loads the save file in JSON format.
  void LoadSaveFile();

  // Resume loading save file after the IO part is done.
  void ResumeLoadingSaveFile(const StatusOr<std::string>& content);

  // Writes the save file in JSON format. Returns an OK status if everything
  // other than file writing itself succeeds. If file writing fails, it will be
  // logged from the IO sequence.
  Status WriteSaveFile() const;

  // Is the given creation time and offset newer than the currently saved.
  bool IsNewer(base::Time uploads_log_creation_time,
               uint64_t uploads_log_offset) const;

  // This instance is always on the same sequence as that of the owning
  // `FatalCrashEventsObserver` instance.
  SEQUENCE_CHECKER(sequence_checker_);

  // The JSON file that saves the creation time and offset of uploads.log
  // since last report.
  const base::FilePath save_file_ GUARDED_BY_CONTEXT(sequence_checker_);

  // The temporary save file that was written to before updating `save_file_`.
  const base::FilePath save_file_tmp_ GUARDED_BY_CONTEXT(sequence_checker_){
      save_file_.AddExtension(".tmp")};

  // The creation time of uploads.log of the last reported crash. Initialize
  // this to minimum creation time possible so that the first uploaded crash
  // (which always has a creation time larger than `base::Time::Min()`) would
  // always be reported.
  base::Time uploads_log_creation_time_ GUARDED_BY_CONTEXT(sequence_checker_){
      base::Time::Min()};

  // The offset of uploads.log of the last reported crash.
  uint64_t uploads_log_offset_ GUARDED_BY_CONTEXT(sequence_checker_){0u};

  // Indicates whether loading has finished.
  bool save_file_loaded_ GUARDED_BY_CONTEXT(sequence_checker_){false};

  // Called when the save file is loaded. Should only be called once because the
  // save file is only loaded once throughout the lifetime of this class
  // instance.
  SaveFileLoadedCallback save_file_loaded_callback_
      GUARDED_BY_CONTEXT(sequence_checker_);

  // The task runner that performs IO.
  const scoped_refptr<base::SequencedTaskRunner> io_task_runner_;

  // The counter that keeps track of the number of IO tasks posted. This is
  // mostly used to avoid duplicate IO tasks, i.e., a later save file writing
  // task is posted and there's no need to executing an earlier file writing
  // task that has not started. It is always deleted on the IO thread so that
  // when an instance of this class is destroyed, this counter remains
  // accessible for all tasks posted to the IO thread.
  const std::unique_ptr<std::atomic<uint64_t>, base::OnTaskRunnerDeleter>
      latest_save_file_writing_task_id_{
          new std::atomic<uint64_t>(0u),
          base::OnTaskRunnerDeleter(io_task_runner_)};

  base::WeakPtrFactory<UploadedCrashInfoManager> weak_factory_{this};
};
}  // namespace reporting

#endif  // CHROME_BROWSER_ASH_POLICY_REPORTING_METRICS_REPORTING_FATAL_CRASH_FATAL_CRASH_EVENTS_OBSERVER_UPLOADED_CRASH_INFO_MANAGER_H_