chromium/chromeos/ash/components/growth/campaigns_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 CHROMEOS_ASH_COMPONENTS_GROWTH_CAMPAIGNS_MANAGER_H_
#define CHROMEOS_ASH_COMPONENTS_GROWTH_CAMPAIGNS_MANAGER_H_

#include "base/component_export.h"
#include "base/functional/callback_forward.h"
#include "base/memory/raw_ptr.h"
#include "base/observer_list.h"
#include "base/observer_list_types.h"
#include "chromeos/ash/components/growth/action_performer.h"
#include "chromeos/ash/components/growth/campaigns_logger.h"
#include "chromeos/ash/components/growth/campaigns_manager_client.h"
#include "chromeos/ash/components/growth/campaigns_matcher.h"
#include "chromeos/ash/components/growth/campaigns_model.h"

class PrefService;
class PrefRegistrySimple;

namespace growth {

// A class that manages growth campaigns.
class COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_GROWTH) CampaignsManager {
 public:
  using GetCampaignCallback =
      base::OnceCallback<void(const Campaign* campaign)>;

  // Interface for observing the CampaignsManager.
  class Observer : public base::CheckedObserver {
   public:
    ~Observer() override = default;

    // Trigger when complete loading growth campaigns. CampaignsManager is ready
    // for serving campaigns at this point.
    virtual void OnCampaignsLoadCompleted() = 0;
  };

  CampaignsManager(CampaignsManagerClient* client, PrefService* local_state);
  CampaignsManager(const CampaignsManager&) = delete;
  CampaignsManager& operator=(const CampaignsManager&) = delete;
  ~CampaignsManager();

  // Static.
  static CampaignsManager* Get();
  static void RegisterProfilePrefs(PrefRegistrySimple* registry);

  void AddObserver(Observer* observer);
  void RemoveObserver(Observer* observer);

  void SetPrefs(PrefService* prefs);

  // Download and install campaigns. Once installed, trigger the
  // `OnCampaignsLoaded` to install campaigns and notifier observers when
  // complete loading campaigns.
  void LoadCampaigns(base::OnceClosure load_callback, bool in_oobe = false);

  // Get campaigns by slot and register sythetical trial for current session.
  // This is used by reactive slots to query campaign that targets the given
  // `slot`. It a heavy operation which should be called when's necessary.
  // TODO(b/308684443): Rename this to `GetCampaignBySlotAndRegisterTrial`.
  const Campaign* GetCampaignBySlot(Slot slot) const;

  // Get latest opened URL.
  const GURL& GetActiveUrl() const;

  // Set the current active URL. Used in `CampaignsMatcher` for matching
  // URL targeting
  void SetActiveUrl(const GURL& url);

  // Get latest opened app id.
  const std::string& GetOpenedAppId() const;

  // Set the current opened app. Used in `CampaignsMatcher` for matching
  // opened app targeting.
  void SetOpenedApp(const std::string& app_id);

  // Get latest trigger.
  const Trigger& GetTrigger() const;

  // Set the current trigger type and event. Used in `CampaignsMatcher` for
  // matching trigger targeting.
  void SetTrigger(const Trigger&& trigger_type);

  // Set whether the current user is device owner.
  void SetIsUserOwner(bool is_user_owner);

  // Select action performer based on the given `action`. Action includes the
  // action type and action params for performing action. The caller should
  // check if action is defined before calling this method.
  void PerformAction(int campaign_id,
                     std::optional<int> group_id,
                     const Action* action);

  // Select action performer based on the action type and perform action with
  // action params.
  void PerformAction(int campaign_id,
                     std::optional<int> group_id,
                     const ActionType action_type,
                     const base::Value::Dict* params);

  // Clear event stored in the Feature Engagement framework.
  void ClearEvent(CampaignEvent event, const std::string& id);
  void ClearEvent(const std::string& event);

  // Record event to the Feature Engagement framework. Event will be stored and
  // could be used for targeting.
  // TODO: b/342283711 - Refactor this into two functions with
  // `RecordSurfaceUiEvent` and `RecordEventAppOpened`.
  void RecordEventForTargeting(growth::CampaignEvent event,
                               const std::string& id);

  void SetOobeCompleteTimeForTesting(base::Time time);
  void SetTrackerInitializedForTesting();
  const Campaigns* GetCampaignsBySlotForTesting(Slot slot) const;
  std::optional<base::Time> GetRegisteredTimeForTesting();

 private:
  // Triggred when campaigns component loaded.
  void OnCampaignsComponentLoaded(
      base::OnceClosure load_callback,
      bool in_oobe,
      const std::optional<const base::FilePath>& file_path);

  // Triggered when campaigns are loaded from the campaigns component mounted
  // path.
  void OnCampaignsLoaded(base::OnceClosure load_callback,
                         std::optional<base::Value::Dict> campaigns);

  // Triggered when loading OOBE timestamp completed.
  void OnOobeTimestampLoaded(base::OnceClosure load_callback,
                             const std::optional<const base::FilePath>& path,
                             base::Time oobe_time);

  // Triggered when the feature_engagement tracker is initialized.
  void OnTrackerInitialized(base::OnceClosure load_callback,
                            const std::optional<const base::FilePath>& path,
                            bool init_success);

  // Notify observers that campaigns are loaded and CampaignsManager is ready
  // to query.
  void NotifyCampaignsLoaded();

  // Register synthetic trial for growth. It will not work if campaign is
  // incomplete, i.e. missing id.
  void RegisterTrialForCampaign(const Campaign* campaign) const;

  void RecordEvent(const std::string& event);

  raw_ptr<CampaignsManagerClient> client_ = nullptr;

  // True if campaigns are loaded.
  bool campaigns_loaded_ = false;

  // Campaigns store owns all campaigns, including proactive and reactive
  // campaigns.
  CampaignsPerSlot campaigns_;
  // Campaigns matcher for selecting campaigns based on criteria.
  CampaignsMatcher matcher_;

  CampaignsLogger logger_;

  // Maps action type to the action.
  ActionMap actions_map_;

  // Keeps track of when downloading campaigns begins.
  base::TimeTicks campaigns_download_start_time_;

  base::Time oobe_complete_time_for_test_;

  bool tracker_initialized_for_test_ = false;

  base::ObserverList<Observer> observers_;

  base::WeakPtrFactory<CampaignsManager> weak_factory_{this};
};

}  // namespace growth

#endif  // CHROMEOS_ASH_COMPONENTS_GROWTH_CAMPAIGNS_MANAGER_H_