Skip to content

Commit

Permalink
Add connection cost support on Android
Browse files Browse the repository at this point in the history
Following previous CL[1], this CL makes sure UMA metric[2] is reported
when Android users declare their Wi-Fi network connection is metered.

[1]: https://chromium-review.googlesource.com/c/chromium/src/+/3535704
[2]: Net.NetworkChangeNotifier.NewConnectionCost histogram

Change-Id: I4d633cf64be8430a734a5328629bde665df14bda
Bug: 1297055
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3536788
Reviewed-by: Nate Fischer <ntfschr@chromium.org>
Reviewed-by: Yutaka Hirano <yhirano@chromium.org>
Reviewed-by: David Trainor <dtrainor@chromium.org>
Commit-Queue: Fr <beaufort.francois@gmail.com>
Cr-Commit-Position: refs/heads/main@{#985458}
  • Loading branch information
beaufortfrancois authored and Chromium LUCI CQ committed Mar 25, 2022
1 parent bb378f4 commit 49bb83e
Show file tree
Hide file tree
Showing 13 changed files with 260 additions and 11 deletions.
Expand Up @@ -44,6 +44,8 @@ AwNetworkChangeNotifier::GetCurrentDefaultNetwork() const {

void AwNetworkChangeNotifier::OnConnectionTypeChanged() {}

void AwNetworkChangeNotifier::OnConnectionCostChanged() {}

void AwNetworkChangeNotifier::OnMaxBandwidthChanged(
double max_bandwidth_mbps,
ConnectionType type) {
Expand Down
Expand Up @@ -44,6 +44,7 @@ class AwNetworkChangeNotifier

// NetworkChangeNotifierDelegateAndroid::Observer:
void OnConnectionTypeChanged() override;
void OnConnectionCostChanged() override;
void OnMaxBandwidthChanged(double max_bandwidth_mbps,
ConnectionType type) override;
void OnNetworkConnected(NetworkHandle network) override;
Expand Down
Expand Up @@ -101,6 +101,8 @@ public void onConnectionTypeChanged(int newConnectionType) {
runOnMainThread(() -> { mObserver.onConnectionTypeChanged(newConnectionType); });
}
@Override
public void onConnectionCostChanged(int newConnectionCost) {}
@Override
public void onConnectionSubtypeChanged(int newConnectionSubtype) {}
@Override
public void onNetworkConnect(long netId, int connectionType) {}
Expand Down
Expand Up @@ -142,6 +142,9 @@ public void onConnectionTypeChanged(@ConnectionType int newConnectionType) {
broadcastNetworkChangeIfNecessary(newConnectionType);
}

@Override
public void onConnectionCostChanged(int newConnectionCost) {}

@Override
public void onConnectionSubtypeChanged(int newConnectionSubtype) {}

Expand Down
39 changes: 39 additions & 0 deletions net/android/java/src/org/chromium/net/NetworkChangeNotifier.java
Expand Up @@ -41,6 +41,8 @@ public interface ConnectionTypeObserver {
private NetworkChangeNotifierAutoDetect mAutoDetector;
// Last value broadcast via ConnectionTypeChange signal.
private int mCurrentConnectionType = ConnectionType.CONNECTION_UNKNOWN;
// Last value broadcast via ConnectionCostChange signal.
private int mCurrentConnectionCost = ConnectionCost.UNKNOWN;

@SuppressLint("StaticFieldLeak")
private static NetworkChangeNotifier sInstance;
Expand Down Expand Up @@ -83,6 +85,11 @@ public int getCurrentConnectionSubtype() {
: mAutoDetector.getCurrentNetworkState().getConnectionSubtype();
}

@CalledByNative
public int getCurrentConnectionCost() {
return mCurrentConnectionCost;
}

/**
* Returns NetID of device's current default connected network used for
* communication. Only available on Lollipop and newer releases and when
Expand Down Expand Up @@ -192,6 +199,10 @@ public void onConnectionTypeChanged(int newConnectionType) {
updateCurrentConnectionType(newConnectionType);
}
@Override
public void onConnectionCostChanged(int newConnectionCost) {
notifyObserversOfConnectionCostChange(newConnectionCost);
}
@Override
public void onConnectionSubtypeChanged(int newConnectionSubtype) {
notifyObserversOfConnectionSubtypeChange(newConnectionSubtype);
}
Expand All @@ -216,6 +227,7 @@ public void purgeActiveNetworkList(long[] activeNetIds) {
final NetworkChangeNotifierAutoDetect.NetworkState networkState =
mAutoDetector.getCurrentNetworkState();
updateCurrentConnectionType(networkState.getConnectionType());
updateCurrentConnectionCost(networkState.getConnectionCost());
notifyObserversOfConnectionSubtypeChange(networkState.getConnectionSubtype());
}
} else {
Expand Down Expand Up @@ -282,6 +294,14 @@ public static void fakeDefaultNetwork(long netId, int connectionType) {
getInstance().notifyObserversOfConnectionTypeChange(connectionType, netId);
}

// For testing, pretend the connection cost has changed.
@CalledByNative
@VisibleForTesting
public static void fakeConnectionCostChanged(int connectionCost) {
setAutoDetectConnectivityState(false);
getInstance().notifyObserversOfConnectionCostChange(connectionCost);
}

// For testing, pretend the connection subtype has changed.
@CalledByNative
public static void fakeConnectionSubtypeChanged(int connectionSubtype) {
Expand Down Expand Up @@ -311,6 +331,21 @@ private void notifyObserversOfConnectionTypeChange(int newConnectionType, long d
}
}

private void updateCurrentConnectionCost(int newConnectionCost) {
mCurrentConnectionCost = newConnectionCost;
notifyObserversOfConnectionCostChange(newConnectionCost);
}

/**
* Alerts all observers of a connection cost change.
*/
void notifyObserversOfConnectionCostChange(int newConnectionCost) {
for (Long nativeChangeNotifier : mNativeChangeNotifiers) {
NetworkChangeNotifierJni.get().notifyConnectionCostChanged(
nativeChangeNotifier, NetworkChangeNotifier.this, newConnectionCost);
}
}

/**
* Alerts all observers of a bandwidth change.
*/
Expand Down Expand Up @@ -405,6 +440,10 @@ interface Natives {
void notifyConnectionTypeChanged(long nativePtr, NetworkChangeNotifier caller,
int newConnectionType, long defaultNetId);

@NativeClassQualifiedName("NetworkChangeNotifierDelegateAndroid")
void notifyConnectionCostChanged(
long nativePtr, NetworkChangeNotifier caller, int newConnectionCost);

@NativeClassQualifiedName("NetworkChangeNotifierDelegateAndroid")
void notifyMaxBandwidthChanged(long nativePtr, NetworkChangeNotifier caller, int subType);

Expand Down
Expand Up @@ -63,6 +63,7 @@ public static class NetworkState {
private final boolean mConnected;
private final int mType;
private final int mSubtype;
private final boolean mIsMetered;
// WIFI SSID of the connection on pre-Marshmallow, NetID starting with Marshmallow. Always
// non-null (i.e. instead of null it'll be an empty string) to facilitate .equals().
private final String mNetworkIdentifier;
Expand All @@ -71,11 +72,12 @@ public static class NetworkState {
// Indicates the DNS-over-TLS server in use, if specified.
private final String mPrivateDnsServerName;

public NetworkState(boolean connected, int type, int subtype, String networkIdentifier,
boolean isPrivateDnsActive, String privateDnsServerName) {
public NetworkState(boolean connected, int type, int subtype, boolean isMetered,
String networkIdentifier, boolean isPrivateDnsActive, String privateDnsServerName) {
mConnected = connected;
mType = type;
mSubtype = subtype;
mIsMetered = isMetered;
mNetworkIdentifier = networkIdentifier == null ? "" : networkIdentifier;
mIsPrivateDnsActive = isPrivateDnsActive;
mPrivateDnsServerName = privateDnsServerName == null ? "" : privateDnsServerName;
Expand All @@ -89,6 +91,10 @@ public int getNetworkType() {
return mType;
}

public boolean isMetered() {
return mIsMetered;
}

public int getNetworkSubType() {
return mSubtype;
}
Expand All @@ -109,6 +115,17 @@ public int getConnectionType() {
return convertToConnectionType(getNetworkType(), getNetworkSubType());
}

/**
* Returns the connection cost for the given NetworkState.
*/
@ConnectionCost
public int getConnectionCost() {
if (isMetered()) {
return ConnectionCost.METERED;
}
return ConnectionCost.UNMETERED;
}

/**
* Returns the connection subtype for the given NetworkState.
*/
Expand Down Expand Up @@ -246,16 +263,21 @@ NetworkState getNetworkState(WifiManagerDelegate wifiManagerDelegate) {
}
networkInfo = processActiveNetworkInfo(networkInfo);
if (networkInfo == null) {
return new NetworkState(false, -1, -1, null, false, "");
return new NetworkState(false, -1, -1, false, null, false, "");
}

if (network != null) {
final NetworkCapabilities capabilities = getNetworkCapabilities(network);
boolean isMetered = (capabilities != null
&& !capabilities.hasCapability(
NetworkCapabilities.NET_CAPABILITY_NOT_METERED));
DnsStatus dnsStatus = AndroidNetworkLibrary.getDnsStatus(network);
if (dnsStatus == null) {
return new NetworkState(true, networkInfo.getType(), networkInfo.getSubtype(),
String.valueOf(networkToNetId(network)), false, "");
isMetered, String.valueOf(networkToNetId(network)), false, "");
} else {
return new NetworkState(true, networkInfo.getType(), networkInfo.getSubtype(),
String.valueOf(networkToNetId(network)),
isMetered, String.valueOf(networkToNetId(network)),
dnsStatus.getPrivateDnsActive(), dnsStatus.getPrivateDnsServerName());
}
}
Expand All @@ -265,14 +287,14 @@ NetworkState getNetworkState(WifiManagerDelegate wifiManagerDelegate) {
// Since Android 4.2 the SSID can be retrieved from NetworkInfo.getExtraInfo().
if (networkInfo.getExtraInfo() != null && !"".equals(networkInfo.getExtraInfo())) {
return new NetworkState(true, networkInfo.getType(), networkInfo.getSubtype(),
networkInfo.getExtraInfo(), false, "");
false, networkInfo.getExtraInfo(), false, "");
}
// Fetch WiFi SSID directly from WifiManagerDelegate if not in NetworkInfo.
return new NetworkState(true, networkInfo.getType(), networkInfo.getSubtype(),
wifiManagerDelegate.getWifiSsid(), false, "");
false, wifiManagerDelegate.getWifiSsid(), false, "");
}
return new NetworkState(
true, networkInfo.getType(), networkInfo.getSubtype(), null, false, "");
true, networkInfo.getType(), networkInfo.getSubtype(), false, null, false, "");
}

/**
Expand Down Expand Up @@ -591,7 +613,7 @@ public void onLost(final Network network) {
mLinkProperties = null;
mNetworkCapabilities = null;
if (mRegistered) {
connectionTypeChangedTo(new NetworkState(false, -1, -1, null, false, ""));
connectionTypeChangedTo(new NetworkState(false, -1, -1, false, null, false, ""));
}
}

Expand Down Expand Up @@ -648,7 +670,10 @@ private NetworkState getNetworkState(Network network) {
NetworkInfo networkInfo = mConnectivityManagerDelegate.getNetworkInfo(network);
type = networkInfo != null ? networkInfo.getType() : ConnectivityManager.TYPE_VPN;
}
return new NetworkState(true, type, subtype, String.valueOf(networkToNetId(network)),
boolean isMetered = !mNetworkCapabilities.hasCapability(
NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
return new NetworkState(true, type, subtype, isMetered,
String.valueOf(networkToNetId(network)),
ApiHelperForP.isPrivateDnsActive(mLinkProperties),
ApiHelperForP.getPrivateDnsServerName(mLinkProperties));
}
Expand Down Expand Up @@ -896,6 +921,10 @@ public static interface Observer {
* Called when default network changes.
*/
public void onConnectionTypeChanged(@ConnectionType int newConnectionType);
/**
* Called when connection cost of default network changes.
*/
public void onConnectionCostChanged(int newConnectionCost);
/**
* Called when connection subtype of default network changes.
*/
Expand Down Expand Up @@ -1284,6 +1313,9 @@ private void connectionTypeChangedTo(NetworkState networkState) {
|| networkState.getConnectionSubtype() != mNetworkState.getConnectionSubtype()) {
mObserver.onConnectionSubtypeChanged(networkState.getConnectionSubtype());
}
if (networkState.getConnectionCost() != mNetworkState.getConnectionCost()) {
mObserver.onConnectionCostChanged(networkState.getConnectionCost());
}
mNetworkState = networkState;
}

Expand Down
Expand Up @@ -189,14 +189,15 @@ NetworkCapabilities getCapabilities() {
private boolean mActiveNetworkExists;
private int mNetworkType;
private int mNetworkSubtype;
private boolean mIsMetered;
private boolean mIsPrivateDnsActive;
private String mPrivateDnsServerName;
private NetworkCallback mLastRegisteredNetworkCallback;
private NetworkCallback mLastRegisteredDefaultNetworkCallback;

@Override
public NetworkState getNetworkState(WifiManagerDelegate wifiManagerDelegate) {
return new NetworkState(mActiveNetworkExists, mNetworkType, mNetworkSubtype,
return new NetworkState(mActiveNetworkExists, mNetworkType, mNetworkSubtype, mIsMetered,
mNetworkType == ConnectivityManager.TYPE_WIFI
? wifiManagerDelegate.getWifiSsid()
: null,
Expand Down Expand Up @@ -271,6 +272,10 @@ public void setNetworkType(int networkType) {
mNetworkType = networkType;
}

public void setIsMetered(boolean isMetered) {
mIsMetered = isMetered;
}

public void setNetworkSubtype(int networkSubtype) {
mNetworkSubtype = networkSubtype;
}
Expand Down Expand Up @@ -374,6 +379,8 @@ private static class TestNetworkChangeNotifierAutoDetectObserver
@Override
public void onConnectionTypeChanged(int newConnectionType) {}
@Override
public void onConnectionCostChanged(int newConnectionCost) {}
@Override
public void onConnectionSubtypeChanged(int newConnectionSubtype) {}

@Override
Expand Down Expand Up @@ -485,6 +492,10 @@ private int getCurrentConnectionType() {
return mReceiver.getCurrentNetworkState().getConnectionType();
}

private int getCurrentConnectionCost() {
return mReceiver.getCurrentNetworkState().getConnectionCost();
}

@Before
public void setUp() throws Throwable {
LibraryLoader.getInstance().setLibraryProcessType(LibraryProcessType.PROCESS_BROWSER);
Expand Down Expand Up @@ -558,6 +569,20 @@ public void testNetworkChangeNotifierRegistersForIntents() {
Assert.assertTrue(mReceiver.isReceiverRegisteredForTesting());
}

/**
* Tests that getCurrentConnectionCost() returns the correct result.
*/
@Test
@UiThreadTest
@MediumTest
@Feature({"Android-AppBase"})
public void testNetworkChangeNotifierConnectionCost() {
mConnectivityDelegate.setIsMetered(true);
Assert.assertEquals(ConnectionCost.METERED, getCurrentConnectionCost());
mConnectivityDelegate.setIsMetered(false);
Assert.assertEquals(ConnectionCost.UNMETERED, getCurrentConnectionCost());
}

/**
* Tests that changing the network type changes the connection subtype.
*/
Expand Down
9 changes: 9 additions & 0 deletions net/android/network_change_notifier_android.cc
Expand Up @@ -124,6 +124,11 @@ NetworkChangeNotifierAndroid::GetCurrentConnectionType() const {
return delegate_->GetCurrentConnectionType();
}

NetworkChangeNotifier::ConnectionCost
NetworkChangeNotifierAndroid::GetCurrentConnectionCost() {
return delegate_->GetCurrentConnectionCost();
}

NetworkChangeNotifier::ConnectionSubtype
NetworkChangeNotifierAndroid::GetCurrentConnectionSubtype() const {
return delegate_->GetCurrentConnectionSubtype();
Expand Down Expand Up @@ -169,6 +174,10 @@ void NetworkChangeNotifierAndroid::OnConnectionTypeChanged() {
BlockingThreadObjects::NotifyNetworkChangeNotifierObservers();
}

void NetworkChangeNotifierAndroid::OnConnectionCostChanged() {
NetworkChangeNotifier::NotifyObserversOfConnectionCostChange();
}

void NetworkChangeNotifierAndroid::OnMaxBandwidthChanged(
double max_bandwidth_mbps,
ConnectionType type) {
Expand Down
2 changes: 2 additions & 0 deletions net/android/network_change_notifier_android.h
Expand Up @@ -63,6 +63,7 @@ class NET_EXPORT_PRIVATE NetworkChangeNotifierAndroid

// NetworkChangeNotifier:
ConnectionType GetCurrentConnectionType() const override;
ConnectionCost GetCurrentConnectionCost() override;
// Requires ACCESS_WIFI_STATE permission in order to provide precise WiFi link
// speed.
void GetCurrentMaxBandwidthAndConnectionType(
Expand All @@ -79,6 +80,7 @@ class NET_EXPORT_PRIVATE NetworkChangeNotifierAndroid

// NetworkChangeNotifierDelegateAndroid::Observer:
void OnConnectionTypeChanged() override;
void OnConnectionCostChanged() override;
void OnMaxBandwidthChanged(double max_bandwidth_mbps,
ConnectionType type) override;
void OnNetworkConnected(NetworkHandle network) override;
Expand Down

0 comments on commit 49bb83e

Please sign in to comment.