Commit 282196be authored by Tim Volodine's avatar Tim Volodine Committed by Commit Bot

Reland "Move AwMetricsServiceClient logic into components for reuse by WebLayer"

This reverts commit 5d840711.

Reason for revert:
the previous breakage and revert was due to an internal patch not having rolled into chromium. It has now, so relanding this CL.
(crbug.com/1046775)

Original change's description:
> Revert "Move AwMetricsServiceClient logic into components for reuse by WebLayer"
> 
> This reverts commit 43e5153c.
> 
> Reason for revert:
> broke some build bots, see https://bugs.chromium.org/p/chromium/issues/detail?id=1046775
> 
> https://ci.chromium.org/p/chrome/builders/ci/arm64-builder-rel/20782
> 
> 
> Original change's description:
> > Move AwMetricsServiceClient logic into components for reuse by WebLayer
> > 
> > The corresponding WebLayer changes are in a follow on CL. The TBR is for
> > the new DEP file which needs content/public/browser.
> > 
> > TBR=jam@chromium.org
> > 
> > Bug: 1025781
> > Change-Id: Ie895ba7246761416d52c98ae3d4b241fed62fa45
> > Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1926505
> > Commit-Queue: Alex Clarke <alexclarke@chromium.org>
> > Reviewed-by: Bo <boliu@chromium.org>
> > Reviewed-by: Nate Fischer <ntfschr@chromium.org>
> > Reviewed-by: Alexei Svitkine <asvitkine@chromium.org>
> > Cr-Commit-Position: refs/heads/master@{#736313}
> 
> TBR=jam@chromium.org,boliu@chromium.org,asvitkine@chromium.org,blundell@chromium.org,alexclarke@chromium.org,tobiasjs@chromium.org,ntfschr@chromium.org
> 
> Change-Id: I2e52f5da2c62f769cedbcaa4671f1a0ebe193be5
> No-Presubmit: true
> No-Tree-Checks: true
> No-Try: true
> Bug: 1025781
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2028047
> Reviewed-by: Tim Volodine <timvolodine@chromium.org>
> Commit-Queue: Tim Volodine <timvolodine@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#736366}

TBR=jam@chromium.org,boliu@chromium.org,asvitkine@chromium.org,blundell@chromium.org,timvolodine@chromium.org,alexclarke@chromium.org,tobiasjs@chromium.org,ntfschr@chromium.org

Change-Id: I24cbdc73208dcc78040ca1e5a2904cf0d06c6e52
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: 1025781
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2028127Reviewed-by: default avatarTim Volodine <timvolodine@chromium.org>
Commit-Queue: Tim Volodine <timvolodine@chromium.org>
Cr-Commit-Position: refs/heads/master@{#736373}
parent 47fc1dff
......@@ -336,7 +336,6 @@ generate_jni("browser_jni_headers") {
"java/src/org/chromium/android_webview/gfx/AwPicture.java",
"java/src/org/chromium/android_webview/gfx/JavaBrowserViewRendererHelper.java",
"java/src/org/chromium/android_webview/gfx/RootBeginFrameSourceWebView.java",
"java/src/org/chromium/android_webview/metrics/AwMetricsLogUploader.java",
"java/src/org/chromium/android_webview/metrics/AwMetricsServiceClient.java",
"java/src/org/chromium/android_webview/permission/AwPermissionRequest.java",
"java/src/org/chromium/android_webview/safe_browsing/AwSafeBrowsingConfigHelper.java",
......@@ -430,7 +429,6 @@ android_library("browser_java") {
"java/src/org/chromium/android_webview/gfx/AwPicture.java",
"java/src/org/chromium/android_webview/gfx/JavaBrowserViewRendererHelper.java",
"java/src/org/chromium/android_webview/gfx/RootBeginFrameSourceWebView.java",
"java/src/org/chromium/android_webview/metrics/AwMetricsLogUploader.java",
"java/src/org/chromium/android_webview/metrics/AwMetricsServiceClient.java",
"java/src/org/chromium/android_webview/permission/AwGeolocationCallback.java",
"java/src/org/chromium/android_webview/permission/AwPermissionRequest.java",
......@@ -510,6 +508,7 @@ android_library("common_platform_services_java") {
deps = [
"//base:base_java",
"//components/embedder_support/android/metrics:java",
"//third_party/android_deps:androidx_annotation_annotation_java",
]
......
......@@ -206,6 +206,7 @@ source_set("browser") {
"//components/crash/content/app",
"//components/crash/content/browser",
"//components/embedder_support/android:web_contents_delegate",
"//components/embedder_support/android/metrics",
"//components/google/core/common",
"//components/heap_profiling",
"//components/metrics",
......
......@@ -16,6 +16,7 @@ include_rules = [
"+components/crash/content/browser",
"+components/crash/core",
"+components/download/public/common",
"+components/embedder_support/android/metrics",
"+components/heap_profiling",
"+components/keyed_service/core",
"+components/minidump_uploader",
......
......@@ -14,7 +14,6 @@
#include "android_webview/browser/aw_content_browser_client.h"
#include "android_webview/browser/aw_web_ui_controller_factory.h"
#include "android_webview/browser/metrics/aw_metrics_service_client.h"
#include "android_webview/browser/metrics/memory_metrics_logger.h"
#include "android_webview/browser/network_service/aw_network_change_notifier_factory.h"
#include "android_webview/common/aw_descriptors.h"
#include "android_webview/common/aw_paths.h"
......@@ -33,6 +32,7 @@
#include "base/message_loop/message_pump_type.h"
#include "base/path_service.h"
#include "components/crash/content/browser/child_exit_observer_android.h"
#include "components/embedder_support/android/metrics/memory_metrics_logger.h"
#include "components/heap_profiling/supervisor.h"
#include "components/services/heap_profiling/public/cpp/settings.h"
#include "components/user_prefs/user_prefs.h"
......@@ -124,7 +124,7 @@ void AwBrowserMainParts::PreMainMessageLoopRun() {
content::WebUIControllerFactory::RegisterFactory(
AwWebUIControllerFactory::GetInstance());
content::RenderFrameHost::AllowInjectingJavaScript();
metrics_logger_ = std::make_unique<MemoryMetricsLogger>();
metrics_logger_ = std::make_unique<metrics::MemoryMetricsLogger>();
}
bool AwBrowserMainParts::MainMessageLoopRun(int* result_code) {
......
......@@ -13,11 +13,14 @@
#include "base/task/single_thread_task_executor.h"
#include "content/public/browser/browser_main_parts.h"
namespace metrics {
class MemoryMetricsLogger;
}
namespace android_webview {
class AwBrowserProcess;
class AwContentBrowserClient;
class MemoryMetricsLogger;
class AwBrowserMainParts : public content::BrowserMainParts {
public:
......@@ -37,7 +40,7 @@ class AwBrowserMainParts : public content::BrowserMainParts {
AwContentBrowserClient* browser_client_;
std::unique_ptr<MemoryMetricsLogger> metrics_logger_;
std::unique_ptr<metrics::MemoryMetricsLogger> metrics_logger_;
std::unique_ptr<AwBrowserProcess> browser_process_;
......
......@@ -76,7 +76,6 @@ const char* const kPersistentPrefsWhitelist[] = {
prefs::kRestartsWithStaleSeed,
};
// Shows notifications which correspond to PersistentPrefStore's reading errors.
void HandleReadError(PersistentPrefStore::PrefReadError error) {}
base::FilePath GetPrefStorePath() {
......
......@@ -4,19 +4,16 @@
source_set("metrics") {
sources = [
"aw_metrics_log_uploader.cc",
"aw_metrics_log_uploader.h",
"aw_metrics_service_client.cc",
"aw_metrics_service_client.h",
"aw_stability_metrics_provider.cc",
"aw_stability_metrics_provider.h",
"memory_metrics_logger.cc",
"memory_metrics_logger.h",
]
deps = [
"//android_webview:browser_jni_headers",
"//android_webview/common",
"//base",
"//components/embedder_support/android/metrics",
"//components/metrics",
"//components/metrics:gpu",
"//components/metrics:net",
......@@ -25,6 +22,5 @@ source_set("metrics") {
"//components/version_info",
"//components/version_info/android:channel_getter",
"//content/public/browser",
"//services/resource_coordinator/public/cpp/memory_instrumentation:browser",
]
}
// Copyright 2017 The Chromium Authors. All rights reserved.
// 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_LOG_UPLOADER_H_
#define ANDROID_WEBVIEW_BROWSER_METRICS_AW_METRICS_LOG_UPLOADER_H_
#include <jni.h>
#include <string>
#include "components/metrics/metrics_log_uploader.h"
namespace android_webview {
// Uploads UMA logs for WebView using the platform logging mechanism.
class AwMetricsLogUploader : public ::metrics::MetricsLogUploader {
public:
explicit AwMetricsLogUploader(
const ::metrics::MetricsLogUploader::UploadCallback& on_upload_complete);
~AwMetricsLogUploader() override;
// ::metrics::MetricsLogUploader:
// Note: |log_hash| and |log_signature| are only used by the normal UMA
// server. WebView uses the platform logging mechanism instead of the normal
// UMA server, so |log_hash| and |log_signature| aren't used.
void UploadLog(const std::string& compressed_log_data,
const std::string& log_hash,
const std::string& log_signature,
const metrics::ReportingInfo& reporting_info) override;
private:
const metrics::MetricsLogUploader::UploadCallback on_upload_complete_;
DISALLOW_COPY_AND_ASSIGN(AwMetricsLogUploader);
};
} // namespace android_webview
#endif // ANDROID_WEBVIEW_BROWSER_METRICS_AW_METRICS_LOG_UPLOADER_H_
......@@ -6,45 +6,27 @@
#include <jni.h>
#include <cstdint>
#include <memory>
#include "android_webview/browser/metrics/aw_metrics_log_uploader.h"
#include "android_webview/browser/metrics/aw_stability_metrics_provider.h"
#include "android_webview/browser_jni_headers/AwMetricsServiceClient_jni.h"
#include "android_webview/common/aw_features.h"
#include "base/android/jni_android.h"
#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "base/base_paths_android.h"
#include "base/feature_list.h"
#include "base/hash/hash.h"
#include "base/i18n/rtl.h"
#include "base/metrics/histogram_functions.h"
#include "base/no_destructor.h"
#include "base/strings/string16.h"
#include "base/time/time.h"
#include "components/metrics/android_metrics_provider.h"
#include "components/metrics/call_stack_profile_metrics_provider.h"
#include "components/metrics/cpu_metrics_provider.h"
#include "components/metrics/drive_metrics_provider.h"
#include "components/metrics/enabled_state_provider.h"
#include "components/metrics/entropy_state_provider.h"
#include "components/metrics/gpu/gpu_metrics_provider.h"
#include "components/metrics/metrics_log_uploader.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/ui/screen_info_metrics_provider.h"
#include "components/metrics/version_utils.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "components/version_info/android/channel_getter.h"
#include "components/version_info/version_info.h"
#include "content/public/browser/network_service_instance.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_types.h"
namespace android_webview {
......@@ -71,30 +53,6 @@ const double kBetaDevCanarySampledInRate = 0.99;
// consulting with the privacy team.
const double kPackageNameLimitRate = 0.10;
// Callbacks for metrics::MetricsStateManager::Create. Store/LoadClientInfo
// allow Windows Chrome to back up ClientInfo. They're no-ops for WebView.
void StoreClientInfo(const metrics::ClientInfo& client_info) {}
std::unique_ptr<metrics::ClientInfo> LoadClientInfo() {
std::unique_ptr<metrics::ClientInfo> client_info;
return client_info;
}
bool UintFallsInBottomPercentOfValues(uint32_t value, double percent) {
DCHECK_GT(percent, 0);
DCHECK_LT(percent, 1.00);
// Since hashing is ~uniform, the chance that the value falls in the bottom
// X% of possible values is X%. UINT32_MAX fits within the range of integers
// that can be expressed precisely by a 64-bit double. Casting back to a
// uint32_t means we can determine if the value falls within the bottom X%,
// within a 1/UINT32_MAX error margin.
uint32_t value_threshold =
static_cast<uint32_t>(static_cast<double>(UINT32_MAX) * percent);
return value < value_threshold;
}
// Normally kMetricsReportingEnabledTimestamp would be set by the
// MetricsStateManager. However, it assumes kMetricsClientID and
// kMetricsReportingEnabledTimestamp are always set together. Because WebView
......@@ -116,36 +74,6 @@ void SetReportingEnabledDateIfNotSet(PrefService* prefs) {
backfill_date.ToTimeT());
}
std::unique_ptr<metrics::MetricsService> CreateMetricsService(
metrics::MetricsStateManager* state_manager,
metrics::MetricsServiceClient* client,
PrefService* prefs) {
auto service =
std::make_unique<metrics::MetricsService>(state_manager, client, prefs);
service->RegisterMetricsProvider(
std::make_unique<metrics::NetworkMetricsProvider>(
content::CreateNetworkConnectionTrackerAsyncGetter()));
service->RegisterMetricsProvider(
std::make_unique<android_webview::AwStabilityMetricsProvider>(prefs));
service->RegisterMetricsProvider(
std::make_unique<metrics::AndroidMetricsProvider>());
service->RegisterMetricsProvider(
std::make_unique<metrics::CPUMetricsProvider>());
service->RegisterMetricsProvider(
std::make_unique<metrics::EntropyStateProvider>(prefs));
service->RegisterMetricsProvider(
std::make_unique<metrics::GPUMetricsProvider>());
service->RegisterMetricsProvider(
std::make_unique<metrics::DriveMetricsProvider>(
base::DIR_ANDROID_APP_DATA));
service->RegisterMetricsProvider(
std::make_unique<metrics::ScreenInfoMetricsProvider>());
service->RegisterMetricsProvider(
std::make_unique<metrics::CallStackProfileMetricsProvider>());
service->InitializeMetricsRecordingState();
return service;
}
// Queries the system for the app's first install time and uses this in the
// kInstallDate pref. Must be called before created a MetricsStateManager.
// TODO(https://crbug.com/1012025): remove this when the kInstallDate pref has
......@@ -184,143 +112,17 @@ void PopulateSystemInstallDateIfNecessary(PrefService* prefs) {
// static
AwMetricsServiceClient* AwMetricsServiceClient::GetInstance() {
static base::NoDestructor<AwMetricsServiceClient> client;
DCHECK_CALLED_ON_VALID_SEQUENCE(client.get()->sequence_checker_);
client->EnsureOnValidSequence();
return client.get();
}
AwMetricsServiceClient::AwMetricsServiceClient() = default;
AwMetricsServiceClient::~AwMetricsServiceClient() = default;
// static
void AwMetricsServiceClient::RegisterPrefs(PrefRegistrySimple* registry) {
metrics::MetricsService::RegisterPrefs(registry);
metrics::StabilityMetricsHelper::RegisterPrefs(registry);
}
void AwMetricsServiceClient::Initialize(PrefService* pref_service) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!init_finished_);
pref_service_ = pref_service;
PopulateSystemInstallDateIfNecessary(pref_service_);
metrics_state_manager_ = metrics::MetricsStateManager::Create(
pref_service_, this, base::string16(),
base::BindRepeating(&StoreClientInfo),
base::BindRepeating(&LoadClientInfo));
init_finished_ = true;
MaybeStartMetrics();
}
void AwMetricsServiceClient::MaybeStartMetrics() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// 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 (init_finished_ && set_consent_finished_) {
if (app_consent_ && user_consent_or_flag) {
metrics_service_ = CreateMetricsService(metrics_state_manager_.get(),
this, pref_service_);
// Register for notifications so we can detect when the user or app are
// interacting with WebView. We use these as signals to wake up the
// MetricsService.
RegisterForNotifications();
metrics_state_manager_->ForceClientIdCreation();
SetReportingEnabledDateIfNotSet(pref_service_);
is_in_sample_ = IsInSample();
is_in_package_name_sample_ = IsInPackageNameSample();
if (IsReportingEnabled()) {
// WebView has no shutdown sequence, so there's no need for a matching
// Stop() call.
metrics_service_->Start();
}
} else {
pref_service_->ClearPref(metrics::prefs::kMetricsClientID);
pref_service_->ClearPref(
metrics::prefs::kMetricsReportingEnabledTimestamp);
}
}
}
void AwMetricsServiceClient::RegisterForNotifications() {
registrar_.Add(this, content::NOTIFICATION_LOAD_START,
content::NotificationService::AllSources());
registrar_.Add(this, content::NOTIFICATION_LOAD_STOP,
content::NotificationService::AllSources());
registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
content::NotificationService::AllSources());
registrar_.Add(this, content::NOTIFICATION_RENDER_WIDGET_HOST_HANG,
content::NotificationService::AllSources());
}
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;
}
std::unique_ptr<const base::FieldTrial::EntropyProvider>
AwMetricsServiceClient::CreateLowEntropyProvider() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return metrics_state_manager_->CreateLowEntropyProvider();
}
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() && is_in_sample_);
}
metrics::MetricsService* AwMetricsServiceClient::GetMetricsService() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// This will be null if initialization hasn't finished, or if metrics
// collection is disabled.
return metrics_service_.get();
}
// In Chrome, UMA and Breakpad 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 WebView, UMA and Breakpad 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();
}
bool AwMetricsServiceClient::GetBrand(std::string* brand_code) {
// WebView doesn't use brand codes.
return false;
}
metrics::SystemProfileProto::Channel AwMetricsServiceClient::GetChannel() {
return metrics::AsProtobufChannel(version_info::android::GetChannel());
}
......@@ -329,105 +131,56 @@ std::string AwMetricsServiceClient::GetVersionString() {
return version_info::GetVersionNumber();
}
void AwMetricsServiceClient::CollectFinalMetricsForLog(
base::OnceClosure done_callback) {
std::move(done_callback).Run();
double AwMetricsServiceClient::GetSampleRate() {
// 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 kStableSampledInRate;
}
return kBetaDevCanarySampledInRate;
}
std::unique_ptr<metrics::MetricsLogUploader>
AwMetricsServiceClient::CreateUploader(
const GURL& server_url,
const GURL& insecure_server_url,
base::StringPiece mime_type,
metrics::MetricsLogUploader::MetricServiceType service_type,
const metrics::MetricsLogUploader::UploadCallback& on_upload_complete) {
// |server_url|, |insecure_server_url|, and |mime_type| are unused because
// WebView sends metrics to the platform logging mechanism rather than to
// Chrome's metrics server.
return std::make_unique<AwMetricsLogUploader>(on_upload_complete);
void AwMetricsServiceClient::InitInternal() {
PopulateSystemInstallDateIfNecessary(pref_service());
}
base::TimeDelta AwMetricsServiceClient::GetStandardUploadInterval() {
// In WebView, 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 WebView 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 */);
void AwMetricsServiceClient::OnMetricsStart() {
SetReportingEnabledDateIfNotSet(pref_service());
}
double AwMetricsServiceClient::GetPackageNameLimitRate() {
return kPackageNameLimitRate;
}
bool AwMetricsServiceClient::ShouldStartUpFastForTesting() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return fast_startup_for_testing_;
bool AwMetricsServiceClient::ShouldWakeMetricsService() {
return base::FeatureList::IsEnabled(features::kWebViewWakeMetricsService);
}
std::string AwMetricsServiceClient::GetAppPackageName() {
if (is_in_package_name_sample_ && CanRecordPackageNameForAppType()) {
void AwMetricsServiceClient::RegisterAdditionalMetricsProviders(
metrics::MetricsService* service) {
service->RegisterMetricsProvider(
std::make_unique<android_webview::AwStabilityMetricsProvider>(
pref_service()));
service->RegisterMetricsProvider(
std::make_unique<metrics::AndroidMetricsProvider>());
service->RegisterMetricsProvider(
std::make_unique<metrics::DriveMetricsProvider>(
base::DIR_ANDROID_APP_DATA));
service->RegisterMetricsProvider(
std::make_unique<metrics::GPUMetricsProvider>());
}
std::string AwMetricsServiceClient::GetAppPackageNameInternal() {
JNIEnv* env = base::android::AttachCurrentThread();
base::android::ScopedJavaLocalRef<jstring> j_app_name =
Java_AwMetricsServiceClient_getAppPackageName(env);
if (j_app_name)
return ConvertJavaStringToUTF8(env, j_app_name);
}
return std::string();
}
void AwMetricsServiceClient::Observe(
int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
switch (type) {
case content::NOTIFICATION_LOAD_STOP:
case content::NOTIFICATION_LOAD_START:
case content::NOTIFICATION_RENDERER_PROCESS_CLOSED:
case content::NOTIFICATION_RENDER_WIDGET_HOST_HANG:
if (base::FeatureList::IsEnabled(features::kWebViewWakeMetricsService))
metrics_service_->OnApplicationNotIdle();
break;
default:
NOTREACHED();
}
}
// WebView metrics are sampled at (possibly) different rates depending on
// channel, based on the client ID. Sampling is hard-coded (rather than
// controlled via variations, as in Chrome) because:
// - WebView is slow to download the variations seed and propagate it to each
// app, so we'd miss metrics from the first few runs of each app.
// - WebView uses the low-entropy source for all studies, so there would be
// crosstalk between the metrics sampling study and all other studies.
double AwMetricsServiceClient::GetSampleRate() {
double sampled_in_rate = kBetaDevCanarySampledInRate;
// 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) {
sampled_in_rate = kStableSampledInRate;
}
return sampled_in_rate;
}
bool AwMetricsServiceClient::IsInSample() {
// Called in MaybeStartMetrics(), after metrics_service_ is created.
return IsInSample(base::PersistentHash(metrics_service_->GetClientId()));
}
bool AwMetricsServiceClient::IsInSample(uint32_t value) {
return UintFallsInBottomPercentOfValues(value, GetSampleRate());
}
bool AwMetricsServiceClient::CanRecordPackageNameForAppType() {
// 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).
......@@ -435,21 +188,6 @@ bool AwMetricsServiceClient::CanRecordPackageNameForAppType() {
return Java_AwMetricsServiceClient_canRecordPackageNameForAppType(env);
}
bool AwMetricsServiceClient::IsInPackageNameSample() {
// Check if this client falls within the group for which it's acceptable to
// log package name. This guarantees we enforce the privacy requirement
// because we never log package names for more than kPackageNameLimitRate
// percent of clients. We'll actually log package name for less than this,
// because we also filter out packages for certain types of apps (see
// CanRecordPackageNameForAppType()).
return IsInPackageNameSample(
base::PersistentHash(metrics_service_->GetClientId()));
}
bool AwMetricsServiceClient::IsInPackageNameSample(uint32_t value) {
return UintFallsInBottomPercentOfValues(value, kPackageNameLimitRate);
}
// static
void JNI_AwMetricsServiceClient_SetHaveMetricsConsent(JNIEnv* env,
jboolean user_consent,
......
......@@ -13,19 +13,13 @@
#include "base/no_destructor.h"
#include "base/sequence_checker.h"
#include "base/time/time.h"
#include "components/embedder_support/android/metrics/android_metrics_service_client.h"
#include "components/metrics/enabled_state_provider.h"
#include "components/metrics/metrics_log_uploader.h"
#include "components/metrics/metrics_service_client.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
class PrefRegistrySimple;
class PrefService;
namespace metrics {
class MetricsStateManager;
}
namespace android_webview {
// These values are persisted to logs. Entries should not be renumbered and
......@@ -93,9 +87,8 @@ enum class BackfillInstallDate {
// 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::NotificationObserver {
class AwMetricsServiceClient : public ::metrics::AndroidMetricsServiceClient {
friend class base::NoDestructor<AwMetricsServiceClient>;
public:
......@@ -104,103 +97,23 @@ class AwMetricsServiceClient : public metrics::MetricsServiceClient,
AwMetricsServiceClient();
~AwMetricsServiceClient() override;
// Registers local state prefs used by this class.
static void RegisterPrefs(PrefRegistrySimple* registry);
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);
std::unique_ptr<const base::FieldTrial::EntropyProvider>
CreateLowEntropyProvider();
// metrics::EnabledStateProvider
bool IsConsentGiven() const override;
bool IsReportingEnabled() const override;
// metrics::MetricsServiceClient
metrics::MetricsService* GetMetricsService() override;
void SetMetricsClientId(const std::string& client_id) override;
int32_t GetProduct() override;
std::string GetApplicationLocale() override;
bool GetBrand(std::string* brand_code) override;
metrics::SystemProfileProto::Channel GetChannel() override;
std::string GetVersionString() override;
void CollectFinalMetricsForLog(base::OnceClosure done_callback) override;
std::unique_ptr<metrics::MetricsLogUploader> CreateUploader(
const GURL& server_url,
const GURL& insecure_server_url,
base::StringPiece mime_type,
metrics::MetricsLogUploader::MetricServiceType service_type,
const metrics::MetricsLogUploader::UploadCallback& on_upload_complete)
override;
base::TimeDelta GetStandardUploadInterval() override;
bool ShouldStartUpFastForTesting() const override;
// Gets the embedding app's package name if it's OK to log. Otherwise, this
// returns the empty string.
std::string GetAppPackageName() override;
// content::NotificationObserver
void Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) override;
protected:
// Returns the metrics sampling rate, to be used by IsInSample(). This is a
// double in the non-inclusive range (0.00, 1.00). Virtual for testing.
virtual double GetSampleRate();
// Determines if the client is within the random sample of clients for which
// we log metrics. If this returns false, AwMetricsServiceClient should
// indicate reporting is disabled. Sampling is due to storage/bandwidth
// considerations. Virtual for testing.
virtual bool IsInSample();
// Prefer calling the IsInSample() which takes no arguments. Virtual for
// testing.
virtual bool IsInSample(uint32_t value);
// Determines if the embedder app is the type of app for which we may log the
// package name. If this returns false, GetAppPackageName() must return empty
// string. Virtual for testing.
virtual bool CanRecordPackageNameForAppType();
// Determines if this client falls within the group for which it's acceptable
// to include the embedding app's package name. If this returns false,
// GetAppPackageName() must return the empty string (for
// privacy/fingerprintability reasons). Virtual for testing.
virtual bool IsInPackageNameSample();
// Prefer calling the IsInPackageNameSample() which takes no arguments.
// Virtual for testing.
virtual bool IsInPackageNameSample(uint32_t value);
// metrics::AndroidMetricsServiceClient:
void InitInternal() override;
void OnMetricsStart() override;
double GetSampleRate() override;
double GetPackageNameLimitRate() override;
bool ShouldWakeMetricsService() override;
void RegisterAdditionalMetricsProviders(
metrics::MetricsService* service) override;
bool CanRecordPackageNameForAppType() override;
std::string GetAppPackageNameInternal() override;
private:
void MaybeStartMetrics();
void RegisterForNotifications();
std::unique_ptr<metrics::MetricsStateManager> metrics_state_manager_;
std::unique_ptr<metrics::MetricsService> metrics_service_;
content::NotificationRegistrar registrar_;
PrefService* pref_service_ = nullptr;
bool init_finished_ = false;
bool set_consent_finished_ = false;
bool user_consent_ = false;
bool app_consent_ = false;
bool is_in_sample_ = false;
bool is_in_package_name_sample_ = false;
bool fast_startup_for_testing_ = false;
// When non-zero, this overrides the default value in
// GetStandardUploadInterval().
base::TimeDelta overridden_upload_interval_;
// AwMetricsServiceClient may be created before the UI thread is promoted to
// BrowserThread::UI. Use |sequence_checker_| to enforce that the
// AwMetricsServiceClient is used on a single thread.
base::SequenceChecker sequence_checker_;
DISALLOW_COPY_AND_ASSIGN(AwMetricsServiceClient);
};
......
......@@ -23,16 +23,6 @@
namespace android_webview {
namespace {
// Scales up a uint32_t in the inverse of how UintFallsInBottomPercentOfValues()
// makes its judgment. This is useful so tests can use integers, which itself
// helps to avoid rounding issues.
uint32_t ScaleValue(uint32_t value) {
DCHECK_GE(value, 0u);
DCHECK_LE(value, 100u);
double rate = static_cast<double>(value) / 100;
return UINT32_MAX * rate;
}
// For client ID format, see:
// https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_(random)
const char kTestClientId[] = "01234567-89ab-40cd-80ef-0123456789ab";
......@@ -60,12 +50,9 @@ class TestClient : public AwMetricsServiceClient {
void SetInPackageNameSample(bool value) { in_package_name_sample_ = value; }
// Expose the super class implementation for testing.
bool IsInSample(uint32_t value) override {
return AwMetricsServiceClient::IsInSample(value);
}
bool IsInPackageNameSample(uint32_t value) override {
return AwMetricsServiceClient::IsInPackageNameSample(value);
}
using AwMetricsServiceClient::GetAppPackageNameInternal;
using AwMetricsServiceClient::IsInPackageNameSample;
using AwMetricsServiceClient::IsInSample;
protected:
double GetSampleRate() override { return sampled_in_rate_; }
......@@ -121,65 +108,6 @@ class AwMetricsServiceClientTest : public testing::Test {
DISALLOW_COPY_AND_ASSIGN(AwMetricsServiceClientTest);
};
TEST_F(AwMetricsServiceClientTest, TestSetConsentTrueBeforeInit) {
auto prefs = CreateTestPrefs();
auto client = std::make_unique<TestClient>();
client->SetHaveMetricsConsent(true, true);
client->Initialize(prefs.get());
EXPECT_TRUE(client->IsRecordingActive());
EXPECT_TRUE(prefs->HasPrefPath(metrics::prefs::kMetricsClientID));
EXPECT_TRUE(
prefs->HasPrefPath(metrics::prefs::kMetricsReportingEnabledTimestamp));
}
TEST_F(AwMetricsServiceClientTest, TestSetConsentFalseBeforeInit) {
auto prefs = CreateTestPrefs();
auto client = std::make_unique<TestClient>();
client->SetHaveMetricsConsent(false, false);
client->Initialize(prefs.get());
EXPECT_FALSE(client->IsRecordingActive());
EXPECT_FALSE(prefs->HasPrefPath(metrics::prefs::kMetricsClientID));
EXPECT_FALSE(
prefs->HasPrefPath(metrics::prefs::kMetricsReportingEnabledTimestamp));
}
TEST_F(AwMetricsServiceClientTest, TestSetConsentTrueAfterInit) {
auto prefs = CreateTestPrefs();
auto client = CreateAndInitTestClient(prefs.get());
client->SetHaveMetricsConsent(true, true);
EXPECT_TRUE(client->IsRecordingActive());
EXPECT_TRUE(prefs->HasPrefPath(metrics::prefs::kMetricsClientID));
EXPECT_TRUE(
prefs->HasPrefPath(metrics::prefs::kMetricsReportingEnabledTimestamp));
}
TEST_F(AwMetricsServiceClientTest, TestSetConsentFalseAfterInit) {
auto prefs = CreateTestPrefs();
auto client = CreateAndInitTestClient(prefs.get());
client->SetHaveMetricsConsent(false, false);
EXPECT_FALSE(client->IsRecordingActive());
EXPECT_FALSE(prefs->HasPrefPath(metrics::prefs::kMetricsClientID));
EXPECT_FALSE(
prefs->HasPrefPath(metrics::prefs::kMetricsReportingEnabledTimestamp));
}
// If there is already a valid client ID and enabled date, they should be
// reused.
TEST_F(AwMetricsServiceClientTest, TestKeepExistingClientIdAndEnabledDate) {
auto prefs = CreateTestPrefs();
prefs->SetString(metrics::prefs::kMetricsClientID, kTestClientId);
int64_t enabled_date = 12345;
prefs->SetInt64(metrics::prefs::kMetricsReportingEnabledTimestamp,
enabled_date);
auto client = CreateAndInitTestClient(prefs.get());
client->SetHaveMetricsConsent(true, true);
EXPECT_TRUE(client->IsRecordingActive());
EXPECT_TRUE(prefs->HasPrefPath(metrics::prefs::kMetricsClientID));
EXPECT_EQ(kTestClientId, prefs->GetString(metrics::prefs::kMetricsClientID));
EXPECT_EQ(enabled_date,
prefs->GetInt64(metrics::prefs::kMetricsReportingEnabledTimestamp));
}
// TODO(https://crbug.com/995544): remove this when the
// kMetricsReportingEnabledTimestamp pref has been persisted for one or two
// milestones.
......@@ -194,154 +122,12 @@ TEST_F(AwMetricsServiceClientTest, TestBackfillEnabledDateIfMissing) {
prefs->HasPrefPath(metrics::prefs::kMetricsReportingEnabledTimestamp));
}
TEST_F(AwMetricsServiceClientTest, TestSetConsentFalseClearsIdAndEnabledDate) {
auto prefs = CreateTestPrefs();
prefs->SetString(metrics::prefs::kMetricsClientID, kTestClientId);
auto client = CreateAndInitTestClient(prefs.get());
client->SetHaveMetricsConsent(false, false);
EXPECT_FALSE(client->IsRecordingActive());
EXPECT_FALSE(prefs->HasPrefPath(metrics::prefs::kMetricsClientID));
EXPECT_FALSE(
prefs->HasPrefPath(metrics::prefs::kMetricsReportingEnabledTimestamp));
}
TEST_F(AwMetricsServiceClientTest, TestShouldNotUploadPackageName_AppType) {
auto prefs = CreateTestPrefs();
prefs->SetString(metrics::prefs::kMetricsClientID, kTestClientId);
auto client = CreateAndInitTestClient(prefs.get());
client->SetRecordPackageNameForAppType(false);
client->SetInPackageNameSample(true);
client->SetHaveMetricsConsent(true, true);
std::string package_name = client->GetAppPackageName();
EXPECT_TRUE(package_name.empty());
}
TEST_F(AwMetricsServiceClientTest, TestShouldNotUploadPackageName_SampledOut) {
TEST_F(AwMetricsServiceClientTest, TestGetPackageNameInternal) {
auto prefs = CreateTestPrefs();
prefs->SetString(metrics::prefs::kMetricsClientID, kTestClientId);
auto client = CreateAndInitTestClient(prefs.get());
client->SetRecordPackageNameForAppType(true);
client->SetInPackageNameSample(false);
client->SetHaveMetricsConsent(true, true);
std::string package_name = client->GetAppPackageName();
EXPECT_TRUE(package_name.empty());
}
TEST_F(AwMetricsServiceClientTest, TestCanUploadPackageName) {
auto prefs = CreateTestPrefs();
prefs->SetString(metrics::prefs::kMetricsClientID, kTestClientId);
auto client = CreateAndInitTestClient(prefs.get());
client->SetRecordPackageNameForAppType(true);
client->SetInPackageNameSample(true);
client->SetHaveMetricsConsent(true, true);
std::string package_name = client->GetAppPackageName();
EXPECT_FALSE(package_name.empty());
}
TEST_F(AwMetricsServiceClientTest, TestPackageNameLogic_SampleRateBelowTen) {
auto prefs = CreateTestPrefs();
prefs->SetString(metrics::prefs::kMetricsClientID, kTestClientId);
auto client = CreateAndInitTestClient(prefs.get());
double sample_rate = 0.08;
client->SetSampleRate(sample_rate);
// When GetSampleRate() <= 0.10, everything in-sample should also be in the
// package name sample.
for (uint32_t value = 0; value < 8; ++value) {
EXPECT_TRUE(client->IsInSample(ScaleValue(value)))
<< "Value " << value << " should be in-sample";
EXPECT_TRUE(client->IsInPackageNameSample(ScaleValue(value)))
<< "Value " << value << " should be in the package name sample";
}
// After this, the only thing we care about is that we're out of sample (the
// package name logic shouldn't matter at this point, because we won't upload
// any records).
for (uint32_t value = 8; value < 100; ++value) {
EXPECT_FALSE(client->IsInSample(ScaleValue(value)))
<< "Value " << value << " should be out of sample";
}
}
TEST_F(AwMetricsServiceClientTest, TestPackageNameLogic_SampleRateAboveTen) {
auto prefs = CreateTestPrefs();
prefs->SetString(metrics::prefs::kMetricsClientID, kTestClientId);
auto client = CreateAndInitTestClient(prefs.get());
double sample_rate = 0.90;
client->SetSampleRate(sample_rate);
// When GetSampleRate() > 0.10, only values up to 0.10 should be in the
// package name sample.
for (uint32_t value = 0; value < 10; ++value) {
EXPECT_TRUE(client->IsInSample(ScaleValue(value)))
<< "Value " << value << " should be in-sample";
EXPECT_TRUE(client->IsInPackageNameSample(ScaleValue(value)))
<< "Value " << value << " should be in the package name sample";
}
// After this (but until we hit the sample rate), clients should be in sample
// but not upload the package name.
for (uint32_t value = 10; value < 90; ++value) {
EXPECT_TRUE(client->IsInSample(ScaleValue(value)))
<< "Value " << value << " should be in-sample";
EXPECT_FALSE(client->IsInPackageNameSample(ScaleValue(value)))
<< "Value " << value << " should be out of the package name sample";
}
// After this, the only thing we care about is that we're out of sample (the
// package name logic shouldn't matter at this point, because we won't upload
// any records).
for (uint32_t value = 90; value < 100; ++value) {
EXPECT_FALSE(client->IsInSample(ScaleValue(value)))
<< "Value " << value << " should be out of sample";
}
}
TEST_F(AwMetricsServiceClientTest, TestCanForceEnableMetrics) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
metrics::switches::kForceEnableMetricsReporting);
auto prefs = CreateTestPrefs();
auto client = std::make_unique<TestClient>();
// Flag should have higher precedence than sampling or user consent (but not
// app consent, so we set that to 'true' for this case).
client->SetHaveMetricsConsent(false, /* app_consent */ true);
client->SetInSample(false);
client->Initialize(prefs.get());
EXPECT_TRUE(client->IsReportingEnabled());
EXPECT_TRUE(client->IsRecordingActive());
}
TEST_F(AwMetricsServiceClientTest, TestCanForceEnableMetricsIfAlreadyEnabled) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
metrics::switches::kForceEnableMetricsReporting);
auto prefs = CreateTestPrefs();
auto client = std::make_unique<TestClient>();
// This is a sanity check: flip consent and sampling to true, just to make
// sure the flag continues to work.
client->SetHaveMetricsConsent(true, true);
client->SetInSample(true);
client->Initialize(prefs.get());
EXPECT_TRUE(client->IsReportingEnabled());
EXPECT_TRUE(client->IsRecordingActive());
}
TEST_F(AwMetricsServiceClientTest, TestCannotForceEnableMetricsIfAppOptsOut) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
metrics::switches::kForceEnableMetricsReporting);
auto prefs = CreateTestPrefs();
auto client = std::make_unique<TestClient>();
// Even with the flag, app consent should be respected.
client->SetHaveMetricsConsent(true, /* app_consent */ false);
client->SetInSample(true);
client->Initialize(prefs.get());
EXPECT_FALSE(client->IsReportingEnabled());
EXPECT_FALSE(client->IsRecordingActive());
// Make sure GetPackageNameInternal returns a non-empty string.
EXPECT_FALSE(client->GetAppPackageNameInternal().empty());
}
// TODO(https://crbug.com/1012025): remove this when the kInstallDate pref has
......
include_rules = [
"+components/background_task_scheduler/android/java",
"+components/embedder_support/android/metrics/java",
"+components/minidump_uploader/android/java",
"-android_webview/glue",
......
......@@ -11,7 +11,9 @@ import android.os.HandlerThread;
import androidx.annotation.NonNull;
import org.chromium.base.Callback;
import org.chromium.base.Consumer;
import org.chromium.base.ThreadUtils;
import org.chromium.components.metrics.AndroidMetricsLogUploader;
/**
* This class manages platform-specific services. (i.e. Google Services) The platform
......@@ -27,7 +29,14 @@ public abstract class PlatformServiceBridge {
private static Handler sHandler;
private static final Object sHandlerLock = new Object();
protected PlatformServiceBridge() {}
protected PlatformServiceBridge() {
AndroidMetricsLogUploader.setUploader(new Consumer<byte[]>() {
@Override
public void accept(byte[] data) {
logMetrics(data);
}
});
}
@SuppressWarnings("unused")
public static PlatformServiceBridge getInstance() {
......
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.android_webview.metrics;
import org.chromium.android_webview.common.PlatformServiceBridge;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace;
/**
* Passes UMA logs from native to PlatformServiceBridge.
*/
@JNINamespace("android_webview")
public class AwMetricsLogUploader {
@CalledByNative
public static void uploadLog(byte[] data) {
// This relies on WebViewChromiumFactoryProvider having already created the
// PlatformServiceBridge. This is guaranteed because metrics won't start until the
// PlatformServiceBridge.queryMetricsSetting() callback fires.
PlatformServiceBridge.getInstance().logMetrics(data);
}
}
......@@ -350,10 +350,9 @@ source_set("webview_instrumentation_test_native_jni_impl") {
deps = [
":webview_instrumentation_test_native_jni",
"//android_webview/browser",
"//android_webview/browser/metrics",
"//base",
"//base/test:test_support",
"//components/embedder_support/android/metrics",
]
}
......
include_rules = [
"+components/embedder_support/android/metrics",
"+components/policy/android/javatests",
"-content/public/android/java",
"+content/public/android/java/src/org/chromium/content_public",
......
......@@ -4,17 +4,18 @@
#include "android_webview/test/webview_instrumentation_test_native_jni/MemoryMetricsLoggerUtils_jni.h"
#include "android_webview/browser/metrics/memory_metrics_logger.h"
#include "base/run_loop.h"
#include "base/test/bind_test_util.h"
#include "base/test/task_environment.h"
#include "base/test/test_timeouts.h"
#include "components/embedder_support/android/metrics/memory_metrics_logger.h"
namespace android_webview {
// static
jboolean JNI_MemoryMetricsLoggerUtils_ForceRecordHistograms(JNIEnv* env) {
auto* memory_metrics_logger = MemoryMetricsLogger::GetInstanceForTesting();
auto* memory_metrics_logger =
::metrics::MemoryMetricsLogger::GetInstanceForTesting();
if (!memory_metrics_logger)
return false;
......
......@@ -311,6 +311,7 @@ test("components_unittests") {
"//components/crash/android:java",
"//components/crash/android:unit_tests",
"//components/download/internal/common:internal_java",
"//components/embedder_support/android/metrics:unit_tests",
"//components/gcm_driver/instance_id:test_support",
"//components/gcm_driver/instance_id/android:instance_id_driver_java",
"//components/gcm_driver/instance_id/android:instance_id_driver_test_support_java",
......
Collection of Android libraries that provides embedders with
typical implementations of interfaces of content layer.
typical implementations of content and component layer interfaces.
# Copyright 2020 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("//build/config/android/config.gni")
import("//build/config/android/rules.gni")
assert(is_android, "This component can only be depended on for android builds")
# Intended for content embedders such as WebView & WebLayer.
static_library("metrics") {
sources = [
"android_metrics_log_uploader.cc",
"android_metrics_log_uploader.h",
"android_metrics_service_client.cc",
"android_metrics_service_client.h",
"memory_metrics_logger.cc",
"memory_metrics_logger.h",
]
deps = [
":jni",
"//components/metrics",
"//components/metrics:net",
"//components/metrics:ui",
"//components/prefs",
"//content/public/browser",
"//services/resource_coordinator/public/cpp/memory_instrumentation:browser",
]
}
android_library("java") {
sources = [
"java/src/org/chromium/components/metrics/AndroidMetricsLogUploader.java",
]
deps = [
"//base:base_java",
"//base:jni_java",
]
annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
}
generate_jni("jni") {
sources = [
"java/src/org/chromium/components/metrics/AndroidMetricsLogUploader.java",
]
}
source_set("unit_tests") {
testonly = true
sources = [ "android_metrics_service_client_unittest.cc" ]
deps = [
":metrics",
"//base/test:test_support",
"//components/metrics",
"//components/prefs:test_support",
"//testing/gtest",
]
}
include_rules = [
"+components/metrics",
"+components/prefs",
"+content/public/browser",
"+services/resource_coordinator/public/cpp/memory_instrumentation",
]
file://base/metrics/OWNERS
ntfschr@chromium.org
# COMPONENT: Internals>Metrics
# TEAM: chromium-dev@chromium.org
// Copyright 2017 The Chromium Authors. All rights reserved.
// Copyright 2020 The Chromium Authors. All rights reserved.
// 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_log_uploader.h"
#include "components/embedder_support/android/metrics/android_metrics_log_uploader.h"
#include "android_webview/browser_jni_headers/AwMetricsLogUploader_jni.h"
#include "base/android/jni_array.h"
#include "components/embedder_support/android/metrics/jni/AndroidMetricsLogUploader_jni.h"
#include "components/metrics/log_decoder.h"
using base::android::ScopedJavaLocalRef;
using base::android::ToJavaByteArray;
namespace android_webview {
namespace metrics {
AwMetricsLogUploader::AwMetricsLogUploader(
const metrics::MetricsLogUploader::UploadCallback& on_upload_complete)
AndroidMetricsLogUploader::AndroidMetricsLogUploader(
const MetricsLogUploader::UploadCallback& on_upload_complete)
: on_upload_complete_(on_upload_complete) {}
AwMetricsLogUploader::~AwMetricsLogUploader() {}
AndroidMetricsLogUploader::~AndroidMetricsLogUploader() = default;
void AwMetricsLogUploader::UploadLog(
void AndroidMetricsLogUploader::UploadLog(
const std::string& compressed_log_data,
const std::string& /*log_hash*/,
const std::string& /*log_signature*/,
const metrics::ReportingInfo& reporting_info) {
// WebView uses the platform logging mechanism instead of the normal UMA
const ReportingInfo& reporting_info) {
// This uploader uses the platform logging mechanism instead of the normal UMA
// server. The platform mechanism does its own compression, so undo the
// previous compression.
std::string log_data;
if (!metrics::DecodeLogData(compressed_log_data, &log_data)) {
if (!DecodeLogData(compressed_log_data, &log_data)) {
// If the log is corrupt, pretend the server rejected it (HTTP Bad Request).
on_upload_complete_.Run(400, 0, true);
return;
......@@ -37,7 +37,7 @@ void AwMetricsLogUploader::UploadLog(
JNIEnv* env = base::android::AttachCurrentThread();
ScopedJavaLocalRef<jbyteArray> java_data = ToJavaByteArray(
env, reinterpret_cast<const uint8_t*>(log_data.data()), log_data.size());
Java_AwMetricsLogUploader_uploadLog(env, java_data);
Java_AndroidMetricsLogUploader_uploadLog(env, java_data);
// The platform mechanism doesn't provide a response code or any way to handle
// failures, so we have nothing to pass to on_upload_complete. Just pass 200
......@@ -45,4 +45,4 @@ void AwMetricsLogUploader::UploadLog(
on_upload_complete_.Run(200, 0, true);
}
} // namespace android_webview
} // namespace metrics
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_EMBEDDER_SUPPORT_ANDROID_METRICS_ANDROID_METRICS_LOG_UPLOADER_H_
#define COMPONENTS_EMBEDDER_SUPPORT_ANDROID_METRICS_ANDROID_METRICS_LOG_UPLOADER_H_
#include <string>
#include "components/metrics/metrics_log_uploader.h"
namespace metrics {
// Uploads UMA logs using the platform logging mechanism.
class AndroidMetricsLogUploader : public MetricsLogUploader {
public:
explicit AndroidMetricsLogUploader(
const MetricsLogUploader::UploadCallback& on_upload_complete);
~AndroidMetricsLogUploader() override;
AndroidMetricsLogUploader(const AndroidMetricsLogUploader&) = delete;
AndroidMetricsLogUploader& operator=(const AndroidMetricsLogUploader&) =
delete;
// MetricsLogUploader:
// Note: |log_hash| and |log_signature| are only used by the normal UMA
// server. This uploader uses a Java logging mechanism that ignores these
// fields.
void UploadLog(const std::string& compressed_log_data,
const std::string& log_hash,
const std::string& log_signature,
const ReportingInfo& reporting_info) override;
private:
const MetricsLogUploader::UploadCallback on_upload_complete_;
};
} // namespace metrics
#endif // COMPONENTS_EMBEDDER_SUPPORT_ANDROID_METRICS_ANDROID_METRICS_LOG_UPLOADER_H_
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/embedder_support/android/metrics/android_metrics_service_client.h"
#include <cstdint>
#include "base/i18n/rtl.h"
#include "build/build_config.h"
#include "components/embedder_support/android/metrics/android_metrics_log_uploader.h"
#include "components/metrics/call_stack_profile_metrics_provider.h"
#include "components/metrics/cpu_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/stability_metrics_helper.h"
#include "components/metrics/ui/screen_info_metrics_provider.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/network_service_instance.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_types.h"
namespace metrics {
namespace {
// Callbacks for MetricsStateManager::Create. Store/LoadClientInfo
// allow Windows Chrome to back up ClientInfo. They're no-ops for
// AndroidMetricsServiceClient.
void StoreClientInfo(const ClientInfo& client_info) {}
std::unique_ptr<ClientInfo> LoadClientInfo() {
std::unique_ptr<ClientInfo> client_info;
return client_info;
}
bool UintFallsInBottomPercentOfValues(uint32_t value, double fraction) {
DCHECK_GT(fraction, 0);
DCHECK_LT(fraction, 1.00);
// Since hashing is ~uniform, the chance that the value falls in the bottom
// X% of possible values is X%. UINT32_MAX fits within the range of integers
// that can be expressed precisely by a 64-bit double. Casting back to a
// uint32_t means we can determine if the value falls within the bottom X%,
// within a 1/UINT32_MAX error margin.
uint32_t value_threshold =
static_cast<uint32_t>(static_cast<double>(UINT32_MAX) * fraction);
return value < value_threshold;
}
} // namespace
AndroidMetricsServiceClient::AndroidMetricsServiceClient() = default;
AndroidMetricsServiceClient::~AndroidMetricsServiceClient() = default;
// static
void AndroidMetricsServiceClient::RegisterPrefs(PrefRegistrySimple* registry) {
MetricsService::RegisterPrefs(registry);
StabilityMetricsHelper::RegisterPrefs(registry);
}
void AndroidMetricsServiceClient::Initialize(PrefService* pref_service) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!init_finished_);
pref_service_ = pref_service;
InitInternal();
metrics_state_manager_ =
MetricsStateManager::Create(pref_service_, this, base::string16(),
base::BindRepeating(&StoreClientInfo),
base::BindRepeating(&LoadClientInfo));
init_finished_ = true;
MaybeStartMetrics();
}
void AndroidMetricsServiceClient::MaybeStartMetrics() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// 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 (init_finished_ && set_consent_finished_) {
if (app_consent_ && user_consent_or_flag) {
metrics_service_ = CreateMetricsService(metrics_state_manager_.get(),
this, pref_service_);
// 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.
RegisterForNotifications();
metrics_state_manager_->ForceClientIdCreation();
OnMetricsStart();
is_in_sample_ = IsInSample();
if (IsReportingEnabled()) {
// We assume the embedder has no shutdown sequence, so there's no need
// for a matching Stop() call.
metrics_service_->Start();
}
} else {
pref_service_->ClearPref(prefs::kMetricsClientID);
}
}
}
std::unique_ptr<MetricsService>
AndroidMetricsServiceClient::CreateMetricsService(
MetricsStateManager* state_manager,
AndroidMetricsServiceClient* client,
PrefService* prefs) {
auto service = std::make_unique<MetricsService>(state_manager, client, prefs);
// Although targeted at mobile, the unit tests runs on all platforms and the
// chrome os version CHECK fails if we include NetworkMetricsProvider.
#if !defined(OS_CHROMEOS)
service->RegisterMetricsProvider(std::make_unique<NetworkMetricsProvider>(
content::CreateNetworkConnectionTrackerAsyncGetter()));
#endif // defined(OS_CHROMEOS)
service->RegisterMetricsProvider(std::make_unique<CPUMetricsProvider>());
service->RegisterMetricsProvider(
std::make_unique<ScreenInfoMetricsProvider>());
service->RegisterMetricsProvider(
std::make_unique<CallStackProfileMetricsProvider>());
RegisterAdditionalMetricsProviders(service.get());
service->InitializeMetricsRecordingState();
return service;
}
void AndroidMetricsServiceClient::RegisterForNotifications() {
registrar_.Add(this, content::NOTIFICATION_LOAD_START,
content::NotificationService::AllSources());
registrar_.Add(this, content::NOTIFICATION_LOAD_STOP,
content::NotificationService::AllSources());
registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
content::NotificationService::AllSources());
registrar_.Add(this, content::NOTIFICATION_RENDER_WIDGET_HOST_HANG,
content::NotificationService::AllSources());
}
void AndroidMetricsServiceClient::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 AndroidMetricsServiceClient::SetFastStartupForTesting(
bool fast_startup_for_testing) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
fast_startup_for_testing_ = fast_startup_for_testing;
}
void AndroidMetricsServiceClient::SetUploadIntervalForTesting(
const base::TimeDelta& upload_interval) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
overridden_upload_interval_ = upload_interval;
}
std::unique_ptr<const base::FieldTrial::EntropyProvider>
AndroidMetricsServiceClient::CreateLowEntropyProvider() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return metrics_state_manager_->CreateLowEntropyProvider();
}
bool AndroidMetricsServiceClient::IsConsentGiven() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return user_consent_ && app_consent_;
}
bool AndroidMetricsServiceClient::IsReportingEnabled() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!app_consent_)
return false;
return IsMetricsReportingForceEnabled() ||
(EnabledStateProvider::IsReportingEnabled() && is_in_sample_);
}
MetricsService* AndroidMetricsServiceClient::GetMetricsService() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// This will be null if initialization hasn't finished, or if metrics
// collection is disabled.
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 AndroidMetricsServiceClients
// UMA and Crashpad are independent, so this is a no-op.
void AndroidMetricsServiceClient::SetMetricsClientId(
const std::string& client_id) {}
std::string AndroidMetricsServiceClient::GetApplicationLocale() {
return base::i18n::GetConfiguredLocale();
}
bool AndroidMetricsServiceClient::GetBrand(std::string* brand_code) {
// AndroidMetricsServiceClients don't use brand codes.
return false;
}
void AndroidMetricsServiceClient::CollectFinalMetricsForLog(
base::OnceClosure done_callback) {
std::move(done_callback).Run();
}
std::unique_ptr<MetricsLogUploader> AndroidMetricsServiceClient::CreateUploader(
const GURL& server_url,
const GURL& insecure_server_url,
base::StringPiece mime_type,
MetricsLogUploader::MetricServiceType service_type,
const MetricsLogUploader::UploadCallback& on_upload_complete) {
// |server_url|, |insecure_server_url|, and |mime_type| are unused because
// AndroidMetricsServiceClients send metrics to the platform logging mechanism
// rather than to Chrome's metrics server.
return std::make_unique<AndroidMetricsLogUploader>(on_upload_complete);
}
base::TimeDelta AndroidMetricsServiceClient::GetStandardUploadInterval() {
// In AndroidMetricsServiceClients, 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
// AndroidMetricsServiceClients 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 AndroidMetricsServiceClient::ShouldStartUpFastForTesting() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return fast_startup_for_testing_;
}
void AndroidMetricsServiceClient::Observe(
int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
switch (type) {
case content::NOTIFICATION_LOAD_STOP:
case content::NOTIFICATION_LOAD_START:
case content::NOTIFICATION_RENDERER_PROCESS_CLOSED:
case content::NOTIFICATION_RENDER_WIDGET_HOST_HANG:
if (ShouldWakeMetricsService())
metrics_service_->OnApplicationNotIdle();
break;
default:
NOTREACHED();
}
}
bool AndroidMetricsServiceClient::IsInSample() {
// Called in MaybeStartMetrics(), after |metrics_service_| is created.
// NOTE IsInSample and IsInPackageNameSample deliberately use the same hash to
// guarantee we never exceed 10% of total, opted-in clients for PackageNames.
return IsInSample(base::PersistentHash(metrics_service_->GetClientId()));
}
bool AndroidMetricsServiceClient::IsInSample(uint32_t value) {
return UintFallsInBottomPercentOfValues(value, GetSampleRate());
}
bool AndroidMetricsServiceClient::IsInPackageNameSample() {
// Check if this client falls within the group for which it's acceptable to
// log package name. This guarantees we enforce the privacy requirement
// because we never log package names for more than kPackageNameLimitRate
// percent of clients. We'll actually log package name for less than this,
// because we also filter out packages for certain types of apps (see
// CanRecordPackageNameForAppType()).
return IsInPackageNameSample(
base::PersistentHash(metrics_service_->GetClientId()));
}
bool AndroidMetricsServiceClient::IsInPackageNameSample(uint32_t value) {
return UintFallsInBottomPercentOfValues(value, GetPackageNameLimitRate());
}
std::string AndroidMetricsServiceClient::GetAppPackageName() {
if (IsInPackageNameSample() && CanRecordPackageNameForAppType())
return GetAppPackageNameInternal();
return std::string();
}
} // namespace metrics
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_EMBEDDER_SUPPORT_ANDROID_METRICS_ANDROID_METRICS_SERVICE_CLIENT_H_
#define COMPONENTS_EMBEDDER_SUPPORT_ANDROID_METRICS_ANDROID_METRICS_SERVICE_CLIENT_H_
#include <memory>
#include <string>
#include "base/metrics/field_trial.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 "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
class PrefRegistrySimple;
class PrefService;
namespace metrics {
class MetricsStateManager;
// AndroidMetricsServiceClient is a singleton which manages metrics collection
// intended for use by WebView & WebLayer.
//
// Metrics should be enabled iff all these conditions are met:
// - The user has not opted out.
// - The app has not opted out.
// - This client is in the 10% 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".
// AndroidMetricsServiceClient 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.
//
// AndroidMetricsServiceClient metrics set up happens like so:
//
// startup
// │
// ├────────────┐
// │ ▼
// │ query for consent
// ▼ │
// Initialize() │
// │ ▼
// │ 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.
//
// Each path sets a flag, |init_finished_| or |set_consent_finished_|, to show
// that path has finished, and then calls MaybeStartMetrics(). When
// MaybeStartMetrics() is called the first time, it sees only one flag is true,
// and does nothing. When MaybeStartMetrics() is called the second time, it
// decides whether to start metrics.
//
// 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 AndroidMetricsServiceClient : public MetricsServiceClient,
public EnabledStateProvider,
public content::NotificationObserver {
public:
AndroidMetricsServiceClient();
~AndroidMetricsServiceClient() override;
AndroidMetricsServiceClient(const AndroidMetricsServiceClient&) = delete;
AndroidMetricsServiceClient& operator=(const AndroidMetricsServiceClient&) =
delete;
static void RegisterPrefs(PrefRegistrySimple* registry);
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);
std::unique_ptr<const base::FieldTrial::EntropyProvider>
CreateLowEntropyProvider();
// EnabledStateProvider
bool IsConsentGiven() const override;
bool IsReportingEnabled() const override;
// MetricsServiceClient
MetricsService* GetMetricsService() override;
void SetMetricsClientId(const std::string& client_id) override;
std::string GetApplicationLocale() override;
bool GetBrand(std::string* brand_code) override;
void CollectFinalMetricsForLog(
const base::OnceClosure done_callback) override;
std::unique_ptr<MetricsLogUploader> CreateUploader(
const GURL& server_url,
const GURL& insecure_server_url,
base::StringPiece mime_type,
MetricsLogUploader::MetricServiceType service_type,
const MetricsLogUploader::UploadCallback& on_upload_complete) override;
base::TimeDelta GetStandardUploadInterval() override;
bool ShouldStartUpFastForTesting() const override;
// Gets the embedding app's package name if it's OK to log. Otherwise, this
// returns the empty string.
std::string GetAppPackageName() override;
// content::NotificationObserver
void Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) override;
protected:
// Called by Initialize() to allow embedder specific initialization.
virtual void InitInternal() = 0;
// Called by MaybeStartMetrics() to allow embedder specific initialization.
virtual void OnMetricsStart() = 0;
// Returns the metrics sampling rate, to be used by IsInSample(). This is a
// double in the non-inclusive range (0.00, 1.00). Virtual for testing.
virtual double GetSampleRate() = 0;
// Determines if the client is within the random sample of clients for which
// we log metrics. If this returns false, MetricsServiceClient should
// indicate reporting is disabled. Sampling is due to storage/bandwidth
// considerations. Virtual for testing.
virtual bool IsInSample();
// Prefer calling the IsInSample() which takes no arguments. Virtual for
// testing.
virtual bool IsInSample(uint32_t value);
// Determines if the embedder app is the type of app for which we may log the
// package name. If this returns false, GetAppPackageName() must return empty
// string. Virtual for testing.
virtual bool CanRecordPackageNameForAppType() = 0;
// Determines if this client falls within the group for which it's acceptable
// to include the embedding app's package name. If this returns false,
// GetAppPackageName() must return the empty string (for
// privacy/fingerprintability reasons). Virtual for testing.
virtual bool IsInPackageNameSample();
// Prefer calling the IsInPackageNameSample() which takes no arguments.
// Virtual for testing.
virtual bool IsInPackageNameSample(uint32_t value);
// Caps the rate at which we upload package names. This is privacy sensitive.
virtual double GetPackageNameLimitRate() = 0;
// Whether or not MetricsService::OnApplicationNotIdle should be called for
// notifications.
virtual bool ShouldWakeMetricsService() = 0;
// Called by CreateMetricsService, allows the embedder to register additional
// MetricsProviders.
virtual void RegisterAdditionalMetricsProviders(MetricsService* service) = 0;
// Returns the embedding application's package name.
virtual std::string GetAppPackageNameInternal() = 0;
void EnsureOnValidSequence() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
PrefService* pref_service() const { return pref_service_; }
private:
void MaybeStartMetrics();
void RegisterForNotifications();
std::unique_ptr<MetricsService> CreateMetricsService(
MetricsStateManager* state_manager,
AndroidMetricsServiceClient* client,
PrefService* prefs);
std::unique_ptr<MetricsStateManager> metrics_state_manager_;
std::unique_ptr<MetricsService> metrics_service_;
content::NotificationRegistrar registrar_;
PrefService* pref_service_ = nullptr;
bool init_finished_ = false;
bool set_consent_finished_ = false;
bool user_consent_ = false;
bool app_consent_ = false;
bool is_in_sample_ = false;
bool fast_startup_for_testing_ = false;
// When non-zero, this overrides the default value in
// GetStandardUploadInterval().
base::TimeDelta overridden_upload_interval_;
// 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_);
};
} // namespace metrics
#endif // COMPONENTS_EMBEDDER_SUPPORT_ANDROID_METRICS_ANDROID_METRICS_SERVICE_CLIENT_H_
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/embedder_support/android/metrics/android_metrics_service_client.h"
#include <memory>
#include "base/command_line.h"
#include "base/macros.h"
#include "base/memory/scoped_refptr.h"
#include "base/run_loop.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/task_environment.h"
#include "base/test/test_simple_task_runner.h"
#include "components/metrics/metrics_pref_names.h"
#include "components/metrics/metrics_service.h"
#include "components/metrics/metrics_switches.h"
#include "components/prefs/testing_pref_service.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace metrics {
namespace {
// Scales up a uint32_t in the inverse of how UintFallsInBottomPercentOfValues()
// makes its judgment. This is useful so tests can use integers, which itself
// helps to avoid rounding issues.
uint32_t ScaleValue(uint32_t value) {
DCHECK_GE(value, 0u);
DCHECK_LE(value, 100u);
double rate = static_cast<double>(value) / 100.0;
return static_cast<uint32_t>(static_cast<double>(UINT32_MAX) * rate);
}
// For client ID format, see:
// https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_(random)
const char kTestClientId[] = "01234567-89ab-40cd-80ef-0123456789ab";
class TestClient : public AndroidMetricsServiceClient {
public:
TestClient()
: sampled_in_rate_(1.00),
in_sample_(true),
record_package_name_for_app_type_(true),
in_package_name_sample_(true) {}
~TestClient() override = default;
bool IsRecordingActive() {
auto* service = GetMetricsService();
if (service)
return service->recording_active();
return false;
}
void SetSampleRate(double value) { sampled_in_rate_ = value; }
void SetInSample(bool value) { in_sample_ = value; }
void SetRecordPackageNameForAppType(bool value) {
record_package_name_for_app_type_ = value;
}
void SetInPackageNameSample(bool value) { in_package_name_sample_ = value; }
// Expose the super class implementation for testing.
using AndroidMetricsServiceClient::IsInPackageNameSample;
using AndroidMetricsServiceClient::IsInSample;
protected:
void InitInternal() override {}
void OnMetricsStart() override {}
double GetSampleRate() override { return sampled_in_rate_; }
bool IsInSample() override { return in_sample_; }
bool CanRecordPackageNameForAppType() override {
return record_package_name_for_app_type_;
}
bool IsInPackageNameSample() override { return in_package_name_sample_; }
// AndroidMetricsServiceClient:
SystemProfileProto::Channel GetChannel() override {
return SystemProfileProto::CHANNEL_BETA;
}
std::string GetVersionString() override { return "1.1.1.1"; }
int32_t GetProduct() override {
return metrics::ChromeUserMetricsExtension::CHROME;
}
double GetPackageNameLimitRate() override {
// This is slightly under 0.1 to ensure
// TestPackageNameLogic_SampleRateAboveTen passes. There's something odd
// with the rounding which causes that test to fail if this is 0.1
return 0.0999;
}
bool ShouldWakeMetricsService() override {
NOTREACHED();
return true;
}
void RegisterAdditionalMetricsProviders(MetricsService* service) override {}
std::string GetAppPackageNameInternal() override { return "TestPackage"; }
private:
double sampled_in_rate_;
bool in_sample_;
bool record_package_name_for_app_type_;
bool in_package_name_sample_;
DISALLOW_COPY_AND_ASSIGN(TestClient);
};
std::unique_ptr<TestingPrefServiceSimple> CreateTestPrefs() {
auto prefs = std::make_unique<TestingPrefServiceSimple>();
metrics::MetricsService::RegisterPrefs(prefs->registry());
return prefs;
}
std::unique_ptr<TestClient> CreateAndInitTestClient(PrefService* prefs) {
auto client = std::make_unique<TestClient>();
client->Initialize(prefs);
return client;
}
} // namespace
class AndroidMetricsServiceClientTest : public testing::Test {
public:
AndroidMetricsServiceClientTest()
: test_begin_time_(base::Time::Now().ToTimeT()),
task_runner_(new base::TestSimpleTaskRunner) {
// Required by MetricsService.
base::SetRecordActionTaskRunner(task_runner_);
}
const int64_t test_begin_time_;
protected:
~AndroidMetricsServiceClientTest() override = default;
private:
base::test::TaskEnvironment task_environment_;
scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
DISALLOW_COPY_AND_ASSIGN(AndroidMetricsServiceClientTest);
};
TEST_F(AndroidMetricsServiceClientTest, TestSetConsentTrueBeforeInit) {
auto prefs = CreateTestPrefs();
auto client = std::make_unique<TestClient>();
client->SetHaveMetricsConsent(true, true);
client->Initialize(prefs.get());
EXPECT_TRUE(client->IsRecordingActive());
EXPECT_TRUE(prefs->HasPrefPath(metrics::prefs::kMetricsClientID));
EXPECT_TRUE(
prefs->HasPrefPath(metrics::prefs::kMetricsReportingEnabledTimestamp));
}
TEST_F(AndroidMetricsServiceClientTest, TestSetConsentFalseBeforeInit) {
auto prefs = CreateTestPrefs();
auto client = std::make_unique<TestClient>();
client->SetHaveMetricsConsent(false, false);
client->Initialize(prefs.get());
EXPECT_FALSE(client->IsRecordingActive());
EXPECT_FALSE(prefs->HasPrefPath(metrics::prefs::kMetricsClientID));
EXPECT_FALSE(
prefs->HasPrefPath(metrics::prefs::kMetricsReportingEnabledTimestamp));
}
TEST_F(AndroidMetricsServiceClientTest, TestSetConsentTrueAfterInit) {
auto prefs = CreateTestPrefs();
auto client = CreateAndInitTestClient(prefs.get());
client->SetHaveMetricsConsent(true, true);
EXPECT_TRUE(client->IsRecordingActive());
EXPECT_TRUE(prefs->HasPrefPath(metrics::prefs::kMetricsClientID));
EXPECT_GE(prefs->GetInt64(prefs::kMetricsReportingEnabledTimestamp),
test_begin_time_);
}
TEST_F(AndroidMetricsServiceClientTest, TestSetConsentFalseAfterInit) {
auto prefs = CreateTestPrefs();
auto client = CreateAndInitTestClient(prefs.get());
client->SetHaveMetricsConsent(false, false);
EXPECT_FALSE(client->IsRecordingActive());
EXPECT_FALSE(prefs->HasPrefPath(metrics::prefs::kMetricsClientID));
EXPECT_FALSE(prefs->HasPrefPath(prefs::kMetricsReportingEnabledTimestamp));
}
// If there is already a valid client ID and enabled date, they should be
// reused.
TEST_F(AndroidMetricsServiceClientTest,
TestKeepExistingClientIdAndEnabledDate) {
auto prefs = CreateTestPrefs();
prefs->SetString(metrics::prefs::kMetricsClientID, kTestClientId);
int64_t enabled_date = 12345;
prefs->SetInt64(metrics::prefs::kMetricsReportingEnabledTimestamp,
enabled_date);
auto client = CreateAndInitTestClient(prefs.get());
client->SetHaveMetricsConsent(true, true);
EXPECT_TRUE(client->IsRecordingActive());
EXPECT_TRUE(prefs->HasPrefPath(metrics::prefs::kMetricsClientID));
EXPECT_EQ(kTestClientId, prefs->GetString(metrics::prefs::kMetricsClientID));
EXPECT_EQ(enabled_date,
prefs->GetInt64(metrics::prefs::kMetricsReportingEnabledTimestamp));
}
TEST_F(AndroidMetricsServiceClientTest,
TestSetConsentFalseClearsIdAndEnabledDate) {
auto prefs = CreateTestPrefs();
prefs->SetString(metrics::prefs::kMetricsClientID, kTestClientId);
auto client = CreateAndInitTestClient(prefs.get());
client->SetHaveMetricsConsent(false, false);
EXPECT_FALSE(client->IsRecordingActive());
EXPECT_FALSE(prefs->HasPrefPath(metrics::prefs::kMetricsClientID));
EXPECT_FALSE(
prefs->HasPrefPath(metrics::prefs::kMetricsReportingEnabledTimestamp));
}
TEST_F(AndroidMetricsServiceClientTest,
TestShouldNotUploadPackageName_AppType) {
auto prefs = CreateTestPrefs();
prefs->SetString(metrics::prefs::kMetricsClientID, kTestClientId);
auto client = CreateAndInitTestClient(prefs.get());
client->SetHaveMetricsConsent(true, true);
client->SetRecordPackageNameForAppType(false);
client->SetInPackageNameSample(true);
std::string package_name = client->GetAppPackageName();
EXPECT_TRUE(package_name.empty());
}
TEST_F(AndroidMetricsServiceClientTest,
TestShouldNotUploadPackageName_SampledOut) {
auto prefs = CreateTestPrefs();
prefs->SetString(metrics::prefs::kMetricsClientID, kTestClientId);
auto client = CreateAndInitTestClient(prefs.get());
client->SetHaveMetricsConsent(true, true);
client->SetRecordPackageNameForAppType(true);
client->SetInPackageNameSample(false);
std::string package_name = client->GetAppPackageName();
EXPECT_TRUE(package_name.empty());
}
TEST_F(AndroidMetricsServiceClientTest, TestCanUploadPackageName) {
auto prefs = CreateTestPrefs();
prefs->SetString(metrics::prefs::kMetricsClientID, kTestClientId);
auto client = CreateAndInitTestClient(prefs.get());
client->SetHaveMetricsConsent(true, true);
client->SetRecordPackageNameForAppType(true);
client->SetInPackageNameSample(true);
std::string package_name = client->GetAppPackageName();
EXPECT_FALSE(package_name.empty());
}
TEST_F(AndroidMetricsServiceClientTest,
TestPackageNameLogic_SampleRateBelowTen) {
auto prefs = CreateTestPrefs();
prefs->SetString(metrics::prefs::kMetricsClientID, kTestClientId);
auto client = CreateAndInitTestClient(prefs.get());
double sample_rate = 0.08;
client->SetSampleRate(sample_rate);
// When GetSampleRate() <= 0.10, everything in-sample should also be in the
// package name sample.
for (uint32_t value = 0; value < 8; ++value) {
EXPECT_TRUE(client->IsInSample(ScaleValue(value)))
<< "Value " << value << " should be in-sample";
EXPECT_TRUE(client->IsInPackageNameSample(ScaleValue(value)))
<< "Value " << value << " should be in the package name sample";
}
// After this, the only thing we care about is that we're out of sample (the
// package name logic shouldn't matter at this point, because we won't upload
// any records).
for (uint32_t value = 8; value < 100; ++value) {
EXPECT_FALSE(client->IsInSample(ScaleValue(value)))
<< "Value " << value << " should be out of sample";
}
}
TEST_F(AndroidMetricsServiceClientTest,
TestPackageNameLogic_SampleRateAboveTen) {
auto prefs = CreateTestPrefs();
prefs->SetString(metrics::prefs::kMetricsClientID, kTestClientId);
auto client = CreateAndInitTestClient(prefs.get());
double sample_rate = 0.90;
client->SetSampleRate(sample_rate);
// When GetSampleRate() > 0.10, only values up to 0.10 should be in the
// package name sample.
for (uint32_t value = 0; value < 10; ++value) {
EXPECT_TRUE(client->IsInSample(ScaleValue(value)))
<< "Value " << value << " should be in-sample";
EXPECT_TRUE(client->IsInPackageNameSample(ScaleValue(value)))
<< "Value " << value << " should be in the package name sample";
}
// After this (but until we hit the sample rate), clients should be in sample
// but not upload the package name.
for (uint32_t value = 10; value < 90; ++value) {
EXPECT_TRUE(client->IsInSample(ScaleValue(value)))
<< "Value " << value << " should be in-sample";
EXPECT_FALSE(client->IsInPackageNameSample(ScaleValue(value)))
<< "Value " << value << " should be out of the package name sample";
}
// After this, the only thing we care about is that we're out of sample (the
// package name logic shouldn't matter at this point, because we won't upload
// any records).
for (uint32_t value = 90; value < 100; ++value) {
EXPECT_FALSE(client->IsInSample(ScaleValue(value)))
<< "Value " << value << " should be out of sample";
}
}
TEST_F(AndroidMetricsServiceClientTest, TestCanForceEnableMetrics) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
metrics::switches::kForceEnableMetricsReporting);
auto prefs = CreateTestPrefs();
auto client = std::make_unique<TestClient>();
// Flag should have higher precedence than sampling or user consent (but not
// app consent, so we set that to 'true' for this case).
client->SetHaveMetricsConsent(false, /* app_consent */ true);
client->SetInSample(false);
client->Initialize(prefs.get());
EXPECT_TRUE(client->IsReportingEnabled());
EXPECT_TRUE(client->IsRecordingActive());
}
TEST_F(AndroidMetricsServiceClientTest,
TestCanForceEnableMetricsIfAlreadyEnabled) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
metrics::switches::kForceEnableMetricsReporting);
auto prefs = CreateTestPrefs();
auto client = std::make_unique<TestClient>();
// This is a sanity check: flip consent and sampling to true, just to make
// sure the flag continues to work.
client->SetHaveMetricsConsent(true, true);
client->SetInSample(true);
client->Initialize(prefs.get());
EXPECT_TRUE(client->IsReportingEnabled());
EXPECT_TRUE(client->IsRecordingActive());
}
TEST_F(AndroidMetricsServiceClientTest,
TestCannotForceEnableMetricsIfAppOptsOut) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
metrics::switches::kForceEnableMetricsReporting);
auto prefs = CreateTestPrefs();
auto client = std::make_unique<TestClient>();
// Even with the flag, app consent should be respected.
client->SetHaveMetricsConsent(true, /* app_consent */ false);
client->SetInSample(true);
client->Initialize(prefs.get());
EXPECT_FALSE(client->IsReportingEnabled());
EXPECT_FALSE(client->IsRecordingActive());
}
} // namespace metrics
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.components.metrics;
import org.chromium.base.Consumer;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace;
/**
* Passes UMA logs from native to a java uploader.
*/
@JNINamespace("metrics")
public class AndroidMetricsLogUploader {
private static volatile Consumer<byte[]> sUploader;
/**
* Configures the consumer of logs data submitted via uploadLog, should be called once during
* start up.
*
* @param uploader The consumer of logs data submitted via uploadLog.
*/
public static void setUploader(Consumer<byte[]> uploader) {
sUploader = uploader;
}
@CalledByNative
public static void uploadLog(byte[] data) {
final Consumer<byte[]> uploader = sUploader;
if (uploader != null) {
uploader.accept(data);
}
}
}
// Copyright 2019 The Chromium Authors. All rights reserved.
// Copyright 2020 The Chromium Authors. All rights reserved.
// 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/memory_metrics_logger.h"
#include "components/embedder_support/android/metrics/memory_metrics_logger.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
......@@ -20,7 +20,7 @@
using memory_instrumentation::GetPrivateFootprintHistogramName;
using memory_instrumentation::HistogramProcessType;
namespace android_webview {
namespace metrics {
namespace {
MemoryMetricsLogger* g_instance = nullptr;
......@@ -56,6 +56,8 @@ void RecordMemoryMetricsImpl(
}
// WebView only supports the browser and possibly renderer process.
// TODO(weblayer-team): refactor to allow the embedder to record GPU
// metrics.
case memory_instrumentation::mojom::ProcessType::GPU:
FALLTHROUGH;
case memory_instrumentation::mojom::ProcessType::ARC:
......@@ -146,4 +148,4 @@ void MemoryMetricsLogger::RecordMemoryMetrics(scoped_refptr<State> state,
RecordMemoryMetricsAfterDelay(state);
}
} // namespace android_webview
} // namespace metrics
// Copyright 2019 The Chromium Authors. All rights reserved.
// Copyright 2020 The Chromium Authors. All rights reserved.
// 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_MEMORY_METRICS_LOGGER_H_
#define ANDROID_WEBVIEW_BROWSER_METRICS_MEMORY_METRICS_LOGGER_H_
#include <jni.h>
#include <memory>
#ifndef COMPONENTS_EMBEDDER_SUPPORT_ANDROID_METRICS_MEMORY_METRICS_LOGGER_H_
#define COMPONENTS_EMBEDDER_SUPPORT_ANDROID_METRICS_MEMORY_METRICS_LOGGER_H_
#include "base/callback_forward.h"
#include "base/macros.h"
#include "base/memory/scoped_refptr.h"
namespace android_webview {
namespace metrics {
// MemoryMetricsLogger is responsible for logging the memory related heartbeat
// metrics. MemoryMetricsLogger logs metrics at certain intervals for as long
......@@ -26,11 +22,8 @@ class MemoryMetricsLogger {
MemoryMetricsLogger();
~MemoryMetricsLogger();
private:
struct State;
friend jboolean JNI_MemoryMetricsLoggerUtils_ForceRecordHistograms(
JNIEnv* env);
MemoryMetricsLogger(const MemoryMetricsLogger&) = delete;
MemoryMetricsLogger& operator=(const MemoryMetricsLogger&) = delete;
// Returns the single instance, if one was created.
static MemoryMetricsLogger* GetInstanceForTesting();
......@@ -40,6 +33,9 @@ class MemoryMetricsLogger {
// TaskRunner.
void ScheduleRecordForTesting(RecordCallback done_callback);
private:
struct State;
// Called on the task runner to record metrics after a delay.
static void RecordMemoryMetricsAfterDelay(scoped_refptr<State> state);
......@@ -50,10 +46,8 @@ class MemoryMetricsLogger {
RecordCallback done_callback);
scoped_refptr<State> state_;
DISALLOW_COPY_AND_ASSIGN(MemoryMetricsLogger);
};
} // namespace android_webview
} // namespace metrics
#endif // ANDROID_WEBVIEW_BROWSER_METRICS_MEMORY_METRICS_LOGGER_H_
#endif // COMPONENTS_EMBEDDER_SUPPORT_ANDROID_METRICS_MEMORY_METRICS_LOGGER_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment