Commit 81edb9c6 authored by Cliff Smolinsky's avatar Cliff Smolinsky Committed by Chromium LUCI CQ

Add support for detecting metered networks on Windows.

This change adds GetConnectionCost() to the NetworkChangeNotifier class.
The structure is modeled after the existing functions of the class. The
base class implements a basic best-effort guess that works on platforms
that can detect cellular versus non-cellular types, which is essentially
the same as how "metered" network checks exist today.

This change updates the NetworkChangeNotifierWin class to retrieve the
metered network status and register for updates from the OS. It also
creates an Observer class similar to the other existing ones to notify
other components when it changes.

It's important to note that NetworkChangeNotifierWin will only register
for updates from the OS once an Observer is connected. If there is no
observer connected and someone asks for the current connection cost it
will be retrieved from the OS at that time rather than using the cached
value.

Bug: 1143428
Change-Id: I6f03ae91869087c6ad72a7f5891df158815137d7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2542047Reviewed-by: default avatarPaul Jensen <pauljensen@chromium.org>
Reviewed-by: default avatarKinuko Yasuda <kinuko@chromium.org>
Reviewed-by: default avatarTarun Bansal <tbansal@chromium.org>
Commit-Queue: Cliff Smolinsky <cliffsmo@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#844383}
parent 28e8856b
...@@ -93,12 +93,20 @@ void MockNetworkChangeNotifier::SetConnectionTypeAndNotifyObservers( ...@@ -93,12 +93,20 @@ void MockNetworkChangeNotifier::SetConnectionTypeAndNotifyObservers(
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
} }
MockNetworkChangeNotifier::ConnectionCost
MockNetworkChangeNotifier::GetCurrentConnectionCost() {
if (use_default_connection_cost_implementation_)
return NetworkChangeNotifier::GetCurrentConnectionCost();
return connection_cost_;
}
MockNetworkChangeNotifier::MockNetworkChangeNotifier( MockNetworkChangeNotifier::MockNetworkChangeNotifier(
std::unique_ptr<SystemDnsConfigChangeNotifier> dns_config_notifier) std::unique_ptr<SystemDnsConfigChangeNotifier> dns_config_notifier)
: NetworkChangeNotifier(NetworkChangeCalculatorParams(), : NetworkChangeNotifier(NetworkChangeCalculatorParams(),
dns_config_notifier.get()), dns_config_notifier.get()),
force_network_handles_supported_(false), force_network_handles_supported_(false),
connection_type_(CONNECTION_UNKNOWN), connection_type_(CONNECTION_UNKNOWN),
connection_cost_(CONNECTION_COST_UNKNOWN),
dns_config_notifier_(std::move(dns_config_notifier)) {} dns_config_notifier_(std::move(dns_config_notifier)) {}
ScopedMockNetworkChangeNotifier::ScopedMockNetworkChangeNotifier() ScopedMockNetworkChangeNotifier::ScopedMockNetworkChangeNotifier()
......
...@@ -53,6 +53,30 @@ class MockNetworkChangeNotifier : public NetworkChangeNotifier { ...@@ -53,6 +53,30 @@ class MockNetworkChangeNotifier : public NetworkChangeNotifier {
void SetConnectionTypeAndNotifyObservers(ConnectionType connection_type); void SetConnectionTypeAndNotifyObservers(ConnectionType connection_type);
// Sets the cached value of the connection cost. If
// use_default_connection_cost_implementation is set to true, this value gets
// ignored.
void SetConnectionCost(ConnectionCost connection_cost) {
connection_cost_ = connection_cost;
}
// Tells this class to ignore its cached connection cost value and instead
// call the base class's implementation. This is intended to allow tests to
// mock the product code's fallback to the default implementation in certain
// situations. For example, the APIs to support this functionality exist on
// Win10 only so it falls back to the default on Win7, so this function allows
// tests to validate the default implementation's behavior on Win10 machines.
void SetUseDefaultConnectionCostImplementation(
bool use_default_connection_cost_implementation) {
use_default_connection_cost_implementation_ =
use_default_connection_cost_implementation;
}
// Returns either the cached connection cost value or the default
// implementation's result, depending on whether
// use_default_connection_cost_implementation is set to true.
ConnectionCost GetCurrentConnectionCost() override;
private: private:
// Create using MockNetworkChangeNotifier::Create(). // Create using MockNetworkChangeNotifier::Create().
MockNetworkChangeNotifier( MockNetworkChangeNotifier(
...@@ -60,6 +84,8 @@ class MockNetworkChangeNotifier : public NetworkChangeNotifier { ...@@ -60,6 +84,8 @@ class MockNetworkChangeNotifier : public NetworkChangeNotifier {
bool force_network_handles_supported_; bool force_network_handles_supported_;
ConnectionType connection_type_; ConnectionType connection_type_;
ConnectionCost connection_cost_;
bool use_default_connection_cost_implementation_ = false;
NetworkChangeNotifier::NetworkList connected_networks_; NetworkChangeNotifier::NetworkList connected_networks_;
std::unique_ptr<SystemDnsConfigChangeNotifier> dns_config_notifier_; std::unique_ptr<SystemDnsConfigChangeNotifier> dns_config_notifier_;
}; };
......
...@@ -258,6 +258,14 @@ std::unique_ptr<NetworkChangeNotifier> NetworkChangeNotifier::CreateIfNeeded( ...@@ -258,6 +258,14 @@ std::unique_ptr<NetworkChangeNotifier> NetworkChangeNotifier::CreateIfNeeded(
#endif #endif
} }
// static
NetworkChangeNotifier::ConnectionCost
NetworkChangeNotifier::GetConnectionCost() {
return g_network_change_notifier
? g_network_change_notifier->GetCurrentConnectionCost()
: CONNECTION_COST_UNKNOWN;
}
// static // static
NetworkChangeNotifier::ConnectionType NetworkChangeNotifier::ConnectionType
NetworkChangeNotifier::GetConnectionType() { NetworkChangeNotifier::GetConnectionType() {
...@@ -541,6 +549,11 @@ NetworkChangeNotifier::MaxBandwidthObserver::~MaxBandwidthObserver() = default; ...@@ -541,6 +549,11 @@ NetworkChangeNotifier::MaxBandwidthObserver::~MaxBandwidthObserver() = default;
NetworkChangeNotifier::NetworkObserver::NetworkObserver() = default; NetworkChangeNotifier::NetworkObserver::NetworkObserver() = default;
NetworkChangeNotifier::NetworkObserver::~NetworkObserver() = default; NetworkChangeNotifier::NetworkObserver::~NetworkObserver() = default;
NetworkChangeNotifier::ConnectionCostObserver::ConnectionCostObserver() =
default;
NetworkChangeNotifier::ConnectionCostObserver::~ConnectionCostObserver() =
default;
void NetworkChangeNotifier::AddIPAddressObserver(IPAddressObserver* observer) { void NetworkChangeNotifier::AddIPAddressObserver(IPAddressObserver* observer) {
if (g_network_change_notifier && if (g_network_change_notifier &&
g_network_change_notifier->can_add_observers_) { g_network_change_notifier->can_add_observers_) {
...@@ -599,6 +612,17 @@ void NetworkChangeNotifier::AddNetworkObserver(NetworkObserver* observer) { ...@@ -599,6 +612,17 @@ void NetworkChangeNotifier::AddNetworkObserver(NetworkObserver* observer) {
} }
} }
void NetworkChangeNotifier::AddConnectionCostObserver(
ConnectionCostObserver* observer) {
if (g_network_change_notifier &&
g_network_change_notifier->can_add_observers_) {
observer->observer_list_ =
g_network_change_notifier->connection_cost_observer_list_;
observer->observer_list_->AddObserver(observer);
g_network_change_notifier->ConnectionCostObserverAdded();
}
}
void NetworkChangeNotifier::RemoveIPAddressObserver( void NetworkChangeNotifier::RemoveIPAddressObserver(
IPAddressObserver* observer) { IPAddressObserver* observer) {
if (observer->observer_list_) { if (observer->observer_list_) {
...@@ -645,6 +669,14 @@ void NetworkChangeNotifier::RemoveNetworkObserver(NetworkObserver* observer) { ...@@ -645,6 +669,14 @@ void NetworkChangeNotifier::RemoveNetworkObserver(NetworkObserver* observer) {
} }
} }
void NetworkChangeNotifier::RemoveConnectionCostObserver(
ConnectionCostObserver* observer) {
if (observer->observer_list_) {
observer->observer_list_->RemoveObserver(observer);
observer->observer_list_.reset();
}
}
void NetworkChangeNotifier::TriggerNonSystemDnsChange() { void NetworkChangeNotifier::TriggerNonSystemDnsChange() {
NetworkChangeNotifier::NotifyObserversOfDNSChange(); NetworkChangeNotifier::NotifyObserversOfDNSChange();
} }
...@@ -685,6 +717,13 @@ void NetworkChangeNotifier::NotifyObserversOfMaxBandwidthChangeForTests( ...@@ -685,6 +717,13 @@ void NetworkChangeNotifier::NotifyObserversOfMaxBandwidthChangeForTests(
} }
} }
// static
void NetworkChangeNotifier::NotifyObserversOfConnectionCostChangeForTests(
ConnectionCost cost) {
if (g_network_change_notifier)
g_network_change_notifier->NotifyObserversOfConnectionCostChangeImpl(cost);
}
// static // static
void NetworkChangeNotifier::SetTestNotificationsOnly(bool test_only) { void NetworkChangeNotifier::SetTestNotificationsOnly(bool test_only) {
DCHECK(!g_network_change_notifier); DCHECK(!g_network_change_notifier);
...@@ -713,6 +752,9 @@ NetworkChangeNotifier::NetworkChangeNotifier( ...@@ -713,6 +752,9 @@ NetworkChangeNotifier::NetworkChangeNotifier(
base::ObserverListPolicy::EXISTING_ONLY)), base::ObserverListPolicy::EXISTING_ONLY)),
network_observer_list_(new base::ObserverListThreadSafe<NetworkObserver>( network_observer_list_(new base::ObserverListThreadSafe<NetworkObserver>(
base::ObserverListPolicy::EXISTING_ONLY)), base::ObserverListPolicy::EXISTING_ONLY)),
connection_cost_observer_list_(
new base::ObserverListThreadSafe<ConnectionCostObserver>(
base::ObserverListPolicy::EXISTING_ONLY)),
system_dns_config_notifier_(system_dns_config_notifier), system_dns_config_notifier_(system_dns_config_notifier),
system_dns_config_observer_(std::make_unique<SystemDnsConfigObserver>()), system_dns_config_observer_(std::make_unique<SystemDnsConfigObserver>()),
network_change_calculator_(new NetworkChangeCalculator(params)), network_change_calculator_(new NetworkChangeCalculator(params)),
...@@ -738,6 +780,16 @@ NetworkChangeNotifier::GetAddressTrackerInternal() const { ...@@ -738,6 +780,16 @@ NetworkChangeNotifier::GetAddressTrackerInternal() const {
} }
#endif #endif
NetworkChangeNotifier::ConnectionCost
NetworkChangeNotifier::GetCurrentConnectionCost() {
// This is the default non-platform specific implementation and assumes that
// cellular connectivity is metered and non-cellular is not. The function can
// be specialized on each platform specific notifier implementation.
return IsConnectionCellular(GetCurrentConnectionType())
? CONNECTION_COST_METERED
: CONNECTION_COST_UNMETERED;
}
NetworkChangeNotifier::ConnectionSubtype NetworkChangeNotifier::ConnectionSubtype
NetworkChangeNotifier::GetCurrentConnectionSubtype() const { NetworkChangeNotifier::GetCurrentConnectionSubtype() const {
return SUBTYPE_UNKNOWN; return SUBTYPE_UNKNOWN;
...@@ -838,6 +890,15 @@ void NetworkChangeNotifier::NotifyObserversOfSpecificNetworkChange( ...@@ -838,6 +890,15 @@ void NetworkChangeNotifier::NotifyObserversOfSpecificNetworkChange(
} }
} }
// static
void NetworkChangeNotifier::NotifyObserversOfConnectionCostChange() {
if (g_network_change_notifier &&
!NetworkChangeNotifier::test_notifications_only_) {
g_network_change_notifier->NotifyObserversOfConnectionCostChangeImpl(
GetConnectionCost());
}
}
void NetworkChangeNotifier::StopSystemDnsConfigNotifier() { void NetworkChangeNotifier::StopSystemDnsConfigNotifier() {
if (!system_dns_config_notifier_) if (!system_dns_config_notifier_)
return; return;
...@@ -900,6 +961,12 @@ void NetworkChangeNotifier::NotifyObserversOfSpecificNetworkChangeImpl( ...@@ -900,6 +961,12 @@ void NetworkChangeNotifier::NotifyObserversOfSpecificNetworkChangeImpl(
} }
} }
void NetworkChangeNotifier::NotifyObserversOfConnectionCostChangeImpl(
ConnectionCost cost) {
connection_cost_observer_list_->Notify(
FROM_HERE, &ConnectionCostObserver::OnConnectionCostChanged, cost);
}
NetworkChangeNotifier::DisableForTest::DisableForTest() NetworkChangeNotifier::DisableForTest::DisableForTest()
: network_change_notifier_(g_network_change_notifier) { : network_change_notifier_(g_network_change_notifier) {
DCHECK(g_network_change_notifier); DCHECK(g_network_change_notifier);
......
...@@ -105,6 +105,12 @@ class NET_EXPORT NetworkChangeNotifier { ...@@ -105,6 +105,12 @@ class NET_EXPORT NetworkChangeNotifier {
SUBTYPE_LAST = SUBTYPE_WIFI_AD SUBTYPE_LAST = SUBTYPE_WIFI_AD
}; };
enum ConnectionCost {
CONNECTION_COST_UNKNOWN = 0,
CONNECTION_COST_UNMETERED,
CONNECTION_COST_METERED,
};
// DEPRECATED. Please use NetworkChangeObserver instead. crbug.com/754695. // DEPRECATED. Please use NetworkChangeObserver instead. crbug.com/754695.
class NET_EXPORT IPAddressObserver { class NET_EXPORT IPAddressObserver {
public: public:
...@@ -228,6 +234,33 @@ class NET_EXPORT NetworkChangeNotifier { ...@@ -228,6 +234,33 @@ class NET_EXPORT NetworkChangeNotifier {
observer_list_; observer_list_;
}; };
class NET_EXPORT ConnectionCostObserver {
public:
// Not copyable or movable
ConnectionCostObserver(const ConnectionCostObserver&) = delete;
ConnectionCostObserver& operator=(const ConnectionCostObserver&) = delete;
// Will be called when the connection cost of the default network connection
// of the system has changed. This will only fire if the connection cost
// actually changes, regardless of any other network-related changes that
// might have occurred (for example, changing from ethernet to wifi won't
// update this unless that change also results in a cost change). The cost
// is not tied directly to any other network-related states, as you could
// simply change the current connection from unmetered to metered. It is
// safe to assume that network traffic will default to this cost once this
// has fired.
virtual void OnConnectionCostChanged(ConnectionCost Cost) = 0;
protected:
ConnectionCostObserver();
virtual ~ConnectionCostObserver();
private:
friend NetworkChangeNotifier;
scoped_refptr<base::ObserverListThreadSafe<ConnectionCostObserver>>
observer_list_;
};
// Opaque handle for device-wide connection to a particular network. For // Opaque handle for device-wide connection to a particular network. For
// example an association with a particular WiFi network with a particular // example an association with a particular WiFi network with a particular
// SSID or a connection to particular cellular network. // SSID or a connection to particular cellular network.
...@@ -300,6 +333,12 @@ class NET_EXPORT NetworkChangeNotifier { ...@@ -300,6 +333,12 @@ class NET_EXPORT NetworkChangeNotifier {
NetworkChangeNotifier::ConnectionType initial_type = CONNECTION_NONE, NetworkChangeNotifier::ConnectionType initial_type = CONNECTION_NONE,
NetworkChangeNotifier::ConnectionSubtype initial_subtype = SUBTYPE_NONE); NetworkChangeNotifier::ConnectionSubtype initial_subtype = SUBTYPE_NONE);
// Returns the most likely cost attribute for the default network connection.
// The value does not indicate with absolute certainty if using the connection
// will or will not incur a monetary cost to the user. It is a best guess
// based on Operating System information and network interface type.
static ConnectionCost GetConnectionCost();
// Returns the connection type. // Returns the connection type.
// A return value of |CONNECTION_NONE| is a pretty strong indicator that the // A return value of |CONNECTION_NONE| is a pretty strong indicator that the
// user won't be able to connect to remote sites. However, another return // user won't be able to connect to remote sites. However, another return
...@@ -425,6 +464,7 @@ class NET_EXPORT NetworkChangeNotifier { ...@@ -425,6 +464,7 @@ class NET_EXPORT NetworkChangeNotifier {
static void AddNetworkChangeObserver(NetworkChangeObserver* observer); static void AddNetworkChangeObserver(NetworkChangeObserver* observer);
static void AddMaxBandwidthObserver(MaxBandwidthObserver* observer); static void AddMaxBandwidthObserver(MaxBandwidthObserver* observer);
static void AddNetworkObserver(NetworkObserver* observer); static void AddNetworkObserver(NetworkObserver* observer);
static void AddConnectionCostObserver(ConnectionCostObserver* observer);
// Unregisters |observer| from receiving notifications. This must be called // Unregisters |observer| from receiving notifications. This must be called
// on the same thread on which AddObserver() was called. Like AddObserver(), // on the same thread on which AddObserver() was called. Like AddObserver(),
...@@ -444,6 +484,7 @@ class NET_EXPORT NetworkChangeNotifier { ...@@ -444,6 +484,7 @@ class NET_EXPORT NetworkChangeNotifier {
static void RemoveNetworkChangeObserver(NetworkChangeObserver* observer); static void RemoveNetworkChangeObserver(NetworkChangeObserver* observer);
static void RemoveMaxBandwidthObserver(MaxBandwidthObserver* observer); static void RemoveMaxBandwidthObserver(MaxBandwidthObserver* observer);
static void RemoveNetworkObserver(NetworkObserver* observer); static void RemoveNetworkObserver(NetworkObserver* observer);
static void RemoveConnectionCostObserver(ConnectionCostObserver* observer);
// Called to signify a non-system DNS config change. // Called to signify a non-system DNS config change.
static void TriggerNonSystemDnsChange(); static void TriggerNonSystemDnsChange();
...@@ -457,6 +498,8 @@ class NET_EXPORT NetworkChangeNotifier { ...@@ -457,6 +498,8 @@ class NET_EXPORT NetworkChangeNotifier {
static void NotifyObserversOfMaxBandwidthChangeForTests( static void NotifyObserversOfMaxBandwidthChangeForTests(
double max_bandwidth_mbps, double max_bandwidth_mbps,
ConnectionType type); ConnectionType type);
static void NotifyObserversOfConnectionCostChangeForTests(
ConnectionCost cost);
// Enable or disable notifications from the host. After setting to true, be // Enable or disable notifications from the host. After setting to true, be
// sure to pump the RunLoop until idle to finish any preexisting // sure to pump the RunLoop until idle to finish any preexisting
...@@ -539,6 +582,7 @@ class NET_EXPORT NetworkChangeNotifier { ...@@ -539,6 +582,7 @@ class NET_EXPORT NetworkChangeNotifier {
// See the description of the corresponding functions named without "Current". // See the description of the corresponding functions named without "Current".
// Implementations must be thread-safe. Implementations must also be // Implementations must be thread-safe. Implementations must also be
// cheap as they are called often. // cheap as they are called often.
virtual ConnectionCost GetCurrentConnectionCost();
virtual ConnectionType GetCurrentConnectionType() const = 0; virtual ConnectionType GetCurrentConnectionType() const = 0;
virtual ConnectionSubtype GetCurrentConnectionSubtype() const; virtual ConnectionSubtype GetCurrentConnectionSubtype() const;
virtual void GetCurrentMaxBandwidthAndConnectionType( virtual void GetCurrentMaxBandwidthAndConnectionType(
...@@ -562,6 +606,7 @@ class NET_EXPORT NetworkChangeNotifier { ...@@ -562,6 +606,7 @@ class NET_EXPORT NetworkChangeNotifier {
ConnectionType type); ConnectionType type);
static void NotifyObserversOfSpecificNetworkChange(NetworkChangeType type, static void NotifyObserversOfSpecificNetworkChange(NetworkChangeType type,
NetworkHandle network); NetworkHandle network);
static void NotifyObserversOfConnectionCostChange();
// Infer connection type from |GetNetworkList|. If all network interfaces // Infer connection type from |GetNetworkList|. If all network interfaces
// have the same type, return it, otherwise return CONNECTION_UNKNOWN. // have the same type, return it, otherwise return CONNECTION_UNKNOWN.
...@@ -575,6 +620,13 @@ class NET_EXPORT NetworkChangeNotifier { ...@@ -575,6 +620,13 @@ class NET_EXPORT NetworkChangeNotifier {
// as early as possible in the destructor to prevent races. // as early as possible in the destructor to prevent races.
void ClearGlobalPointer(); void ClearGlobalPointer();
// Called whenever a new ConnectionCostObserver is added. This method is
// needed so that the implementation class can be notified and
// potentially take action when an observer gets added. Since the act of
// adding an observer and the observer list itself are both static, the
// implementation class has no direct capability to watch for changes.
virtual void ConnectionCostObserverAdded() {}
private: private:
friend class HostResolverManagerDnsTest; friend class HostResolverManagerDnsTest;
friend class NetworkChangeNotifierAndroidTest; friend class NetworkChangeNotifierAndroidTest;
...@@ -592,6 +644,7 @@ class NET_EXPORT NetworkChangeNotifier { ...@@ -592,6 +644,7 @@ class NET_EXPORT NetworkChangeNotifier {
ConnectionType type); ConnectionType type);
void NotifyObserversOfSpecificNetworkChangeImpl(NetworkChangeType type, void NotifyObserversOfSpecificNetworkChangeImpl(NetworkChangeType type,
NetworkHandle network); NetworkHandle network);
void NotifyObserversOfConnectionCostChangeImpl(ConnectionCost cost);
const scoped_refptr<base::ObserverListThreadSafe<IPAddressObserver>> const scoped_refptr<base::ObserverListThreadSafe<IPAddressObserver>>
ip_address_observer_list_; ip_address_observer_list_;
...@@ -605,6 +658,8 @@ class NET_EXPORT NetworkChangeNotifier { ...@@ -605,6 +658,8 @@ class NET_EXPORT NetworkChangeNotifier {
max_bandwidth_observer_list_; max_bandwidth_observer_list_;
const scoped_refptr<base::ObserverListThreadSafe<NetworkObserver>> const scoped_refptr<base::ObserverListThreadSafe<NetworkObserver>>
network_observer_list_; network_observer_list_;
const scoped_refptr<base::ObserverListThreadSafe<ConnectionCostObserver>>
connection_cost_observer_list_;
SystemDnsConfigChangeNotifier* system_dns_config_notifier_; SystemDnsConfigChangeNotifier* system_dns_config_notifier_;
std::unique_ptr<SystemDnsConfigObserver> system_dns_config_observer_; std::unique_ptr<SystemDnsConfigObserver> system_dns_config_observer_;
......
...@@ -198,7 +198,7 @@ TEST(NetworkChangeNotifierTest, GetConnectionSubtype) { ...@@ -198,7 +198,7 @@ TEST(NetworkChangeNotifierTest, GetConnectionSubtype) {
} }
class NetworkChangeNotifierMockedTest : public TestWithTaskEnvironment { class NetworkChangeNotifierMockedTest : public TestWithTaskEnvironment {
private: protected:
test::ScopedMockNetworkChangeNotifier mock_notifier_; test::ScopedMockNetworkChangeNotifier mock_notifier_;
}; };
...@@ -226,4 +226,92 @@ TEST_F(NetworkChangeNotifierMockedTest, TriggerNonSystemDnsChange) { ...@@ -226,4 +226,92 @@ TEST_F(NetworkChangeNotifierMockedTest, TriggerNonSystemDnsChange) {
NetworkChangeNotifier::RemoveDNSObserver(&observer); NetworkChangeNotifier::RemoveDNSObserver(&observer);
} }
class TestConnectionCostObserver
: public NetworkChangeNotifier::ConnectionCostObserver {
public:
void OnConnectionCostChanged(
NetworkChangeNotifier::ConnectionCost cost) override {
cost_changed_inputs_.push_back(cost);
++cost_changed_calls_;
}
int cost_changed_calls() const { return cost_changed_calls_; }
std::vector<NetworkChangeNotifier::ConnectionCost> cost_changed_inputs()
const {
return cost_changed_inputs_;
}
private:
int cost_changed_calls_ = 0;
std::vector<NetworkChangeNotifier::ConnectionCost> cost_changed_inputs_;
};
TEST_F(NetworkChangeNotifierMockedTest, TriggerConnectionCostChange) {
TestConnectionCostObserver observer;
NetworkChangeNotifier::AddConnectionCostObserver(&observer);
ASSERT_EQ(0, observer.cost_changed_calls());
NetworkChangeNotifier::NotifyObserversOfConnectionCostChangeForTests(
NetworkChangeNotifier::CONNECTION_COST_METERED);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, observer.cost_changed_calls());
EXPECT_EQ(NetworkChangeNotifier::CONNECTION_COST_METERED,
observer.cost_changed_inputs()[0]);
NetworkChangeNotifier::RemoveConnectionCostObserver(&observer);
NetworkChangeNotifier::NotifyObserversOfConnectionCostChangeForTests(
NetworkChangeNotifier::CONNECTION_COST_UNMETERED);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, observer.cost_changed_calls());
}
TEST_F(NetworkChangeNotifierMockedTest, ConnectionCostDefaultsToCellular) {
mock_notifier_.mock_network_change_notifier()
->SetUseDefaultConnectionCostImplementation(true);
mock_notifier_.mock_network_change_notifier()->SetConnectionType(
NetworkChangeNotifier::CONNECTION_4G);
EXPECT_TRUE(NetworkChangeNotifier::IsConnectionCellular(
NetworkChangeNotifier::GetConnectionType()));
EXPECT_EQ(NetworkChangeNotifier::CONNECTION_COST_METERED,
NetworkChangeNotifier::GetConnectionCost());
mock_notifier_.mock_network_change_notifier()->SetConnectionType(
NetworkChangeNotifier::CONNECTION_WIFI);
EXPECT_FALSE(NetworkChangeNotifier::IsConnectionCellular(
NetworkChangeNotifier::GetConnectionType()));
EXPECT_EQ(NetworkChangeNotifier::CONNECTION_COST_UNMETERED,
NetworkChangeNotifier::GetConnectionCost());
}
class NetworkChangeNotifierConnectionCostTest : public TestWithTaskEnvironment {
public:
void SetUp() override {
network_change_notifier_ = NetworkChangeNotifier::CreateIfNeeded();
}
private:
// Allows creating a new NetworkChangeNotifier. Must be created before
// |network_change_notifier_| and destroyed after it to avoid DCHECK failures.
NetworkChangeNotifier::DisableForTest disable_for_test_;
std::unique_ptr<NetworkChangeNotifier> network_change_notifier_;
};
TEST_F(NetworkChangeNotifierConnectionCostTest, GetConnectionCost) {
EXPECT_NE(NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_UNKNOWN,
NetworkChangeNotifier::GetConnectionCost());
}
TEST_F(NetworkChangeNotifierConnectionCostTest, AddObserver) {
TestConnectionCostObserver observer;
EXPECT_NO_FATAL_FAILURE(
NetworkChangeNotifier::AddConnectionCostObserver(&observer));
// RunUntilIdle because the secondary work resulting from adding an observer
// may be posted to a task queue.
base::RunLoop().RunUntilIdle();
}
} // namespace net } // namespace net
This diff is collapsed.
...@@ -5,7 +5,11 @@ ...@@ -5,7 +5,11 @@
#ifndef NET_BASE_NETWORK_CHANGE_NOTIFIER_WIN_H_ #ifndef NET_BASE_NETWORK_CHANGE_NOTIFIER_WIN_H_
#define NET_BASE_NETWORK_CHANGE_NOTIFIER_WIN_H_ #define NET_BASE_NETWORK_CHANGE_NOTIFIER_WIN_H_
#include <netlistmgr.h>
#include <ocidl.h>
#include <windows.h> #include <windows.h>
#include <wrl.h>
#include <wrl/client.h>
#include <memory> #include <memory>
...@@ -14,6 +18,7 @@ ...@@ -14,6 +18,7 @@
#include "base/memory/scoped_refptr.h" #include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h" #include "base/sequence_checker.h"
#include "base/thread_annotations.h"
#include "base/timer/timer.h" #include "base/timer/timer.h"
#include "base/win/object_watcher.h" #include "base/win/object_watcher.h"
#include "net/base/net_export.h" #include "net/base/net_export.h"
...@@ -25,6 +30,8 @@ class SequencedTaskRunner; ...@@ -25,6 +30,8 @@ class SequencedTaskRunner;
namespace net { namespace net {
class NetworkCostManagerEventSink;
// NetworkChangeNotifierWin uses a SequenceChecker, as all its internal // NetworkChangeNotifierWin uses a SequenceChecker, as all its internal
// notification code must be called on the sequence it is created and destroyed // notification code must be called on the sequence it is created and destroyed
// on. All the NetworkChangeNotifier methods it implements are threadsafe. // on. All the NetworkChangeNotifier methods it implements are threadsafe.
...@@ -57,6 +64,8 @@ class NET_EXPORT_PRIVATE NetworkChangeNotifierWin ...@@ -57,6 +64,8 @@ class NET_EXPORT_PRIVATE NetworkChangeNotifierWin
friend class TestNetworkChangeNotifierWin; friend class TestNetworkChangeNotifierWin;
// NetworkChangeNotifier methods: // NetworkChangeNotifier methods:
ConnectionCost GetCurrentConnectionCost() override;
ConnectionType GetCurrentConnectionType() const override; ConnectionType GetCurrentConnectionType() const override;
// ObjectWatcher::Delegate methods: // ObjectWatcher::Delegate methods:
...@@ -91,6 +100,26 @@ class NET_EXPORT_PRIVATE NetworkChangeNotifierWin ...@@ -91,6 +100,26 @@ class NET_EXPORT_PRIVATE NetworkChangeNotifierWin
static NetworkChangeCalculatorParams NetworkChangeCalculatorParamsWin(); static NetworkChangeCalculatorParams NetworkChangeCalculatorParamsWin();
// Gets the current network connection cost (if possible) and caches it.
void InitializeConnectionCost();
// Does the work of initializing for thread safety.
bool InitializeConnectionCostOnce();
// Retrieves the current network connection cost from the OS's Cost Manager.
HRESULT UpdateConnectionCostFromCostManager();
// Converts the OS enum values to the enum values used in our code.
static ConnectionCost ConnectionCostFromNlmCost(NLM_CONNECTION_COST cost);
// Sets the cached network connection cost value.
void SetCurrentConnectionCost(ConnectionCost connection_cost);
// Callback method for the notification event sink.
void OnCostChanged();
// Tells this class that an observer was added and therefore this class needs
// to register for notifications.
void ConnectionCostObserverAdded() override;
// Since ConnectionCostObserverAdded() can be called on any thread and we
// don't want to do a bunch of work on an arbitrary thread, this method used
// to post task to do the work.
void OnConnectionCostObserverAdded();
// All member variables may only be accessed on the sequence |this| was // All member variables may only be accessed on the sequence |this| was
// created on. // created on.
...@@ -112,12 +141,23 @@ class NET_EXPORT_PRIVATE NetworkChangeNotifierWin ...@@ -112,12 +141,23 @@ class NET_EXPORT_PRIVATE NetworkChangeNotifierWin
mutable base::Lock last_computed_connection_type_lock_; mutable base::Lock last_computed_connection_type_lock_;
ConnectionType last_computed_connection_type_; ConnectionType last_computed_connection_type_;
std::atomic<ConnectionCost> last_computed_connection_cost_;
// Result of IsOffline() when NotifyObserversOfConnectionTypeChange() // Result of IsOffline() when NotifyObserversOfConnectionTypeChange()
// was last called. // was last called.
bool last_announced_offline_; bool last_announced_offline_;
// Number of times polled to check if still offline. // Number of times polled to check if still offline.
int offline_polls_; int offline_polls_;
Microsoft::WRL::ComPtr<INetworkCostManager> network_cost_manager_;
Microsoft::WRL::ComPtr<NetworkCostManagerEventSink>
network_cost_manager_event_sink_;
// Used to ensure that all registration actions are properly sequenced on the
// same thread regardless of which thread was used to call into the
// NetworkChangeNotifier API.
scoped_refptr<base::SequencedTaskRunner> sequence_runner_for_registration_;
SEQUENCE_CHECKER(sequence_checker_); SEQUENCE_CHECKER(sequence_checker_);
// Used for calling WatchForAddressChange again on failure. // Used for calling WatchForAddressChange again on failure.
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "base/run_loop.h" #include "base/run_loop.h"
#include "base/single_thread_task_runner.h" #include "base/single_thread_task_runner.h"
#include "base/threading/thread_task_runner_handle.h" #include "base/threading/thread_task_runner_handle.h"
#include "base/win/windows_version.h"
#include "net/base/network_change_notifier.h" #include "net/base/network_change_notifier.h"
#include "net/base/network_change_notifier_factory.h" #include "net/base/network_change_notifier_factory.h"
#include "net/test/test_with_task_environment.h" #include "net/test/test_with_task_environment.h"
...@@ -30,6 +31,8 @@ class TestNetworkChangeNotifierWin : public NetworkChangeNotifierWin { ...@@ -30,6 +31,8 @@ class TestNetworkChangeNotifierWin : public NetworkChangeNotifierWin {
TestNetworkChangeNotifierWin() { TestNetworkChangeNotifierWin() {
last_computed_connection_type_ = NetworkChangeNotifier::CONNECTION_UNKNOWN; last_computed_connection_type_ = NetworkChangeNotifier::CONNECTION_UNKNOWN;
last_announced_offline_ = false; last_announced_offline_ = false;
last_computed_connection_cost_ = ConnectionCost::CONNECTION_COST_UNKNOWN;
sequence_runner_for_registration_ = base::SequencedTaskRunnerHandle::Get();
} }
TestNetworkChangeNotifierWin(const TestNetworkChangeNotifierWin&) = delete; TestNetworkChangeNotifierWin(const TestNetworkChangeNotifierWin&) = delete;
...@@ -216,6 +219,23 @@ class NetworkChangeNotifierWinTest : public TestWithTaskEnvironment { ...@@ -216,6 +219,23 @@ class NetworkChangeNotifierWinTest : public TestWithTaskEnvironment {
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
} }
bool HasNetworkCostManager() {
return network_change_notifier_.network_cost_manager_.Get() != nullptr;
}
bool HasNetworkCostManagerEventSink() {
return network_change_notifier_.network_cost_manager_event_sink_.Get() !=
nullptr;
}
NetworkChangeNotifier::ConnectionCost LastComputedConnectionCost() {
return network_change_notifier_.last_computed_connection_cost_;
}
NetworkChangeNotifier::ConnectionCost GetCurrentConnectionCost() {
return network_change_notifier_.GetCurrentConnectionCost();
}
private: private:
// Note that the order of declaration here is important. // Note that the order of declaration here is important.
...@@ -267,4 +287,58 @@ TEST_F(NetworkChangeNotifierWinTest, NetChangeWinFailSignalTwice) { ...@@ -267,4 +287,58 @@ TEST_F(NetworkChangeNotifierWinTest, NetChangeWinFailSignalTwice) {
RetryAndSucceed(); RetryAndSucceed();
} }
class TestConnectionCostObserver
: public NetworkChangeNotifier::ConnectionCostObserver {
public:
TestConnectionCostObserver() {}
TestConnectionCostObserver(const TestConnectionCostObserver&) = delete;
TestConnectionCostObserver& operator=(const TestConnectionCostObserver&) =
delete;
~TestConnectionCostObserver() override {
NetworkChangeNotifier::RemoveConnectionCostObserver(this);
}
void OnConnectionCostChanged(NetworkChangeNotifier::ConnectionCost) override {
}
void Register() { NetworkChangeNotifier::AddConnectionCostObserver(this); }
};
TEST_F(NetworkChangeNotifierWinTest, NetworkCostManagerIntegration) {
// NetworkCostManager integration only exist on Win10+.
if (base::win::GetVersion() < base::win::Version::WIN10)
return;
// Upon creation, none of the NetworkCostManager integration should be
// initialized yet.
ASSERT_FALSE(HasNetworkCostManager());
ASSERT_FALSE(HasNetworkCostManagerEventSink());
ASSERT_EQ(NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_UNKNOWN,
LastComputedConnectionCost());
// Asking for the current connection cost should initialize the
// NetworkCostManager integration, but not the event sink.
// Note that the actual ConnectionCost value return is irrelevant beyond the
// fact that it shouldn't be UNKNOWN anymore if the integration is initialized
// properly.
NetworkChangeNotifier::ConnectionCost current_connection_cost =
GetCurrentConnectionCost();
EXPECT_NE(NetworkChangeNotifier::ConnectionCost::CONNECTION_COST_UNKNOWN,
current_connection_cost);
EXPECT_EQ(current_connection_cost, LastComputedConnectionCost());
EXPECT_TRUE(HasNetworkCostManager());
EXPECT_FALSE(HasNetworkCostManagerEventSink());
// Adding a ConnectionCostObserver should initialize the event sink. If the
// subsequent registration for updates fails, the event sink will get
// destroyed.
TestConnectionCostObserver test_connection_cost_observer;
test_connection_cost_observer.Register();
// The actual registration happens on a callback, so need to run until idle.
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(HasNetworkCostManagerEventSink());
}
} // namespace net } // namespace net
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment