blob: 3a169d3d4a2f234440cc5e7e57a37d62f3b20a51 [file] [log] [blame]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ANDROID_WEBVIEW_BROWSER_METRICS_AW_METRICS_SERVICE_CLIENT_H_
#define ANDROID_WEBVIEW_BROWSER_METRICS_AW_METRICS_SERVICE_CLIENT_H_
#include <memory>
#include <string>
#include <string_view>
#include "android_webview/browser/lifecycle/webview_app_state_observer.h"
#include "base/functional/callback_forward.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/metrics/field_trial.h"
#include "base/no_destructor.h"
#include "base/scoped_multi_source_observation.h"
#include "base/scoped_observation.h"
#include "base/sequence_checker.h"
#include "base/time/time.h"
#include "components/metrics/enabled_state_provider.h"
#include "components/metrics/metrics_log_uploader.h"
#include "components/metrics/metrics_service_client.h"
#include "components/metrics/persistent_synthetic_trial_observer.h"
#include "components/variations/synthetic_trial_registry.h"
#include "components/version_info/android/channel_getter.h"
#include "content/public/browser/render_process_host_creation_observer.h"
#include "content/public/browser/render_process_host_observer.h"
#include "content/public/browser/web_contents.h"
class PrefRegistrySimple;
class PrefService;
namespace metrics {
class MetricsStateManager;
} // namespace metrics
namespace android_webview {
extern const char kCrashpadHistogramAllocatorName[];
// AwMetricsServiceClient is a singleton which manages WebView metrics
// collection.
//
// Metrics should be enabled iff all these conditions are met:
// - The user has not opted out (controlled by GMS).
// - The app has not opted out (controlled by manifest tag).
// - This client is in the 2% sample (controlled by client ID hash).
// The first two are recorded in |user_consent_| and |app_consent_|, which are
// set by SetHaveMetricsConsent(). The last is recorded in |is_in_sample_|.
//
// Metrics are pseudonymously identified by a randomly-generated "client ID".
// WebView stores this in prefs, written to the app's data directory. There's a
// different such directory for each user, for each app, on each device. So the
// ID should be unique per (device, app, user) tuple.
//
// In order to be transparent about not associating an ID with an opted out user
// or app, the client ID should only be created and retained when neither the
// user nor the app have opted out. Otherwise, the presence of the ID could give
// the impression that metrics were being collected.
//
// WebView metrics set up happens like so:
//
// startup
// │
// ├───────────────┐
// │ ▼
// │ query GMS for consent
// ▼ │
// Initialize() │
// │ │
// ▼ │
// SetUpMetricsDir() │
// │ ▼
// │ SetHaveMetricsConsent()
// │ │
// │ ┌─────────────┘
// ▼ ▼
// MaybeStartMetrics()
// │
// ▼
// MetricsService::Start()
//
// All the named functions in this diagram happen on the UI thread. Querying GMS
// happens in the background, and the result is posted back to the UI thread, to
// SetHaveMetricsConsent(). Querying GMS is slow, so SetHaveMetricsConsent()
// typically happens after Initialize(), but it may happen before.
//
// Initialize() is called before Finch is set up, and SetUpMetricsDir() is
// called afterward to allow it to check base::Feature flags.
//
// Each path sets a flag, |init_finished_| or |set_consent_finished_|, to show
// that path has finished, and |metrics_dir_| must also have been set. Each of
// the steps ends by calling MaybeStartMetrics(), which does nothing unless all
// three steps have happened.
//
// If consent was granted, MaybeStartMetrics() determines sampling by hashing
// the client ID (generating a new ID if there was none). If this client is in
// the sample, it then calls MetricsService::Start(). If consent was not
// granted, MaybeStartMetrics() instead clears the client ID, if any.
class AwMetricsServiceClient
: public metrics::MetricsServiceClient,
public metrics::EnabledStateProvider,
public content::RenderProcessHostCreationObserver,
public content::RenderProcessHostObserver,
public WebViewAppStateObserver {
friend class base::NoDestructor<AwMetricsServiceClient>;
public:
// This interface define the tasks that depend on the
// android_webview/browser directory.
class Delegate {
public:
Delegate();
virtual ~Delegate();
// Not copyable or movable
Delegate(const Delegate&) = delete;
Delegate& operator=(const Delegate&) = delete;
Delegate(Delegate&&) = delete;
Delegate& operator=(Delegate&&) = delete;
virtual void RegisterAdditionalMetricsProviders(
metrics::MetricsService* service) = 0;
virtual void AddWebViewAppStateObserver(
WebViewAppStateObserver* observer) = 0;
virtual bool HasAwContentsEverCreated() const = 0;
};
static AwMetricsServiceClient* GetInstance();
static void SetInstance(
std::unique_ptr<AwMetricsServiceClient> aw_metrics_service_client);
static void RegisterMetricsPrefs(PrefRegistrySimple* registry);
explicit AwMetricsServiceClient(std::unique_ptr<Delegate> delegate);
AwMetricsServiceClient(const AwMetricsServiceClient&) = delete;
AwMetricsServiceClient& operator=(const AwMetricsServiceClient&) = delete;
~AwMetricsServiceClient() override;
// Initializes, but does not necessarily start, the MetricsService. See the
// documentation at the top of the file for more details.
void Initialize(PrefService* pref_service);
void SetHaveMetricsConsent(bool user_consent, bool app_consent);
void SetFastStartupForTesting(bool fast_startup_for_testing);
void SetUploadIntervalForTesting(const base::TimeDelta& upload_interval);
// EnabledStateProvider:
bool IsConsentGiven() const override;
bool IsReportingEnabled() const override;
// Returns the MetricService only if it has been started (which means consent
// was given).
metrics::MetricsService* GetMetricsServiceIfStarted();
// MetricsServiceClient:
variations::SyntheticTrialRegistry* GetSyntheticTrialRegistry() override;
metrics::MetricsService* GetMetricsService() 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 MergeSubprocessHistograms() override;
void CollectFinalMetricsForLog(
const 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;
bool ShouldStartUpFast() const override;
// Gets the embedding app's package name if it's OK to log. Otherwise, this
// returns the empty string.
std::string GetAppPackageNameIfLoggable() override;
void OnWebContentsCreated(content::WebContents* web_contents);
// content::RenderProcessHostCreationObserver:
void OnRenderProcessHostCreated(content::RenderProcessHost* host) override;
// RenderProcessHostObserver:
void RenderProcessExited(
content::RenderProcessHost* host,
const content::ChildProcessTerminationInfo& info) override;
// Runs |closure| when CollectFinalMetricsForLog() is called, when we begin
// collecting final metrics.
void SetCollectFinalMetricsForLogClosureForTesting(base::OnceClosure closure);
// Runs |listener| after all final metrics have been collected.
void SetOnFinalMetricsCollectedListenerForTesting(
base::RepeatingClosure listener);
metrics::MetricsStateManager* metrics_state_manager() const {
return metrics_state_manager_.get();
}
// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.android_webview.metrics
enum class InstallerPackageType {
// App has been initially preinstalled in the system image.
SYSTEM_APP,
// App has been installed/updated by Google Play Store. Doesn't apply for
// apps whose most recent updates are sideloaded, even if the app was
// installed via Google Play Store.
GOOGLE_PLAY_STORE,
// App has been Sideloaded or installed/updated through a 3rd party app
// store.
OTHER,
};
// Returns the embedding application's package name (unconditionally). The
// value returned by this method shouldn't be logged/stored anywhere, callers
// should use `GetAppPackageNameIfLoggable`.
std::string GetAppPackageName();
// Returns the installer type of the app. Virtual for testing.
virtual InstallerPackageType GetInstallerPackageType();
// Path where files related to metrics are stored.
base::FilePath GetMetricsDir();
// Set up the path used to store metrics. Separate from `Initialize` to enable
// this to check feature flags, which aren't initialized yet when `Initialize`
// runs.
void SetUpMetricsDir();
// WebViewAppStateObserver
void OnAppStateChanged(WebViewAppStateObserver::State state) override;
// Determines if the client should have metrics filtering applied, or if they
// are in the sample of clients which upload unfiltered metrics.
virtual bool ShouldApplyMetricsFiltering() const;
protected:
// Returns the unfiltered metrics sampling rate, to be used by
// ShouldApplyMetricsFiltering(). This is a per mille value, so this integer
// must always be in the inclusive range [0, 1000]. A value of 0 will always
// be out-of-sample, and a value of 1000 is always in-sample.
virtual int GetUnfilteredSampleRatePerMille() const;
// Returns a value in the inclusive range [0, 999], to be compared against a
// per mille sample rate. This value will be based on a persisted value, so it
// should be consistent across restarts. This value should also be mostly
// consistent across upgrades, to avoid significantly impacting IsInSample().
// Virtual for testing.
virtual int GetSampleBucketValue() const;
// Determines if the embedder app is the type of app for which we may log the
// package name. If this returns false, GetAppPackageNameIfLoggable() must
// return empty string. Virtual for testing.
virtual bool CanRecordPackageNameForAppType();
private:
bool IsReadyToStart() const;
void MaybeStartMetrics();
void RegisterForNotifications();
void RegisterMetricsProvidersAndInitState();
void OnApplicationNotIdle();
void OnDidStartLoading();
std::unique_ptr<metrics::MetricsStateManager> metrics_state_manager_;
std::unique_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_};
std::unique_ptr<metrics::MetricsService> metrics_service_;
base::ScopedMultiSourceObservation<content::RenderProcessHost,
content::RenderProcessHostObserver>
host_observation_{this};
raw_ptr<PrefService> pref_service_ = nullptr;
bool init_finished_ = false;
bool set_consent_finished_ = false;
bool user_consent_ = false;
bool app_consent_ = false;
bool is_client_id_forced_ = false;
bool fast_startup_for_testing_ = false;
bool did_start_metrics_ = false;
// When non-zero, this overrides the default value in
// GetStandardUploadInterval().
base::TimeDelta overridden_upload_interval_;
base::OnceClosure collect_final_metrics_for_log_closure_;
base::RepeatingClosure on_final_metrics_collected_listener_;
#if DCHECK_IS_ON()
bool did_start_metrics_with_consent_ = false;
#endif
// MetricsServiceClient may be created before the UI thread is promoted to
// BrowserThread::UI. Use |sequence_checker_| to enforce that the
// MetricsServiceClient is used on a single thread.
SEQUENCE_CHECKER(sequence_checker_);
bool app_in_foreground_ = false;
base::Time time_created_;
std::unique_ptr<Delegate> delegate_;
base::FilePath metrics_dir_;
base::WeakPtrFactory<AwMetricsServiceClient> weak_ptr_factory_{this};
};
} // namespace android_webview
#endif // ANDROID_WEBVIEW_BROWSER_METRICS_AW_METRICS_SERVICE_CLIENT_H_