From 092adb2451c74c4dc97e1808bfbe2352ad21da9d Mon Sep 17 00:00:00 2001 From: amohammadkhan Date: Fri, 11 Sep 2015 14:08:49 -0700 Subject: [PATCH] Support needed to measure user and service traffic in Chrome. Some of the traffic of Chrome is not coming from direct request of user and it comes from services running on it such as Sync, Suggestion, or Complete. To understand their contribution to total amount of Chrome traffic, it is necessary to record services traffic use besides the user traffic use. To be able to get more useful data, it is also necessary to record the condition in which the request is made and the response is received. Two important aspects are the connection type (Cellular or Wifi) and application state (Foreground or Background). Also it is helpful to have the distribution of exchanged message size for different directions (Upload or Download). Besides the needed support for measuring, the suggestion service code is instrumented to record its data use. This instrumentation is done as an example and if this CL lands other target services will be instrumented too. BUG=516434 Review URL: https://codereview.chromium.org/1279543002 Cr-Commit-Position: refs/heads/master@{#348490} --- chrome/browser/BUILD.gn | 1 + chrome/browser/net/DEPS | 1 + chrome/browser/net/chrome_network_delegate.cc | 9 + chrome/browser/net/chrome_network_delegate.h | 9 + .../net/chrome_network_delegate_unittest.cc | 230 +++++++++++++++++++-- chrome/chrome_browser.gypi | 1 + chrome/chrome_tests_unit.gypi | 1 + chrome/test/BUILD.gn | 3 +- components/OWNERS | 3 + components/components.gyp | 1 + components/components_tests.gyp | 6 + components/data_use_measurement.gypi | 33 +++ components/data_use_measurement/OWNERS | 2 + components/data_use_measurement/content/BUILD.gn | 32 +++ components/data_use_measurement/content/DEPS | 6 + .../content/data_use_measurement.cc | 148 +++++++++++++ .../content/data_use_measurement.h | 99 +++++++++ .../content/data_use_measurement_unittest.cc | 151 ++++++++++++++ components/data_use_measurement/core/BUILD.gn | 14 ++ components/data_use_measurement/core/DEPS | 3 + .../core/data_use_user_data.cc | 44 ++++ .../data_use_measurement/core/data_use_user_data.h | 60 ++++++ components/suggestions.gypi | 1 + components/suggestions/BUILD.gn | 1 + components/suggestions/DEPS | 1 + components/suggestions/suggestions_service.cc | 3 + tools/metrics/histograms/histograms.xml | 69 +++++++ 27 files changed, 911 insertions(+), 21 deletions(-) create mode 100644 components/data_use_measurement.gypi create mode 100644 components/data_use_measurement/OWNERS create mode 100644 components/data_use_measurement/content/BUILD.gn create mode 100644 components/data_use_measurement/content/DEPS create mode 100644 components/data_use_measurement/content/data_use_measurement.cc create mode 100644 components/data_use_measurement/content/data_use_measurement.h create mode 100644 components/data_use_measurement/content/data_use_measurement_unittest.cc create mode 100644 components/data_use_measurement/core/BUILD.gn create mode 100644 components/data_use_measurement/core/DEPS create mode 100644 components/data_use_measurement/core/data_use_user_data.cc create mode 100644 components/data_use_measurement/core/data_use_user_data.h diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn index 0a5b732dd14d..e1811023a1fd 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn @@ -273,6 +273,7 @@ source_set("browser") { "//components/autofill/content/browser", "//components/browsing_data", "//components/data_reduction_proxy/content/browser", + "//components/data_use_measurement/content", "//components/devtools_discovery", "//components/devtools_http_handler", "//components/dom_distiller/content:content_browser", diff --git a/chrome/browser/net/DEPS b/chrome/browser/net/DEPS index fc675bcd7379..7553b1f5e866 100644 --- a/chrome/browser/net/DEPS +++ b/chrome/browser/net/DEPS @@ -1,4 +1,5 @@ include_rules = [ + "+components/data_use_measurement", # For nss_context_chromeos_browsertest.cc. "+components/user_manager", ] diff --git a/chrome/browser/net/chrome_network_delegate.cc b/chrome/browser/net/chrome_network_delegate.cc index bed47f796f2a..289cd33d57aa 100644 --- a/chrome/browser/net/chrome_network_delegate.cc +++ b/chrome/browser/net/chrome_network_delegate.cc @@ -468,6 +468,10 @@ int ChromeNetworkDelegate::OnHeadersReceived( void ChromeNetworkDelegate::OnBeforeRedirect(net::URLRequest* request, const GURL& new_location) { +// Recording data use of request on redirects. +#if !defined(OS_IOS) + data_use_measurement_.ReportDataUseUMA(request); +#endif if (domain_reliability_monitor_) domain_reliability_monitor_->OnBeforeRedirect(request); extensions_delegate_->OnBeforeRedirect(request, new_location); @@ -491,6 +495,11 @@ void ChromeNetworkDelegate::OnNetworkBytesReceived( void ChromeNetworkDelegate::OnCompleted(net::URLRequest* request, bool started) { +#if !defined(OS_IOS) + // TODO(amohammadkhan): Verify that there is no double recording in data use + // of redirected requests. + data_use_measurement_.ReportDataUseUMA(request); +#endif RecordNetworkErrorHistograms(request); if (started) { // Only call in for requests that were started, to obey the precondition diff --git a/chrome/browser/net/chrome_network_delegate.h b/chrome/browser/net/chrome_network_delegate.h index e4f699075566..dfcb8435f604 100644 --- a/chrome/browser/net/chrome_network_delegate.h +++ b/chrome/browser/net/chrome_network_delegate.h @@ -18,6 +18,10 @@ #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_metrics.h" #include "net/base/network_delegate_impl.h" +#if !defined(OS_IOS) +#include "components/data_use_measurement/content/data_use_measurement.h" +#endif + class ChromeExtensionsNetworkDelegate; class PrefService; @@ -202,6 +206,11 @@ class ChromeNetworkDelegate : public net::NetworkDelegateImpl { // When true, allow access to all file:// URLs. static bool g_allow_file_access_; +// Component to measure data use. +#if !defined(OS_IOS) + data_use_measurement::DataUseMeasurement data_use_measurement_; +#endif + bool experimental_web_platform_features_enabled_; DISALLOW_COPY_AND_ASSIGN(ChromeNetworkDelegate); diff --git a/chrome/browser/net/chrome_network_delegate_unittest.cc b/chrome/browser/net/chrome_network_delegate_unittest.cc index 3ca7bbe09a07..ea7b8ca4e38d 100644 --- a/chrome/browser/net/chrome_network_delegate_unittest.cc +++ b/chrome/browser/net/chrome_network_delegate_unittest.cc @@ -9,52 +9,242 @@ #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" #include "base/prefs/pref_member.h" +#include "base/run_loop.h" +#include "base/test/histogram_tester.h" +#include "chrome/browser/browser_process.h" #include "chrome/browser/content_settings/cookie_settings_factory.h" #include "chrome/browser/net/safe_search_util.h" #include "chrome/common/pref_names.h" +#include "chrome/test/base/testing_browser_process.h" #include "chrome/test/base/testing_pref_service_syncable.h" #include "chrome/test/base/testing_profile.h" +#include "chrome/test/base/testing_profile_manager.h" #include "components/content_settings/core/browser/cookie_settings.h" #include "components/content_settings/core/common/pref_names.h" +#include "content/public/browser/resource_request_info.h" #include "content/public/common/content_switches.h" +#include "content/public/common/resource_type.h" #include "content/public/test/test_browser_thread_bundle.h" #include "net/base/request_priority.h" +#include "net/socket/socket_test_util.h" #include "net/url_request/url_request.h" #include "net/url_request/url_request_test_util.h" #include "testing/gtest/include/gtest/gtest.h" +#include "url/gurl.h" #if defined(ENABLE_EXTENSIONS) #include "chrome/browser/extensions/event_router_forwarder.h" #endif -TEST(ChromeNetworkDelegateTest, DisableFirstPartyOnlyCookiesIffFlagDisabled) { - BooleanPrefMember pref_member_; - scoped_ptr delegate; +#if !defined(OS_IOS) +#include "components/data_use_measurement/core/data_use_user_data.h" +#endif + +namespace { + +// This function requests a URL, and makes it return a known response. If +// |from_user| is true, it attaches a ResourceRequestInfo to the URLRequest, +// because requests from users have this info. If |from_user| is false, the +// request is presumed to be from a service, and the service name is set in the +// request's user data. (As an example suggestions service tag is attached). if +// |redirect| is true, it adds necessary socket data to have it follow redirect +// before getting the final response. +void RequestURL(net::URLRequestContext* context, + net::MockClientSocketFactory* socket_factory, + bool from_user, + bool redirect) { + net::MockRead redirect_mock_reads[] = { + net::MockRead("HTTP/1.1 302 Found\r\n" + "Location: http://bar.com/\r\n\r\n"), + net::MockRead(net::SYNCHRONOUS, net::OK), + }; + net::StaticSocketDataProvider redirect_socket_data_provider( + redirect_mock_reads, arraysize(redirect_mock_reads), nullptr, 0); + + if (redirect) + socket_factory->AddSocketDataProvider(&redirect_socket_data_provider); + net::MockRead response_mock_reads[] = { + net::MockRead("HTTP/1.1 200 OK\r\n\r\n"), net::MockRead("response body"), + net::MockRead(net::SYNCHRONOUS, net::OK), + }; + net::StaticSocketDataProvider response_socket_data_provider( + response_mock_reads, arraysize(response_mock_reads), nullptr, 0); + socket_factory->AddSocketDataProvider(&response_socket_data_provider); + net::TestDelegate test_delegate; + test_delegate.set_quit_on_complete(true); + scoped_ptr request(context->CreateRequest( + GURL("http://example.com"), net::DEFAULT_PRIORITY, &test_delegate)); + + if (from_user) { + content::ResourceRequestInfo::AllocateForTesting( + request.get(), content::RESOURCE_TYPE_MAIN_FRAME, nullptr, -2, -2, -2, + true, false, true, true); + } else { + request->SetUserData( + data_use_measurement::DataUseUserData::kUserDataKey, + new data_use_measurement::DataUseUserData( + data_use_measurement::DataUseUserData::SUGGESTIONS)); + } + request->Start(); + base::RunLoop().RunUntilIdle(); +} + +} // namespace +class ChromeNetworkDelegateTest : public testing::Test { + public: + ChromeNetworkDelegateTest() + : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP), + context_(new net::TestURLRequestContext(true)) { #if defined(ENABLE_EXTENSIONS) - scoped_refptr forwarder = - new extensions::EventRouterForwarder(); - delegate.reset(new ChromeNetworkDelegate(forwarder.get(), &pref_member_)); -#else - delegate.reset(new ChromeNetworkDelegate(nullptr, &pref_member_)); + forwarder_ = new extensions::EventRouterForwarder(); #endif - EXPECT_FALSE(delegate->FirstPartyOnlyCookieExperimentEnabled()); -} + } -TEST(ChromeNetworkDelegateTest, EnableFirstPartyOnlyCookiesIffFlagEnabled) { - base::CommandLine::ForCurrentProcess()->AppendSwitch( - switches::kEnableExperimentalWebPlatformFeatures); - BooleanPrefMember pref_member_; - scoped_ptr delegate; + void SetUp() override { + ChromeNetworkDelegate::InitializePrefsOnUIThread( + &enable_referrers_, nullptr, nullptr, nullptr, + profile_.GetTestingPrefService()); + profile_manager_.reset( + new TestingProfileManager(TestingBrowserProcess::GetGlobal())); + ASSERT_TRUE(profile_manager_->SetUp()); + } + + void Initialize() { + network_delegate_.reset( + new ChromeNetworkDelegate(forwarder(), &enable_referrers_)); + context_->set_client_socket_factory(&socket_factory_); + context_->set_network_delegate(network_delegate_.get()); + context_->Init(); + } + + net::TestURLRequestContext* context() { return context_.get(); } + net::NetworkDelegate* network_delegate() { return network_delegate_.get(); } + net::MockClientSocketFactory* socket_factory() { return &socket_factory_; } + extensions::EventRouterForwarder* forwarder() { #if defined(ENABLE_EXTENSIONS) - scoped_refptr forwarder = - new extensions::EventRouterForwarder(); - delegate.reset(new ChromeNetworkDelegate(forwarder.get(), &pref_member_)); + return forwarder_.get(); #else - delegate.reset(new ChromeNetworkDelegate(nullptr, &pref_member_)); + return nullptr; +#endif + } + + private: + scoped_ptr profile_manager_; + content::TestBrowserThreadBundle thread_bundle_; +#if defined(ENABLE_EXTENSIONS) + scoped_refptr forwarder_; +#endif + TestingProfile profile_; + BooleanPrefMember enable_referrers_; + scoped_ptr network_delegate_; + net::MockClientSocketFactory socket_factory_; + scoped_ptr context_; +}; + +// This function tests data use measurement for requests by services. it makes a +// query which is similar to a query of a service, so it should affect +// DataUse.TrafficSize.System.Dimensions and DataUse.MessageSize.ServiceName +// histograms. AppState and ConnectionType dimensions are always Foreground and +// NotCellular respectively. +#if !defined(OS_IOS) +TEST_F(ChromeNetworkDelegateTest, DataUseMeasurementServiceTest) { + Initialize(); + base::HistogramTester histogram_tester; + + // A query from a service without redirection. + RequestURL(context(), socket_factory(), false, false); + histogram_tester.ExpectTotalCount( + "DataUse.TrafficSize.System.Downstream.Foreground.NotCellular", 1); + histogram_tester.ExpectTotalCount( + "DataUse.TrafficSize.System.Upstream.Foreground.NotCellular", 1); + // One upload and one download message, so totalCount should be 2. + histogram_tester.ExpectTotalCount("DataUse.MessageSize.Suggestions", 2); + histogram_tester.ExpectTotalCount( + "DataUse.TrafficSize.User.Downstream.Foreground.NotCellular", 0); + histogram_tester.ExpectTotalCount( + "DataUse.TrafficSize.User.Upstream.Foreground.NotCellular", 0); +} + +// This function tests data use measurement for requests by user.The query from +// a user should affect DataUse.TrafficSize.User.Dimensions histogram. AppState +// and ConnectionType dimensions are always Foreground and NotCellular +// respectively. +TEST_F(ChromeNetworkDelegateTest, DataUseMeasurementUserTest) { + Initialize(); + base::HistogramTester histogram_tester; + + // A query from user without redirection. + RequestURL(context(), socket_factory(), true, false); + histogram_tester.ExpectTotalCount( + "DataUse.TrafficSize.User.Downstream.Foreground.NotCellular", 1); + histogram_tester.ExpectTotalCount( + "DataUse.TrafficSize.User.Upstream.Foreground.NotCellular", 1); + histogram_tester.ExpectTotalCount( + "DataUse.TrafficSize.System.Downstream.Foreground.NotCellular", 0); + histogram_tester.ExpectTotalCount( + "DataUse.TrafficSize.System.Upstream.Foreground.NotCellular", 0); + histogram_tester.ExpectTotalCount("DataUse.MessageSize.Suggestions", 0); +} + +// This function tests data use measurement for requests by services in case the +// request is redirected once. it makes a query which is similar to a query of a +// service, so it should affect DataUse.TrafficSize.System.Dimensions and +// DataUse.MessageSize.ServiceName histograms. AppState and ConnectionType +// dimensions are always Foreground and NotCellular respectively. +TEST_F(ChromeNetworkDelegateTest, DataUseMeasurementServiceTestWithRedirect) { + Initialize(); + base::HistogramTester histogram_tester; + + // A query from user with one redirection. + RequestURL(context(), socket_factory(), false, true); + histogram_tester.ExpectTotalCount( + "DataUse.TrafficSize.System.Downstream.Foreground.NotCellular", 2); + histogram_tester.ExpectTotalCount( + "DataUse.TrafficSize.System.Upstream.Foreground.NotCellular", 2); + // Two uploads and two downloads message, so totalCount should be 4. + histogram_tester.ExpectTotalCount("DataUse.MessageSize.Suggestions", 4); + histogram_tester.ExpectTotalCount( + "DataUse.TrafficSize.User.Downstream.Foreground.NotCellular", 0); + histogram_tester.ExpectTotalCount( + "DataUse.TrafficSize.User.Upstream.Foreground.NotCellular", 0); +} + +// This function tests data use measurement for requests by user in case the +// request is redirected once.The query from a user should affect +// DataUse.TrafficSize.User.Dimensions histogram. AppState and ConnectionType +// dimensions are always Foreground and NotCellular respectively. +TEST_F(ChromeNetworkDelegateTest, DataUseMeasurementUserTestWithRedirect) { + Initialize(); + base::HistogramTester histogram_tester; + + // A query from user with one redirection. + RequestURL(context(), socket_factory(), true, true); + + histogram_tester.ExpectTotalCount( + "DataUse.TrafficSize.User.Downstream.Foreground.NotCellular", 2); + histogram_tester.ExpectTotalCount( + "DataUse.TrafficSize.User.Upstream.Foreground.NotCellular", 2); + histogram_tester.ExpectTotalCount( + "DataUse.TrafficSize.System.Downstream.Foreground.NotCellular", 0); + histogram_tester.ExpectTotalCount( + "DataUse.TrafficSize.System.Upstream.Foreground.NotCellular", 0); + histogram_tester.ExpectTotalCount("DataUse.MessageSize.Suggestions", 0); +} + #endif - EXPECT_TRUE(delegate->FirstPartyOnlyCookieExperimentEnabled()); + +TEST_F(ChromeNetworkDelegateTest, DisableFirstPartyOnlyCookiesIffFlagDisabled) { + Initialize(); + EXPECT_FALSE(network_delegate()->FirstPartyOnlyCookieExperimentEnabled()); +} + +TEST_F(ChromeNetworkDelegateTest, EnableFirstPartyOnlyCookiesIffFlagEnabled) { + base::CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kEnableExperimentalWebPlatformFeatures); + Initialize(); + EXPECT_TRUE(network_delegate()->FirstPartyOnlyCookieExperimentEnabled()); } class ChromeNetworkDelegateSafeSearchTest : public testing::Test { diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index d438c298dc45..c33ec1de7387 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -3236,6 +3236,7 @@ '../components/components.gyp:browsing_data', '../components/components.gyp:certificate_reporting', '../components/components.gyp:data_reduction_proxy_content_browser', + '../components/components.gyp:data_use_measurement_content', '../components/components.gyp:devtools_discovery', '../components/components.gyp:devtools_http_handler', '../components/components.gyp:dom_distiller_content_browser', diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi index 6cf9f097d183..c7d40c3640e0 100644 --- a/chrome/chrome_tests_unit.gypi +++ b/chrome/chrome_tests_unit.gypi @@ -2213,6 +2213,7 @@ '../components/components.gyp:autofill_content_test_support', '../components/components.gyp:component_metrics_proto', '../components/components.gyp:data_reduction_proxy_test_support', + '../components/components.gyp:data_use_measurement_core', '../components/components.gyp:safe_json_test_support', '../components/components.gyp:webdata_services_test_support', '../components/components_strings.gyp:components_strings', diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn index 82bdab652454..86da6deb0a10 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn @@ -1451,8 +1451,9 @@ if (!is_android) { "//components/audio_modem:test_support", "//components/autofill/content/browser/wallet:test_support", "//components/autofill/content/renderer:test_support", - "//components/metrics/proto", "//components/data_reduction_proxy/core/browser:test_support", + "//components/data_use_measurement/core", + "//components/metrics/proto", "//components/safe_json:test_support", "//components/webdata_services:test_support", "//components/strings", diff --git a/components/OWNERS b/components/OWNERS index a312b8472e13..a92f0bd38006 100644 --- a/components/OWNERS +++ b/components/OWNERS @@ -69,6 +69,9 @@ per-file data_reduction_proxy*=bolian@chromium.org per-file data_reduction_proxy*=sclittle@chromium.org per-file data_reduction_proxy*=jeremyim@chromium.org +per-file data_use_measurement*=sclittle@chromium.org +per-file data_use_measurement*=bengr@chromium.org + per-file devtools_discovery.gyp*=dgozman@chromium.org per-file devtools_discovery.gyp*=pfeldman@chromium.org diff --git a/components/components.gyp b/components/components.gyp index abd3e5e8cc4a..b2ab95e2b68a 100644 --- a/components/components.gyp +++ b/components/components.gyp @@ -25,6 +25,7 @@ 'cronet.gypi', 'crx_file.gypi', 'data_reduction_proxy.gypi', + 'data_use_measurement.gypi', 'device_event_log.gypi', 'dom_distiller.gypi', 'domain_reliability.gypi', diff --git a/components/components_tests.gyp b/components/components_tests.gyp index 29684c1edc70..9cae71463a7b 100644 --- a/components/components_tests.gyp +++ b/components/components_tests.gyp @@ -215,6 +215,9 @@ 'cronet_unittest_sources': [ 'cronet/histogram_manager_unittest.cc', ], + 'data_use_measurement_unittest_sources': [ + 'data_use_measurement/content/data_use_measurement_unittest.cc', + ], 'enhanced_bookmarks_unittest_sources': [ 'enhanced_bookmarks/enhanced_bookmark_model_unittest.cc', 'enhanced_bookmarks/image_store_ios_unittest.mm', @@ -792,6 +795,7 @@ '<@(crash_unittest_sources)', '<@(crx_file_unittest_sources)', '<@(data_reduction_proxy_unittest_sources)', + '<@(data_use_measurement_unittest_sources)', '<@(device_event_log_unittest_sources)', '<@(dom_distiller_unittest_sources)', '<@(domain_reliability_unittest_sources)', @@ -890,6 +894,7 @@ 'components.gyp:data_reduction_proxy_core_browser', 'components.gyp:data_reduction_proxy_core_common', 'components.gyp:data_reduction_proxy_test_support', + 'components.gyp:data_use_measurement_core', 'components.gyp:device_event_log_component', 'components.gyp:dom_distiller_core', 'components.gyp:dom_distiller_protos', @@ -1039,6 +1044,7 @@ 'components.gyp:certificate_transparency', 'components.gyp:crash_test_support', 'components.gyp:data_reduction_proxy_content_browser', + 'components.gyp:data_use_measurement_content', 'components.gyp:devtools_http_handler', 'components.gyp:dom_distiller_content_browser', 'components.gyp:error_page_renderer', diff --git a/components/data_use_measurement.gypi b/components/data_use_measurement.gypi new file mode 100644 index 000000000000..7f80e4ef87ba --- /dev/null +++ b/components/data_use_measurement.gypi @@ -0,0 +1,33 @@ +# Copyright 2015 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. +{ + 'targets': [ + { + 'target_name': 'data_use_measurement_content', + 'type': 'static_library', + 'dependencies': [ + '../base/base.gyp:base', + '../content/content.gyp:content_browser', + '../net/net.gyp:net', + 'data_use_measurement_core', + ], + 'sources': [ + 'data_use_measurement/content/data_use_measurement.cc', + 'data_use_measurement/content/data_use_measurement.h', + ] + }, + { + 'target_name': 'data_use_measurement_core', + 'type': 'static_library', + 'dependencies': [ + '../base/base.gyp:base', + '../net/net.gyp:net', + ], + 'sources': [ + 'data_use_measurement/core/data_use_user_data.cc', + 'data_use_measurement/core/data_use_user_data.h', + ] + } + ] +} diff --git a/components/data_use_measurement/OWNERS b/components/data_use_measurement/OWNERS new file mode 100644 index 000000000000..f63ec14c9c26 --- /dev/null +++ b/components/data_use_measurement/OWNERS @@ -0,0 +1,2 @@ +sclittle@chromium.org +bengr@chromium.org diff --git a/components/data_use_measurement/content/BUILD.gn b/components/data_use_measurement/content/BUILD.gn new file mode 100644 index 000000000000..6c7a1496ced2 --- /dev/null +++ b/components/data_use_measurement/content/BUILD.gn @@ -0,0 +1,32 @@ +# Copyright 2015 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. + +source_set("content") { + sources = [ + "data_use_measurement.cc", + "data_use_measurement.h", + ] + deps = [ + "//base", + "//components/data_use_measurement/core", + "//content/public/browser", + "//net", + ] +} +source_set("data_use_measurement_unittests") { + sources = [ + "data_use_measurement_unittest.cc", + ] + testonly = true + deps = [ + ":content", + "//base", + "//components/data_use_measurement/core", + "//content/public/browser", + "//net", + "//net:test_support", + "//testing/gtest", + "//url", + ] +} diff --git a/components/data_use_measurement/content/DEPS b/components/data_use_measurement/content/DEPS new file mode 100644 index 000000000000..75cbca3336ee --- /dev/null +++ b/components/data_use_measurement/content/DEPS @@ -0,0 +1,6 @@ +include_rules = [ + "+content/public/browser", + "+net", + "+testing", + "+url", +] diff --git a/components/data_use_measurement/content/data_use_measurement.cc b/components/data_use_measurement/content/data_use_measurement.cc new file mode 100644 index 000000000000..18f910482f7c --- /dev/null +++ b/components/data_use_measurement/content/data_use_measurement.cc @@ -0,0 +1,148 @@ +// Copyright 2015 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/data_use_measurement/content/data_use_measurement.h" + +#include "base/metrics/histogram.h" +#include "base/metrics/sparse_histogram.h" +#include "base/strings/stringprintf.h" +#include "content/public/browser/resource_request_info.h" +#include "net/base/network_change_notifier.h" +#include "net/base/upload_data_stream.h" +#include "net/http/http_response_headers.h" +#include "net/url_request/url_request.h" + +namespace data_use_measurement { + +namespace { + +// Records the occurrence of |sample| in |name| histogram. Conventional UMA +// histograms are not used because the |name| is not static. +void RecordUMAHistogramCount(const std::string& name, int64_t sample) { + base::HistogramBase* histogram_pointer = base::Histogram::FactoryGet( + name, + 1, // Minimum sample size in bytes. + 1000000, // Maximum sample size in bytes. Should cover most of the + // requests by services. + 50, // Bucket count. + base::HistogramBase::kUmaTargetedHistogramFlag); + histogram_pointer->Add(sample); +} + +// This function increases the value of |sample| bucket in |name| sparse +// histogram by |value|. Conventional UMA histograms are not used because |name| +// is not static. +void IncreaseSparseHistogramByValue(const std::string& name, + int64_t sample, + int64_t value) { + base::HistogramBase* histogram = base::SparseHistogram::FactoryGet( + name, base::HistogramBase::kUmaTargetedHistogramFlag); + histogram->AddCount(sample, value); +} + +} // namespace + +DataUseMeasurement::DataUseMeasurement() +#if defined(OS_ANDROID) + : app_state_(base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES), + app_listener_(new base::android::ApplicationStatusListener( + base::Bind(&DataUseMeasurement::OnApplicationStateChange, + base::Unretained(this)))) +#endif +{ +} + +DataUseMeasurement::~DataUseMeasurement(){}; + +void DataUseMeasurement::ReportDataUseUMA( + const net::URLRequest* request) const { + const content::ResourceRequestInfo* info = + content::ResourceRequestInfo::ForRequest(request); + // Having |info| is the sign of a request for a web content from user. For now + // we could add a condition to check ProcessType in info is + // content::PROCESS_TYPE_RENDERER, but it won't be compatible with upcoming + // PlzNavigate architecture. So just existence of |info| is verified, and the + // current check should be compatible with upcoming changes in PlzNavigate. + bool is_user_traffic = info != nullptr; + + // Counts rely on URLRequest::GetTotalReceivedBytes() and + // URLRequest::GetTotalSentBytes(), which does not include the send path, + // network layer overhead, TLS overhead, and DNS. + // TODO(amohammadkhan): Make these measured bytes more in line with number of + // bytes in lower levels. + int64_t total_upload_bytes = request->GetTotalSentBytes(); + int64_t total_received_bytes = request->GetTotalReceivedBytes(); + + RecordUMAHistogramCount( + GetHistogramName(is_user_traffic ? "DataUse.TrafficSize.User" + : "DataUse.TrafficSize.System", + UPSTREAM), + total_upload_bytes); + RecordUMAHistogramCount( + GetHistogramName(is_user_traffic ? "DataUse.TrafficSize.User" + : "DataUse.TrafficSize.System", + DOWNSTREAM), + total_received_bytes); + + DataUseUserData* attached_service_data = reinterpret_cast( + request->GetUserData(DataUseUserData::kUserDataKey)); + + if (!is_user_traffic) { + DataUseUserData::ServiceName service_name = + attached_service_data ? attached_service_data->service_name() + : DataUseUserData::NOT_TAGGED; + ReportDataUsageServices(service_name, UPSTREAM, total_upload_bytes); + ReportDataUsageServices(service_name, DOWNSTREAM, total_received_bytes); + } +} + +#if defined(OS_ANDROID) +void DataUseMeasurement::OnApplicationStateChangeForTesting( + base::android::ApplicationState application_state) { + app_state_ = application_state; +} +#endif + +DataUseMeasurement::AppState DataUseMeasurement::CurrentAppState() const { +#if defined(OS_ANDROID) + if (app_state_ != base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES) + return BACKGROUND; +#endif + // If the OS is not Android, all the requests are considered Foreground. + return FOREGROUND; +} + +std::string DataUseMeasurement::GetHistogramName(const char* prefix, + TrafficDirection dir) const { + AppState app_state = CurrentAppState(); + bool is_conn_cellular = net::NetworkChangeNotifier::IsConnectionCellular( + net::NetworkChangeNotifier::GetConnectionType()); + return base::StringPrintf( + "%s.%s.%s.%s", prefix, dir == UPSTREAM ? "Upstream" : "Downstream", + app_state == BACKGROUND ? "Background" : "Foreground", + is_conn_cellular ? "Cellular" : "NotCellular"); +} + +#if defined(OS_ANDROID) +void DataUseMeasurement::OnApplicationStateChange( + base::android::ApplicationState application_state) { + app_state_ = application_state; +} +#endif + +void DataUseMeasurement::ReportDataUsageServices( + DataUseUserData::ServiceName service, + TrafficDirection dir, + int64_t message_size) const { + RecordUMAHistogramCount( + "DataUse.MessageSize." + DataUseUserData::GetServiceNameAsString(service), + message_size); + if (message_size > 0) { + IncreaseSparseHistogramByValue( + GetHistogramName("DataUse.MessageSize.AllServices", dir), service, + message_size); + } +} + +} // namespace data_use_measurement diff --git a/components/data_use_measurement/content/data_use_measurement.h b/components/data_use_measurement/content/data_use_measurement.h new file mode 100644 index 000000000000..a5b8f7136359 --- /dev/null +++ b/components/data_use_measurement/content/data_use_measurement.h @@ -0,0 +1,99 @@ +// Copyright 2015 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_DATA_USE_MEASUREMENT_CONTENT_DATA_USE_MEASUREMENT_H_ +#define COMPONENTS_DATA_USE_MEASUREMENT_CONTENT_DATA_USE_MEASUREMENT_H_ + +#include + +#include + +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "build/build_config.h" +#include "components/data_use_measurement/core/data_use_user_data.h" + +#if defined(OS_ANDROID) +#include "base/android/application_status_listener.h" +#endif + +namespace net { +class URLRequest; +} + +namespace data_use_measurement { + +// Records the data use of user traffic and various services in UMA histograms. +// The UMA is broken down by network technology used (Wi-Fi vs cellular). On +// Android, the UMA is further broken down by whether the application was in the +// background or foreground during the request. +// TODO(amohammadkhan): Complete the layered architecture. +// http://crbug.com/527460 +class DataUseMeasurement { + public: + DataUseMeasurement(); + ~DataUseMeasurement(); + + // Records the data use of the |request|, thus |request| must be non-null. + void ReportDataUseUMA(const net::URLRequest* request) const; + +#if defined(OS_ANDROID) + // This function should just be used for testing purposes. A change in + // application state can be simulated by calling this function. + void OnApplicationStateChangeForTesting( + base::android::ApplicationState application_state); +#endif + + private: + // Specifies that data is received or sent, respectively. + enum TrafficDirection { DOWNSTREAM, UPSTREAM }; + + // The state of the application. Only available on Android and on other + // platforms it is always FOREGROUND. + enum AppState { BACKGROUND, FOREGROUND }; + + // Returns the current application state (Foreground or Background). It always + // returns Foreground if Chrome is not running on Android. + AppState CurrentAppState() const; + + // Makes the full name of the histogram. It is made from |prefix| and suffix + // which is made based on network and application status. suffix is a string + // representing whether the data use was on the send ("Upstream") or receive + // ("Downstream") path, whether the app was in the "Foreground" or + // "Background", and whether a "Cellular" or "WiFi" network was use. For + // example, "Prefix.Upstream.Foreground.Cellular" is a possible output. + std::string GetHistogramName(const char* prefix, TrafficDirection dir) const; + +#if defined(OS_ANDROID) + // Called whenever the application transitions from foreground to background + // and vice versa. + void OnApplicationStateChange( + base::android::ApplicationState application_state); +#endif + + // A helper function used to record data use of services. It gets the size of + // exchanged message, its direction (which is upstream or downstream) and + // reports to two histogram groups. DataUse.MessageSize.ServiceName and + // DataUse.Services.{Dimensions}. In the second one, services are buckets. + void ReportDataUsageServices( + data_use_measurement::DataUseUserData::ServiceName service, + TrafficDirection dir, + int64_t message_size) const; + +#if defined(OS_ANDROID) + // Application listener store the last known state of the application in this + // field. + base::android::ApplicationState app_state_; + + // ApplicationStatusListener used to monitor whether the application is in the + // foreground or in the background. It is owned by DataUseMeasurement. + scoped_ptr app_listener_; +#endif + + DISALLOW_COPY_AND_ASSIGN(DataUseMeasurement); +}; + +} // namespace data_use_measurement + +#endif // COMPONENTS_DATA_USE_MEASUREMENT_CONTENT_DATA_USE_MEASUREMENT_H_ diff --git a/components/data_use_measurement/content/data_use_measurement_unittest.cc b/components/data_use_measurement/content/data_use_measurement_unittest.cc new file mode 100644 index 000000000000..638dadb37213 --- /dev/null +++ b/components/data_use_measurement/content/data_use_measurement_unittest.cc @@ -0,0 +1,151 @@ +// Copyright 2015 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/data_use_measurement/content/data_use_measurement.h" + +#include + +#include "base/memory/scoped_ptr.h" +#include "base/test/histogram_tester.h" +#include "content/public/browser/resource_request_info.h" +#include "net/base/network_change_notifier.h" +#include "net/base/request_priority.h" +#include "net/socket/socket_test_util.h" +#include "net/url_request/url_request.h" +#include "net/url_request/url_request_test_util.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "url/gurl.h" + +#if defined(OS_ANDROID) +#include "base/android/application_status_listener.h" +#endif + +namespace data_use_measurement { + +class DataUseMeasurementTest : public testing::Test { + public: + DataUseMeasurementTest() { + // During the test it is expected to not have cellular connection. + DCHECK(!net::NetworkChangeNotifier::IsConnectionCellular( + net::NetworkChangeNotifier::GetConnectionType())); + } + + // This function makes a user request and confirms that its effect is + // reflected in proper histograms. + void TestForAUserRequest(const std::string& target_dimension) { + net::TestDelegate test_delegate; + InitializeContext(); + base::HistogramTester histogram_tester; + net::MockRead reads[] = {net::MockRead("HTTP/1.1 200 OK\r\n" + "Content-Length: 12\r\n\r\n"), + net::MockRead("Test Content")}; + net::StaticSocketDataProvider socket_data(reads, arraysize(reads), nullptr, + 0); + socket_factory_->AddSocketDataProvider(&socket_data); + + scoped_ptr request(context_->CreateRequest( + GURL("http://foo.com"), net::DEFAULT_PRIORITY, &test_delegate)); + request->SetUserData( + data_use_measurement::DataUseUserData::kUserDataKey, + new data_use_measurement::DataUseUserData( + data_use_measurement::DataUseUserData::SUGGESTIONS)); + request->Start(); + loop_.RunUntilIdle(); + + data_use_measurement_.ReportDataUseUMA(request.get()); + histogram_tester.ExpectTotalCount("DataUse.TrafficSize.System.Downstream." + + target_dimension + kConnectionType, + 1); + histogram_tester.ExpectTotalCount("DataUse.TrafficSize.System.Upstream." + + target_dimension + kConnectionType, + 1); + // One upload and one download message, so total count should be 2. + histogram_tester.ExpectTotalCount("DataUse.MessageSize.Suggestions", 2); + } + + // This function makes a service request and confirms that its effect is + // reflected in proper histograms. + void TestForAServiceRequest(const std::string& target_dimension) { + net::TestDelegate test_delegate; + InitializeContext(); + base::HistogramTester histogram_tester; + + net::MockRead reads[] = {net::MockRead("HTTP/1.1 200 OK\r\n" + "Content-Length: 12\r\n\r\n"), + net::MockRead("Test Content")}; + net::StaticSocketDataProvider socket_data(reads, arraysize(reads), nullptr, + 0); + socket_factory_->AddSocketDataProvider(&socket_data); + + scoped_ptr request(context_->CreateRequest( + GURL("http://foo.com"), net::DEFAULT_PRIORITY, &test_delegate)); + content::ResourceRequestInfo::AllocateForTesting( + request.get(), content::RESOURCE_TYPE_MAIN_FRAME, nullptr, -2, -2, -2, + true, false, true, true); + request->Start(); + loop_.RunUntilIdle(); + + data_use_measurement_.ReportDataUseUMA(request.get()); + histogram_tester.ExpectTotalCount("DataUse.TrafficSize.User.Downstream." + + target_dimension + kConnectionType, + 1); + histogram_tester.ExpectTotalCount("DataUse.TrafficSize.User.Upstream." + + target_dimension + kConnectionType, + 1); + histogram_tester.ExpectTotalCount( + "DataUse.MessageSize.AllServices.Upstream." + target_dimension + + kConnectionType, + 0); + histogram_tester.ExpectTotalCount( + "DataUse.MessageSize.AllServices.Downstream." + target_dimension + + kConnectionType, + 0); + } + + DataUseMeasurement* data_use_measurement() { return &data_use_measurement_; } + + private: + void InitializeContext() { + context_.reset(new net::TestURLRequestContext(true)); + socket_factory_.reset(new net::MockClientSocketFactory()); + context_->set_client_socket_factory(socket_factory_.get()); + context_->Init(); + } + + base::MessageLoopForIO loop_; + DataUseMeasurement data_use_measurement_; + scoped_ptr socket_factory_; + scoped_ptr context_; + const std::string kConnectionType = "NotCellular"; + + DISALLOW_COPY_AND_ASSIGN(DataUseMeasurementTest); +}; + +// This test function tests recording of data use information in UMA histogram +// when packet is originated from user or services when the app is in the +// foreground or the OS is not Android. +// TODO(amohammadkhan): Add tests for Cellular/non-cellular connection types +// when support for testing is provided in its class. +TEST_F(DataUseMeasurementTest, UserNotUserTest) { +#if defined(OS_ANDROID) + data_use_measurement()->OnApplicationStateChangeForTesting( + base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES); +#endif + TestForAServiceRequest("Foreground."); + TestForAUserRequest("Foreground."); +} + +#if defined(OS_ANDROID) +// This test function tests recording of data use information in UMA histogram +// when packet is originated from user or services when the app is in the +// background and OS is Android. +TEST_F(DataUseMeasurementTest, ApplicationStateTest) { + data_use_measurement()->OnApplicationStateChangeForTesting( + base::android::APPLICATION_STATE_HAS_STOPPED_ACTIVITIES); + TestForAServiceRequest("Background."); + TestForAUserRequest("Background."); +} +#endif + +} // namespace data_use_measurement diff --git a/components/data_use_measurement/core/BUILD.gn b/components/data_use_measurement/core/BUILD.gn new file mode 100644 index 000000000000..c9de08dac008 --- /dev/null +++ b/components/data_use_measurement/core/BUILD.gn @@ -0,0 +1,14 @@ +# Copyright 2015 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. + +source_set("core") { + sources = [ + "data_use_user_data.cc", + "data_use_user_data.h", + ] + deps = [ + "//base", + "//net", + ] +} diff --git a/components/data_use_measurement/core/DEPS b/components/data_use_measurement/core/DEPS new file mode 100644 index 000000000000..8fa9d48d882c --- /dev/null +++ b/components/data_use_measurement/core/DEPS @@ -0,0 +1,3 @@ +include_rules = [ + "+net", +] diff --git a/components/data_use_measurement/core/data_use_user_data.cc b/components/data_use_measurement/core/data_use_user_data.cc new file mode 100644 index 000000000000..d5870b580610 --- /dev/null +++ b/components/data_use_measurement/core/data_use_user_data.cc @@ -0,0 +1,44 @@ +// Copyright 2015 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/data_use_measurement/core/data_use_user_data.h" + +#include "net/url_request/url_fetcher.h" + +namespace data_use_measurement { + +DataUseUserData::DataUseUserData(ServiceName service_name) + : service_name_(service_name) {} + +DataUseUserData::~DataUseUserData() {} + +// static +const void* DataUseUserData::kUserDataKey = + static_cast(&DataUseUserData::kUserDataKey); + +// static +base::SupportsUserData::Data* DataUseUserData::Create( + ServiceName service_name) { + return new DataUseUserData(service_name); +} + +// static +std::string DataUseUserData::GetServiceNameAsString(ServiceName service_name) { + switch (service_name) { + case SUGGESTIONS: + return "Suggestions"; + case NOT_TAGGED: + return "NotTagged"; + } + return "INVALID"; +} + +// static +void DataUseUserData::AttachToFetcher(net::URLFetcher* fetcher, + ServiceName service_name) { + fetcher->SetURLRequestUserData(kUserDataKey, + base::Bind(&Create, service_name)); +} + +} // namespace data_use_measurement diff --git a/components/data_use_measurement/core/data_use_user_data.h b/components/data_use_measurement/core/data_use_user_data.h new file mode 100644 index 000000000000..55bdd1f35114 --- /dev/null +++ b/components/data_use_measurement/core/data_use_user_data.h @@ -0,0 +1,60 @@ +// Copyright 2015 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_DATA_USE_MEASUREMENT_CORE_DATA_USE_USER_DATA_H_ +#define COMPONENTS_DATA_USE_MEASUREMENT_CORE_DATA_USE_USER_DATA_H_ + +#include + +#include "base/macros.h" +#include "base/supports_user_data.h" + +namespace net { +class URLFetcher; +} + +namespace data_use_measurement { + +// Used to annotate URLRequests with the service name if the URLRequest is used +// by a service. +class DataUseUserData : public base::SupportsUserData::Data { + public: + // This enum should be synced with DataUseServices enum in histograms.xml. + // Please keep them synced after any updates. Also add service name to + // GetServiceNameAsString function. + enum ServiceName { + NOT_TAGGED, + SUGGESTIONS, + }; + + explicit DataUseUserData(ServiceName service_name); + ~DataUseUserData() override; + + // Helper function to create DataUseUserData. The caller takes the ownership + // of the returned object. + static base::SupportsUserData::Data* Create( + DataUseUserData::ServiceName service); + + // Return the service name of the ServiceName enum. + static std::string GetServiceNameAsString(ServiceName service); + + // Services should use this function to attach their |service_name| to the + // URLFethcer serving them. + static void AttachToFetcher(net::URLFetcher* fetcher, + ServiceName service_name); + + ServiceName service_name() const { return service_name_; } + + // The key for retrieving back this type of user data. + static const void* kUserDataKey; + + private: + const ServiceName service_name_; + + DISALLOW_COPY_AND_ASSIGN(DataUseUserData); +}; + +} // data_use_measurement namespace + +#endif // COMPONENTS_DATA_USE_MEASUREMENT_CORE_DATA_USE_USER_DATA_H_ diff --git a/components/suggestions.gypi b/components/suggestions.gypi index 2f91f39d4a58..7be7e725e757 100644 --- a/components/suggestions.gypi +++ b/components/suggestions.gypi @@ -16,6 +16,7 @@ '../net/net.gyp:net', '../ui/gfx/gfx.gyp:gfx', '../url/url.gyp:url_lib', + 'components.gyp:data_use_measurement_core', 'components.gyp:keyed_service_core', 'components.gyp:pref_registry', 'components.gyp:variations', diff --git a/components/suggestions/BUILD.gn b/components/suggestions/BUILD.gn index 389a19bb10e8..28ff8228ca42 100644 --- a/components/suggestions/BUILD.gn +++ b/components/suggestions/BUILD.gn @@ -29,6 +29,7 @@ source_set("suggestions") { "//url", ] deps = [ + "//components/data_use_measurement/core", "//components/keyed_service/core", "//components/pref_registry", "//components/variations", diff --git a/components/suggestions/DEPS b/components/suggestions/DEPS index cf3bb097bab7..d8083d9aedbd 100644 --- a/components/suggestions/DEPS +++ b/components/suggestions/DEPS @@ -1,4 +1,5 @@ include_rules = [ + "+components/data_use_measurement/core", "+components/keyed_service/core", "+components/leveldb_proto", "+components/pref_registry", diff --git a/components/suggestions/suggestions_service.cc b/components/suggestions/suggestions_service.cc index cf12bfe4f09d..b46a83be3bf9 100644 --- a/components/suggestions/suggestions_service.cc +++ b/components/suggestions/suggestions_service.cc @@ -16,6 +16,7 @@ #include "base/strings/stringprintf.h" #include "base/thread_task_runner_handle.h" #include "base/time/time.h" +#include "components/data_use_measurement/core/data_use_user_data.h" #include "components/pref_registry/pref_registry_syncable.h" #include "components/suggestions/blacklist_store.h" #include "components/suggestions/suggestions_store.h" @@ -263,6 +264,8 @@ scoped_ptr SuggestionsService::CreateSuggestionsRequest( const GURL& url) { scoped_ptr request = net::URLFetcher::Create(0, url, net::URLFetcher::GET, this); + data_use_measurement::DataUseUserData::AttachToFetcher( + request.get(), data_use_measurement::DataUseUserData::SUGGESTIONS); request->SetLoadFlags(net::LOAD_DISABLE_CACHE); request->SetRequestContext(url_request_context_); // Add Chrome experiment state to the request headers. diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index df5d5ba281bd..d847369e9a1a 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml @@ -5935,6 +5935,30 @@ http://cs/file:chrome/histograms.xml - but prefer this file for new entries. + + amohammadkhan@chromium.org + bengr@chromium.org + + The request and reponse size of the messages exchanged by a service. It is + logged when the URLReqeust of a service is completed. The service name is + added as a suffix to this histogram name. + + + + + amohammadkhan@chromium.org + bengr@chromium.org + + The request and reponse size of the messages exchanged by all the services. + Whenever a URLRequest of a service is completed, the number of exchanged + bytes is logged in this histogram. The buckets in this histogram are + services, so it makes it possible to compare the use of different services + in different conditions. Different conditions are added as suffixes to this + histogram. If the OS is not Android all the requests are considered + foreground. + + + amohammadkhan@chromium.org bengr@chromium.org @@ -5983,6 +6007,26 @@ http://cs/file:chrome/histograms.xml - but prefer this file for new entries. + + amohammadkhan@chromium.org + bengr@chromium.org + + The total data use of Chrome's services. There is no ResourceRequestInfo + attached to these requests. If the OS is not Android all the requests are + considered foreground. + + + + + amohammadkhan@chromium.org + bengr@chromium.org + + The total amount of data use of Chrome for user traffic. This traffic has + content::ResourceRequestInfo attached to its request. If the OS is not + Android all the requests are considered foreground. + + + gab@chromium.org @@ -55632,6 +55676,11 @@ http://cs/file:chrome/histograms.xml - but prefer this file for new entries. label="Arrived at settings menu by another path: entered on, exited on"/> + + + + + @@ -75272,6 +75321,26 @@ To add a new entry, add it with any value and run test to compute valid value. name="DataReductionProxy.RequestCompletionErrorCodes.MainFrame"/> + + + + + + + + + + + + + + + + + + + + -- 2.11.4.GIT