910e62b5创建于 1月15日历史提交
// 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.

#ifndef CONTENT_BROWSER_AGGREGATION_SERVICE_AGGREGATABLE_REPORT_SCHEDULER_H_
#define CONTENT_BROWSER_AGGREGATION_SERVICE_AGGREGATABLE_REPORT_SCHEDULER_H_

#include <optional>
#include <set>
#include <vector>

#include "base/functional/callback.h"
#include "base/memory/raw_ref.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "content/browser/aggregation_service/aggregation_service_storage.h"
#include "content/browser/aggregation_service/report_scheduler_timer.h"
#include "content/common/content_export.h"

namespace base {
class ElapsedTimer;
class Time;
}  // namespace base

namespace content {

class AggregatableReportRequest;
class AggregationServiceStorageContext;

// UI thread class that is responsible for interacting with the storage layer
// for report requests, waiting for a report's reporting time to be reached and
// then calling the appropriate callback.
//
// Note that report requests can have one of two states: "pending" or
// "in-progress". This represents whether the request is currently being
// processed (i.e. assembled and sent). To ensure that no report is 'lost',
// all requests (that haven't been deleted) become pending once more on start
// up. Note that all reports with future report times are pending (unless wall
// clock time goes backward).
class CONTENT_EXPORT AggregatableReportScheduler {
 public:
  // Add uniform random noise in the range of [0, 1 minutes] to the report time
  // when the browser comes back online. Aligned with
  // `AttributionStorageDelegateImpl::GetOfflineReportDelayConfig()`.
  static constexpr base::TimeDelta kOfflineReportTimeMinimumDelay =
      base::Minutes(0);
  static constexpr base::TimeDelta kOfflineReportTimeMaximumDelay =
      base::Minutes(1);

  // Configuration for retrying to send reports that failed to send
  static constexpr int kMaxRetries = 2;
  static constexpr base::TimeDelta kInitialRetryDelay = base::Minutes(5);
  static constexpr int kRetryDelayFactor = 3;

  AggregatableReportScheduler(
      AggregationServiceStorageContext* storage_context,
      base::RepeatingCallback<
          void(std::vector<AggregationServiceStorage::RequestAndId>)>
          on_scheduled_report_time_reached);

  AggregatableReportScheduler(const AggregatableReportScheduler& other) =
      delete;
  AggregatableReportScheduler& operator=(
      const AggregatableReportScheduler& other) = delete;
  virtual ~AggregatableReportScheduler();

  // Methods are virtual for testing.

  // Schedules the `request` to be assembled and sent at
  // `request.shared_info().scheduled_report_time`.
  virtual void ScheduleRequest(AggregatableReportRequest request);

  // Notifies that the request to assemble and send the report with `request_id`
  // was successfully completed. There must be an in-progress request stored
  // with that `request_id`.
  virtual void NotifyInProgressRequestSucceeded(
      AggregationServiceStorage::RequestId request_id);

  // Notifies that the request to assemble and send the report with `request_id`
  // completed unsuccessfully. There must be an in-progress request stored with
  // that `request_id`.`failed_attemps_before_sending` is the number of times
  // that this request previously failed. ie. not counting the failure being
  // notified on.
  // Returns true when the request will be scheduled to be retried.
  // Returns false when the request is dropped. ie. it wont be retried.
  virtual bool NotifyInProgressRequestFailed(
      AggregationServiceStorage::RequestId request_id,
      int previous_failed_attempts);

 private:
  class TimerDelegate : public ReportSchedulerTimer::Delegate {
   public:
    TimerDelegate(AggregationServiceStorageContext* storage_context,
                  base::RepeatingCallback<void(
                      std::vector<AggregationServiceStorage::RequestAndId>)>
                      on_scheduled_report_time_reached);
    ~TimerDelegate() override;

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

    // Notifies that we no longer need to track `request_id` as in-progress.
    void NotifySendAttemptCompleted(
        AggregationServiceStorage::RequestId request_id);

    base::WeakPtr<AggregatableReportScheduler::TimerDelegate> GetWeakPtr() {
      return weak_ptr_factory_.GetWeakPtr();
    }

   private:
    // ReportSchedulerTimer::Delegate:
    void GetNextReportTime(base::OnceCallback<void(std::optional<base::Time>)>,
                           base::Time now) override;
    void OnReportingTimeReached(base::Time now,
                                base::Time timer_desired_run_time) override;
    void AdjustOfflineReportTimes(
        base::OnceCallback<void(std::optional<base::Time>)>) override;

    void OnRequestsReturnedFromStorage(
        base::ElapsedTimer task_timer,
        std::vector<AggregationServiceStorage::RequestAndId> requests_and_ids);

    // Using a raw reference is safe because `storage_context_` is guaranteed to
    // outlive `this`.
    raw_ref<AggregationServiceStorageContext> storage_context_;

    // Called when a report request's scheduled report time is reached. May
    // return multiple requests if their report times were all reached before
    // the timer fired. If multiple requests are returned, they will be ordered
    // by the report time (which may differ from the initially scheduled report
    // time).
    base::RepeatingCallback<void(
        std::vector<AggregationServiceStorage::RequestAndId>)>
        on_scheduled_report_time_reached_;

    // The set of IDs of all in-progress report requests.
    // TODO(alexmt): Consider switching to a flat_set when we have metrics on
    // the typical size.
    std::set<AggregationServiceStorage::RequestId> in_progress_requests_;

    // Set iff the private aggregation developer mode is enabled.
    bool should_not_delay_reports_;

    base::WeakPtrFactory<AggregatableReportScheduler::TimerDelegate>
        weak_ptr_factory_{this};
  };

  // Returns how long to wait before attempting to send a report that has
  // previously failed to be sent failed_send_attempts times. Returns
  // `std::nullopt` to indicate that no more attempts should be made.
  // Otherwise, the return value must be positive. `failed_send_attempts`
  // must be positive.
  static std::optional<base::TimeDelta> GetFailedReportDelay(
      int failed_send_attempts);

  // Using a raw reference is safe because `storage_context_` is guaranteed to
  // outlive `this`.
  raw_ref<AggregationServiceStorageContext> storage_context_;

  // Using a raw pointer is safe because it's owned by `timer_`. Will be cleared
  // in the destructor to avoid a dangling pointer.
  raw_ptr<TimerDelegate> timer_delegate_;

  ReportSchedulerTimer timer_;
};

}  // namespace content

#endif  // CONTENT_BROWSER_AGGREGATION_SERVICE_AGGREGATABLE_REPORT_SCHEDULER_H_