Commit 97088b96 authored by Alex Clarke's avatar Alex Clarke Committed by Commit Bot

Make WebLayer use mobile_metrics_service

A subsequent patch will add Finch so I've added
FeatureListCreator to encapsulate UMA & Finch setup.

Bug: 1025781
Change-Id: I6c2a71e714733caf94cddc9d8dcf3875789a8298
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1932543Reviewed-by: default avatarRichard Coles <torne@chromium.org>
Reviewed-by: default avatarAlexei Svitkine <asvitkine@chromium.org>
Reviewed-by: default avatarGabriel Charette <gab@chromium.org>
Reviewed-by: default avatarNate Fischer <ntfschr@chromium.org>
Auto-Submit: Alex Clarke <alexclarke@chromium.org>
Commit-Queue: Alex Clarke <alexclarke@chromium.org>
Cr-Commit-Position: refs/heads/master@{#736898}
parent 95ca327c
...@@ -109,6 +109,8 @@ jumbo_static_library("weblayer_lib") { ...@@ -109,6 +109,8 @@ jumbo_static_library("weblayer_lib") {
"browser/download_manager_delegate_impl.h", "browser/download_manager_delegate_impl.h",
"browser/fake_permission_controller_delegate.cc", "browser/fake_permission_controller_delegate.cc",
"browser/fake_permission_controller_delegate.h", "browser/fake_permission_controller_delegate.h",
"browser/feature_list_creator.cc",
"browser/feature_list_creator.h",
"browser/file_select_helper.cc", "browser/file_select_helper.cc",
"browser/file_select_helper.h", "browser/file_select_helper.h",
"browser/i18n_util.cc", "browser/i18n_util.cc",
...@@ -181,6 +183,8 @@ jumbo_static_library("weblayer_lib") { ...@@ -181,6 +183,8 @@ jumbo_static_library("weblayer_lib") {
sources += [ sources += [
"browser/android/metrics/uma_utils.cc", "browser/android/metrics/uma_utils.cc",
"browser/android/metrics/uma_utils.h", "browser/android/metrics/uma_utils.h",
"browser/android/metrics/weblayer_metrics_service_client.cc",
"browser/android/metrics/weblayer_metrics_service_client.h",
"browser/devtools_manager_delegate_android.cc", "browser/devtools_manager_delegate_android.cc",
"browser/devtools_manager_delegate_android.h", "browser/devtools_manager_delegate_android.h",
"browser/devtools_server_android.cc", "browser/devtools_server_android.cc",
...@@ -221,7 +225,10 @@ jumbo_static_library("weblayer_lib") { ...@@ -221,7 +225,10 @@ jumbo_static_library("weblayer_lib") {
"//components/embedder_support", "//components/embedder_support",
"//components/find_in_page", "//components/find_in_page",
"//components/keyed_service/content", "//components/keyed_service/content",
"//components/metrics",
"//components/network_time", "//components/network_time",
"//components/policy/core/browser",
"//components/pref_registry:pref_registry",
"//components/prefs", "//components/prefs",
"//components/safe_browsing/core:features", "//components/safe_browsing/core:features",
"//components/security_interstitials/content:security_interstitial_page", "//components/security_interstitials/content:security_interstitial_page",
...@@ -232,6 +239,8 @@ jumbo_static_library("weblayer_lib") { ...@@ -232,6 +239,8 @@ jumbo_static_library("weblayer_lib") {
"//components/ssl_errors", "//components/ssl_errors",
"//components/startup_metric_utils/browser", "//components/startup_metric_utils/browser",
"//components/user_prefs", "//components/user_prefs",
"//components/variations",
"//components/variations/service",
"//components/version_info", "//components/version_info",
"//components/web_cache/browser", "//components/web_cache/browser",
"//content:content_resources", "//content:content_resources",
...@@ -248,6 +257,7 @@ jumbo_static_library("weblayer_lib") { ...@@ -248,6 +257,7 @@ jumbo_static_library("weblayer_lib") {
"//net:net_resources", "//net:net_resources",
"//sandbox", "//sandbox",
"//services/network/public/mojom", "//services/network/public/mojom",
"//services/preferences/tracked",
"//services/service_manager/embedder:embedder_result_codes", "//services/service_manager/embedder:embedder_result_codes",
"//skia", "//skia",
"//third_party/blink/public/common", "//third_party/blink/public/common",
...@@ -297,6 +307,7 @@ jumbo_static_library("weblayer_lib") { ...@@ -297,6 +307,7 @@ jumbo_static_library("weblayer_lib") {
"//components/android_system_error_page", "//components/android_system_error_page",
"//components/autofill/android:provider", "//components/autofill/android:provider",
"//components/crash/android:crashpad_main", "//components/crash/android:crashpad_main",
"//components/embedder_support/android/metrics",
"//components/metrics", "//components/metrics",
"//components/minidump_uploader", "//components/minidump_uploader",
"//components/safe_browsing/content/renderer:throttles", "//components/safe_browsing/content/renderer:throttles",
...@@ -330,7 +341,11 @@ jumbo_static_library("weblayer_lib") { ...@@ -330,7 +341,11 @@ jumbo_static_library("weblayer_lib") {
if (is_android) { if (is_android) {
deps += [ deps += [
"//components/embedder_support/android:web_contents_delegate", "//components/embedder_support/android:web_contents_delegate",
"//components/metrics:gpu",
"//components/metrics:net",
"//components/metrics:ui",
"//components/version_info", "//components/version_info",
"//services/resource_coordinator/public/cpp/memory_instrumentation:browser",
"//ui/android", "//ui/android",
"//weblayer/browser/java:jni", "//weblayer/browser/java:jni",
] ]
......
...@@ -11,7 +11,9 @@ include_rules = [ ...@@ -11,7 +11,9 @@ include_rules = [
"+components/embedder_support", "+components/embedder_support",
"+components/find_in_page", "+components/find_in_page",
"+components/keyed_service/content", "+components/keyed_service/content",
"+components/metrics",
"+components/network_time", "+components/network_time",
"+components/pref_registry",
"+components/prefs", "+components/prefs",
"+components/user_prefs", "+components/user_prefs",
"+components/safe_browsing/core/common", "+components/safe_browsing/core/common",
......
// 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 "weblayer/browser/android/metrics/weblayer_metrics_service_client.h"
#include <jni.h>
#include <cstdint>
#include <memory>
#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "base/base_paths_android.h"
#include "base/no_destructor.h"
#include "components/metrics/android_metrics_provider.h"
#include "components/metrics/drive_metrics_provider.h"
#include "components/metrics/gpu/gpu_metrics_provider.h"
#include "components/metrics/metrics_service.h"
#include "components/metrics/version_utils.h"
#include "components/version_info/android/channel_getter.h"
#include "components/version_info/version_info.h"
#include "weblayer/browser/java/jni/MetricsServiceClient_jni.h"
namespace weblayer {
namespace {
// IMPORTANT: DO NOT CHANGE sample rates without first ensuring the Chrome
// Metrics team has the appropriate backend bandwidth and storage.
// Sample at 10%, which is the same as chrome.
const double kStableSampledInRate = 0.1;
// 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 double kBetaDevCanarySampledInRate = 0.99;
// As a mitigation to preserve user privacy, the privacy team has asked that we
// upload package name with no more than 10% of UMA records. This is to mitigate
// fingerprinting for users on low-usage applications (if an app only has a
// a small handful of users, there's a very good chance many of them won't be
// uploading UMA records due to sampling). Do not change this constant without
// consulting with the privacy team.
const double kPackageNameLimitRate = 0.10;
} // namespace
// static
WebLayerMetricsServiceClient* WebLayerMetricsServiceClient::GetInstance() {
static base::NoDestructor<WebLayerMetricsServiceClient> client;
client->EnsureOnValidSequence();
return client.get();
}
WebLayerMetricsServiceClient::WebLayerMetricsServiceClient() = default;
WebLayerMetricsServiceClient::~WebLayerMetricsServiceClient() = default;
int32_t WebLayerMetricsServiceClient::GetProduct() {
return metrics::ChromeUserMetricsExtension::ANDROID_WEBLAYER;
}
metrics::SystemProfileProto::Channel
WebLayerMetricsServiceClient::GetChannel() {
return metrics::AsProtobufChannel(version_info::android::GetChannel());
}
std::string WebLayerMetricsServiceClient::GetVersionString() {
return version_info::GetVersionNumber();
}
std::string WebLayerMetricsServiceClient::GetAppPackageNameInternal() {
JNIEnv* env = base::android::AttachCurrentThread();
base::android::ScopedJavaLocalRef<jstring> j_app_name =
Java_MetricsServiceClient_getAppPackageName(env);
if (j_app_name)
return ConvertJavaStringToUTF8(env, j_app_name);
return std::string();
}
double WebLayerMetricsServiceClient::GetSampleRate() {
version_info::Channel channel = version_info::android::GetChannel();
if (channel == version_info::Channel::STABLE ||
channel == version_info::Channel::UNKNOWN) {
return kStableSampledInRate;
}
return kBetaDevCanarySampledInRate;
}
void WebLayerMetricsServiceClient::InitInternal() {}
void WebLayerMetricsServiceClient::OnMetricsStart() {}
double WebLayerMetricsServiceClient::GetPackageNameLimitRate() {
return kPackageNameLimitRate;
}
bool WebLayerMetricsServiceClient::ShouldWakeMetricsService() {
return true;
}
void WebLayerMetricsServiceClient::RegisterAdditionalMetricsProviders(
metrics::MetricsService* 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>());
}
bool WebLayerMetricsServiceClient::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).
JNIEnv* env = base::android::AttachCurrentThread();
return Java_MetricsServiceClient_canRecordPackageNameForAppType(env);
}
// static
void JNI_MetricsServiceClient_SetHaveMetricsConsent(JNIEnv* env,
jboolean user_consent,
jboolean app_consent) {
WebLayerMetricsServiceClient::GetInstance()->SetHaveMetricsConsent(
user_consent, app_consent);
}
} // namespace weblayer
// 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 WEBLAYER_BROWSER_ANDROID_METRICS_WEBLAYER_METRICS_SERVICE_CLIENT_H_
#define WEBLAYER_BROWSER_ANDROID_METRICS_WEBLAYER_METRICS_SERVICE_CLIENT_H_
#include <memory>
#include <string>
#include "base/macros.h"
#include "base/metrics/field_trial.h"
#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"
namespace weblayer {
class WebLayerMetricsServiceClient
: public ::metrics::AndroidMetricsServiceClient {
friend class base::NoDestructor<WebLayerMetricsServiceClient>;
public:
static WebLayerMetricsServiceClient* GetInstance();
WebLayerMetricsServiceClient();
~WebLayerMetricsServiceClient() override;
// metrics::WebLayerMetricsServiceClient
int32_t GetProduct() override;
metrics::SystemProfileProto::Channel GetChannel() override;
double GetSampleRate() override;
std::string GetVersionString() override;
// metrics::MobileWebLayerMetricsServiceClient:
void InitInternal() override;
void OnMetricsStart() override;
double GetPackageNameLimitRate() override;
bool ShouldWakeMetricsService() override;
void RegisterAdditionalMetricsProviders(
metrics::MetricsService* service) override;
bool CanRecordPackageNameForAppType() override;
std::string GetAppPackageNameInternal() override;
private:
DISALLOW_COPY_AND_ASSIGN(WebLayerMetricsServiceClient);
};
} // namespace weblayer
#endif // WEBLAYER_BROWSER_ANDROID_METRICS_WEBLAYER_METRICS_SERVICE_CLIENT_H_
...@@ -116,6 +116,12 @@ int BrowserMainPartsImpl::PreEarlyInitialization() { ...@@ -116,6 +116,12 @@ int BrowserMainPartsImpl::PreEarlyInitialization() {
return service_manager::RESULT_CODE_NORMAL_EXIT; return service_manager::RESULT_CODE_NORMAL_EXIT;
} }
void BrowserMainPartsImpl::PostEarlyInitialization() {
#if defined(OS_ANDROID)
CreateLocalState();
#endif
}
void BrowserMainPartsImpl::PreMainMessageLoopRun() { void BrowserMainPartsImpl::PreMainMessageLoopRun() {
ui::MaterialDesignController::Initialize(); ui::MaterialDesignController::Initialize();
// It's necessary to have a complete dependency graph of // It's necessary to have a complete dependency graph of
...@@ -140,6 +146,7 @@ void BrowserMainPartsImpl::PreMainMessageLoopRun() { ...@@ -140,6 +146,7 @@ void BrowserMainPartsImpl::PreMainMessageLoopRun() {
// Record collected startup metrics. // Record collected startup metrics.
startup_metric_utils::RecordBrowserMainMessageLoopStart( startup_metric_utils::RecordBrowserMainMessageLoopStart(
base::TimeTicks::Now(), /* is_first_run */ false); base::TimeTicks::Now(), /* is_first_run */ false);
memory_metrics_logger_ = std::make_unique<metrics::MemoryMetricsLogger>();
#endif #endif
} }
...@@ -159,4 +166,12 @@ void BrowserMainPartsImpl::PreDefaultMainMessageLoopRun( ...@@ -159,4 +166,12 @@ void BrowserMainPartsImpl::PreDefaultMainMessageLoopRun(
base::BindOnce(StopMessageLoop, std::move(quit_closure))); base::BindOnce(StopMessageLoop, std::move(quit_closure)));
} }
void BrowserMainPartsImpl::CreateLocalState() {
DCHECK(!local_state_);
feature_list_creator_ = std::make_unique<FeatureListCreator>();
feature_list_creator_->CreateLocalState();
local_state_ = feature_list_creator_->TakePrefService();
CHECK(local_state_);
}
} // namespace weblayer } // namespace weblayer
...@@ -10,8 +10,11 @@ ...@@ -10,8 +10,11 @@
#include "base/macros.h" #include "base/macros.h"
#include "base/metrics/field_trial.h" #include "base/metrics/field_trial.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "components/embedder_support/android/metrics/memory_metrics_logger.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/browser_main_parts.h" #include "content/public/browser/browser_main_parts.h"
#include "content/public/common/main_function_params.h" #include "content/public/common/main_function_params.h"
#include "weblayer/browser/feature_list_creator.h"
namespace weblayer { namespace weblayer {
class BrowserProcess; class BrowserProcess;
...@@ -26,6 +29,7 @@ class BrowserMainPartsImpl : public content::BrowserMainParts { ...@@ -26,6 +29,7 @@ class BrowserMainPartsImpl : public content::BrowserMainParts {
// BrowserMainParts overrides. // BrowserMainParts overrides.
int PreCreateThreads() override; int PreCreateThreads() override;
int PreEarlyInitialization() override; int PreEarlyInitialization() override;
void PostEarlyInitialization() override;
void PreMainMessageLoopStart() override; void PreMainMessageLoopStart() override;
void PreMainMessageLoopRun() override; void PreMainMessageLoopRun() override;
void PostMainMessageLoopRun() override; void PostMainMessageLoopRun() override;
...@@ -33,9 +37,16 @@ class BrowserMainPartsImpl : public content::BrowserMainParts { ...@@ -33,9 +37,16 @@ class BrowserMainPartsImpl : public content::BrowserMainParts {
void PreDefaultMainMessageLoopRun(base::OnceClosure quit_closure) override; void PreDefaultMainMessageLoopRun(base::OnceClosure quit_closure) override;
private: private:
void CreateLocalState();
MainParams* params_; MainParams* params_;
std::unique_ptr<BrowserProcess> browser_process_; std::unique_ptr<BrowserProcess> browser_process_;
#if defined(OS_ANDROID)
std::unique_ptr<metrics::MemoryMetricsLogger> memory_metrics_logger_;
#endif // defined(OS_ANDROID)
std::unique_ptr<FeatureListCreator> feature_list_creator_;
std::unique_ptr<PrefService> local_state_;
// For running weblayer_browsertests. // For running weblayer_browsertests.
const content::MainFunctionParams main_function_params_; const content::MainFunctionParams main_function_params_;
......
// Copyright 2019 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 "weblayer/browser/feature_list_creator.h"
#include "base/base_switches.h"
#include "base/path_service.h"
#include "build/build_config.h"
#include "components/metrics/metrics_pref_names.h"
#include "components/metrics/metrics_service.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/in_memory_pref_store.h"
#include "components/prefs/json_pref_store.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/pref_service_factory.h"
#include "weblayer/browser/android/metrics/weblayer_metrics_service_client.h"
namespace weblayer {
namespace {
void HandleReadError(PersistentPrefStore::PrefReadError error) {}
#if defined(OS_ANDROID)
base::FilePath GetPrefStorePath() {
base::FilePath path;
base::PathService::Get(base::DIR_ANDROID_APP_DATA, &path);
path = path.Append(FILE_PATH_LITERAL("pref_store"));
return path;
}
#endif
std::unique_ptr<PrefService> CreatePrefService() {
auto pref_registry = base::MakeRefCounted<user_prefs::PrefRegistrySyncable>();
#if defined(OS_ANDROID)
metrics::AndroidMetricsServiceClient::RegisterPrefs(pref_registry.get());
#endif
// TODO(weblayer-dev): Register prefs with VariationsService
PrefServiceFactory pref_service_factory;
#if defined(OS_ANDROID)
pref_service_factory.set_user_prefs(
base::MakeRefCounted<JsonPrefStore>(GetPrefStorePath()));
#else
// For now just use in memory PrefStore for desktop.
// TODO(weblayer-dev): Find a long term solution.
pref_service_factory.set_user_prefs(
base::MakeRefCounted<InMemoryPrefStore>());
#endif
pref_service_factory.set_read_error_callback(
base::BindRepeating(&HandleReadError));
return pref_service_factory.Create(pref_registry);
}
} // namespace
FeatureListCreator::FeatureListCreator() = default;
FeatureListCreator::~FeatureListCreator() = default;
void FeatureListCreator::CreateLocalState() {
local_state_ = CreatePrefService();
#if defined(OS_ANDROID)
WebLayerMetricsServiceClient::GetInstance()->Initialize(local_state_.get());
#endif
}
} // namespace weblayer
// Copyright 2019 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 WEBLAYER_BROWSER_FEATURE_LIST_CREATOR_H_
#define WEBLAYER_BROWSER_FEATURE_LIST_CREATOR_H_
#include <memory>
#include "components/prefs/pref_service.h"
namespace weblayer {
// TODO(weblayer-dev): // Set up field trials based on the stored variations
// seed data.
class FeatureListCreator {
public:
FeatureListCreator();
~FeatureListCreator();
void CreateLocalState();
// Passes ownership of the |local_state_| to the caller.
std::unique_ptr<PrefService> TakePrefService() {
DCHECK(local_state_);
return std::move(local_state_);
}
private:
std::unique_ptr<PrefService> local_state_;
DISALLOW_COPY_AND_ASSIGN(FeatureListCreator);
};
} // namespace weblayer
#endif // WEBLAYER_BROWSER_FEATURE_LIST_CREATOR_H_
...@@ -55,6 +55,7 @@ android_library("java") { ...@@ -55,6 +55,7 @@ android_library("java") {
"org/chromium/weblayer_private/WebLayerFactoryImpl.java", "org/chromium/weblayer_private/WebLayerFactoryImpl.java",
"org/chromium/weblayer_private/WebLayerImpl.java", "org/chromium/weblayer_private/WebLayerImpl.java",
"org/chromium/weblayer_private/WebLayerNetworkChangeNotifierRegistrationPolicy.java", "org/chromium/weblayer_private/WebLayerNetworkChangeNotifierRegistrationPolicy.java",
"org/chromium/weblayer_private/metrics/MetricsServiceClient.java",
"org/chromium/weblayer_private/metrics/UmaUtils.java", "org/chromium/weblayer_private/metrics/UmaUtils.java",
] ]
...@@ -72,6 +73,7 @@ android_library("java") { ...@@ -72,6 +73,7 @@ android_library("java") {
"//components/embedder_support/android:application_java", "//components/embedder_support/android:application_java",
"//components/embedder_support/android:web_contents_delegate_java", "//components/embedder_support/android:web_contents_delegate_java",
"//components/find_in_page/android:java", "//components/find_in_page/android:java",
"//components/metrics:metrics_java",
"//components/minidump_uploader:minidump_uploader_java", "//components/minidump_uploader:minidump_uploader_java",
"//components/spellcheck/browser/android:java", "//components/spellcheck/browser/android:java",
"//components/version_info/android:version_constants_java", "//components/version_info/android:version_constants_java",
...@@ -127,6 +129,7 @@ generate_jni("jni") { ...@@ -127,6 +129,7 @@ generate_jni("jni") {
"org/chromium/weblayer_private/TabImpl.java", "org/chromium/weblayer_private/TabImpl.java",
"org/chromium/weblayer_private/TopControlsContainerView.java", "org/chromium/weblayer_private/TopControlsContainerView.java",
"org/chromium/weblayer_private/WebLayerImpl.java", "org/chromium/weblayer_private/WebLayerImpl.java",
"org/chromium/weblayer_private/metrics/MetricsServiceClient.java",
"org/chromium/weblayer_private/metrics/UmaUtils.java", "org/chromium/weblayer_private/metrics/UmaUtils.java",
] ]
} }
...@@ -159,7 +162,10 @@ android_library("gms_bridge_java") { ...@@ -159,7 +162,10 @@ android_library("gms_bridge_java") {
"org/chromium/weblayer_private/GmsBridgeImpl.java", "org/chromium/weblayer_private/GmsBridgeImpl.java",
] ]
deps = [ "//base:base_java" ] deps = [
"//base:base_java",
"//components/embedder_support/android/metrics:java",
]
# The appropriate .class file will be loaded via a dependency to a library # The appropriate .class file will be loaded via a dependency to a library
# like :gms_bridge_upstream_impl_java below. # like :gms_bridge_upstream_impl_java below.
......
...@@ -7,6 +7,11 @@ package org.chromium.weblayer_private; ...@@ -7,6 +7,11 @@ package org.chromium.weblayer_private;
import android.os.Handler; import android.os.Handler;
import android.os.HandlerThread; import android.os.HandlerThread;
import org.chromium.base.Callback;
import org.chromium.base.Consumer;
import org.chromium.base.ThreadUtils;
import org.chromium.components.metrics.AndroidMetricsLogUploader;
/** /**
* This class manages functionality related to Google Mobile Services (i.e. GMS). * This class manages functionality related to Google Mobile Services (i.e. GMS).
* Platform specific implementations are provided in GmsBridgeImpl.java. * Platform specific implementations are provided in GmsBridgeImpl.java.
...@@ -19,7 +24,14 @@ public abstract class GmsBridge { ...@@ -19,7 +24,14 @@ public abstract class GmsBridge {
private static Handler sHandler; private static Handler sHandler;
private static final Object sHandlerLock = new Object(); private static final Object sHandlerLock = new Object();
protected GmsBridge() {} protected GmsBridge() {
AndroidMetricsLogUploader.setUploader(new Consumer<byte[]>() {
@Override
public void accept(byte[] data) {
logMetrics(data);
}
});
}
public static GmsBridge getInstance() { public static GmsBridge getInstance() {
synchronized (sInstanceLock) { synchronized (sInstanceLock) {
...@@ -32,6 +44,13 @@ public abstract class GmsBridge { ...@@ -32,6 +44,13 @@ public abstract class GmsBridge {
} }
} }
// Provide a mocked GmsBridge for testing.
public static void injectInstance(GmsBridge testBridge) {
synchronized (sInstanceLock) {
sInstance = testBridge;
}
}
// Return a handler appropriate for executing blocking Platform Service tasks. // Return a handler appropriate for executing blocking Platform Service tasks.
public static Handler getHandler() { public static Handler getHandler() {
synchronized (sHandlerLock) { synchronized (sHandlerLock) {
...@@ -52,4 +71,14 @@ public abstract class GmsBridge { ...@@ -52,4 +71,14 @@ public abstract class GmsBridge {
public void setSafeBrowsingHandler() { public void setSafeBrowsingHandler() {
// We don't have this specialized service here. // We don't have this specialized service here.
} }
// Overriding implementations may call "callback" asynchronously. For simplicity (and not
// because of any technical limitation) we require that "queryMetricsSetting" and "callback"
// both get called on WebLayer's UI thread.
public void queryMetricsSetting(Callback<Boolean> callback) {
ThreadUtils.assertOnUiThread();
callback.onResult(false);
}
public void logMetrics(byte[] data) {}
} }
...@@ -44,6 +44,7 @@ import org.chromium.weblayer_private.interfaces.IRemoteFragmentClient; ...@@ -44,6 +44,7 @@ import org.chromium.weblayer_private.interfaces.IRemoteFragmentClient;
import org.chromium.weblayer_private.interfaces.IWebLayer; import org.chromium.weblayer_private.interfaces.IWebLayer;
import org.chromium.weblayer_private.interfaces.ObjectWrapper; import org.chromium.weblayer_private.interfaces.ObjectWrapper;
import org.chromium.weblayer_private.interfaces.StrictModeWorkaround; import org.chromium.weblayer_private.interfaces.StrictModeWorkaround;
import org.chromium.weblayer_private.metrics.MetricsServiceClient;
import org.chromium.weblayer_private.metrics.UmaUtils; import org.chromium.weblayer_private.metrics.UmaUtils;
import java.io.File; import java.io.File;
...@@ -103,9 +104,6 @@ public final class WebLayerImpl extends IWebLayer.Stub { ...@@ -103,9 +104,6 @@ public final class WebLayerImpl extends IWebLayer.Stub {
StrictModeWorkaround.apply(); StrictModeWorkaround.apply();
init(appContextWrapper, remoteContextWrapper); init(appContextWrapper, remoteContextWrapper);
NetworkChangeNotifier.init();
WebLayerNetworkChangeNotifierRegistrationPolicy registrationPolicy =
new WebLayerNetworkChangeNotifierRegistrationPolicy();
final ValueCallback<Boolean> loadedCallback = (ValueCallback<Boolean>) ObjectWrapper.unwrap( final ValueCallback<Boolean> loadedCallback = (ValueCallback<Boolean>) ObjectWrapper.unwrap(
loadedCallbackWrapper, ValueCallback.class); loadedCallbackWrapper, ValueCallback.class);
BrowserStartupController.get(LibraryProcessType.PROCESS_WEBLAYER) BrowserStartupController.get(LibraryProcessType.PROCESS_WEBLAYER)
...@@ -114,8 +112,7 @@ public final class WebLayerImpl extends IWebLayer.Stub { ...@@ -114,8 +112,7 @@ public final class WebLayerImpl extends IWebLayer.Stub {
new BrowserStartupController.StartupCallback() { new BrowserStartupController.StartupCallback() {
@Override @Override
public void onSuccess() { public void onSuccess() {
CrashReporterControllerImpl.getInstance().notifyNativeInitialized(); onNativeLoaded(appContextWrapper);
configureNetworkChangeNotifier(registrationPolicy);
loadedCallback.onReceiveValue(true); loadedCallback.onReceiveValue(true);
} }
@Override @Override
...@@ -138,9 +135,17 @@ public final class WebLayerImpl extends IWebLayer.Stub { ...@@ -138,9 +135,17 @@ public final class WebLayerImpl extends IWebLayer.Stub {
BrowserStartupController.get(LibraryProcessType.PROCESS_WEBLAYER) BrowserStartupController.get(LibraryProcessType.PROCESS_WEBLAYER)
.startBrowserProcessesSync( .startBrowserProcessesSync(
/* singleProcess*/ false); /* singleProcess*/ false);
onNativeLoaded(appContextWrapper);
}
private void onNativeLoaded(IObjectWrapper appContextWrapper) {
CrashReporterControllerImpl.getInstance().notifyNativeInitialized(); CrashReporterControllerImpl.getInstance().notifyNativeInitialized();
NetworkChangeNotifier.init(); NetworkChangeNotifier.init();
configureNetworkChangeNotifier(new WebLayerNetworkChangeNotifierRegistrationPolicy()); configureNetworkChangeNotifier(new WebLayerNetworkChangeNotifierRegistrationPolicy());
// This issues JNI calls which require native code to be loaded.
MetricsServiceClient.init();
} }
// Configure NetworkChangeNotifier to auto detect changes in network // Configure NetworkChangeNotifier to auto detect changes in network
......
// 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.weblayer_private.metrics;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import org.chromium.base.ContextUtils;
import org.chromium.base.Log;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace;
import org.chromium.base.annotations.NativeMethods;
import org.chromium.weblayer_private.GmsBridge;
/**
* Determines user consent and app opt-out for metrics. See metrics_service_client.h for more
* explanation.
*
* TODO(weblayer-team): Consider compoentizing once requirements are nailed down.
*/
@JNINamespace("weblayer")
public class MetricsServiceClient {
private static final String TAG = "MetricsServiceClie-";
private static final String PLAY_STORE_PACKAGE_NAME = "com.android.vending";
// Individual apps can use this meta-data tag in their manifest to opt out of metrics.
private static final String AUTO_UPLOAD_METADATA_STR = "android.WebLayer.MetricsAutoUpload";
private static boolean isAppOptedOut(Context ctx) {
try {
ApplicationInfo info = ctx.getPackageManager().getApplicationInfo(
ctx.getPackageName(), PackageManager.GET_META_DATA);
if (info.metaData == null) {
// null means no such tag was found which we interpret as not opting out.
return false;
}
// getBoolean returns false if the key is not found, which is what we want.
return info.metaData.getBoolean(AUTO_UPLOAD_METADATA_STR);
} catch (PackageManager.NameNotFoundException e) {
// This should never happen.
Log.e(TAG, "App could not find itself by package name!");
// The conservative thing is to assume the app HAS opted out.
return true;
}
}
public static void init() {
GmsBridge.getInstance().queryMetricsSetting(userConsent -> {
MetricsServiceClientJni.get().setHaveMetricsConsent(
userConsent, !isAppOptedOut(ContextUtils.getApplicationContext()));
});
}
@CalledByNative
private static String getAppPackageName() {
// Return this unconditionally; let native code enforce whether or not it's OK to include
// this in the logs.
Context ctx = ContextUtils.getApplicationContext();
return ctx.getPackageName();
}
@CalledByNative
private static boolean canRecordPackageNameForAppType() {
// Only record if it's a system app or it was installed from Play Store.
Context ctx = ContextUtils.getApplicationContext();
String packageName = ctx.getPackageName();
String installerPackageName = ctx.getPackageManager().getInstallerPackageName(packageName);
return (ctx.getApplicationInfo().flags & ApplicationInfo.FLAG_SYSTEM) != 0
|| (PLAY_STORE_PACKAGE_NAME.equals(installerPackageName));
}
@NativeMethods
interface Natives {
void setHaveMetricsConsent(boolean userConsent, boolean appConsent);
}
}
include_rules = [ include_rules = [
"+components/metrics",
"+content/public/android", "+content/public/android",
"+content/public/app", "+content/public/app",
"+content/public/test", "+content/public/test",
"+ui/android", "+ui/android",
"+third_party/metrics_proto"
] ]
// Copyright 2019 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 <deque>
#include "base/android/jni_android.h"
#include "base/command_line.h"
#include "base/no_destructor.h"
#include "base/test/bind_test_util.h"
#include "components/metrics/metrics_switches.h"
#include "third_party/metrics_proto/chrome_user_metrics_extension.pb.h"
#include "weblayer/browser/android/metrics/weblayer_metrics_service_client.h"
#include "weblayer/browser/profile_impl.h"
#include "weblayer/public/navigation_controller.h"
#include "weblayer/public/profile.h"
#include "weblayer/public/tab.h"
#include "weblayer/shell/browser/shell.h"
#include "weblayer/test/weblayer_browser_test.h"
#include "weblayer/test/weblayer_browsertests_jni/MetricsTestHelper_jni.h"
namespace weblayer {
class MetricsBrowserTest : public WebLayerBrowserTest {
public:
void SetUp() override {
instance_ = this;
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
command_line->AppendSwitch(metrics::switches::kForceEnableMetricsReporting);
Java_MetricsTestHelper_installTestGmsBridge(
base::android::AttachCurrentThread(), HasUserConsent());
WebLayerMetricsServiceClient::GetInstance()->SetFastStartupForTesting(true);
WebLayerMetricsServiceClient::GetInstance()->SetUploadIntervalForTesting(
base::TimeDelta::FromMilliseconds(10));
WebLayerBrowserTest::SetUp();
}
void TearDown() override {
Java_MetricsTestHelper_removeTestGmsBridge(
base::android::AttachCurrentThread());
instance_ = nullptr;
WebLayerBrowserTest::TearDown();
}
static void OnLogMetrics(const metrics::ChromeUserMetricsExtension& metric) {
if (!instance_)
return;
instance_->metrics_logs_.push_back(metric);
std::move(instance_->on_new_log_).Run();
}
metrics::ChromeUserMetricsExtension waitForNextMetricsLog() {
if (metrics_logs_.empty()) {
base::RunLoop run_loop;
on_new_log_ = run_loop.QuitClosure();
run_loop.Run();
DCHECK(!metrics_logs_.empty());
}
metrics::ChromeUserMetricsExtension tmp = std::move(metrics_logs_.front());
metrics_logs_.pop_front();
return tmp;
}
size_t GetNumLogs() const { return metrics_logs_.size(); }
virtual bool HasUserConsent() { return true; }
private:
std::unique_ptr<Profile> profile_;
std::deque<metrics::ChromeUserMetricsExtension> metrics_logs_;
base::OnceClosure on_new_log_;
static MetricsBrowserTest* instance_;
};
MetricsBrowserTest* MetricsBrowserTest::instance_ = nullptr;
void JNI_MetricsTestHelper_OnLogMetrics(
JNIEnv* env,
const base::android::JavaParamRef<jbyteArray>& data) {
metrics::ChromeUserMetricsExtension proto;
jbyte* src_bytes = env->GetByteArrayElements(data, nullptr);
proto.ParseFromArray(src_bytes, env->GetArrayLength(data.obj()));
env->ReleaseByteArrayElements(data, src_bytes, JNI_ABORT);
MetricsBrowserTest::OnLogMetrics(proto);
}
IN_PROC_BROWSER_TEST_F(MetricsBrowserTest, ProtoHasExpectedFields) {
metrics::ChromeUserMetricsExtension log = waitForNextMetricsLog();
EXPECT_EQ(metrics::ChromeUserMetricsExtension::ANDROID_WEBLAYER,
log.product());
EXPECT_TRUE(log.has_client_id());
EXPECT_TRUE(log.has_session_id());
const metrics::SystemProfileProto& system_profile = log.system_profile();
EXPECT_TRUE(system_profile.has_build_timestamp());
EXPECT_TRUE(system_profile.has_app_version());
EXPECT_TRUE(system_profile.has_channel());
EXPECT_TRUE(system_profile.has_install_date());
EXPECT_TRUE(system_profile.has_application_locale());
EXPECT_EQ("Android", system_profile.os().name());
EXPECT_TRUE(system_profile.os().has_version());
EXPECT_TRUE(system_profile.hardware().has_system_ram_mb());
EXPECT_TRUE(system_profile.hardware().has_hardware_class());
EXPECT_TRUE(system_profile.hardware().has_screen_count());
EXPECT_TRUE(system_profile.hardware().has_primary_screen_width());
EXPECT_TRUE(system_profile.hardware().has_primary_screen_height());
EXPECT_TRUE(system_profile.hardware().has_primary_screen_scale_factor());
EXPECT_TRUE(system_profile.hardware().has_cpu_architecture());
EXPECT_TRUE(system_profile.hardware().cpu().has_vendor_name());
EXPECT_TRUE(system_profile.hardware().cpu().has_signature());
EXPECT_TRUE(system_profile.hardware().cpu().has_num_cores());
EXPECT_TRUE(system_profile.hardware().cpu().has_is_hypervisor());
EXPECT_TRUE(system_profile.hardware().gpu().has_driver_version());
EXPECT_TRUE(system_profile.hardware().gpu().has_gl_vendor());
EXPECT_TRUE(system_profile.hardware().gpu().has_gl_renderer());
// We assert we do *not* log data which is deemed to be privacy sensitive.
// It's OK to reverse these test conditions, but only if we log these fields
// in a manner which is approved to preserve user privacy.
EXPECT_FALSE(system_profile.hardware().has_bluetooth());
EXPECT_FALSE(system_profile.hardware().has_usb());
}
IN_PROC_BROWSER_TEST_F(MetricsBrowserTest, PageLoadsEnableMultipleUploads) {
waitForNextMetricsLog();
// At this point, the MetricsService should be asleep, and should not have
// created any more metrics logs.
ASSERT_EQ(0u, GetNumLogs());
// The start of a page load should be enough to indicate to the MetricsService
// that the app is "in use" and it's OK to upload the next record. No need to
// wait for onPageFinished, since this behavior should be gated on
// NOTIFICATION_LOAD_START.
shell()->tab()->GetNavigationController()->Navigate(GURL("about:blank"));
// This may take slightly longer than UPLOAD_INTERVAL_MS, due to the time
// spent processing the metrics log, but should be well within the timeout
// (unless something is broken).
waitForNextMetricsLog();
// If we get here, we got a second metrics log (and the test may pass). If
// there was no second metrics log, then the above call will check fail with a
// timeout. We should not assert the logs are empty however, because it's
// possible we got a metrics log between onPageStarted & onPageFinished, in
// which case onPageFinished would *also* wake up the metrics service, and we
// might potentially have a third metrics log in the queue.
}
class MetricsBrowserTestWithUserOptOut : public MetricsBrowserTest {
bool HasUserConsent() override { return false; }
};
IN_PROC_BROWSER_TEST_F(MetricsBrowserTestWithUserOptOut, MetricsNotRecorded) {
base::RunLoop().RunUntilIdle();
ASSERT_EQ(0u, GetNumLogs());
}
} // namespace weblayer
// Copyright 2019 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.weblayer_private;
import org.chromium.base.Callback;
import org.chromium.base.ThreadUtils;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace;
import org.chromium.base.annotations.NativeMethods;
/**
* Helper for metrics_browsertest.cc
*/
@JNINamespace("weblayer")
class MetricsTestHelper {
private static class TestGmsBridge extends GmsBridge {
private final boolean mUserConsent;
public TestGmsBridge(boolean userConsent) {
mUserConsent = userConsent;
}
@Override
public boolean canUseGms() {
return true;
}
@Override
public void setSafeBrowsingHandler() {
// We don't have this specialized service here.
}
@Override
public void queryMetricsSetting(Callback<Boolean> callback) {
ThreadUtils.assertOnUiThread();
callback.onResult(mUserConsent);
}
@Override
public void logMetrics(byte[] data) {
MetricsTestHelperJni.get().onLogMetrics(data);
}
}
@CalledByNative
private static void installTestGmsBridge(boolean userConsent) {
GmsBridge.injectInstance(new TestGmsBridge(userConsent));
}
@CalledByNative
private static void removeTestGmsBridge() {
GmsBridge.injectInstance(null);
}
@NativeMethods
interface Natives {
void onLogMetrics(byte[] data);
}
}
...@@ -26,11 +26,14 @@ if (is_android) { ...@@ -26,11 +26,14 @@ if (is_android) {
sources = [ sources = [
"../shell/android/browsertests_apk/src/org/chromium/weblayer_browsertests_apk/WebLayerBrowserTestsActivity.java", "../shell/android/browsertests_apk/src/org/chromium/weblayer_browsertests_apk/WebLayerBrowserTestsActivity.java",
"../shell/android/browsertests_apk/src/org/chromium/weblayer_browsertests_apk/WebLayerBrowserTestsApplication.java", "../shell/android/browsertests_apk/src/org/chromium/weblayer_browsertests_apk/WebLayerBrowserTestsApplication.java",
"../shell/android/browsertests_apk/src/org/chromium/weblayer_private/MetricsTestHelper.java",
] ]
deps = [ deps = [
":weblayer_browsertests_jni",
":weblayer_browsertests_manifest", ":weblayer_browsertests_manifest",
"//base:base_java", "//base:base_java",
"//base:base_java_test_support", "//base:base_java_test_support",
"//base:jni_java",
"//components/safe_browsing/android:safe_browsing_java", "//components/safe_browsing/android:safe_browsing_java",
"//content/public/android:content_java", "//content/public/android:content_java",
"//content/public/test/android:content_java_test_support", "//content/public/test/android:content_java_test_support",
...@@ -38,12 +41,20 @@ if (is_android) { ...@@ -38,12 +41,20 @@ if (is_android) {
"//third_party/android_deps:android_support_v4_java", "//third_party/android_deps:android_support_v4_java",
"//third_party/android_deps:com_android_support_support_compat_java", "//third_party/android_deps:com_android_support_support_compat_java",
"//ui/android:ui_java", "//ui/android:ui_java",
"//weblayer/browser/java",
"//weblayer/browser/java:gms_bridge_java",
"//weblayer/public/java", "//weblayer/public/java",
] ]
annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
android_manifest_for_lint = weblayer_browsertests_manifest android_manifest_for_lint = weblayer_browsertests_manifest
} }
generate_jni("weblayer_browsertests_jni") {
sources = [ "../shell/android/browsertests_apk/src/org/chromium/weblayer_private/MetricsTestHelper.java" ]
}
android_assets("weblayer_test_assets") { android_assets("weblayer_test_assets") {
testonly = true testonly = true
disable_compression = true disable_compression = true
...@@ -118,14 +129,17 @@ test("weblayer_browsertests") { ...@@ -118,14 +129,17 @@ test("weblayer_browsertests") {
if (is_android) { if (is_android) {
sources += [ sources += [
"../browser/safe_browsing/safe_browsing_browsertest.cc", "../browser/safe_browsing/safe_browsing_browsertest.cc",
"../shell/android/browsertests_apk/metrics_browsertest.cc",
"../shell/android/browsertests_apk/weblayer_browser_tests_jni_onload.cc", "../shell/android/browsertests_apk/weblayer_browser_tests_jni_onload.cc",
] ]
deps += [ deps += [
":weblayer_browsertests_java", ":weblayer_browsertests_java",
":weblayer_browsertests_jni",
":weblayer_test_assets", ":weblayer_test_assets",
"//android_webview:generate_aw_strings_grit", "//android_webview:generate_aw_strings_grit",
"//android_webview:locale_pak_assets", "//android_webview:locale_pak_assets",
"//android_webview:pak_file_assets", "//android_webview:pak_file_assets",
"//components/metrics",
"//components/safe_browsing/android:safe_browsing_api_handler", "//components/safe_browsing/android:safe_browsing_api_handler",
"//components/safe_browsing/content", "//components/safe_browsing/content",
"//components/safe_browsing/core:features", "//components/safe_browsing/core:features",
...@@ -135,6 +149,7 @@ test("weblayer_browsertests") { ...@@ -135,6 +149,7 @@ test("weblayer_browsertests") {
"//content/test:android_test_message_pump_support", "//content/test:android_test_message_pump_support",
"//services/tracing:test_utils", "//services/tracing:test_utils",
"//testing/android/native_test:native_test_support", "//testing/android/native_test:native_test_support",
"//third_party/metrics_proto",
"//ui/android:android", "//ui/android:android",
"//ui/touch_selection:touch_selection", "//ui/touch_selection:touch_selection",
"//weblayer:locale_pak_assets", "//weblayer:locale_pak_assets",
......
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