// Copyright 2014 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_METRICS_CHROME_METRICS_SERVICE_CLIENT_H_
#define CHROME_BROWSER_METRICS_CHROME_METRICS_SERVICE_CLIENT_H_

#include <stdint.h>

#include <memory>
#include <string>
#include <string_view>

#include "base/callback_list.h"
#include "base/functional/callback.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/scoped_multi_source_observation.h"
#include "base/scoped_observation.h"
#include "base/sequence_checker.h"
#include "build/build_config.h"
#include "chrome/browser/metrics/cached_metrics_profile.h"
#include "chrome/browser/metrics/incognito_observer.h"
#include "chrome/browser/metrics/metrics_memory_details.h"
#include "chrome/browser/profiles/profile_manager_observer.h"
#include "chrome/browser/profiles/profile_observer.h"
#include "components/metrics/file_metrics_provider.h"
#include "components/metrics/metrics_log_uploader.h"
#include "components/metrics/metrics_reporting_choice_service.h"
#include "components/metrics/metrics_service_client.h"
#include "components/metrics/persistent_synthetic_trial_observer.h"
#include "components/omnibox/browser/omnibox_event_global_tracker.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/ukm/observers/history_delete_observer.h"
#include "components/ukm/observers/ukm_consent_state_observer.h"
#include "components/variations/synthetic_trial_registry.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_process_host_creation_observer.h"
#include "content/public/browser/render_process_host_observer.h"
#include "third_party/metrics_proto/system_profile.pb.h"
#include "ui/base/user_activity/user_activity_detector.h"
#include "ui/base/user_activity/user_activity_observer.h"

#if BUILDFLAG(IS_CHROMEOS)
#include "chrome/browser/metrics/cros_pre_choice_metrics_manager.h"
#endif

class BrowserActivityWatcher;
class Profile;
class ProfileManager;
class PrefRegistrySimple;

namespace regional_capabilities {
class CountryIdHolder;
}

namespace network_time {
class NetworkTimeTracker;
}  // namespace network_time

namespace metrics::private_metrics {
class PumaService;
}

namespace metrics {
class MetricsService;
class MetricsStateManager;

#if BUILDFLAG(IS_CHROMEOS)
class PerUserStateManagerChromeOS;
#endif
}  // namespace metrics

