[network service] Add a NetworkChangeManager interface
This CL adds an interface to allow consumers to subscribe/unsubscribe
to network change events.
- mojom::NetworkChangeManager
Listens to net::NetworkChangeNotifier and propagate notifications to
mojom::NetworkChangeManagerClient.
NetworkService will have a pointer to the impl of this interface.
- mojom::NetworkChangeManagerClient
The implementation of this interface(content::NetworkConnectionTracker)
receives notifications from mojom::NetworkChangeManager, and then sends
those down to its observers.
Design doc:
https://docs.google.com/document/d/1kBp_vTIH-1Jx4M9DN7mnqRRyUmeHe3BZ1NqTbsJ4_S8/edit
Bug: 754709
Change-Id: Ia98dc41da9bb618fd00adfd6412d70d839cd13bb
Reviewed-on: https://chromium-review.googlesource.com/644352
Commit-Queue: Helen Li <xunjieli@chromium.org>
Reviewed-by: John Abd-El-Malek <jam@chromium.org>
Reviewed-by: Matt Menke <mmenke@chromium.org>
Reviewed-by: Paul Jensen <pauljensen@chromium.org>
Reviewed-by: Tom Sepez <tsepez@chromium.org>
Cr-Commit-Position: refs/heads/master@{#510117}
diff --git a/content/network/BUILD.gn b/content/network/BUILD.gn
index a3e60b9..e01ab8b 100644
--- a/content/network/BUILD.gn
+++ b/content/network/BUILD.gn
@@ -39,6 +39,8 @@
"cookie_manager_impl.h",
"http_server_properties_pref_delegate.cc",
"http_server_properties_pref_delegate.h",
+ "network_change_manager_impl.cc",
+ "network_change_manager_impl.h",
"network_context.cc",
"network_context.h",
"network_service_impl.cc",
diff --git a/content/network/DEPS b/content/network/DEPS
index 7241411..c460224c 100644
--- a/content/network/DEPS
+++ b/content/network/DEPS
@@ -9,6 +9,7 @@
"+content/public/common/appcache_info.h",
"+content/public/common/content_client.h",
"+content/public/common/content_switches.h",
+ "+content/public/common/network_change_manager.mojom.h",
"+content/public/common/network_service.mojom.h",
"+content/public/common/referrer.h",
"+content/public/common/resource_request.h",
diff --git a/content/network/network_change_manager_impl.cc b/content/network/network_change_manager_impl.cc
new file mode 100644
index 0000000..0b431a5
--- /dev/null
+++ b/content/network/network_change_manager_impl.cc
@@ -0,0 +1,67 @@
+// 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.
+
+#include "content/network/network_change_manager_impl.h"
+
+#include <algorithm>
+#include <utility>
+
+#include "base/logging.h"
+#include "net/base/network_change_notifier.h"
+
+namespace content {
+
+NetworkChangeManagerImpl::NetworkChangeManagerImpl(
+ std::unique_ptr<net::NetworkChangeNotifier> network_change_notifier)
+ : network_change_notifier_(std::move(network_change_notifier)) {
+ net::NetworkChangeNotifier::AddNetworkChangeObserver(this);
+ connection_type_ =
+ mojom::ConnectionType(net::NetworkChangeNotifier::GetConnectionType());
+}
+
+NetworkChangeManagerImpl::~NetworkChangeManagerImpl() {
+ net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
+}
+
+void NetworkChangeManagerImpl::AddRequest(
+ mojom::NetworkChangeManagerRequest request) {
+ bindings_.AddBinding(this, std::move(request));
+}
+
+void NetworkChangeManagerImpl::RequestNotifications(
+ mojom::NetworkChangeManagerClientPtr client_ptr) {
+ client_ptr.set_connection_error_handler(
+ base::Bind(&NetworkChangeManagerImpl::NotificationPipeBroken,
+ // base::Unretained is safe as destruction of the
+ // NetworkChangeManagerImpl will also destroy the
+ // |clients_| list (which this object will be
+ // inserted into, below), which will destroy the
+ // client_ptr, rendering this callback moot.
+ base::Unretained(this), base::Unretained(client_ptr.get())));
+ client_ptr->OnInitialConnectionType(connection_type_);
+ clients_.push_back(std::move(client_ptr));
+}
+
+size_t NetworkChangeManagerImpl::GetNumClientsForTesting() const {
+ return clients_.size();
+}
+
+void NetworkChangeManagerImpl::NotificationPipeBroken(
+ mojom::NetworkChangeManagerClient* client) {
+ clients_.erase(
+ std::find_if(clients_.begin(), clients_.end(),
+ [client](mojom::NetworkChangeManagerClientPtr& ptr) {
+ return ptr.get() == client;
+ }));
+}
+
+void NetworkChangeManagerImpl::OnNetworkChanged(
+ net::NetworkChangeNotifier::ConnectionType type) {
+ connection_type_ = mojom::ConnectionType(type);
+ for (const auto& client : clients_) {
+ client->OnNetworkChanged(connection_type_);
+ }
+}
+
+} // namespace content
diff --git a/content/network/network_change_manager_impl.h b/content/network/network_change_manager_impl.h
new file mode 100644
index 0000000..139ce16
--- /dev/null
+++ b/content/network/network_change_manager_impl.h
@@ -0,0 +1,64 @@
+// 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 CONTENT_NETWORK_NETWORK_CHANGE_MANAGER_IMPL_H_
+#define CONTENT_NETWORK_NETWORK_CHANGE_MANAGER_IMPL_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "build/build_config.h"
+#include "content/common/content_export.h"
+#include "content/public/common/network_change_manager.mojom.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+#include "net/base/network_change_notifier.h"
+
+namespace content {
+
+// Implementation of mojom::NetworkChangeManager. All accesses to this class are
+// done through mojo on the main thread. This registers itself to receive
+// broadcasts from net::NetworkChangeNotifier and rebroadcasts the notifications
+// to mojom::NetworkChangeManagerClients through mojo pipes.
+class CONTENT_EXPORT NetworkChangeManagerImpl
+ : public mojom::NetworkChangeManager,
+ public net::NetworkChangeNotifier::NetworkChangeObserver {
+ public:
+ // If |network_change_notifier| is not null, |this| will take ownership of it.
+ // Otherwise, the global net::NetworkChangeNotifier will be used.
+ explicit NetworkChangeManagerImpl(
+ std::unique_ptr<net::NetworkChangeNotifier> network_change_notifier);
+
+ ~NetworkChangeManagerImpl() override;
+
+ // Binds a NetworkChangeManager request to this object. Mojo messages
+ // coming through the associated pipe will be served by this object.
+ void AddRequest(mojom::NetworkChangeManagerRequest request);
+
+ // mojom::NetworkChangeManager implementation:
+ void RequestNotifications(
+ mojom::NetworkChangeManagerClientPtr client_ptr) override;
+
+ size_t GetNumClientsForTesting() const;
+
+ private:
+ // Handles connection errors on notification pipes.
+ void NotificationPipeBroken(mojom::NetworkChangeManagerClient* client);
+
+ // net::NetworkChangeNotifier::NetworkChangeObserver implementation:
+ void OnNetworkChanged(
+ net::NetworkChangeNotifier::ConnectionType type) override;
+
+ std::unique_ptr<net::NetworkChangeNotifier> network_change_notifier_;
+ mojo::BindingSet<mojom::NetworkChangeManager> bindings_;
+ std::vector<mojom::NetworkChangeManagerClientPtr> clients_;
+ mojom::ConnectionType connection_type_;
+
+ DISALLOW_COPY_AND_ASSIGN(NetworkChangeManagerImpl);
+};
+
+} // namespace content
+
+#endif // CONTENT_NETWORK_NETWORK_CHANGE_MANAGER_IMPL_H_
diff --git a/content/network/network_change_manager_impl_unittest.cc b/content/network/network_change_manager_impl_unittest.cc
new file mode 100644
index 0000000..36681555
--- /dev/null
+++ b/content/network/network_change_manager_impl_unittest.cc
@@ -0,0 +1,220 @@
+// 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.
+
+#include "content/network/network_change_manager_impl.h"
+
+#include <algorithm>
+#include <utility>
+
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/test/scoped_task_environment.h"
+#include "content/public/common/network_change_manager.mojom.h"
+#include "net/base/network_change_notifier.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace content {
+
+namespace {
+
+// Type of notification expected in test.
+enum NotificationType {
+ // Default value.
+ NONE,
+ // OnInitialConnectionType() notification.
+ INITIAL,
+ // OnNetworkChanged() notification.
+ NETWORK_CHANGED,
+};
+
+class TestNetworkChangeManagerClient
+ : public mojom::NetworkChangeManagerClient {
+ public:
+ explicit TestNetworkChangeManagerClient(
+ content::NetworkChangeManagerImpl* network_change_manager)
+ : num_network_changed_(0),
+ run_loop_(std::make_unique<base::RunLoop>()),
+ notification_type_to_wait_(NONE),
+ connection_type_(mojom::ConnectionType::CONNECTION_UNKNOWN),
+ binding_(this) {
+ mojom::NetworkChangeManagerPtr manager_ptr;
+ mojom::NetworkChangeManagerRequest request(mojo::MakeRequest(&manager_ptr));
+ network_change_manager->AddRequest(std::move(request));
+
+ mojom::NetworkChangeManagerClientPtr client_ptr;
+ mojom::NetworkChangeManagerClientRequest client_request(
+ mojo::MakeRequest(&client_ptr));
+ binding_.Bind(std::move(client_request));
+ manager_ptr->RequestNotifications(std::move(client_ptr));
+ }
+
+ ~TestNetworkChangeManagerClient() override {}
+
+ // NetworkChangeManagerClient implementation:
+ void OnInitialConnectionType(mojom::ConnectionType type) override {
+ connection_type_ = type;
+ if (notification_type_to_wait_ == INITIAL)
+ run_loop_->Quit();
+ }
+
+ void OnNetworkChanged(mojom::ConnectionType type) override {
+ num_network_changed_++;
+ connection_type_ = type;
+ if (notification_type_to_wait_ == NETWORK_CHANGED)
+ run_loop_->Quit();
+ }
+
+ // Returns the number of OnNetworkChanged() notifications.
+ size_t num_network_changed() const { return num_network_changed_; }
+
+ void WaitForNotification(NotificationType notification_type) {
+ notification_type_to_wait_ = notification_type;
+ run_loop_->Run();
+ run_loop_.reset(new base::RunLoop());
+ }
+
+ mojom::ConnectionType connection_type() const { return connection_type_; }
+
+ private:
+ size_t num_network_changed_;
+ std::unique_ptr<base::RunLoop> run_loop_;
+ NotificationType notification_type_to_wait_;
+ mojom::ConnectionType connection_type_;
+ mojo::Binding<mojom::NetworkChangeManagerClient> binding_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestNetworkChangeManagerClient);
+};
+
+} // namespace
+
+class NetworkChangeManagerImplTest : public testing::Test {
+ public:
+ NetworkChangeManagerImplTest()
+ : network_change_manager_(new NetworkChangeManagerImpl(
+ base::WrapUnique(net::NetworkChangeNotifier::CreateMock()))) {
+ network_change_manager_client_ =
+ std::make_unique<TestNetworkChangeManagerClient>(
+ network_change_manager_.get());
+ }
+
+ ~NetworkChangeManagerImplTest() override {}
+
+ TestNetworkChangeManagerClient* network_change_manager_client() {
+ return network_change_manager_client_.get();
+ }
+
+ NetworkChangeManagerImpl* network_change_manager() const {
+ return network_change_manager_.get();
+ }
+
+ void SimulateNetworkChange(net::NetworkChangeNotifier::ConnectionType type) {
+ net::NetworkChangeNotifier::NotifyObserversOfNetworkChangeForTests(type);
+ }
+
+ private:
+ base::test::ScopedTaskEnvironment scoped_task_environment_;
+ std::unique_ptr<NetworkChangeManagerImpl> network_change_manager_;
+ std::unique_ptr<TestNetworkChangeManagerClient>
+ network_change_manager_client_;
+
+ DISALLOW_COPY_AND_ASSIGN(NetworkChangeManagerImplTest);
+};
+
+TEST_F(NetworkChangeManagerImplTest, ClientNotified) {
+ // Simulate a new network change.
+ SimulateNetworkChange(net::NetworkChangeNotifier::CONNECTION_3G);
+ network_change_manager_client()->WaitForNotification(NETWORK_CHANGED);
+ EXPECT_EQ(mojom::ConnectionType::CONNECTION_3G,
+ network_change_manager_client()->connection_type());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(1u, network_change_manager_client()->num_network_changed());
+}
+
+TEST_F(NetworkChangeManagerImplTest, OneClientPipeBroken) {
+ auto network_change_manager_client2 =
+ std::make_unique<TestNetworkChangeManagerClient>(
+ network_change_manager());
+
+ // Simulate a network change.
+ SimulateNetworkChange(net::NetworkChangeNotifier::CONNECTION_WIFI);
+
+ network_change_manager_client()->WaitForNotification(NETWORK_CHANGED);
+ network_change_manager_client2->WaitForNotification(NETWORK_CHANGED);
+ EXPECT_EQ(mojom::ConnectionType::CONNECTION_WIFI,
+ network_change_manager_client2->connection_type());
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(2u, network_change_manager()->GetNumClientsForTesting());
+
+ EXPECT_EQ(1u, network_change_manager_client()->num_network_changed());
+ EXPECT_EQ(1u, network_change_manager_client2->num_network_changed());
+ network_change_manager_client2.reset();
+
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(1u, network_change_manager()->GetNumClientsForTesting());
+
+ // Simulate a second network change, and the remaining client should be
+ // notified.
+ SimulateNetworkChange(net::NetworkChangeNotifier::CONNECTION_2G);
+
+ network_change_manager_client()->WaitForNotification(NETWORK_CHANGED);
+ EXPECT_EQ(mojom::ConnectionType::CONNECTION_2G,
+ network_change_manager_client()->connection_type());
+ EXPECT_EQ(2u, network_change_manager_client()->num_network_changed());
+}
+
+TEST_F(NetworkChangeManagerImplTest, NewClientReceivesCurrentType) {
+ // Simulate a network change.
+ SimulateNetworkChange(net::NetworkChangeNotifier::CONNECTION_BLUETOOTH);
+
+ network_change_manager_client()->WaitForNotification(NETWORK_CHANGED);
+ EXPECT_EQ(mojom::ConnectionType::CONNECTION_BLUETOOTH,
+ network_change_manager_client()->connection_type());
+ base::RunLoop().RunUntilIdle();
+
+ // Register a new client after the network change and it should receive the
+ // up-to-date connection type.
+ TestNetworkChangeManagerClient network_change_manager_client2(
+ network_change_manager());
+ network_change_manager_client2.WaitForNotification(INITIAL);
+ EXPECT_EQ(mojom::ConnectionType::CONNECTION_BLUETOOTH,
+ network_change_manager_client2.connection_type());
+}
+
+TEST(NetworkChangeConnectionTypeTest, ConnectionTypeEnumMatch) {
+ for (int typeInt = net::NetworkChangeNotifier::CONNECTION_UNKNOWN;
+ typeInt != net::NetworkChangeNotifier::CONNECTION_LAST; typeInt++) {
+ mojom::ConnectionType mojoType = mojom::ConnectionType(typeInt);
+ switch (typeInt) {
+ case net::NetworkChangeNotifier::CONNECTION_UNKNOWN:
+ EXPECT_EQ(mojom::ConnectionType::CONNECTION_UNKNOWN, mojoType);
+ break;
+ case net::NetworkChangeNotifier::CONNECTION_ETHERNET:
+ EXPECT_EQ(mojom::ConnectionType::CONNECTION_ETHERNET, mojoType);
+ break;
+ case net::NetworkChangeNotifier::CONNECTION_WIFI:
+ EXPECT_EQ(mojom::ConnectionType::CONNECTION_WIFI, mojoType);
+ break;
+ case net::NetworkChangeNotifier::CONNECTION_2G:
+ EXPECT_EQ(mojom::ConnectionType::CONNECTION_2G, mojoType);
+ break;
+ case net::NetworkChangeNotifier::CONNECTION_3G:
+ EXPECT_EQ(mojom::ConnectionType::CONNECTION_3G, mojoType);
+ break;
+ case net::NetworkChangeNotifier::CONNECTION_4G:
+ EXPECT_EQ(mojom::ConnectionType::CONNECTION_4G, mojoType);
+ break;
+ case net::NetworkChangeNotifier::CONNECTION_NONE:
+ EXPECT_EQ(mojom::ConnectionType::CONNECTION_NONE, mojoType);
+ break;
+ case net::NetworkChangeNotifier::CONNECTION_BLUETOOTH:
+ EXPECT_EQ(mojom::ConnectionType::CONNECTION_BLUETOOTH, mojoType);
+ EXPECT_EQ(mojom::ConnectionType::CONNECTION_LAST, mojoType);
+ break;
+ }
+ }
+}
+
+} // namespace content
diff --git a/content/network/network_service_impl.cc b/content/network/network_service_impl.cc
index 021b6bb1..3198d3f 100644
--- a/content/network/network_service_impl.cc
+++ b/content/network/network_service_impl.cc
@@ -4,20 +4,26 @@
#include "content/network/network_service_impl.h"
+#include <utility>
+
#include "base/command_line.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/values.h"
-#include "build/build_config.h"
#include "content/network/network_context.h"
#include "content/public/common/content_switches.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
#include "net/base/logging_network_change_observer.h"
+#include "net/base/network_change_notifier.h"
#include "net/log/file_net_log_observer.h"
#include "net/log/net_log.h"
#include "net/log/net_log_util.h"
#include "net/url_request/url_request_context_builder.h"
+#if defined(OS_ANDROID)
+#include "net/android/network_change_notifier_factory_android.h"
+#endif
+
namespace content {
std::unique_ptr<NetworkService> NetworkService::Create(net::NetLog* net_log) {
@@ -62,11 +68,33 @@
: registry_(std::move(registry)), binding_(this) {
// |registry_| is nullptr when an in-process NetworkService is
// created directly. The latter is done in concert with using
- // CreateNetworkContextWithBuilder to ease the transition to using the network
- // service.
+ // CreateNetworkContextWithBuilder to ease the transition to using the
+ // network service.
if (registry_) {
registry_->AddInterface<mojom::NetworkService>(
base::Bind(&NetworkServiceImpl::Create, base::Unretained(this)));
+
+ // Set up net::NetworkChangeNotifier to watch for network change events.
+ std::unique_ptr<net::NetworkChangeNotifier> network_change_notifier;
+#if defined(OS_ANDROID)
+ network_change_notifier_factory_ =
+ std::make_unique<net::NetworkChangeNotifierFactoryAndroid>();
+ network_change_notifier =
+ base::WrapUnique(network_change_notifier_factory_->CreateInstance());
+#elif defined(OS_CHROMEOS) || defined(OS_IOS)
+ // ChromeOS has its own implementation of NetworkChangeNotifier that lives
+ // outside of //net and iOS doesn't embed //content.
+ // TODO(xunjieli): Figure out what to do for these two platforms.
+ NOTIMPLEMENTED();
+#else
+ network_change_notifier =
+ base::WrapUnique(net::NetworkChangeNotifier::Create());
+#endif
+ network_change_manager_ = std::make_unique<NetworkChangeManagerImpl>(
+ std::move(network_change_notifier));
+ } else {
+ network_change_manager_ =
+ std::make_unique<NetworkChangeManagerImpl>(nullptr);
}
if (net_log) {
@@ -161,6 +189,11 @@
return net_log_;
}
+void NetworkServiceImpl::GetNetworkChangeManager(
+ mojom::NetworkChangeManagerRequest request) {
+ network_change_manager_->AddRequest(std::move(request));
+}
+
void NetworkServiceImpl::OnBindInterface(
const service_manager::BindSourceInfo& source_info,
const std::string& interface_name,
diff --git a/content/network/network_service_impl.h b/content/network/network_service_impl.h
index 746a2f5..39b5690 100644
--- a/content/network/network_service_impl.h
+++ b/content/network/network_service_impl.h
@@ -6,9 +6,13 @@
#define CONTENT_NETWORK_NETWORK_SERVICE_IMPL_H_
#include <memory>
+#include <set>
+#include <string>
#include "base/macros.h"
+#include "build/build_config.h"
#include "content/common/content_export.h"
+#include "content/network/network_change_manager_impl.h"
#include "content/public/common/network_service.mojom.h"
#include "content/public/network/network_service.h"
#include "mojo/public/cpp/bindings/binding.h"
@@ -20,6 +24,9 @@
class LoggingNetworkChangeObserver;
class URLRequestContext;
class URLRequestContextBuilder;
+#if defined(OS_ANDROID)
+class NetworkChangeNotifierFactoryAndroid;
+#endif
} // namespace net
namespace content {
@@ -57,6 +64,8 @@
mojom::NetworkContextParamsPtr params) override;
void DisableQuic() override;
void SetRawHeadersAccess(uint32_t process_id, bool allow) override;
+ void GetNetworkChangeManager(
+ mojom::NetworkChangeManagerRequest request) override;
bool quic_disabled() const { return quic_disabled_; }
bool HasRawHeadersAccess(uint32_t process_id) const;
@@ -85,6 +94,12 @@
// destroyed before them.
std::unique_ptr<net::LoggingNetworkChangeObserver> network_change_observer_;
+#if defined(OS_ANDROID)
+ std::unique_ptr<net::NetworkChangeNotifierFactoryAndroid>
+ network_change_notifier_factory_;
+#endif
+ std::unique_ptr<NetworkChangeManagerImpl> network_change_manager_;
+
std::unique_ptr<service_manager::BinderRegistry> registry_;
mojo::Binding<mojom::NetworkService> binding_;
diff --git a/content/network/network_service_unittest.cc b/content/network/network_service_unittest.cc
index 9dc0e3a..33bf305 100644
--- a/content/network/network_service_unittest.cc
+++ b/content/network/network_service_unittest.cc
@@ -106,11 +106,12 @@
service_factory_bindings_.AddBinding(this, std::move(request));
}
+ std::unique_ptr<service_manager::ServiceContext> service_context_;
+
private:
service_manager::BinderRegistry registry_;
mojo::BindingSet<service_manager::mojom::ServiceFactory>
service_factory_bindings_;
- std::unique_ptr<service_manager::ServiceContext> service_context_;
};
} // namespace
@@ -313,6 +314,118 @@
EXPECT_EQ(net::OK, client()->completion_status().error_code);
}
+class TestNetworkChangeManagerClient
+ : public mojom::NetworkChangeManagerClient {
+ public:
+ explicit TestNetworkChangeManagerClient(
+ mojom::NetworkService* network_service)
+ : connection_type_(mojom::ConnectionType::CONNECTION_UNKNOWN),
+ binding_(this) {
+ mojom::NetworkChangeManagerPtr manager_ptr;
+ mojom::NetworkChangeManagerRequest request(mojo::MakeRequest(&manager_ptr));
+ network_service->GetNetworkChangeManager(std::move(request));
+
+ mojom::NetworkChangeManagerClientPtr client_ptr;
+ mojom::NetworkChangeManagerClientRequest client_request(
+ mojo::MakeRequest(&client_ptr));
+ binding_.Bind(std::move(client_request));
+ manager_ptr->RequestNotifications(std::move(client_ptr));
+ }
+
+ ~TestNetworkChangeManagerClient() override {}
+
+ // NetworkChangeManagerClient implementation:
+ void OnInitialConnectionType(mojom::ConnectionType type) override {
+ if (type == connection_type_)
+ run_loop_.Quit();
+ }
+
+ void OnNetworkChanged(mojom::ConnectionType type) override {
+ if (type == connection_type_)
+ run_loop_.Quit();
+ }
+
+ // Waits for the desired |connection_type| notification.
+ void WaitForNotification(mojom::ConnectionType type) {
+ connection_type_ = type;
+ run_loop_.Run();
+ }
+
+ private:
+ base::RunLoop run_loop_;
+ mojom::ConnectionType connection_type_;
+ mojo::Binding<mojom::NetworkChangeManagerClient> binding_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestNetworkChangeManagerClient);
+};
+
+// mojom:NetworkChangeManager currently doesn't support ChromeOS, which has a
+// different code path to set up net::NetworkChangeNotifier.
+#if defined(OS_CHROMEOS)
+#define MAYBE_NetworkChangeManagerRequest DISABLED_NetworkChangeManagerRequest
+#else
+#define MAYBE_NetworkChangeManagerRequest NetworkChangeManagerRequest
+#endif
+TEST_F(NetworkServiceTest, MAYBE_NetworkChangeManagerRequest) {
+ TestNetworkChangeManagerClient manager_client(service());
+ net::NetworkChangeNotifier::NotifyObserversOfNetworkChangeForTests(
+ net::NetworkChangeNotifier::CONNECTION_3G);
+ manager_client.WaitForNotification(mojom::ConnectionType::CONNECTION_3G);
+}
+
+class NetworkServiceNetworkChangeTest
+ : public service_manager::test::ServiceTest {
+ public:
+ NetworkServiceNetworkChangeTest()
+ : ServiceTest("content_unittests",
+ false,
+ base::test::ScopedTaskEnvironment::MainThreadType::IO) {}
+ ~NetworkServiceNetworkChangeTest() override {}
+
+ mojom::NetworkService* service() { return network_service_.get(); }
+
+ private:
+ // A ServiceTestClient that broadcasts a network change notification in the
+ // network service's process.
+ class ServiceTestClientWithNetworkChange : public ServiceTestClient {
+ public:
+ explicit ServiceTestClientWithNetworkChange(
+ service_manager::test::ServiceTest* test)
+ : ServiceTestClient(test) {}
+ ~ServiceTestClientWithNetworkChange() override {}
+
+ protected:
+ void CreateService(service_manager::mojom::ServiceRequest request,
+ const std::string& name) override {
+ if (name == mojom::kNetworkServiceName) {
+ service_context_.reset(new service_manager::ServiceContext(
+ NetworkServiceImpl::CreateForTesting(), std::move(request)));
+ // Send a broadcast after NetworkServiceImpl is actually created.
+ // Otherwise, this NotifyObservers is a no-op.
+ net::NetworkChangeNotifier::NotifyObserversOfNetworkChangeForTests(
+ net::NetworkChangeNotifier::CONNECTION_3G);
+ }
+ }
+ };
+ std::unique_ptr<service_manager::Service> CreateService() override {
+ return std::make_unique<ServiceTestClientWithNetworkChange>(this);
+ }
+
+ void SetUp() override {
+ service_manager::test::ServiceTest::SetUp();
+ connector()->BindInterface(mojom::kNetworkServiceName, &network_service_);
+ }
+
+ mojom::NetworkServicePtr network_service_;
+
+ DISALLOW_COPY_AND_ASSIGN(NetworkServiceNetworkChangeTest);
+};
+
+TEST_F(NetworkServiceNetworkChangeTest, MAYBE_NetworkChangeManagerRequest) {
+ TestNetworkChangeManagerClient manager_client(service());
+ manager_client.WaitForNotification(mojom::ConnectionType::CONNECTION_3G);
+}
+
} // namespace
} // namespace content
diff --git a/content/public/common/BUILD.gn b/content/public/common/BUILD.gn
index 6fd09e0..1e0c7bc 100644
--- a/content/public/common/BUILD.gn
+++ b/content/public/common/BUILD.gn
@@ -171,6 +171,8 @@
"mojo_channel_switches.cc",
"mojo_channel_switches.h",
"mutable_network_traffic_annotation_tag_struct_traits.h",
+ "network_connection_tracker.cc",
+ "network_connection_tracker.h",
"notification_resources.cc",
"notification_resources.h",
"origin_trial_policy.cc",
@@ -355,6 +357,7 @@
sources = [
"download_stream.mojom",
"mutable_network_traffic_annotation_tag.mojom",
+ "network_change_manager.mojom",
"network_service.mojom",
"network_service_test.mojom",
"push_messaging_status.mojom",
diff --git a/content/public/common/DEPS b/content/public/common/DEPS
index ced58cb..33a09d5 100644
--- a/content/public/common/DEPS
+++ b/content/public/common/DEPS
@@ -14,4 +14,7 @@
"simple_url_loader_unittest\.cc": [
"+content/public/network",
],
+ "network_connection_tracker_unittest\.cc": [
+ "+content/public/network",
+ ],
}
diff --git a/content/public/common/network_change_manager.mojom b/content/public/common/network_change_manager.mojom
new file mode 100644
index 0000000..96170c01
--- /dev/null
+++ b/content/public/common/network_change_manager.mojom
@@ -0,0 +1,50 @@
+// 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.
+
+module content.mojom;
+
+// This needs to match the definition of net::ConnectionType.
+enum ConnectionType {
+ CONNECTION_UNKNOWN = 0, // A connection exists, but its type is unknown.
+ // Also used as a default value.
+ CONNECTION_ETHERNET = 1,
+ CONNECTION_WIFI = 2,
+ CONNECTION_2G = 3,
+ CONNECTION_3G = 4,
+ CONNECTION_4G = 5,
+ CONNECTION_NONE = 6, // No connection.
+ CONNECTION_BLUETOOTH = 7,
+ CONNECTION_LAST = CONNECTION_BLUETOOTH
+};
+
+// A client interface that subscribes to network change events from
+// NetworkChangeManager.
+interface NetworkChangeManagerClient {
+ // Invoked when the initial connection type is ready.
+ OnInitialConnectionType(ConnectionType type);
+
+ // OnNetworkChanged will be called when a change occurs to the host
+ // computer's hardware or software that affects the route network packets
+ // take to any network server. Some examples:
+ // 1. A network connection becoming available or going away. For example
+ // plugging or unplugging an Ethernet cable, WiFi or cellular modem
+ // connecting or disconnecting from a network, or a VPN tunnel being
+ // established or taken down.
+ // 2. An active network connection's IP address changes.
+ // 3. A change to the local IP routing tables.
+ // The signal is produced when the change is complete. For example if a new
+ // network connection has become available, the signal is issued after OS has
+ // finished establishing the connection (i.e. DHCP is done) to the point where
+ // the new connection is usable. |type| indicates the type of the active
+ // primary network connection after the change. OnNetworkChanged will always
+ // be called with CONNECTION_NONE immediately prior to being called with an
+ // online state.
+ OnNetworkChanged(ConnectionType type);
+};
+
+// An interface that broadcasts network change events.
+interface NetworkChangeManager {
+ // Requests to receive notification when there is a network change.
+ RequestNotifications(NetworkChangeManagerClient client_ptr);
+};
diff --git a/content/public/common/network_connection_tracker.cc b/content/public/common/network_connection_tracker.cc
new file mode 100644
index 0000000..a725a6b
--- /dev/null
+++ b/content/public/common/network_connection_tracker.cc
@@ -0,0 +1,139 @@
+// 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.
+
+#include "content/public/common/network_connection_tracker.h"
+
+#include <utility>
+
+#include "base/task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "content/public/common/network_service.mojom.h"
+
+namespace content {
+
+namespace {
+
+// Wraps a |user_callback| when GetConnectionType() is called on a different
+// thread than NetworkConnectionTracker's thread.
+void OnGetConnectionType(
+ scoped_refptr<base::TaskRunner> task_runner,
+ const NetworkConnectionTracker::ConnectionTypeCallback& user_callback,
+ mojom::ConnectionType connection_type) {
+ task_runner->PostTask(
+ FROM_HERE,
+ base::BindOnce(
+ [](NetworkConnectionTracker::ConnectionTypeCallback callback,
+ mojom::ConnectionType type) { callback.Run(type); },
+ user_callback, connection_type));
+}
+
+static const int32_t kConnectionTypeInvalid = -1;
+
+} // namespace
+
+NetworkConnectionTracker::NetworkConnectionTracker(
+ mojom::NetworkService* network_service)
+ : task_runner_(base::ThreadTaskRunnerHandle::Get()),
+ connection_type_(kConnectionTypeInvalid),
+ network_change_observer_list_(
+ new base::ObserverListThreadSafe<NetworkConnectionObserver>(
+ base::ObserverListBase<
+ NetworkConnectionObserver>::NOTIFY_EXISTING_ONLY)),
+ binding_(this) {
+ // Get NetworkChangeManagerPtr.
+ mojom::NetworkChangeManagerPtr manager_ptr;
+ mojom::NetworkChangeManagerRequest request(mojo::MakeRequest(&manager_ptr));
+ network_service->GetNetworkChangeManager(std::move(request));
+
+ // Request notification from NetworkChangeManagerPtr.
+ mojom::NetworkChangeManagerClientPtr client_ptr;
+ mojom::NetworkChangeManagerClientRequest client_request(
+ mojo::MakeRequest(&client_ptr));
+ binding_.Bind(std::move(client_request));
+ manager_ptr->RequestNotifications(std::move(client_ptr));
+}
+
+NetworkConnectionTracker::~NetworkConnectionTracker() {
+ network_change_observer_list_->AssertEmpty();
+}
+
+bool NetworkConnectionTracker::GetConnectionType(
+ mojom::ConnectionType* type,
+ ConnectionTypeCallback callback) {
+ // |connection_type_| is initialized when NetworkService starts up. In most
+ // cases, it won't be kConnectionTypeInvalid and code will return early.
+ base::subtle::Atomic32 type_value =
+ base::subtle::NoBarrier_Load(&connection_type_);
+ if (type_value != kConnectionTypeInvalid) {
+ *type = static_cast<mojom::ConnectionType>(type_value);
+ return true;
+ }
+ base::AutoLock lock(lock_);
+ // Check again after getting the lock, and return early if
+ // OnInitialConnectionType() is called after first NoBarrier_Load.
+ type_value = base::subtle::NoBarrier_Load(&connection_type_);
+ if (type_value != kConnectionTypeInvalid) {
+ *type = static_cast<mojom::ConnectionType>(type_value);
+ return true;
+ }
+ if (!task_runner_->RunsTasksInCurrentSequence()) {
+ connection_type_callbacks_.push_back(
+ base::Bind(&OnGetConnectionType, base::ThreadTaskRunnerHandle::Get(),
+ std::move(callback)));
+ } else {
+ connection_type_callbacks_.push_back(std::move(callback));
+ }
+ return false;
+}
+
+// static
+bool NetworkConnectionTracker::IsConnectionCellular(
+ mojom::ConnectionType type) {
+ bool is_cellular = false;
+ switch (type) {
+ case mojom::ConnectionType::CONNECTION_2G:
+ case mojom::ConnectionType::CONNECTION_3G:
+ case mojom::ConnectionType::CONNECTION_4G:
+ is_cellular = true;
+ break;
+ case mojom::ConnectionType::CONNECTION_UNKNOWN:
+ case mojom::ConnectionType::CONNECTION_ETHERNET:
+ case mojom::ConnectionType::CONNECTION_WIFI:
+ case mojom::ConnectionType::CONNECTION_NONE:
+ case mojom::ConnectionType::CONNECTION_BLUETOOTH:
+ is_cellular = false;
+ break;
+ }
+ return is_cellular;
+}
+
+void NetworkConnectionTracker::AddNetworkConnectionObserver(
+ NetworkConnectionObserver* observer) {
+ network_change_observer_list_->AddObserver(observer);
+}
+
+void NetworkConnectionTracker::RemoveNetworkConnectionObserver(
+ NetworkConnectionObserver* observer) {
+ network_change_observer_list_->RemoveObserver(observer);
+}
+
+void NetworkConnectionTracker::OnInitialConnectionType(
+ mojom::ConnectionType type) {
+ base::AutoLock lock(lock_);
+ base::subtle::NoBarrier_Store(&connection_type_,
+ static_cast<base::subtle::Atomic32>(type));
+ while (!connection_type_callbacks_.empty()) {
+ connection_type_callbacks_.front().Run(type);
+ connection_type_callbacks_.pop_front();
+ }
+}
+
+void NetworkConnectionTracker::OnNetworkChanged(mojom::ConnectionType type) {
+ base::subtle::NoBarrier_Store(&connection_type_,
+ static_cast<base::subtle::Atomic32>(type));
+ network_change_observer_list_->Notify(
+ FROM_HERE, &NetworkConnectionObserver::OnConnectionChanged, type);
+}
+
+} // namespace content
diff --git a/content/public/common/network_connection_tracker.h b/content/public/common/network_connection_tracker.h
new file mode 100644
index 0000000..206428c
--- /dev/null
+++ b/content/public/common/network_connection_tracker.h
@@ -0,0 +1,106 @@
+// 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 CONTENT_PUBLIC_COMMON_NETWORK_CONNECTION_TRACKER_H_
+#define CONTENT_PUBLIC_COMMON_NETWORK_CONNECTION_TRACKER_H_
+
+#include <list>
+#include <memory>
+
+#include "base/atomicops.h"
+#include "base/callback.h"
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/observer_list_threadsafe.h"
+#include "base/synchronization/lock.h"
+#include "content/common/content_export.h"
+#include "content/public/common/network_change_manager.mojom.h"
+#include "content/public/common/network_service.mojom.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+
+namespace content {
+
+// This class subscribes to network change events from
+// mojom::NetworkChangeManager and propogates these notifications to its
+// NetworkConnectionObservers registered through AddObserver()/RemoveObserver().
+class CONTENT_EXPORT NetworkConnectionTracker
+ : public mojom::NetworkChangeManagerClient {
+ public:
+ typedef base::Callback<void(mojom::ConnectionType)> ConnectionTypeCallback;
+
+ class CONTENT_EXPORT NetworkConnectionObserver {
+ public:
+ // Please refer to NetworkChangeManagerClient::OnNetworkChanged for when
+ // this method is invoked.
+ virtual void OnConnectionChanged(mojom::ConnectionType type) = 0;
+
+ protected:
+ NetworkConnectionObserver() {}
+ virtual ~NetworkConnectionObserver() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NetworkConnectionObserver);
+ };
+
+ explicit NetworkConnectionTracker(mojom::NetworkService* network_service);
+
+ ~NetworkConnectionTracker() override;
+
+ // If connection type can be retrieved synchronously, returns true and |type|
+ // will contain the current connection type; Otherwise, returns false, in
+ // which case, |callback| will be called on the calling thread when connection
+ // type is ready. This method is thread safe. Please also refer to
+ // net::NetworkChangeNotifier::GetConnectionType() for documentation.
+ bool GetConnectionType(mojom::ConnectionType* type,
+ ConnectionTypeCallback callback);
+
+ // Returns true if |type| is a cellular connection.
+ // Returns false if |type| is CONNECTION_UNKNOWN, and thus, depending on the
+ // implementation of GetConnectionType(), it is possible that
+ // IsConnectionCellular(GetConnectionType()) returns false even if the
+ // current connection is cellular.
+ static bool IsConnectionCellular(mojom::ConnectionType type);
+
+ // Registers |observer| to receive notifications of network changes. The
+ // thread on which this is called is the thread on which |observer| will be
+ // called back with notifications.
+ void AddNetworkConnectionObserver(NetworkConnectionObserver* observer);
+
+ // Unregisters |observer| from receiving notifications. This must be called
+ // on the same thread on which AddObserver() was called.
+ // All observers must be unregistred before |this| is destroyed.
+ void RemoveNetworkConnectionObserver(NetworkConnectionObserver* observer);
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(NetworkGetConnectionTest,
+ GetConnectionTypeOnDifferentThread);
+ // NetworkChangeManagerClient implementation:
+ void OnInitialConnectionType(mojom::ConnectionType type) override;
+ void OnNetworkChanged(mojom::ConnectionType type) override;
+
+ // The task runner that |this| lives on.
+ scoped_refptr<base::SequencedTaskRunner> task_runner_;
+
+ // Protect access to |connection_type_callbacks_|.
+ base::Lock lock_;
+
+ // Saves user callback if GetConnectionType() cannot complete synchronously.
+ std::list<ConnectionTypeCallback> connection_type_callbacks_;
+
+ // |connection_type_| is set on one thread but read on many threads.
+ // The default value is -1 before OnInitialConnectionType().
+ base::subtle::Atomic32 connection_type_;
+
+ const scoped_refptr<base::ObserverListThreadSafe<NetworkConnectionObserver>>
+ network_change_observer_list_;
+
+ mojo::Binding<mojom::NetworkChangeManagerClient> binding_;
+
+ DISALLOW_COPY_AND_ASSIGN(NetworkConnectionTracker);
+};
+
+} // namespace content
+
+#endif // CONTENT_PUBLIC_COMMON_NETWORK_CONNECTION_TRACKER_H_
diff --git a/content/public/common/network_connection_tracker_unittest.cc b/content/public/common/network_connection_tracker_unittest.cc
new file mode 100644
index 0000000..11907e2
--- /dev/null
+++ b/content/public/common/network_connection_tracker_unittest.cc
@@ -0,0 +1,292 @@
+// 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.
+
+#include "content/public/common/network_connection_tracker.h"
+
+#include "base/macros.h"
+#include "base/run_loop.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/threading/thread.h"
+#include "base/threading/thread_checker.h"
+#include "content/public/common/network_change_manager.mojom.h"
+#include "content/public/network/network_service.h"
+#include "net/base/mock_network_change_notifier.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace content {
+
+namespace {
+
+class TestNetworkConnectionObserver
+ : public NetworkConnectionTracker::NetworkConnectionObserver {
+ public:
+ explicit TestNetworkConnectionObserver(NetworkConnectionTracker* tracker)
+ : num_notifications_(0),
+ tracker_(tracker),
+ run_loop_(std::make_unique<base::RunLoop>()),
+ connection_type_(mojom::ConnectionType::CONNECTION_UNKNOWN) {
+ tracker_->AddNetworkConnectionObserver(this);
+ }
+
+ ~TestNetworkConnectionObserver() override {
+ tracker_->RemoveNetworkConnectionObserver(this);
+ }
+
+ // Helper to synchronously get connection type from NetworkConnectionTracker.
+ mojom::ConnectionType GetConnectionTypeSync() {
+ mojom::ConnectionType type;
+ base::RunLoop run_loop;
+ bool sync = tracker_->GetConnectionType(
+ &type,
+ base::Bind(&TestNetworkConnectionObserver::GetConnectionTypeCallback,
+ &run_loop, &type));
+ if (!sync)
+ run_loop.Run();
+ return type;
+ }
+
+ // NetworkConnectionObserver implementation:
+ void OnConnectionChanged(mojom::ConnectionType type) override {
+ EXPECT_EQ(type, GetConnectionTypeSync());
+
+ num_notifications_++;
+ connection_type_ = type;
+ run_loop_->Quit();
+ }
+
+ size_t num_notifications() const { return num_notifications_; }
+ void WaitForNotification() {
+ run_loop_->Run();
+ run_loop_.reset(new base::RunLoop());
+ }
+
+ mojom::ConnectionType connection_type() const { return connection_type_; }
+
+ private:
+ static void GetConnectionTypeCallback(base::RunLoop* run_loop,
+ mojom::ConnectionType* out,
+ mojom::ConnectionType type) {
+ *out = type;
+ run_loop->Quit();
+ }
+
+ size_t num_notifications_;
+ NetworkConnectionTracker* tracker_;
+ std::unique_ptr<base::RunLoop> run_loop_;
+ mojom::ConnectionType connection_type_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestNetworkConnectionObserver);
+};
+
+// A helper class to call NetworkConnectionTracker::GetConnectionType().
+class ConnectionTypeGetter {
+ public:
+ explicit ConnectionTypeGetter(NetworkConnectionTracker* tracker)
+ : tracker_(tracker),
+ connection_type_(mojom::ConnectionType::CONNECTION_UNKNOWN) {}
+ ~ConnectionTypeGetter() {}
+
+ bool GetConnectionType() {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ return tracker_->GetConnectionType(
+ &connection_type_,
+ base::Bind(&ConnectionTypeGetter::OnGetConnectionType,
+ base::Unretained(this)));
+ }
+
+ void WaitForConnectionType(mojom::ConnectionType expected_connection_type) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ run_loop_.Run();
+ EXPECT_EQ(expected_connection_type, connection_type_);
+ }
+
+ mojom::ConnectionType connection_type() const { return connection_type_; }
+
+ private:
+ void OnGetConnectionType(mojom::ConnectionType type) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ connection_type_ = type;
+ run_loop_.Quit();
+ }
+
+ base::RunLoop run_loop_;
+ NetworkConnectionTracker* tracker_;
+ mojom::ConnectionType connection_type_;
+ THREAD_CHECKER(thread_checker_);
+
+ DISALLOW_COPY_AND_ASSIGN(ConnectionTypeGetter);
+};
+
+} // namespace
+
+class NetworkConnectionTrackerTest : public testing::Test {
+ public:
+ NetworkConnectionTrackerTest()
+ : network_service_(NetworkService::Create()),
+ network_connection_tracker_(network_service_.get()),
+ network_connection_observer_(&network_connection_tracker_) {}
+
+ ~NetworkConnectionTrackerTest() override {}
+
+ NetworkConnectionTracker* network_connection_tracker() {
+ return &network_connection_tracker_;
+ }
+
+ TestNetworkConnectionObserver* network_connection_observer() {
+ return &network_connection_observer_;
+ }
+
+ // Simulates a connection type change and broadcast it to observers.
+ void SimulateConnectionTypeChange(
+ net::NetworkChangeNotifier::ConnectionType type) {
+ mock_network_change_notifier_.NotifyObserversOfNetworkChangeForTests(type);
+ }
+
+ // Sets the current connection type of the mock network change notifier.
+ void SetConnectionType(net::NetworkChangeNotifier::ConnectionType type) {
+ mock_network_change_notifier_.SetConnectionType(type);
+ }
+
+ private:
+ base::test::ScopedTaskEnvironment scoped_task_environment_;
+ net::test::MockNetworkChangeNotifier mock_network_change_notifier_;
+ std::unique_ptr<NetworkService> network_service_;
+ NetworkConnectionTracker network_connection_tracker_;
+ TestNetworkConnectionObserver network_connection_observer_;
+
+ DISALLOW_COPY_AND_ASSIGN(NetworkConnectionTrackerTest);
+};
+
+TEST_F(NetworkConnectionTrackerTest, ObserverNotified) {
+ EXPECT_EQ(mojom::ConnectionType::CONNECTION_UNKNOWN,
+ network_connection_observer()->connection_type());
+
+ // Simulate a network change.
+ SimulateConnectionTypeChange(
+ net::NetworkChangeNotifier::ConnectionType::CONNECTION_3G);
+
+ network_connection_observer()->WaitForNotification();
+ EXPECT_EQ(mojom::ConnectionType::CONNECTION_3G,
+ network_connection_observer()->connection_type());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(1u, network_connection_observer()->num_notifications());
+}
+
+TEST_F(NetworkConnectionTrackerTest, UnregisteredObserverNotNotified) {
+ auto network_connection_observer2 =
+ std::make_unique<TestNetworkConnectionObserver>(
+ network_connection_tracker());
+
+ // Simulate a network change.
+ SimulateConnectionTypeChange(
+ net::NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI);
+
+ network_connection_observer2->WaitForNotification();
+ EXPECT_EQ(mojom::ConnectionType::CONNECTION_WIFI,
+ network_connection_observer2->connection_type());
+ network_connection_observer()->WaitForNotification();
+ EXPECT_EQ(mojom::ConnectionType::CONNECTION_WIFI,
+ network_connection_observer()->connection_type());
+ base::RunLoop().RunUntilIdle();
+
+ network_connection_observer2.reset();
+
+ // Simulate an another network change.
+ SimulateConnectionTypeChange(
+ net::NetworkChangeNotifier::ConnectionType::CONNECTION_2G);
+ network_connection_observer()->WaitForNotification();
+ EXPECT_EQ(mojom::ConnectionType::CONNECTION_2G,
+ network_connection_observer()->connection_type());
+ EXPECT_EQ(2u, network_connection_observer()->num_notifications());
+}
+
+TEST_F(NetworkConnectionTrackerTest, GetConnectionType) {
+ SetConnectionType(net::NetworkChangeNotifier::ConnectionType::CONNECTION_3G);
+ std::unique_ptr<NetworkService> network_service = NetworkService::Create();
+ NetworkConnectionTracker tracker(network_service.get());
+
+ ConnectionTypeGetter getter1(&tracker), getter2(&tracker);
+ // These two GetConnectionType() will finish asynchonously because network
+ // service is not yet set up.
+ EXPECT_FALSE(getter1.GetConnectionType());
+ EXPECT_FALSE(getter2.GetConnectionType());
+
+ getter1.WaitForConnectionType(
+ /*expected_connection_type=*/mojom::ConnectionType::CONNECTION_3G);
+ getter2.WaitForConnectionType(
+ /*expected_connection_type=*/mojom::ConnectionType::CONNECTION_3G);
+
+ ConnectionTypeGetter getter3(&tracker);
+ // This GetConnectionType() should finish synchronously.
+ EXPECT_TRUE(getter3.GetConnectionType());
+ EXPECT_EQ(mojom::ConnectionType::CONNECTION_3G, getter3.connection_type());
+}
+
+// Tests GetConnectionType() on a different thread.
+class NetworkGetConnectionTest : public NetworkConnectionTrackerTest {
+ public:
+ NetworkGetConnectionTest()
+ : getter_thread_("NetworkGetConnectionTestThread") {
+ getter_thread_.Start();
+ }
+
+ ~NetworkGetConnectionTest() override {}
+
+ void GetConnectionType() {
+ DCHECK(getter_thread_.task_runner()->RunsTasksInCurrentSequence());
+ getter_ =
+ std::make_unique<ConnectionTypeGetter>(network_connection_tracker());
+ EXPECT_FALSE(getter_->GetConnectionType());
+ }
+
+ void WaitForConnectionType(mojom::ConnectionType expected_connection_type) {
+ DCHECK(getter_thread_.task_runner()->RunsTasksInCurrentSequence());
+ getter_->WaitForConnectionType(expected_connection_type);
+ }
+
+ base::Thread* getter_thread() { return &getter_thread_; }
+
+ private:
+ base::Thread getter_thread_;
+
+ // Accessed on |getter_thread_|.
+ std::unique_ptr<ConnectionTypeGetter> getter_;
+
+ DISALLOW_COPY_AND_ASSIGN(NetworkGetConnectionTest);
+};
+
+TEST_F(NetworkGetConnectionTest, GetConnectionTypeOnDifferentThread) {
+ // Flush pending OnInitialConnectionType() notification and force |tracker| to
+ // use async for GetConnectionType() calls.
+ base::RunLoop().RunUntilIdle();
+ base::subtle::NoBarrier_Store(&network_connection_tracker()->connection_type_,
+ -1);
+ {
+ base::RunLoop run_loop;
+ getter_thread()->task_runner()->PostTaskAndReply(
+ FROM_HERE,
+ base::BindOnce(&NetworkGetConnectionTest::GetConnectionType,
+ base::Unretained(this)),
+ base::BindOnce([](base::RunLoop* run_loop) { run_loop->Quit(); },
+ base::Unretained(&run_loop)));
+ run_loop.Run();
+ }
+
+ network_connection_tracker()->OnInitialConnectionType(
+ mojom::ConnectionType::CONNECTION_3G);
+ {
+ base::RunLoop run_loop;
+ getter_thread()->task_runner()->PostTaskAndReply(
+ FROM_HERE,
+ base::BindOnce(
+ &NetworkGetConnectionTest::WaitForConnectionType,
+ base::Unretained(this),
+ /*expected_connection_type=*/mojom::ConnectionType::CONNECTION_3G),
+ base::BindOnce([](base::RunLoop* run_loop) { run_loop->Quit(); },
+ base::Unretained(&run_loop)));
+ run_loop.Run();
+ }
+}
+
+} // namespace content
diff --git a/content/public/common/network_service.mojom b/content/public/common/network_service.mojom
index b46ed10..9ef6d6a 100644
--- a/content/public/common/network_service.mojom
+++ b/content/public/common/network_service.mojom
@@ -4,6 +4,7 @@
module content.mojom;
+import "network_change_manager.mojom";
import "mojo/common/file_path.mojom";
import "mojo/common/time.mojom";
import "url_loader.mojom";
@@ -81,12 +82,12 @@
CreateURLLoaderFactory(URLLoaderFactory& url_loader_factory,
uint32 process_id);
- // Handle a request to display cache data to the user. |url| is parsed to
+ // Handles a request to display cache data to the user. |url| is parsed to
// display different parts of the cache.
HandleViewCacheRequest(url.mojom.Url url,
URLLoaderClient client);
- // Get the CookieManager associated with this network context.
+ // Gets the CookieManager associated with this network context.
GetCookieManager(network.mojom.CookieManager& cookie_manager);
// TODO(crbug.com/729800): Switch from {process,frame}_id to the network
@@ -128,4 +129,7 @@
// permission increases risks in case the child process becomes compromised,
// so this should be done only in specific cases (e.g. DevTools attached).
SetRawHeadersAccess(uint32 process_id, bool allow);
+
+ // Gets the NetworkChangeManager.
+ GetNetworkChangeManager(NetworkChangeManager& network_change_manager);
};
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index c0bfc0a..b88b17f 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1509,6 +1509,7 @@
"../common/unique_name_helper_unittest.cc",
"../common/webplugininfo_unittest.cc",
"../network/cookie_manager_impl_unittest.cc",
+ "../network/network_change_manager_impl_unittest.cc",
"../network/network_context_unittest.cc",
"../network/network_service_unittest.cc",
"../network/proxy_resolver_factory_mojo_unittest.cc",
@@ -1516,6 +1517,7 @@
"../network/throttling/throttling_controller_unittest.cc",
"../network/url_loader_unittest.cc",
"../public/common/drop_data_unittest.cc",
+ "../public/common/network_connection_tracker_unittest.cc",
"../public/common/simple_url_loader_unittest.cc",
"../public/common/url_utils_unittest.cc",
"../public/test/referrer_unittest.cc",
diff --git a/net/BUILD.gn b/net/BUILD.gn
index a1fc7125..810a9e93 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -2491,6 +2491,8 @@
"base/load_timing_info_test_util.h",
"base/mock_file_stream.cc",
"base/mock_file_stream.h",
+ "base/mock_network_change_notifier.cc",
+ "base/mock_network_change_notifier.h",
"base/test_completion_callback.cc",
"base/test_completion_callback.h",
"cert/mock_cert_verifier.cc",
@@ -4631,8 +4633,6 @@
"base/lookup_string_in_fixed_set_unittest.cc",
"base/mime_sniffer_unittest.cc",
"base/mime_util_unittest.cc",
- "base/mock_network_change_notifier.cc",
- "base/mock_network_change_notifier.h",
"base/net_string_util_unittest.cc",
"base/network_activity_monitor_unittest.cc",
"base/network_change_notifier_unittest.cc",