|  | // 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. | 
|  |  | 
|  | #include "android_webview/browser/metrics/aw_metrics_service_client.h" | 
|  |  | 
|  | #include <jni.h> | 
|  |  | 
|  | #include <cstdint> | 
|  | #include <string> | 
|  | #include <string_view> | 
|  |  | 
|  | #include "android_webview/browser/metrics/android_metrics_log_uploader.h" | 
|  | #include "android_webview/browser/metrics/android_metrics_provider.h" | 
|  | #include "android_webview/common/aw_features.h" | 
|  | #include "base/android/callback_android.h" | 
|  | #include "base/android/jni_android.h" | 
|  | #include "base/android/jni_string.h" | 
|  | #include "base/barrier_closure.h" | 
|  | #include "base/base_paths_android.h" | 
|  | #include "base/files/file_path.h" | 
|  | #include "base/hash/hash.h" | 
|  | #include "base/i18n/rtl.h" | 
|  | #include "base/metrics/field_trial_params.h" | 
|  | #include "base/metrics/persistent_histogram_allocator.h" | 
|  | #include "base/metrics/statistics_recorder.h" | 
|  | #include "base/path_service.h" | 
|  | #include "base/process/process_handle.h" | 
|  | #include "base/task/single_thread_task_runner.h" | 
|  | #include "base/task/task_traits.h" | 
|  | #include "base/task/thread_pool.h" | 
|  | #include "base/threading/thread_restrictions.h" | 
|  | #include "base/time/time.h" | 
|  | #include "build/build_config.h" | 
|  | #include "cc/base/switches.h" | 
|  | #include "components/metrics/android_metrics_provider.h" | 
|  | #include "components/metrics/call_stacks/call_stack_profile_metrics_provider.h" | 
|  | #include "components/metrics/content/content_stability_metrics_provider.h" | 
|  | #include "components/metrics/content/extensions_helper.h" | 
|  | #include "components/metrics/content/gpu_metrics_provider.h" | 
|  | #include "components/metrics/content/metrics_services_web_contents_observer.h" | 
|  | #include "components/metrics/content/subprocess_metrics_provider.h" | 
|  | #include "components/metrics/cpu_metrics_provider.h" | 
|  | #include "components/metrics/drive_metrics_provider.h" | 
|  | #include "components/metrics/entropy_state_provider.h" | 
|  | #include "components/metrics/file_metrics_provider.h" | 
|  | #include "components/metrics/metrics_pref_names.h" | 
|  | #include "components/metrics/metrics_service.h" | 
|  | #include "components/metrics/metrics_state_manager.h" | 
|  | #include "components/metrics/net/cellular_logic_helper.h" | 
|  | #include "components/metrics/net/network_metrics_provider.h" | 
|  | #include "components/metrics/persistent_histograms.h" | 
|  | #include "components/metrics/persistent_synthetic_trial_observer.h" | 
|  | #include "components/metrics/sampling_metrics_provider.h" | 
|  | #include "components/metrics/stability_metrics_helper.h" | 
|  | #include "components/metrics/ui/form_factor_metrics_provider.h" | 
|  | #include "components/metrics/ui/screen_info_metrics_provider.h" | 
|  | #include "components/metrics/version_utils.h" | 
|  | #include "components/prefs/pref_service.h" | 
|  | #include "components/version_info/android/channel_getter.h" | 
|  | #include "content/public/browser/histogram_fetcher.h" | 
|  | #include "content/public/browser/network_service_instance.h" | 
|  | #include "content/public/browser/web_contents.h" | 
|  | #include "services/network/public/cpp/shared_url_loader_factory.h" | 
|  |  | 
|  | // Must come after all headers that specialize FromJniType() / ToJniType(). | 
|  | #include "android_webview/browser_jni_headers/AndroidMetricsServiceClient_jni.h" | 
|  | #include "android_webview/browser_jni_headers/AwMetricsServiceClient_jni.h" | 
|  |  | 
|  | namespace android_webview { | 
|  |  | 
|  | using InstallerPackageType = AwMetricsServiceClient::InstallerPackageType; | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // This specifies the amount of time to wait for all renderers to send their | 
|  | // data. | 
|  | const int kMaxHistogramGatheringWaitDuration = 60000;  // 60 seconds. | 
|  |  | 
|  | const int kMaxHistogramStorageKiB = 100 << 10;  // 100 MiB | 
|  |  | 
|  | // Divides the spectrum of uint32_t values into 1000 ~equal-sized buckets (range | 
|  | // [0, 999] inclusive), and returns which bucket |value| falls into. Ex. given | 
|  | // 2^30, this would return 250, because 25% of uint32_t values fall below the | 
|  | // given value. | 
|  | int UintToPerMille(uint32_t value) { | 
|  | // We need to divide by UINT32_MAX+1 (2^32), otherwise the fraction could | 
|  | // evaluate to 1000. | 
|  | uint64_t divisor = 1llu << 32; | 
|  | uint64_t value_per_mille = static_cast<uint64_t>(value) * 1000llu / divisor; | 
|  | DCHECK_GE(value_per_mille, 0llu); | 
|  | DCHECK_LE(value_per_mille, 999llu); | 
|  | return static_cast<int>(value_per_mille); | 
|  | } | 
|  |  | 
|  | bool IsProcessRunning(base::ProcessId pid) { | 
|  | // Sending a signal value of 0 will cause error checking to be performed | 
|  | // with no signal being sent. | 
|  | return (kill(pid, 0) == 0 || errno != ESRCH); | 
|  | } | 
|  |  | 
|  | metrics::FileMetricsProvider::FilterAction FilterBrowserMetricsFiles( | 
|  | const base::FilePath& path) { | 
|  | base::ProcessId pid; | 
|  | if (!base::GlobalHistogramAllocator::ParseFilePath(path, nullptr, nullptr, | 
|  | &pid)) { | 
|  | return metrics::FileMetricsProvider::FILTER_PROCESS_FILE; | 
|  | } | 
|  |  | 
|  | if (pid == base::GetCurrentProcId()) { | 
|  | return metrics::FileMetricsProvider::FILTER_ACTIVE_THIS_PID; | 
|  | } | 
|  |  | 
|  | if (IsProcessRunning(pid)) { | 
|  | return metrics::FileMetricsProvider::FILTER_TRY_LATER; | 
|  | } | 
|  |  | 
|  | return metrics::FileMetricsProvider::FILTER_PROCESS_FILE; | 
|  | } | 
|  |  | 
|  | bool IsSamplesCounterEnabled() { | 
|  | return base::GetFieldTrialParamByFeatureAsBool( | 
|  | kPersistentHistogramsFeature, "prev_run_metrics_count_only", false); | 
|  | } | 
|  |  | 
|  | // TODO(crbug.com/40158523): Unify this implementation with the one in | 
|  | // ChromeMetricsServiceClient. | 
|  | std::unique_ptr<metrics::FileMetricsProvider> CreateFileMetricsProvider( | 
|  | PrefService* pref_service, | 
|  | bool metrics_reporting_enabled) { | 
|  | using metrics::FileMetricsProvider; | 
|  |  | 
|  | // Create an object to monitor files of metrics and include them in reports. | 
|  | // `is_fre` will always be false for Android WV because the concept of First | 
|  | // Run Experience is not applicable. | 
|  | std::unique_ptr<FileMetricsProvider> file_metrics_provider = | 
|  | std::make_unique<FileMetricsProvider>(pref_service, | 
|  | /*is_fre=*/false); | 
|  |  | 
|  | base::FilePath user_data_dir; | 
|  | base::PathService::Get(base::DIR_ANDROID_APP_DATA, &user_data_dir); | 
|  |  | 
|  | FileMetricsProvider::Params browser_metrics_params( | 
|  | user_data_dir.AppendASCII(kBrowserMetricsName), | 
|  | FileMetricsProvider::SOURCE_HISTOGRAMS_ATOMIC_DIR, | 
|  | IsSamplesCounterEnabled() | 
|  | ? FileMetricsProvider::ASSOCIATE_INTERNAL_PROFILE_SAMPLES_COUNTER | 
|  | : FileMetricsProvider::ASSOCIATE_INTERNAL_PROFILE, | 
|  | kBrowserMetricsName); | 
|  | browser_metrics_params.max_dir_kib = kMaxHistogramStorageKiB; | 
|  | browser_metrics_params.filter = | 
|  | base::BindRepeating(FilterBrowserMetricsFiles); | 
|  | file_metrics_provider->RegisterSource(browser_metrics_params, | 
|  | metrics_reporting_enabled); | 
|  |  | 
|  | // Register the Crashpad metrics files: | 
|  | // 1. Data from the previous run if crashpad_handler didn't exit cleanly. | 
|  | base::FilePath crashpad_metrics_file = | 
|  | base::GlobalHistogramAllocator::ConstructFilePath( | 
|  | user_data_dir, kCrashpadHistogramAllocatorName); | 
|  | file_metrics_provider->RegisterSource( | 
|  | FileMetricsProvider::Params( | 
|  | crashpad_metrics_file, | 
|  | FileMetricsProvider::SOURCE_HISTOGRAMS_ATOMIC_FILE, | 
|  | FileMetricsProvider::ASSOCIATE_INTERNAL_PROFILE_OR_PREVIOUS_RUN, | 
|  | kCrashpadHistogramAllocatorName), | 
|  | metrics_reporting_enabled); | 
|  |  | 
|  | // 2. Data from the current run. Note: "Active" files don't set "prefs_key" | 
|  | // because they update the file itself. | 
|  | base::FilePath crashpad_active_path = | 
|  | base::GlobalHistogramAllocator::ConstructFilePathForActiveFile( | 
|  | user_data_dir, kCrashpadHistogramAllocatorName); | 
|  | file_metrics_provider->RegisterSource( | 
|  | FileMetricsProvider::Params( | 
|  | crashpad_active_path, | 
|  | FileMetricsProvider::SOURCE_HISTOGRAMS_ACTIVE_FILE, | 
|  | FileMetricsProvider::ASSOCIATE_CURRENT_RUN), | 
|  | metrics_reporting_enabled); | 
|  |  | 
|  | return file_metrics_provider; | 
|  | } | 
|  |  | 
|  | base::OnceClosure CreateChainedClosure(base::OnceClosure cb1, | 
|  | base::OnceClosure cb2) { | 
|  | return base::BindOnce( | 
|  | [](base::OnceClosure cb1, base::OnceClosure cb2) { | 
|  | if (cb1) { | 
|  | std::move(cb1).Run(); | 
|  | } | 
|  | if (cb2) { | 
|  | std::move(cb2).Run(); | 
|  | } | 
|  | }, | 
|  | std::move(cb1), std::move(cb2)); | 
|  | } | 
|  |  | 
|  | // IMPORTANT: DO NOT CHANGE sample rates without first ensuring the Chrome | 
|  | // Metrics team has the appropriate backend bandwidth and storage. | 
|  |  | 
|  | // Sample at 2%, based on storage concerns. We sample at a different rate than | 
|  | // Chrome because we have more metrics "clients" (each app on the device counts | 
|  | // as a separate client). | 
|  | const int kStableSampledInRatePerMille = 20; | 
|  |  | 
|  | // Sample non-stable channels at 99%, to boost volume for pre-stable | 
|  | // experiments. We choose 99% instead of 100% for consistency with Chrome and to | 
|  | // exercise the out-of-sample code path. | 
|  | const int kBetaDevCanarySampledInRatePerMille = 990; | 
|  |  | 
|  | AwMetricsServiceClient* g_aw_metrics_service_client = nullptr; | 
|  |  | 
|  | int GetBaseSampleRatePerMille() { | 
|  | // Down-sample unknown channel as a precaution in case it ends up being | 
|  | // shipped to Stable users. | 
|  | version_info::Channel channel = version_info::android::GetChannel(); | 
|  | if (channel == version_info::Channel::STABLE || | 
|  | channel == version_info::Channel::UNKNOWN) { | 
|  | return kStableSampledInRatePerMille; | 
|  | } | 
|  | return kBetaDevCanarySampledInRatePerMille; | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | // Needs to be kept in sync with the writer in | 
|  | // third_party/crashpad/crashpad/handler/handler_main.cc. | 
|  | const char kCrashpadHistogramAllocatorName[] = "CrashpadMetrics"; | 
|  |  | 
|  | AwMetricsServiceClient::Delegate::Delegate() = default; | 
|  | AwMetricsServiceClient::Delegate::~Delegate() = default; | 
|  |  | 
|  | // static | 
|  | AwMetricsServiceClient* AwMetricsServiceClient::GetInstance() { | 
|  | DCHECK(g_aw_metrics_service_client); | 
|  | DCHECK_CALLED_ON_VALID_SEQUENCE( | 
|  | g_aw_metrics_service_client->sequence_checker_); | 
|  | return g_aw_metrics_service_client; | 
|  | } | 
|  |  | 
|  | // static | 
|  | void AwMetricsServiceClient::SetInstance( | 
|  | std::unique_ptr<AwMetricsServiceClient> aw_metrics_service_client) { | 
|  | DCHECK(!g_aw_metrics_service_client); | 
|  | DCHECK(aw_metrics_service_client); | 
|  | g_aw_metrics_service_client = aw_metrics_service_client.release(); | 
|  | DCHECK_CALLED_ON_VALID_SEQUENCE( | 
|  | g_aw_metrics_service_client->sequence_checker_); | 
|  | } | 
|  |  | 
|  | AwMetricsServiceClient::AwMetricsServiceClient( | 
|  | std::unique_ptr<Delegate> delegate) | 
|  | : time_created_(base::Time::Now()), delegate_(std::move(delegate)) {} | 
|  |  | 
|  | AwMetricsServiceClient::~AwMetricsServiceClient() = default; | 
|  |  | 
|  | void AwMetricsServiceClient::Initialize(PrefService* pref_service) { | 
|  | DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
|  | DCHECK(!init_finished_); | 
|  |  | 
|  | pref_service_ = pref_service; | 
|  |  | 
|  | metrics_state_manager_ = metrics::MetricsStateManager::Create( | 
|  | pref_service_, this, | 
|  | // Pass an empty file path since the path is for Extended Variations Safe | 
|  | // Mode, which is N/A to Android embedders. | 
|  | std::wstring(), base::FilePath(), metrics::StartupVisibility::kUnknown, | 
|  | { | 
|  | // The low entropy provider is used instead of the default provider | 
|  | // because the default provider needs to know if UMA is enabled and | 
|  | // querying GMS to determine whether UMA is enabled is slow. | 
|  | // The low entropy provider has fewer unique experiment combinations, | 
|  | // which is better for privacy, but can have crosstalk issues between | 
|  | // experiments. | 
|  | .default_entropy_provider_type = metrics::EntropyProviderType::kLow, | 
|  | .force_benchmarking_mode = | 
|  | base::CommandLine::ForCurrentProcess()->HasSwitch( | 
|  | switches::kEnableGpuBenchmarking), | 
|  | }); | 
|  |  | 
|  | metrics_state_manager_->InstantiateFieldTrialList(); | 
|  |  | 
|  | init_finished_ = true; | 
|  |  | 
|  | synthetic_trial_registry_ = | 
|  | std::make_unique<variations::SyntheticTrialRegistry>(); | 
|  | synthetic_trial_observation_.Observe(synthetic_trial_registry_.get()); | 
|  |  | 
|  | // Create the MetricsService immediately so that other code can make use of | 
|  | // it. Chrome always creates the MetricsService as well. | 
|  | metrics_service_ = std::make_unique<metrics::MetricsService>( | 
|  | metrics_state_manager_.get(), this, pref_service_); | 
|  |  | 
|  | // Registration of providers has to wait until consent is determined. To | 
|  | // do otherwise means the providers would always be configured with reporting | 
|  | // disabled (because when this is called in production consent hasn't been | 
|  | // determined). If consent has not been determined, this does nothing. | 
|  | MaybeStartMetrics(); | 
|  | } | 
|  |  | 
|  | // TODO:(crbug.com/1148351) Make the initialization consistent with Chrome. | 
|  | void AwMetricsServiceClient::MaybeStartMetrics() { | 
|  | DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
|  |  | 
|  | if (!IsConsentDetermined()) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | #if DCHECK_IS_ON() | 
|  | // This function should be called only once after consent has been determined. | 
|  | DCHECK(!did_start_metrics_with_consent_); | 
|  | did_start_metrics_with_consent_ = true; | 
|  | #endif | 
|  |  | 
|  | // Treat the debugging flag the same as user consent because the user set it, | 
|  | // but keep app_consent_ separate so we never persist data from an opted-out | 
|  | // app. | 
|  | bool user_consent_or_flag = user_consent_ || IsMetricsReportingForceEnabled(); | 
|  | if (app_consent_ && user_consent_or_flag) { | 
|  | did_start_metrics_ = true; | 
|  | // Make GetSampleBucketValue() work properly. | 
|  | metrics_state_manager_->ForceClientIdCreation(); | 
|  | is_client_id_forced_ = true; | 
|  | RegisterMetricsProvidersAndInitState(); | 
|  | // Register for notifications so we can detect when the user or app are | 
|  | // interacting with the embedder. We use these as signals to wake up the | 
|  | // MetricsService. | 
|  | delegate_->AddWebViewAppStateObserver(this); | 
|  |  | 
|  | if (IsReportingEnabled()) { | 
|  | // We assume the embedder has no shutdown sequence, so there's no need | 
|  | // for a matching Stop() call. | 
|  | metrics_service_->Start(); | 
|  | } | 
|  | } else { | 
|  | // Even though reporting is not enabled, CreateFileMetricsProvider() is | 
|  | // called. This ensures on disk state is removed. | 
|  | metrics_service_->RegisterMetricsProvider(CreateFileMetricsProvider( | 
|  | pref_service_, /* metrics_reporting_enabled */ false)); | 
|  | pref_service_->ClearPref(metrics::prefs::kMetricsClientID); | 
|  | pref_service_->ClearPref(metrics::prefs::kMetricsProvisionalClientID); | 
|  | pref_service_->ClearPref(metrics::prefs::kMetricsLogRecordId); | 
|  | } | 
|  | } | 
|  |  | 
|  | void AwMetricsServiceClient::RegisterMetricsProvidersAndInitState() { | 
|  | CHECK(metrics::SubprocessMetricsProvider::GetInstance()); | 
|  |  | 
|  | metrics_service_->RegisterMetricsProvider( | 
|  | std::make_unique<metrics::NetworkMetricsProvider>( | 
|  | content::CreateNetworkConnectionTrackerAsyncGetter())); | 
|  | metrics_service_->RegisterMetricsProvider( | 
|  | std::make_unique<metrics::CPUMetricsProvider>()); | 
|  | metrics_service_->RegisterMetricsProvider( | 
|  | std::make_unique<metrics::EntropyStateProvider>(pref_service_)); | 
|  | metrics_service_->RegisterMetricsProvider( | 
|  | std::make_unique<metrics::ScreenInfoMetricsProvider>()); | 
|  | metrics_service_->RegisterMetricsProvider( | 
|  | std::make_unique<metrics::FormFactorMetricsProvider>()); | 
|  | metrics_service_->RegisterMetricsProvider(CreateFileMetricsProvider( | 
|  | pref_service_, metrics_state_manager_->IsMetricsReportingEnabled())); | 
|  | metrics_service_->RegisterMetricsProvider( | 
|  | std::make_unique<metrics::CallStackProfileMetricsProvider>()); | 
|  | metrics_service_->RegisterMetricsProvider( | 
|  | std::make_unique<metrics::AndroidMetricsProvider>()); | 
|  | metrics_service_->RegisterMetricsProvider( | 
|  | std::make_unique<metrics::DriveMetricsProvider>( | 
|  | base::DIR_ANDROID_APP_DATA)); | 
|  | metrics_service_->RegisterMetricsProvider( | 
|  | std::make_unique<metrics::GPUMetricsProvider>()); | 
|  | metrics_service_->RegisterMetricsProvider( | 
|  | std::make_unique<metrics::SamplingMetricsProvider>( | 
|  | GetSampleRatePerMille())); | 
|  | metrics_service_->RegisterMetricsProvider( | 
|  | std::make_unique<metrics::ContentStabilityMetricsProvider>( | 
|  | pref_service_, /*extensions_helper=*/nullptr)); | 
|  | delegate_->RegisterAdditionalMetricsProviders(metrics_service_.get()); | 
|  |  | 
|  | // The file metrics provider performs IO. | 
|  | base::ScopedAllowBlocking allow_io; | 
|  | metrics_service_->InitializeMetricsRecordingState(); | 
|  | } | 
|  |  | 
|  | void AwMetricsServiceClient::SetHaveMetricsConsent(bool user_consent, | 
|  | bool app_consent) { | 
|  | DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
|  | set_consent_finished_ = true; | 
|  | user_consent_ = user_consent; | 
|  | app_consent_ = app_consent; | 
|  | MaybeStartMetrics(); | 
|  | } | 
|  |  | 
|  | void AwMetricsServiceClient::SetFastStartupForTesting( | 
|  | bool fast_startup_for_testing) { | 
|  | DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
|  | fast_startup_for_testing_ = fast_startup_for_testing; | 
|  | } | 
|  |  | 
|  | void AwMetricsServiceClient::SetUploadIntervalForTesting( | 
|  | const base::TimeDelta& upload_interval) { | 
|  | DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
|  | overridden_upload_interval_ = upload_interval; | 
|  | } | 
|  |  | 
|  | bool AwMetricsServiceClient::IsConsentDetermined() const { | 
|  | return init_finished_ && set_consent_finished_; | 
|  | } | 
|  |  | 
|  | bool AwMetricsServiceClient::IsConsentGiven() const { | 
|  | DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
|  | return user_consent_ && app_consent_; | 
|  | } | 
|  |  | 
|  | bool AwMetricsServiceClient::IsReportingEnabled() const { | 
|  | DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
|  | if (!app_consent_) { | 
|  | return false; | 
|  | } | 
|  | return IsMetricsReportingForceEnabled() || | 
|  | (EnabledStateProvider::IsReportingEnabled() && IsInSample()); | 
|  | } | 
|  |  | 
|  | metrics::MetricsService* AwMetricsServiceClient::GetMetricsServiceIfStarted() { | 
|  | DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
|  | return did_start_metrics_ ? metrics_service_.get() : nullptr; | 
|  | } | 
|  |  | 
|  | variations::SyntheticTrialRegistry* | 
|  | AwMetricsServiceClient::GetSyntheticTrialRegistry() { | 
|  | return synthetic_trial_registry_.get(); | 
|  | } | 
|  |  | 
|  | metrics::MetricsService* AwMetricsServiceClient::GetMetricsService() { | 
|  | DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
|  | // This will be null if initialization hasn't finished. | 
|  | return metrics_service_.get(); | 
|  | } | 
|  |  | 
|  | // In Chrome, UMA and Crashpad are enabled/disabled together by the same | 
|  | // checkbox and they share the same client ID (a.k.a. GUID). SetMetricsClientId | 
|  | // is intended to provide the ID to Breakpad. In AwMetricsServiceClients | 
|  | // UMA and Crashpad are independent, so this is a no-op. | 
|  | void AwMetricsServiceClient::SetMetricsClientId(const std::string& client_id) {} | 
|  |  | 
|  | int32_t AwMetricsServiceClient::GetProduct() { | 
|  | return metrics::ChromeUserMetricsExtension::ANDROID_WEBVIEW; | 
|  | } | 
|  |  | 
|  | std::string AwMetricsServiceClient::GetApplicationLocale() { | 
|  | return base::i18n::GetConfiguredLocale(); | 
|  | } | 
|  |  | 
|  | const network_time::NetworkTimeTracker* | 
|  | AwMetricsServiceClient::GetNetworkTimeTracker() { | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | bool AwMetricsServiceClient::GetBrand(std::string* brand_code) { | 
|  | // AwMetricsServiceClients don't use brand codes. | 
|  | return false; | 
|  | } | 
|  |  | 
|  | metrics::SystemProfileProto::Channel AwMetricsServiceClient::GetChannel() { | 
|  | return metrics::AsProtobufChannel(version_info::android::GetChannel()); | 
|  | } | 
|  |  | 
|  | bool AwMetricsServiceClient::IsExtendedStableChannel() { | 
|  | return false;  // Not supported on AwMetricsServiceClients. | 
|  | } | 
|  |  | 
|  | std::string AwMetricsServiceClient::GetVersionString() { | 
|  | return metrics::GetVersionString(); | 
|  | } | 
|  |  | 
|  | void AwMetricsServiceClient::MergeSubprocessHistograms() { | 
|  | // TODO(crbug.com/40213327): Move this to a shared place to not have to | 
|  | // duplicate the code across different `MetricsServiceClient`s. | 
|  |  | 
|  | // Synchronously fetch subprocess histograms that live in shared memory. | 
|  | base::StatisticsRecorder::ImportProvidedHistogramsSync(); | 
|  |  | 
|  | // Asynchronously fetch subprocess histograms that do not live in shared | 
|  | // memory (e.g., they were emitted before the shared memory was set up). | 
|  | content::FetchHistogramsAsynchronously( | 
|  | base::SingleThreadTaskRunner::GetCurrentDefault(), | 
|  | /*callback=*/base::DoNothing(), | 
|  | /*wait_time=*/base::Milliseconds(kMaxHistogramGatheringWaitDuration)); | 
|  | } | 
|  |  | 
|  | void AwMetricsServiceClient::CollectFinalMetricsForLog( | 
|  | base::OnceClosure done_callback) { | 
|  | auto barrier_closure = | 
|  | base::BarrierClosure(/*num_closures=*/2, std::move(done_callback)); | 
|  |  | 
|  | // Merge histograms from metrics providers into StatisticsRecorder. | 
|  | base::StatisticsRecorder::ImportProvidedHistograms( | 
|  | /*async=*/true, /*done_callback=*/barrier_closure); | 
|  |  | 
|  | base::TimeDelta timeout = | 
|  | base::Milliseconds(kMaxHistogramGatheringWaitDuration); | 
|  |  | 
|  | // Set up the callback task to call after we receive histograms from all | 
|  | // child processes. |timeout| specifies how long to wait before absolutely | 
|  | // calling us back on the task. | 
|  | content::FetchHistogramsAsynchronously( | 
|  | base::SingleThreadTaskRunner::GetCurrentDefault(), | 
|  | CreateChainedClosure(barrier_closure, | 
|  | on_final_metrics_collected_listener_), | 
|  | timeout); | 
|  |  | 
|  | if (collect_final_metrics_for_log_closure_) { | 
|  | std::move(collect_final_metrics_for_log_closure_).Run(); | 
|  | } | 
|  | } | 
|  |  | 
|  | std::unique_ptr<metrics::MetricsLogUploader> | 
|  | AwMetricsServiceClient::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) { | 
|  | CHECK_EQ(service_type, metrics::MetricsLogUploader::UMA); | 
|  | // |server_url|, |insecure_server_url|, and |mime_type| are unused because | 
|  | // AwMetricsServiceClients send metrics to the platform logging mechanism | 
|  | // rather than to Chrome's metrics server. | 
|  | return std::make_unique<metrics::AndroidMetricsLogUploader>( | 
|  | on_upload_complete); | 
|  | } | 
|  |  | 
|  | base::TimeDelta AwMetricsServiceClient::GetStandardUploadInterval() { | 
|  | // In AwMetricsServiceClients, metrics collection (when we batch up all | 
|  | // logged histograms into a ChromeUserMetricsExtension proto) and metrics | 
|  | // uploading (when the proto goes to the server) happen separately. | 
|  | // | 
|  | // This interval controls the metrics collection rate, so we choose the | 
|  | // standard upload interval to make sure we're collecting metrics consistently | 
|  | // with Chrome for Android. The metrics uploading rate for | 
|  | // AwMetricsServiceClients is controlled by the platform logging | 
|  | // mechanism. Since this mechanism has its own logic for rate-limiting on | 
|  | // cellular connections, we disable the component-layer logic. | 
|  | if (!overridden_upload_interval_.is_zero()) { | 
|  | return overridden_upload_interval_; | 
|  | } | 
|  | return metrics::GetUploadInterval(false /* use_cellular_upload_interval */); | 
|  | } | 
|  |  | 
|  | bool AwMetricsServiceClient::ShouldStartUpFast() const { | 
|  | DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
|  | return fast_startup_for_testing_; | 
|  | } | 
|  |  | 
|  | void AwMetricsServiceClient::OnRenderProcessHostCreated( | 
|  | content::RenderProcessHost* host) { | 
|  | if (!host_observation_.IsObservingSource(host)) { | 
|  | host_observation_.AddObservation(host); | 
|  | } | 
|  | } | 
|  |  | 
|  | void AwMetricsServiceClient::RenderProcessExited( | 
|  | content::RenderProcessHost* host, | 
|  | const content::ChildProcessTerminationInfo& info) { | 
|  | host_observation_.RemoveObservation(host); | 
|  |  | 
|  | if (did_start_metrics_) { | 
|  | metrics_service_->OnApplicationNotIdle(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void AwMetricsServiceClient::OnWebContentsCreated( | 
|  | content::WebContents* web_contents) { | 
|  | DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
|  | metrics::MetricsServicesWebContentsObserver::CreateForWebContents( | 
|  | web_contents, | 
|  | /*OnDidStartLoadingCb=*/ | 
|  | base::BindRepeating(&AwMetricsServiceClient::OnDidStartLoading, | 
|  | weak_ptr_factory_.GetWeakPtr()), | 
|  | /*OnDidStopLoadingCb=*/ | 
|  | base::BindRepeating(&AwMetricsServiceClient::OnApplicationNotIdle, | 
|  | weak_ptr_factory_.GetWeakPtr()), | 
|  | /*OnRendererUnresponsiveCb=*/ | 
|  | base::BindRepeating(&AwMetricsServiceClient::OnApplicationNotIdle, | 
|  | weak_ptr_factory_.GetWeakPtr())); | 
|  | } | 
|  |  | 
|  | void AwMetricsServiceClient::SetCollectFinalMetricsForLogClosureForTesting( | 
|  | base::OnceClosure closure) { | 
|  | collect_final_metrics_for_log_closure_ = std::move(closure); | 
|  | } | 
|  |  | 
|  | void AwMetricsServiceClient::SetOnFinalMetricsCollectedListenerForTesting( | 
|  | base::RepeatingClosure listener) { | 
|  | on_final_metrics_collected_listener_ = std::move(listener); | 
|  | } | 
|  |  | 
|  | int AwMetricsServiceClient::GetSampleBucketValue() const { | 
|  | DCHECK(is_client_id_forced_); | 
|  | return UintToPerMille(base::PersistentHash(metrics_service_->GetClientId())); | 
|  | } | 
|  |  | 
|  | bool AwMetricsServiceClient::IsInSample() const { | 
|  | // Called in MaybeStartMetrics(), after |metrics_service_| is created. | 
|  | return GetSampleBucketValue() < GetSampleRatePerMille(); | 
|  | } | 
|  |  | 
|  | InstallerPackageType AwMetricsServiceClient::GetInstallerPackageType() { | 
|  | // Check with Java side, to see if it's OK to log the package name for this | 
|  | // type of app (see Java side for the specific requirements). | 
|  | JNIEnv* env = base::android::AttachCurrentThread(); | 
|  | int type = | 
|  | metrics::Java_AndroidMetricsServiceClient_getInstallerPackageType(env); | 
|  | return static_cast<InstallerPackageType>(type); | 
|  | } | 
|  |  | 
|  | bool AwMetricsServiceClient::CanRecordPackageNameForAppType() { | 
|  | InstallerPackageType installer_type = GetInstallerPackageType(); | 
|  | // Allow recording the app package name of system apps and apps | 
|  | // from the play store. | 
|  | return (installer_type == InstallerPackageType::SYSTEM_APP || | 
|  | installer_type == InstallerPackageType::GOOGLE_PLAY_STORE); | 
|  | } | 
|  |  | 
|  | std::string AwMetricsServiceClient::GetAppPackageNameIfLoggable() { | 
|  | if (CanRecordPackageNameForAppType()) { | 
|  | return GetAppPackageName(); | 
|  | } | 
|  | return std::string(); | 
|  | } | 
|  |  | 
|  | std::string AwMetricsServiceClient::GetAppPackageName() { | 
|  | JNIEnv* env = base::android::AttachCurrentThread(); | 
|  | base::android::ScopedJavaLocalRef<jstring> j_app_name = | 
|  | metrics::Java_AndroidMetricsServiceClient_getAppPackageName(env); | 
|  | if (j_app_name) { | 
|  | return base::android::ConvertJavaStringToUTF8(env, j_app_name); | 
|  | } | 
|  | return std::string(); | 
|  | } | 
|  |  | 
|  | void AwMetricsServiceClient::OnApplicationNotIdle() { | 
|  | auto* metrics_service = GetMetricsServiceIfStarted(); | 
|  | if (!metrics_service) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | metrics_service->OnApplicationNotIdle(); | 
|  | } | 
|  |  | 
|  | void AwMetricsServiceClient::OnDidStartLoading() { | 
|  | OnApplicationNotIdle(); | 
|  |  | 
|  | auto* metrics_service = GetMetricsService(); | 
|  | if (!metrics_service) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | metrics_service->OnPageLoadStarted(); | 
|  | } | 
|  |  | 
|  | int AwMetricsServiceClient::GetSampleRatePerMille() const { | 
|  | return 1000; | 
|  | } | 
|  |  | 
|  | bool AwMetricsServiceClient::ShouldApplyMetricsFiltering() const { | 
|  | bool used_to_sample_in = GetSampleBucketValue() < GetBaseSampleRatePerMille(); | 
|  | return !used_to_sample_in; | 
|  | } | 
|  |  | 
|  | void AwMetricsServiceClient::OnAppStateChanged( | 
|  | WebViewAppStateObserver::State state) { | 
|  | // To match MetricsService's expectation, | 
|  | // - does nothing if no WebView has ever been created. | 
|  | // - starts notifying MetricsService once a WebView is created and the app | 
|  | //   is foreground. | 
|  | // - consolidates the other states other than kForeground into background. | 
|  | // - avoids the duplicated notification. | 
|  | if (state == WebViewAppStateObserver::State::kDestroyed && | 
|  | !delegate_->HasAwContentsEverCreated()) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | bool foreground = state == WebViewAppStateObserver::State::kForeground; | 
|  |  | 
|  | if (foreground == app_in_foreground_) | 
|  | return; | 
|  |  | 
|  | app_in_foreground_ = foreground; | 
|  | if (app_in_foreground_) { | 
|  | GetMetricsService()->OnAppEnterForeground(); | 
|  | } else { | 
|  | // TODO(crbug.com/40118864): Turn on the background recording. | 
|  | // Not recording in background, this matches Chrome's behavior. | 
|  | GetMetricsService()->OnAppEnterBackground( | 
|  | /* keep_recording_in_background = false */); | 
|  | } | 
|  | } | 
|  |  | 
|  | // static | 
|  | void AwMetricsServiceClient::RegisterMetricsPrefs( | 
|  | PrefRegistrySimple* registry) { | 
|  | metrics::MetricsService::RegisterPrefs(registry); | 
|  | metrics::FileMetricsProvider::RegisterSourcePrefs(registry, | 
|  | kBrowserMetricsName); | 
|  | metrics::FileMetricsProvider::RegisterSourcePrefs( | 
|  | registry, kCrashpadHistogramAllocatorName); | 
|  | metrics::FileMetricsProvider::RegisterPrefs(registry); | 
|  | metrics::StabilityMetricsHelper::RegisterPrefs(registry); | 
|  | AndroidMetricsProvider::RegisterPrefs(registry); | 
|  | } | 
|  |  | 
|  | // static | 
|  | void JNI_AwMetricsServiceClient_SetHaveMetricsConsent(JNIEnv* env, | 
|  | jboolean user_consent, | 
|  | jboolean app_consent) { | 
|  | AwMetricsServiceClient::GetInstance()->SetHaveMetricsConsent(user_consent, | 
|  | app_consent); | 
|  | } | 
|  |  | 
|  | // static | 
|  | void JNI_AwMetricsServiceClient_SetFastStartupForTesting( | 
|  | JNIEnv* env, | 
|  | jboolean fast_startup_for_testing) { | 
|  | AwMetricsServiceClient::GetInstance()->SetFastStartupForTesting( | 
|  | fast_startup_for_testing); | 
|  | } | 
|  |  | 
|  | // static | 
|  | void JNI_AwMetricsServiceClient_SetUploadIntervalForTesting( | 
|  | JNIEnv* env, | 
|  | jlong upload_interval_ms) { | 
|  | AwMetricsServiceClient::GetInstance()->SetUploadIntervalForTesting( | 
|  | base::Milliseconds(upload_interval_ms)); | 
|  | } | 
|  |  | 
|  | // static | 
|  | void JNI_AwMetricsServiceClient_SetOnFinalMetricsCollectedListenerForTesting( | 
|  | JNIEnv* env, | 
|  | const base::android::JavaParamRef<jobject>& listener) { | 
|  | AwMetricsServiceClient::GetInstance() | 
|  | ->SetOnFinalMetricsCollectedListenerForTesting(base::BindRepeating( | 
|  | base::android::RunRunnableAndroid, | 
|  | base::android::ScopedJavaGlobalRef<jobject>(listener))); | 
|  | } | 
|  |  | 
|  | }  // namespace android_webview |