// ChromeMetricsServiceClient provides an implementation of MetricsServiceClient
// that depends on chrome/.
class ChromeMetricsServiceClient
    : public metrics::MetricsServiceClient,
      public metrics::MetricsReportingChoiceService,
      public ukm::HistoryDeleteObserver,
      public ukm::UkmConsentStateObserver,
      public content::RenderProcessHostObserver,
      public content::RenderProcessHostCreationObserver,
      public ProfileManagerObserver,
      public ProfileObserver,
      public ui::UserActivityObserver {
 public:
  ChromeMetricsServiceClient(const ChromeMetricsServiceClient&) = delete;
  ChromeMetricsServiceClient& operator=(const ChromeMetricsServiceClient&) =
      delete;

  ~ChromeMetricsServiceClient() override;

  // Factory function.
  static std::unique_ptr<ChromeMetricsServiceClient> Create(
      metrics::MetricsStateManager* state_manager,
      variations::SyntheticTrialRegistry* synthetic_trial_registry);

  // Registers local state prefs used by this class.
  static void RegisterPrefs(PrefRegistrySimple* registry);

#if BUILDFLAG(IS_CHROMEOS)
  // Registers profile prefs used by this class.
  static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
#endif  // BUILDFLAG(IS_CHROMEOS)

  // metrics::MetricsServiceClient:
  variations::SyntheticTrialRegistry* GetSyntheticTrialRegistry() override;
  metrics::MetricsService* GetMetricsService() override;
  ukm::UkmService* GetUkmService() override;
  metrics::dwa::DwaService* GetDwaService() override;
  metrics::private_metrics::PumaService* GetPumaService() override;
  metrics::structured::StructuredMetricsService* GetStructuredMetricsService()
      override;
  void SetMetricsClientId(const std::string& client_id) override;
  int32_t GetProduct() override;
  std::string GetApplicationLocale() override;
  const network_time::NetworkTimeTracker* GetNetworkTimeTracker() override;
  bool GetBrand(std::string* brand_code) override;
  metrics::SystemProfileProto::Channel GetChannel() override;
  bool IsExtendedStableChannel() override;
  std::string GetVersionString() override;
  void OnEnvironmentUpdate(std::string* serialized_environment) override;
  void MergeSubprocessHistograms() override;
  void CollectFinalMetricsForLog(base::OnceClosure done_callback) override;
  std::unique_ptr<metrics::MetricsLogUploader> CreateUploader(
      const GURL& server_url,
      const GURL& insecure_server_url,
      std::string_view mime_type,
      metrics::MetricsLogUploader::MetricServiceType service_type,
      const metrics::MetricsLogUploader::UploadCallback& on_upload_complete)
      override;
  base::TimeDelta GetStandardUploadInterval() override;
  std::optional<base::TimeDelta> GetCustomUploadInterval() const override;
  void LoadingStateChanged(bool is_loading) override;
  bool IsReportingPolicyManaged() override;
  metrics::EnableMetricsDefault GetMetricsReportingDefaultState() override;
  bool IsOnCellularConnection() override;
  bool IsUkmAllowedForAllProfiles() override;
  bool IsDwaAllowedForAllProfiles() override;
  bool AreNotificationListenersEnabledOnAllProfiles() override;
  std::string GetAppPackageNameIfLoggable() override;
  std::string GetUploadSigningKey() override;
  static void SetNotificationListenerSetupFailedForTesting(
      bool simulate_failure);
  bool ShouldResetClientIdsOnClonedInstall() override;
  base::CallbackListSubscription AddOnClonedInstallDetectedCallback(
      base::OnceClosure callback) override;
#if BUILDFLAG(IS_CHROMEOS)
  bool ShouldUploadMetricsForUserId(const uint64_t user_id) override;
  void InitPerUserMetrics() override;
  void UpdateCurrentUserMetricsChoice(bool user_choice) override;
  std::optional<bool> GetCurrentUserMetricsChoice() const override;
  std::optional<std::string> GetCurrentUserId() const override;
#endif  // BUILDFLAG(IS_CHROMEOS)
  std::optional<regional_capabilities::CountryIdHolder>
  GetProfileCountryIdForPrivateMetricsReporting() override;

  // ukm::HistoryDeleteObserver:
  void OnHistoryDeleted() override;

  // ukm::UkmConsentStateObserver:
  void OnUkmAllowedStateChanged(
      bool must_purge,
      ukm::UkmConsentState previous_consent_state) override;

  // content::RenderProcessHostCreationObserver:
  void OnRenderProcessHostCreated(content::RenderProcessHost* host) override;

  // content::RenderProcessHostObserver:
  void RenderProcessExited(
      content::RenderProcessHost* host,
      const content::ChildProcessTerminationInfo& info) override;
  void RenderProcessHostDestroyed(content::RenderProcessHost* host) override;

  // ProfileObserver:
  void OnProfileWillBeDestroyed(Profile* profile) override;

  // ProfileManagerObserver:
  void OnProfileAdded(Profile* profile) override;
  void OnProfileManagerDestroying() override;

  // ui::UserActivityObserver:
  void OnUserActivity(const ui::Event* event) override;

  // Determine what to do with a file based on filename. Visible for testing.
  using IsProcessRunningFunction = bool (*)(base::ProcessId);
  static metrics::FileMetricsProvider::FilterAction FilterBrowserMetricsFiles(
      const base::FilePath& path);
  static void SetIsProcessRunningForTesting(IsProcessRunningFunction func);

 protected:
  explicit ChromeMetricsServiceClient(
      metrics::MetricsStateManager* state_manager,
      variations::SyntheticTrialRegistry* synthetic_trial_registry);

  // Completes the two-phase initialization of ChromeMetricsServiceClient.
  void Initialize();

  // metrics::MetricsReportingChoiceService:
  void OnAdvancedReportingEnabledForAllProfilesChanged(
      bool enabled,
      bool reset_client_state) override;

 private:
  friend class ChromeMetricsServiceClientTest;
  friend class ChromeMetricsServiceClientTestIgnoredForAppMetrics;
  friend class ChromeMetricsServiceClientTestWithoutUKMProviders;
  FRIEND_TEST_ALL_PREFIXES(ChromeMetricsServiceClientTest, IsWebstoreExtension);

  // Registers providers to the MetricsService. These provide data from
  // alternate sources.
  void RegisterMetricsServiceProviders();

  // Registers providers to the UkmService. These provide data from alternate
  // sources.
  virtual void RegisterUKMProviders();

  // Notifies the metrics service that user activity has been detected.
  virtual void NotifyApplicationNotIdle();

  // Returns true iff profiler data should be included in the next metrics log.
  // NOTE: This method is probabilistic and also updates internal state as a
  // side-effect when called, so it should only be called once per log.
  bool ShouldIncludeProfilerDataInLog();

  // Callbacks for various stages of final log info collection. Do not call
  // these directly.
  void CollectFinalHistograms();
  void OnMemoryDetailCollectionDone();
  void OnHistogramSynchronizationDone();

  // Registers |this| as an observer for notifications which indicate that a
  // user is performing work. This is useful to allow some features to sleep,
  // until the machine becomes active, such as precluding UMA uploads unless
  // there was recent activity.
  // Returns true if registration was successful for all profiles.
  bool RegisterObservers();

  // Call to listen for events on the selected profile's services.
  // Returns true if we registered all observers successfully.
  bool RegisterForProfileEvents(Profile* profile);

  // Called when a URL is opened from the Omnibox.
  void OnURLOpenedFromOmnibox(OmniboxLog* log);

#if BUILDFLAG(IS_CHROMEOS)
  // Helper function for initialization of system profile provider.
  virtual void AsyncInitSystemProfileProvider();
#endif

  // Check if an extension is installed via the Web Store.
  static bool IsWebstoreExtension(std::string_view id);

  // Resets client state (i.e. client id) if MSBB or App-sync consent
  // is changed from on to off. For non-ChromeOS platforms, this will no-op.
  void ResetClientStateWhenMsbbOrAppConsentIsRevoked(
      ukm::UkmConsentState previous_consent_state);

  // Creates the Structured Metrics Service based on the platform.
  void CreateStructuredMetricsService();

  SEQUENCE_CHECKER(sequence_checker_);

  // Weak pointer to the MetricsStateManager.
  const raw_ptr<metrics::MetricsStateManager> metrics_state_manager_;

  // The synthetic trial registry shared by metrics_service_ and ukm_service_.
  const raw_ptr<variations::SyntheticTrialRegistry> synthetic_trial_registry_;

  // Metrics service observer for synthetic trials.
  metrics::PersistentSyntheticTrialObserver synthetic_trial_observer_;
  base::ScopedObservation<variations::SyntheticTrialRegistry,
                          variations::SyntheticTrialObserver>
      synthetic_trial_observation_{&synthetic_trial_observer_};

  // |cros_system_profile_provider_| must be declared before |ukm_service_| due
  // to some metrics providers have a dependency on |system_profile_provider_|
  // and it must be destroyed after they are. Manages SystemProfile information
  // needed by other metrics providers.
  std::unique_ptr<metrics::MetricsProvider> cros_system_profile_provider_;

  // The StructuredMetricsService that |this| is a client of.
  std::unique_ptr<metrics::structured::StructuredMetricsService>
      structured_metrics_service_;

  // The MetricsService that |this| is a client of.
  std::unique_ptr<metrics::MetricsService> metrics_service_;

  // The UkmService that |this| is a client of.
  std::unique_ptr<ukm::UkmService> ukm_service_;

  // The DwaService that |this| is a client of.
  std::unique_ptr<metrics::dwa::DwaService> dwa_service_;

  // IMPORTANT: This member's declaration order is critical for shutdown
  // stability. It must be declared *before* `puma_service_` to ensure it is
  // destroyed *after* `puma_service_`. This is because the `PumaService`
  // destructor triggers a callback that uses this `cached_profile_` object.
  // Reordering these members will lead to a use-after-free crash during
  // shutdown. See crbug.com/465698705 for details.
  //
  // This member variable ensures the profile lookup is cached across calls.
  metrics::CachedMetricsProfile cached_profile_;

  // The PumaService that |this| is a client of.
  std::unique_ptr<metrics::private_metrics::PumaService> puma_service_;

  // Listener for changes in incognito activity.
  std::unique_ptr<IncognitoObserver> incognito_observer_;

  // Whether we successfully registered all observers of various types.
  bool observers_active_ = false;

  // Saved callback received from CollectFinalMetricsForLog().
  base::OnceClosure collect_final_metrics_done_callback_;

  // Indicates that collect final metrics step is running.
  bool waiting_for_collect_final_metrics_step_ = false;

  // Number of async histogram fetch requests in progress.
  int num_async_histogram_fetches_in_progress_ = 0;

  // Subscription for receiving callbacks that a URL was opened from the
  // omnibox.
  base::CallbackListSubscription omnibox_url_opened_subscription_;

#if !BUILDFLAG(IS_ANDROID)
  std::unique_ptr<BrowserActivityWatcher> browser_activity_watcher_;
#endif

#if BUILDFLAG(IS_CHROMEOS)
  // PerUserStateManagerChromeOS that |this| is a client of.
  std::unique_ptr<metrics::PerUserStateManagerChromeOS> per_user_state_manager_;

  // Subscription for receiving callbacks that user metrics choice has changed.
  base::CallbackListSubscription per_user_choice_change_subscription_;

  // Used to notify metrics service if user activity has been detected on the
  // system.
  base::ScopedObservation<ui::UserActivityDetector, ui::UserActivityObserver>
      user_activity_observation_{this};

  // Manages the choice of UMA before the user has been created. This object is
  // only created during OOBE before the primary user has given intent to
  // metrics choice.
  std::unique_ptr<metrics::CrOSPreChoiceMetricsManager>
      cros_pre_choice_metrics_manager_;
#endif

  base::ScopedMultiSourceObservation<content::RenderProcessHost,
                                     content::RenderProcessHostObserver>
      scoped_observations_{this};

  base::ScopedObservation<ProfileManager, ProfileManagerObserver>
      profile_manager_observer_{this};

  base::ScopedMultiSourceObservation<Profile, ProfileObserver>
      profile_observations_{this};

  base::WeakPtrFactory<ChromeMetricsServiceClient> weak_ptr_factory_{this};
};

#endif  // CHROME_BROWSER_METRICS_CHROME_METRICS_SERVICE_CLIENT_H_
