summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
Diffstat (limited to 'net')
-rw-r--r--net/android/BUILD.gn2
-rw-r--r--net/android/java/src/org/chromium/net/NetworkChangeNotifier.java135
-rw-r--r--net/android/java/src/org/chromium/net/NetworkChangeNotifierAutoDetect.java302
-rw-r--r--net/android/javatests/src/org/chromium/net/NetworkChangeNotifierTest.java293
-rw-r--r--net/android/network_change_notifier_android.cc51
-rw-r--r--net/android/network_change_notifier_android.h8
-rw-r--r--net/android/network_change_notifier_android_unittest.cc197
-rw-r--r--net/android/network_change_notifier_delegate_android.cc199
-rw-r--r--net/android/network_change_notifier_delegate_android.h63
-rw-r--r--net/base/network_change_notifier.cc93
-rw-r--r--net/base/network_change_notifier.h96
-rw-r--r--net/dns/address_sorter_posix_unittest.cc4
-rw-r--r--net/net.gyp9
-rw-r--r--net/socket/socket_test_util.cc10
-rw-r--r--net/socket/socket_test_util.h2
-rw-r--r--net/tools/net_watcher/net_watcher.cc2
-rw-r--r--net/udp/datagram_client_socket.h8
-rw-r--r--net/udp/udp_client_socket.cc5
-rw-r--r--net/udp/udp_client_socket.h1
-rw-r--r--net/udp/udp_socket_posix.cc41
-rw-r--r--net/udp/udp_socket_posix.h7
-rw-r--r--net/udp/udp_socket_win.cc5
-rw-r--r--net/udp/udp_socket_win.h7
23 files changed, 1488 insertions, 52 deletions
diff --git a/net/android/BUILD.gn b/net/android/BUILD.gn
index 0003962..3344ab7 100644
--- a/net/android/BUILD.gn
+++ b/net/android/BUILD.gn
@@ -70,6 +70,7 @@ java_cpp_enum("net_android_java_enums_srcjar") {
"../base/network_change_notifier.h",
"cert_verify_result_android.h",
"keystore.h",
+ "network_change_notifier_android.cc",
"traffic_stats.cc",
]
outputs = [
@@ -77,6 +78,7 @@ java_cpp_enum("net_android_java_enums_srcjar") {
"org/chromium/net/CertVerifyStatusAndroid.java",
"org/chromium/net/ConnectionSubtype.java",
"org/chromium/net/ConnectionType.java",
+ "org/chromium/net/NetId.java",
"org/chromium/net/PrivateKeyType.java",
"org/chromium/net/TrafficStatsError.java",
]
diff --git a/net/android/java/src/org/chromium/net/NetworkChangeNotifier.java b/net/android/java/src/org/chromium/net/NetworkChangeNotifier.java
index 563ef5c..4008afa 100644
--- a/net/android/java/src/org/chromium/net/NetworkChangeNotifier.java
+++ b/net/android/java/src/org/chromium/net/NetworkChangeNotifier.java
@@ -78,6 +78,29 @@ public class NetworkChangeNotifier {
}
/**
+ * Returns NetID of device's current default connected network used for
+ * communication. Only available on Lollipop and newer releases and when
+ * auto-detection has been enabled, returns NetId.INVALID otherwise.
+ */
+ @CalledByNative
+ public int getCurrentDefaultNetId() {
+ return mAutoDetector == null ? NetId.INVALID : mAutoDetector.getDefaultNetId();
+ }
+
+ /**
+ * Returns an array of all of the device's currently connected
+ * networks and ConnectionTypes. Array elements are a repeated sequence of:
+ * NetID of network
+ * ConnectionType of network
+ * Only available on Lollipop and newer releases and when auto-detection has
+ * been enabled.
+ */
+ @CalledByNative
+ public int[] getCurrentNetworksAndTypes() {
+ return mAutoDetector == null ? new int[0] : mAutoDetector.getNetworksAndTypes();
+ }
+
+ /**
* Calls a native map lookup of subtype to max bandwidth.
*/
public static double getMaxBandwidthForConnectionSubtype(int subtype) {
@@ -153,6 +176,22 @@ public class NetworkChangeNotifier {
public void onMaxBandwidthChanged(double maxBandwidthMbps) {
updateCurrentMaxBandwidth(maxBandwidthMbps);
}
+ @Override
+ public void onNetworkConnect(int netId, int connectionType) {
+ notifyObserversOfNetworkConnect(netId, connectionType);
+ }
+ @Override
+ public void onNetworkSoonToDisconnect(int netId) {
+ notifyObserversOfNetworkSoonToDisconnect(netId);
+ }
+ @Override
+ public void onNetworkDisconnect(int netId) {
+ notifyObserversOfNetworkDisconnect(netId);
+ }
+ @Override
+ public void updateActiveNetworkList(int[] activeNetIds) {
+ notifyObserversToUpdateActiveNetworkList(activeNetIds);
+ }
},
mContext,
alwaysWatchForChanges);
@@ -188,6 +227,41 @@ public class NetworkChangeNotifier {
}
}
+ // For testing, pretend a network connected.
+ @CalledByNative
+ public static void fakeNetworkConnected(int netId, int connectionType) {
+ setAutoDetectConnectivityState(false);
+ getInstance().notifyObserversOfNetworkConnect(netId, connectionType);
+ }
+
+ // For testing, pretend a network will soon disconnect.
+ @CalledByNative
+ public static void fakeNetworkSoonToBeDisconnected(int netId) {
+ setAutoDetectConnectivityState(false);
+ getInstance().notifyObserversOfNetworkSoonToDisconnect(netId);
+ }
+
+ // For testing, pretend a network disconnected.
+ @CalledByNative
+ public static void fakeNetworkDisconnected(int netId) {
+ setAutoDetectConnectivityState(false);
+ getInstance().notifyObserversOfNetworkDisconnect(netId);
+ }
+
+ // For testing, pretend a network lists should be purged.
+ @CalledByNative
+ public static void fakeUpdateActiveNetworkList(int[] activeNetIds) {
+ setAutoDetectConnectivityState(false);
+ getInstance().notifyObserversToUpdateActiveNetworkList(activeNetIds);
+ }
+
+ // For testing, pretend a default network changed.
+ @CalledByNative
+ public static void fakeDefaultNetwork(int netId, int connectionType) {
+ setAutoDetectConnectivityState(false);
+ getInstance().notifyObserversOfConnectionTypeChange(connectionType, netId);
+ }
+
private void updateCurrentConnectionType(int newConnectionType) {
mCurrentConnectionType = newConnectionType;
notifyObserversOfConnectionTypeChange(newConnectionType);
@@ -203,8 +277,13 @@ public class NetworkChangeNotifier {
* Alerts all observers of a connection change.
*/
void notifyObserversOfConnectionTypeChange(int newConnectionType) {
+ notifyObserversOfConnectionTypeChange(newConnectionType, getCurrentDefaultNetId());
+ }
+
+ private void notifyObserversOfConnectionTypeChange(int newConnectionType, int defaultNetId) {
for (Long nativeChangeNotifier : mNativeChangeNotifiers) {
- nativeNotifyConnectionTypeChanged(nativeChangeNotifier, newConnectionType);
+ nativeNotifyConnectionTypeChanged(
+ nativeChangeNotifier, newConnectionType, defaultNetId);
}
for (ConnectionTypeObserver observer : mConnectionTypeObservers) {
observer.onConnectionTypeChanged(newConnectionType);
@@ -221,6 +300,45 @@ public class NetworkChangeNotifier {
}
/**
+ * Alerts all observers of a network connect.
+ */
+ void notifyObserversOfNetworkConnect(int netId, int connectionType) {
+ for (Long nativeChangeNotifier : mNativeChangeNotifiers) {
+ nativeNotifyOfNetworkConnect(nativeChangeNotifier, netId, connectionType);
+ }
+ }
+
+ /**
+ * Alerts all observers of a network soon to be disconnected.
+ */
+ void notifyObserversOfNetworkSoonToDisconnect(int netId) {
+ for (Long nativeChangeNotifier : mNativeChangeNotifiers) {
+ nativeNotifyOfNetworkSoonToDisconnect(nativeChangeNotifier, netId);
+ }
+ }
+
+ /**
+ * Alerts all observers of a network disconnect.
+ */
+ void notifyObserversOfNetworkDisconnect(int netId) {
+ for (Long nativeChangeNotifier : mNativeChangeNotifiers) {
+ nativeNotifyOfNetworkDisconnect(nativeChangeNotifier, netId);
+ }
+ }
+
+ /**
+ * Alerts all observers to purge cached lists of active networks, of any
+ * networks not in the accompanying list of active networks. This is
+ * issued if a period elapsed where disconnected notifications may have
+ * been missed, and acts to keep cached lists of active networks accurate.
+ */
+ void notifyObserversToUpdateActiveNetworkList(int[] activeNetIds) {
+ for (Long nativeChangeNotifier : mNativeChangeNotifiers) {
+ nativeNotifyUpdateActiveNetworkList(nativeChangeNotifier, activeNetIds);
+ }
+ }
+
+ /**
* Adds an observer for any connection type changes.
*/
public static void addConnectionTypeObserver(ConnectionTypeObserver observer) {
@@ -243,11 +361,24 @@ public class NetworkChangeNotifier {
}
@NativeClassQualifiedName("NetworkChangeNotifierDelegateAndroid")
- private native void nativeNotifyConnectionTypeChanged(long nativePtr, int newConnectionType);
+ private native void nativeNotifyConnectionTypeChanged(
+ long nativePtr, int newConnectionType, int defaultNetId);
@NativeClassQualifiedName("NetworkChangeNotifierDelegateAndroid")
private native void nativeNotifyMaxBandwidthChanged(long nativePtr, double maxBandwidthMbps);
+ @NativeClassQualifiedName("NetworkChangeNotifierDelegateAndroid")
+ private native void nativeNotifyOfNetworkConnect(long nativePtr, int netId, int connectionType);
+
+ @NativeClassQualifiedName("NetworkChangeNotifierDelegateAndroid")
+ private native void nativeNotifyOfNetworkSoonToDisconnect(long nativePtr, int netId);
+
+ @NativeClassQualifiedName("NetworkChangeNotifierDelegateAndroid")
+ private native void nativeNotifyOfNetworkDisconnect(long nativePtr, int netId);
+
+ @NativeClassQualifiedName("NetworkChangeNotifierDelegateAndroid")
+ private native void nativeNotifyUpdateActiveNetworkList(long nativePtr, int[] activeNetIds);
+
private static native double nativeGetMaxBandwidthForConnectionSubtype(int subtype);
// For testing only.
diff --git a/net/android/java/src/org/chromium/net/NetworkChangeNotifierAutoDetect.java b/net/android/java/src/org/chromium/net/NetworkChangeNotifierAutoDetect.java
index 466d03f..839600a 100644
--- a/net/android/java/src/org/chromium/net/NetworkChangeNotifierAutoDetect.java
+++ b/net/android/java/src/org/chromium/net/NetworkChangeNotifierAutoDetect.java
@@ -4,21 +4,30 @@
package org.chromium.net;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+
import android.Manifest.permission;
+import android.annotation.SuppressLint;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.Network;
+import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
+import android.net.NetworkRequest;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
+import android.os.Build;
import android.telephony.TelephonyManager;
import android.util.Log;
import org.chromium.base.ApplicationState;
import org.chromium.base.ApplicationStatus;
+import org.chromium.base.ThreadUtils;
import org.chromium.base.VisibleForTesting;
/**
@@ -68,13 +77,109 @@ public class NetworkChangeNotifierAutoDetect extends BroadcastReceiver
mConnectivityManager = null;
}
+ /**
+ * Returns connection type and status information about the current
+ * default network.
+ */
NetworkState getNetworkState() {
- final NetworkInfo networkInfo = mConnectivityManager.getActiveNetworkInfo();
+ return getNetworkState(mConnectivityManager.getActiveNetworkInfo());
+ }
+
+ /**
+ * Returns connection type and status information about |network|.
+ * Only callable on Lollipop and newer releases.
+ */
+ @SuppressLint("NewApi")
+ NetworkState getNetworkState(Network network) {
+ return getNetworkState(mConnectivityManager.getNetworkInfo(network));
+ }
+
+ /**
+ * Returns connection type and status information gleaned from networkInfo.
+ */
+ NetworkState getNetworkState(NetworkInfo networkInfo) {
if (networkInfo == null || !networkInfo.isConnected()) {
return new NetworkState(false, -1, -1);
}
return new NetworkState(true, networkInfo.getType(), networkInfo.getSubtype());
}
+
+ /**
+ * Returns all connected networks.
+ * Only callable on Lollipop and newer releases.
+ */
+ @SuppressLint("NewApi")
+ Network[] getAllNetworks() {
+ return mConnectivityManager.getAllNetworks();
+ }
+
+ /**
+ * Registers networkCallback to receive notifications about networks
+ * that satisfy networkRequest.
+ * Only callable on Lollipop and newer releases.
+ */
+ @SuppressLint("NewApi")
+ void registerNetworkCallback(
+ NetworkRequest networkRequest, NetworkCallback networkCallback) {
+ mConnectivityManager.registerNetworkCallback(networkRequest, networkCallback);
+ }
+
+ /**
+ * Unregisters networkCallback from receiving notifications.
+ * Only callable on Lollipop and newer releases.
+ */
+ @SuppressLint("NewApi")
+ void unregisterNetworkCallback(NetworkCallback networkCallback) {
+ mConnectivityManager.unregisterNetworkCallback(networkCallback);
+ }
+
+ /**
+ * Returns the NetID of the current default network. Returns
+ * NetId.INVALID if no current default network connected.
+ * Only callable on Lollipop and newer releases.
+ */
+ @SuppressLint("NewApi")
+ int getDefaultNetId() {
+ // Android Lollipop had no API to get the default network; only an
+ // API to return the NetworkInfo for the default network. To
+ // determine the default network one can find the network with
+ // type matching that of the default network.
+ final NetworkInfo defaultNetworkInfo = mConnectivityManager.getActiveNetworkInfo();
+ if (defaultNetworkInfo == null) {
+ return NetId.INVALID;
+ }
+ final Network[] networks = getAllNetworks();
+ int defaultNetId = NetId.INVALID;
+ for (Network network : networks) {
+ if (!hasInternetCapability(network)) {
+ continue;
+ }
+ final NetworkInfo networkInfo = mConnectivityManager.getNetworkInfo(network);
+ if (networkInfo != null && networkInfo.getType() == defaultNetworkInfo.getType()) {
+ // There should not be multiple connected networks of the
+ // same type. At least as of Android Marshmallow this is
+ // not supported. If this becomes supported this assertion
+ // may trigger. At that point we could consider using
+ // ConnectivityManager.getDefaultNetwork() though this
+ // may give confusing results with VPNs and is only
+ // available with Android Marshmallow.
+ assert defaultNetId == NetId.INVALID;
+ defaultNetId = networkToNetId(network);
+ }
+ }
+ return defaultNetId;
+ }
+
+ /**
+ * Returns true if {@code network} can provide Internet access. Can be used to
+ * ignore specialized networks (e.g. IMS, FOTA).
+ */
+ @SuppressLint("NewApi")
+ boolean hasInternetCapability(Network network) {
+ final NetworkCapabilities capabilities =
+ mConnectivityManager.getNetworkCapabilities(network);
+ return capabilities != null && capabilities.hasCapability(NET_CAPABILITY_INTERNET);
+ }
}
/** Queries the WifiManager for SSID of the current Wifi connection. */
@@ -137,6 +242,64 @@ public class NetworkChangeNotifierAutoDetect extends BroadcastReceiver
}
}
+ // This class gets called back by ConnectivityManager whenever networks come
+ // and go. It gets called back on a special handler thread
+ // ConnectivityManager creates for making the callbacks. The callbacks in
+ // turn post to the UI thread where mObserver lives.
+ @SuppressLint("NewApi")
+ private class MyNetworkCallback extends NetworkCallback {
+ @Override
+ public void onAvailable(Network network) {
+ final int netId = networkToNetId(network);
+ final int connectionType =
+ getCurrentConnectionType(mConnectivityManagerDelegate.getNetworkState(network));
+ ThreadUtils.postOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mObserver.onNetworkConnect(netId, connectionType);
+ }
+ });
+ }
+
+ @Override
+ public void onCapabilitiesChanged(
+ Network network, NetworkCapabilities networkCapabilities) {
+ // A capabilities change may indicate the ConnectionType has changed,
+ // so forward the new ConnectionType along to observer.
+ final int netId = networkToNetId(network);
+ final int connectionType =
+ getCurrentConnectionType(mConnectivityManagerDelegate.getNetworkState(network));
+ ThreadUtils.postOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mObserver.onNetworkConnect(netId, connectionType);
+ }
+ });
+ }
+
+ @Override
+ public void onLosing(Network network, int maxMsToLive) {
+ final int netId = networkToNetId(network);
+ ThreadUtils.postOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mObserver.onNetworkSoonToDisconnect(netId);
+ }
+ });
+ }
+
+ @Override
+ public void onLost(Network network) {
+ final int netId = networkToNetId(network);
+ ThreadUtils.postOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mObserver.onNetworkDisconnect(netId);
+ }
+ });
+ }
+ }
+
private static final String TAG = "NetworkChangeNotifierAutoDetect";
private static final int UNKNOWN_LINK_SPEED = -1;
private final NetworkConnectivityIntentFilter mIntentFilter;
@@ -144,8 +307,12 @@ public class NetworkChangeNotifierAutoDetect extends BroadcastReceiver
private final Observer mObserver;
private final Context mContext;
+ // mConnectivityManagerDelegates and mWifiManagerDelegate are only non-final for testing.
private ConnectivityManagerDelegate mConnectivityManagerDelegate;
private WifiManagerDelegate mWifiManagerDelegate;
+ // mNetworkCallback and mNetworkRequest are only non-null in Android L and above.
+ private final NetworkCallback mNetworkCallback;
+ private final NetworkRequest mNetworkRequest;
private boolean mRegistered;
private final boolean mApplicationStateRegistered;
private int mConnectionType;
@@ -153,25 +320,71 @@ public class NetworkChangeNotifierAutoDetect extends BroadcastReceiver
private double mMaxBandwidthMbps;
/**
- * Observer notified on the UI thread whenever a new connection type was detected or max
- * bandwidth is changed.
+ * Observer interface by which observer is notified of network changes.
*/
public static interface Observer {
+ /**
+ * Called when default network changes.
+ */
public void onConnectionTypeChanged(int newConnectionType);
+ /**
+ * Called when maximum bandwidth of default network changes.
+ */
public void onMaxBandwidthChanged(double maxBandwidthMbps);
+ /**
+ * Called when device connects to network with NetID netId. For
+ * example device associates with a WiFi access point.
+ * connectionType is the type of the network; a member of
+ * ConnectionType. Only called on Android L and above.
+ */
+ public void onNetworkConnect(int netId, int connectionType);
+ /**
+ * Called when device determines the connection to the network with
+ * NetID netId is no longer preferred, for example when a device
+ * transitions from cellular to WiFi it might deem the cellular
+ * connection no longer preferred. The device will disconnect from
+ * the network in 30s allowing network communications on that network
+ * to wrap up. Only called on Android L and above.
+ */
+ public void onNetworkSoonToDisconnect(int netId);
+ /**
+ * Called when device disconnects from network with NetID netId.
+ * Only called on Android L and above.
+ */
+ public void onNetworkDisconnect(int netId);
+ /**
+ * Called to cause a purge of cached lists of active networks, of any
+ * networks not in the accompanying list of active networks. This is
+ * issued if a period elapsed where disconnected notifications may have
+ * been missed, and acts to keep cached lists of active networks
+ * accurate. Only called on Android L and above.
+ */
+ public void updateActiveNetworkList(int[] activeNetIds);
}
/**
- * Constructs a NetworkChangeNotifierAutoDetect.
+ * Constructs a NetworkChangeNotifierAutoDetect. Should only be called on UI thread.
* @param alwaysWatchForChanges If true, always watch for network changes.
* Otherwise, only watch if app is in foreground.
*/
- public NetworkChangeNotifierAutoDetect(Observer observer, Context context,
- boolean alwaysWatchForChanges) {
+ @SuppressLint("NewApi")
+ public NetworkChangeNotifierAutoDetect(
+ Observer observer, Context context, boolean alwaysWatchForChanges) {
+ // Since BroadcastReceiver is always called back on UI thread, ensure
+ // running on UI thread so notification logic can be single-threaded.
+ ThreadUtils.assertOnUiThread();
mObserver = observer;
mContext = context.getApplicationContext();
mConnectivityManagerDelegate = new ConnectivityManagerDelegate(context);
mWifiManagerDelegate = new WifiManagerDelegate(context);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ mNetworkCallback = new MyNetworkCallback();
+ mNetworkRequest =
+ new NetworkRequest.Builder().addCapability(NET_CAPABILITY_INTERNET).build();
+ } else {
+ mNetworkCallback = null;
+ mNetworkRequest = null;
+ }
final NetworkState networkState = mConnectivityManagerDelegate.getNetworkState();
mConnectionType = getCurrentConnectionType(networkState);
mWifiSSID = getCurrentWifiSSID(networkState);
@@ -226,22 +439,41 @@ public class NetworkChangeNotifierAutoDetect extends BroadcastReceiver
}
/**
- * Register a BroadcastReceiver in the given context.
+ * Registers a BroadcastReceiver in the given context.
*/
private void registerReceiver() {
if (!mRegistered) {
mRegistered = true;
mContext.registerReceiver(this, mIntentFilter);
+ if (mNetworkCallback != null) {
+ mConnectivityManagerDelegate.registerNetworkCallback(
+ mNetworkRequest, mNetworkCallback);
+ // registerNetworkCallback() will rematch our NetworkRequest
+ // against active networks, so a cached list of active networks
+ // will be repopulated immediatly after this. However we need to
+ // purge any cached networks as they may have been disconnected
+ // while mNetworkCallback was unregistered.
+ final Network[] networks = mConnectivityManagerDelegate.getAllNetworks();
+ // Convert Networks to NetIDs.
+ final int[] netIds = new int[networks.length];
+ for (int i = 0; i < networks.length; i++) {
+ netIds[i] = networkToNetId(networks[i]);
+ }
+ mObserver.updateActiveNetworkList(netIds);
+ }
}
}
/**
- * Unregister the BroadcastReceiver in the given context.
+ * Unregisters the BroadcastReceiver in the given context.
*/
private void unregisterReceiver() {
if (mRegistered) {
mRegistered = false;
mContext.unregisterReceiver(this);
+ if (mNetworkCallback != null) {
+ mConnectivityManagerDelegate.unregisterNetworkCallback(mNetworkCallback);
+ }
}
}
@@ -249,6 +481,47 @@ public class NetworkChangeNotifierAutoDetect extends BroadcastReceiver
return mConnectivityManagerDelegate.getNetworkState();
}
+ /**
+ * Returns an array of all of the device's currently connected
+ * networks and ConnectionTypes. Array elements are a repeated sequence of:
+ * NetID of network
+ * ConnectionType of network
+ * Only available on Lollipop and newer releases and when auto-detection has
+ * been enabled.
+ */
+ public int[] getNetworksAndTypes() {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
+ return new int[0];
+ }
+ final Network networks[] = mConnectivityManagerDelegate.getAllNetworks();
+ final int networksAndTypes[] = new int[networks.length * 2];
+ int index = 0;
+ for (Network network : networks) {
+ if (!mConnectivityManagerDelegate.hasInternetCapability(network)) {
+ continue;
+ }
+ networksAndTypes[index++] = networkToNetId(network);
+ networksAndTypes[index++] =
+ getCurrentConnectionType(mConnectivityManagerDelegate.getNetworkState(network));
+ }
+ final int shortenedNetworksAndTypes[] = new int[index];
+ System.arraycopy(networksAndTypes, 0, shortenedNetworksAndTypes, 0, index);
+ return shortenedNetworksAndTypes;
+ }
+
+ /**
+ * Returns NetID of device's current default connected network used for
+ * communication.
+ * Only implemented on Lollipop and newer releases, returns NetId.INVALID
+ * when not implemented.
+ */
+ public int getDefaultNetId() {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
+ return NetId.INVALID;
+ }
+ return mConnectivityManagerDelegate.getDefaultNetId();
+ }
+
public int getCurrentConnectionType(NetworkState networkState) {
if (!networkState.isConnected()) {
return ConnectionType.CONNECTION_NONE;
@@ -293,7 +566,7 @@ public class NetworkChangeNotifierAutoDetect extends BroadcastReceiver
}
/*
- * Returns the bandwidth of the current connection in Mbps. The result is
+ * Returns the bandwidth of the current connection in Mbps. The result is
* derived from the NetInfo v3 specification's mapping from network type to
* max link speed. In cases where more information is available, such as wifi,
* that is used instead. For more on NetInfo, see http://w3c.github.io/netinfo/.
@@ -416,4 +689,15 @@ public class NetworkChangeNotifierAutoDetect extends BroadcastReceiver
if (monitorRSSI) addAction(WifiManager.RSSI_CHANGED_ACTION);
}
}
+
+ /**
+ * Extracts NetID of network. Only available on Lollipop and newer releases.
+ */
+ @SuppressLint("NewApi")
+ private static int networkToNetId(Network network) {
+ // NOTE(pauljensen): This depends on Android framework implementation details.
+ // Fortunately this functionality is unlikely to ever change.
+ // TODO(pauljensen): When we update to Android M SDK, use Network.getNetworkHandle().
+ return Integer.parseInt(network.toString());
+ }
}
diff --git a/net/android/javatests/src/org/chromium/net/NetworkChangeNotifierTest.java b/net/android/javatests/src/org/chromium/net/NetworkChangeNotifierTest.java
index 0d4608c..b28f5ef 100644
--- a/net/android/javatests/src/org/chromium/net/NetworkChangeNotifierTest.java
+++ b/net/android/javatests/src/org/chromium/net/NetworkChangeNotifierTest.java
@@ -4,24 +4,36 @@
package org.chromium.net;
+import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.Network;
+import android.net.NetworkRequest;
import android.net.wifi.WifiManager;
+import android.os.Build;
import android.telephony.TelephonyManager;
import android.test.InstrumentationTestCase;
import android.test.UiThreadTest;
import android.test.suitebuilder.annotation.MediumTest;
import org.chromium.base.ApplicationState;
+import org.chromium.base.ThreadUtils;
import org.chromium.base.library_loader.LibraryLoader;
import org.chromium.base.library_loader.LibraryProcessType;
import org.chromium.base.test.util.Feature;
+import org.chromium.net.NetworkChangeNotifierAutoDetect.ConnectivityManagerDelegate;
import org.chromium.net.NetworkChangeNotifierAutoDetect.NetworkState;
+import java.lang.reflect.Constructor;
+import java.util.concurrent.Callable;
+import java.util.concurrent.FutureTask;
+
/**
* Tests for org.chromium.net.NetworkChangeNotifier.
*/
+@SuppressLint("NewApi")
public class NetworkChangeNotifierTest extends InstrumentationTestCase {
/**
* Listens for alerts fired by the NetworkChangeNotifier when network status changes.
@@ -47,65 +59,157 @@ public class NetworkChangeNotifierTest extends InstrumentationTestCase {
/**
* Mocks out calls to the ConnectivityManager.
*/
- class MockConnectivityManagerDelegate
- extends NetworkChangeNotifierAutoDetect.ConnectivityManagerDelegate {
+ private static class MockConnectivityManagerDelegate extends ConnectivityManagerDelegate {
private boolean mActiveNetworkExists;
private int mNetworkType;
private int mNetworkSubtype;
+ private NetworkCallback mLastRegisteredNetworkCallback;
@Override
- NetworkState getNetworkState() {
+ public NetworkState getNetworkState() {
return new NetworkState(mActiveNetworkExists, mNetworkType, mNetworkSubtype);
}
- void setActiveNetworkExists(boolean networkExists) {
+ // Dummy implementations to avoid NullPointerExceptions in default implementations:
+
+ @Override
+ public int getDefaultNetId() {
+ return NetId.INVALID;
+ }
+
+ @Override
+ public Network[] getAllNetworks() {
+ return new Network[0];
+ }
+
+ @Override
+ public NetworkState getNetworkState(Network network) {
+ return new NetworkState(false, -1, -1);
+ }
+
+ @Override
+ public void unregisterNetworkCallback(NetworkCallback networkCallback) {}
+
+ // Dummy implementation that also records the last registered callback.
+ @Override
+ public void registerNetworkCallback(
+ NetworkRequest networkRequest, NetworkCallback networkCallback) {
+ mLastRegisteredNetworkCallback = networkCallback;
+ }
+
+ public void setActiveNetworkExists(boolean networkExists) {
mActiveNetworkExists = networkExists;
}
- void setNetworkType(int networkType) {
+ public void setNetworkType(int networkType) {
mNetworkType = networkType;
}
- void setNetworkSubtype(int networkSubtype) {
+ public void setNetworkSubtype(int networkSubtype) {
mNetworkSubtype = networkSubtype;
}
+
+ public NetworkCallback getLastRegisteredNetworkCallback() {
+ return mLastRegisteredNetworkCallback;
+ }
}
/**
* Mocks out calls to the WifiManager.
*/
- class MockWifiManagerDelegate
+ private static class MockWifiManagerDelegate
extends NetworkChangeNotifierAutoDetect.WifiManagerDelegate {
private String mWifiSSID;
private int mLinkSpeedMbps;
@Override
- String getWifiSSID() {
+ public String getWifiSSID() {
return mWifiSSID;
}
- void setWifiSSID(String wifiSSID) {
+ public void setWifiSSID(String wifiSSID) {
mWifiSSID = wifiSSID;
}
@Override
- int getLinkSpeedInMbps() {
+ public int getLinkSpeedInMbps() {
return mLinkSpeedMbps;
}
- void setLinkSpeedInMbps(int linkSpeedInMbps) {
+ public void setLinkSpeedInMbps(int linkSpeedInMbps) {
mLinkSpeedMbps = linkSpeedInMbps;
}
}
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- LibraryLoader.get(LibraryProcessType.PROCESS_BROWSER)
- .ensureInitialized(getInstrumentation().getTargetContext());
- createTestNotifier(WatchForChanges.ONLY_WHEN_APP_IN_FOREGROUND);
+ // Types of network changes. Each is associated with a NetworkChangeNotifierAutoDetect.Observer
+ // callback, and NONE is provided to indicate no callback observed.
+ private static enum ChangeType { NONE, CONNECT, SOON_TO_DISCONNECT, DISCONNECT, PURGE_LIST }
+
+ // NetworkChangeNotifierAutoDetect.Observer used to verify proper notifications are sent out.
+ // Notifications come back on UI thread. assertLastChange() called on test thread.
+ private static class TestNetworkChangeNotifierAutoDetectObserver
+ implements NetworkChangeNotifierAutoDetect.Observer {
+ private volatile ChangeType mLastChangeSeen = ChangeType.NONE;
+ private volatile int mLastNetIdSeen = NetId.INVALID;
+
+ @Override
+ public void onConnectionTypeChanged(int newConnectionType) {}
+ @Override
+ public void onMaxBandwidthChanged(double maxBandwidthMbps) {}
+
+ @Override
+ public void onNetworkConnect(int netId, int connectionType) {
+ ThreadUtils.assertOnUiThread();
+ assertEquals(mLastChangeSeen, ChangeType.NONE);
+ assertEquals(mLastNetIdSeen, NetId.INVALID);
+ mLastChangeSeen = ChangeType.CONNECT;
+ mLastNetIdSeen = netId;
+ }
+
+ @Override
+ public void onNetworkSoonToDisconnect(int netId) {
+ ThreadUtils.assertOnUiThread();
+ assertEquals(mLastChangeSeen, ChangeType.NONE);
+ assertEquals(mLastNetIdSeen, NetId.INVALID);
+ mLastChangeSeen = ChangeType.SOON_TO_DISCONNECT;
+ mLastNetIdSeen = netId;
+ }
+
+ @Override
+ public void onNetworkDisconnect(int netId) {
+ ThreadUtils.assertOnUiThread();
+ assertEquals(mLastChangeSeen, ChangeType.NONE);
+ assertEquals(mLastNetIdSeen, NetId.INVALID);
+ mLastChangeSeen = ChangeType.DISCONNECT;
+ mLastNetIdSeen = netId;
+ }
+
+ @Override
+ public void updateActiveNetworkList(int[] activeNetIds) {
+ ThreadUtils.assertOnUiThread();
+ assertEquals(mLastChangeSeen, ChangeType.NONE);
+ assertEquals(mLastNetIdSeen, NetId.INVALID);
+ mLastChangeSeen = ChangeType.PURGE_LIST;
+ if (activeNetIds.length >= 1) {
+ mLastNetIdSeen = activeNetIds[0];
+ } else {
+ mLastNetIdSeen = NetId.INVALID;
+ }
+ }
+
+ // Verify last notification was the expected one.
+ public void assertLastChange(ChangeType type, int netId) throws Exception {
+ // Make sure notification processed.
+ flushUiThreadTaskQueue();
+ assertEquals(type, mLastChangeSeen);
+ assertEquals(netId, mLastNetIdSeen);
+ mLastChangeSeen = ChangeType.NONE;
+ mLastNetIdSeen = NetId.INVALID;
+ }
}
+ // Network.Network(int netId) pointer.
+ private Constructor<Network> mNetworkConstructor;
private NetworkChangeNotifierAutoDetect mReceiver;
private MockConnectivityManagerDelegate mConnectivityDelegate;
private MockWifiManagerDelegate mWifiDelegate;
@@ -153,6 +257,36 @@ public class NetworkChangeNotifierTest extends InstrumentationTestCase {
return mReceiver.getCurrentConnectionType(networkState);
}
+ // Create Network object given a NetID.
+ private Network netIdToNetwork(int netId) throws Exception {
+ return mNetworkConstructor.newInstance(netId);
+ }
+
+ // Flush UI thread task queue.
+ private static void flushUiThreadTaskQueue() throws Exception {
+ FutureTask<Void> task = new FutureTask<Void>(new Runnable() {
+ public void run() {}
+ }, null);
+ ThreadUtils.postOnUiThread(task);
+ task.get();
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ LibraryLoader.get(LibraryProcessType.PROCESS_BROWSER)
+ .ensureInitialized(getInstrumentation().getTargetContext());
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ // Find Network.Network(int netId) using reflection.
+ mNetworkConstructor = Network.class.getConstructor(Integer.TYPE);
+ }
+ ThreadUtils.postOnUiThread(new Runnable() {
+ public void run() {
+ createTestNotifier(WatchForChanges.ONLY_WHEN_APP_IN_FOREGROUND);
+ }
+ });
+ }
+
/**
* Tests that the receiver registers for connectivity intents during construction.
*/
@@ -163,12 +297,7 @@ public class NetworkChangeNotifierTest extends InstrumentationTestCase {
Context context = getInstrumentation().getTargetContext();
NetworkChangeNotifierAutoDetect.Observer observer =
- new NetworkChangeNotifierAutoDetect.Observer() {
- @Override
- public void onConnectionTypeChanged(int newConnectionType) {}
- @Override
- public void onMaxBandwidthChanged(double maxBandwidthMbps) {}
- };
+ new TestNetworkChangeNotifierAutoDetectObserver();
NetworkChangeNotifierAutoDetect receiver = new NetworkChangeNotifierAutoDetect(
observer, context, false /* always watch for changes */) {
@@ -360,4 +489,122 @@ public class NetworkChangeNotifierTest extends InstrumentationTestCase {
mReceiver.onReceive(getInstrumentation().getTargetContext(), connectivityIntent);
assertTrue(observer.hasReceivedNotification());
}
+
+ /**
+ * Tests that ConnectivityManagerDelegate doesn't crash. This test cannot rely on having any
+ * active network connections so it cannot usefully check results, but it can at least check
+ * that the functions don't crash.
+ */
+ @UiThreadTest
+ @MediumTest
+ @Feature({"Android-AppBase"})
+ public void testConnectivityManagerDelegateDoesNotCrash() {
+ ConnectivityManagerDelegate delegate =
+ new ConnectivityManagerDelegate(getInstrumentation().getTargetContext());
+ delegate.getNetworkState();
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ Network[] networks = delegate.getAllNetworks();
+ if (networks.length >= 1) {
+ delegate.getNetworkState(networks[0]);
+ delegate.hasInternetCapability(networks[0]);
+ }
+ delegate.getDefaultNetId();
+ NetworkCallback networkCallback = new NetworkCallback();
+ NetworkRequest networkRequest = new NetworkRequest.Builder().build();
+ delegate.registerNetworkCallback(networkRequest, networkCallback);
+ delegate.unregisterNetworkCallback(networkCallback);
+ }
+ }
+
+ /**
+ * Tests that NetworkChangeNotifierAutoDetect queryable APIs don't crash. This test cannot rely
+ * on having any active network connections so it cannot usefully check results, but it can at
+ * least check that the functions don't crash.
+ */
+ @UiThreadTest
+ @MediumTest
+ @Feature({"Android-AppBase"})
+ public void testQueryableAPIsDoNotCrash() {
+ NetworkChangeNotifierAutoDetect.Observer observer =
+ new TestNetworkChangeNotifierAutoDetectObserver();
+ NetworkChangeNotifierAutoDetect ncn = new NetworkChangeNotifierAutoDetect(
+ observer, getInstrumentation().getTargetContext(), true);
+ ncn.getNetworksAndTypes();
+ ncn.getDefaultNetId();
+ }
+
+ /**
+ * Tests that callbacks are issued to Observers when NetworkChangeNotifierAutoDetect receives
+ * the right signals (via its NetworkCallback).
+ */
+ @MediumTest
+ @Feature({"Android-AppBase"})
+ public void testNetworkCallbacks() throws Exception {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
+ return;
+ }
+ // Setup NetworkChangeNotifierAutoDetect
+ final Context context = getInstrumentation().getTargetContext();
+ final TestNetworkChangeNotifierAutoDetectObserver observer =
+ new TestNetworkChangeNotifierAutoDetectObserver();
+ Callable<NetworkChangeNotifierAutoDetect> callable =
+ new Callable<NetworkChangeNotifierAutoDetect>() {
+ public NetworkChangeNotifierAutoDetect call() {
+ return new NetworkChangeNotifierAutoDetect(
+ observer, context, false /* always watch for changes */) {
+ // This override prevents NetworkChangeNotifierAutoDetect from
+ // registering for events right off the bat. We'll delay this
+ // until our MockConnectivityManagerDelegate is first installed
+ // to prevent inadvertent communication with the real
+ // ConnectivityManager.
+ @Override
+ int getApplicationState() {
+ return ApplicationState.HAS_PAUSED_ACTIVITIES;
+ }
+ };
+ }
+ };
+ FutureTask<NetworkChangeNotifierAutoDetect> task =
+ new FutureTask<NetworkChangeNotifierAutoDetect>(callable);
+ ThreadUtils.postOnUiThread(task);
+ NetworkChangeNotifierAutoDetect ncn = task.get();
+
+ // Insert mock ConnectivityDelegate
+ mConnectivityDelegate = new MockConnectivityManagerDelegate();
+ ncn.setConnectivityManagerDelegateForTests(mConnectivityDelegate);
+ // Now that mock ConnectivityDelegate is inserted, pretend app is foregrounded
+ // so NetworkChangeNotifierAutoDetect will register its NetworkCallback.
+ assertFalse(ncn.isReceiverRegisteredForTesting());
+ ncn.onApplicationStateChange(ApplicationState.HAS_RUNNING_ACTIVITIES);
+ assertTrue(ncn.isReceiverRegisteredForTesting());
+
+ // Find NetworkChangeNotifierAutoDetect's NetworkCallback, which should have been registered
+ // with mConnectivityDelegate.
+ NetworkCallback networkCallback = mConnectivityDelegate.getLastRegisteredNetworkCallback();
+ assertNotNull(networkCallback);
+
+ // First thing we'll receive is a purge to initialize any network lists.
+ observer.assertLastChange(ChangeType.PURGE_LIST, NetId.INVALID);
+
+ // Test connected signal is passed along.
+ networkCallback.onAvailable(netIdToNetwork(100));
+ observer.assertLastChange(ChangeType.CONNECT, 100);
+
+ // Test soon-to-be-disconnected signal is passed along.
+ networkCallback.onLosing(netIdToNetwork(101), 30);
+ observer.assertLastChange(ChangeType.SOON_TO_DISCONNECT, 101);
+
+ // Test connected signal is passed along.
+ networkCallback.onLost(netIdToNetwork(102));
+ observer.assertLastChange(ChangeType.DISCONNECT, 102);
+
+ // Simulate app backgrounding then foregrounding.
+ assertTrue(ncn.isReceiverRegisteredForTesting());
+ ncn.onApplicationStateChange(ApplicationState.HAS_PAUSED_ACTIVITIES);
+ assertFalse(ncn.isReceiverRegisteredForTesting());
+ ncn.onApplicationStateChange(ApplicationState.HAS_RUNNING_ACTIVITIES);
+ assertTrue(ncn.isReceiverRegisteredForTesting());
+ // Verify network list purged.
+ observer.assertLastChange(ChangeType.PURGE_LIST, NetId.INVALID);
+ }
}
diff --git a/net/android/network_change_notifier_android.cc b/net/android/network_change_notifier_android.cc
index a967b85..eac6589 100644
--- a/net/android/network_change_notifier_android.cc
+++ b/net/android/network_change_notifier_android.cc
@@ -65,6 +65,17 @@
namespace net {
+// Expose kInvalidNetworkHandle out to Java as NetId.INVALID. The notion of
+// a NetID is an Android framework one, see android.net.Network.netId.
+// NetworkChangeNotifierAndroid implements NetworkHandle to simply be the NetID.
+// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.net
+enum NetId {
+ // Cannot use |kInvalidNetworkHandle| here as the Java generator fails,
+ // instead enforce their equality with CHECK in
+ // NetworkChangeNotifierAndroid().
+ INVALID = -1
+};
+
// Thread on which we can run DnsConfigService, which requires a TYPE_IO
// message loop to monitor /system/etc/hosts.
class NetworkChangeNotifierAndroid::DnsConfigServiceThread
@@ -151,6 +162,22 @@ void NetworkChangeNotifierAndroid::GetCurrentMaxBandwidthAndConnectionType(
connection_type);
}
+void NetworkChangeNotifierAndroid::GetCurrentConnectedNetworks(
+ NetworkChangeNotifier::NetworkList* networks) const {
+ delegate_->GetCurrentlyConnectedNetworks(networks);
+}
+
+NetworkChangeNotifier::ConnectionType
+NetworkChangeNotifierAndroid::GetCurrentNetworkConnectionType(
+ NetworkHandle network) const {
+ return delegate_->GetNetworkConnectionType(network);
+}
+
+NetworkChangeNotifier::NetworkHandle
+NetworkChangeNotifierAndroid::GetCurrentDefaultNetwork() const {
+ return delegate_->GetCurrentDefaultNetwork();
+}
+
void NetworkChangeNotifierAndroid::OnConnectionTypeChanged() {
DnsConfigServiceThread::NotifyNetworkChangeNotifierObservers();
}
@@ -162,6 +189,28 @@ void NetworkChangeNotifierAndroid::OnMaxBandwidthChanged(
type);
}
+void NetworkChangeNotifierAndroid::OnNetworkConnected(NetworkHandle network) {
+ NetworkChangeNotifier::NotifyObserversOfSpecificNetworkChange(
+ NetworkChangeType::CONNECTED, network);
+}
+
+void NetworkChangeNotifierAndroid::OnNetworkSoonToDisconnect(
+ NetworkHandle network) {
+ NetworkChangeNotifier::NotifyObserversOfSpecificNetworkChange(
+ NetworkChangeType::SOON_TO_DISCONNECT, network);
+}
+
+void NetworkChangeNotifierAndroid::OnNetworkDisconnected(
+ NetworkHandle network) {
+ NetworkChangeNotifier::NotifyObserversOfSpecificNetworkChange(
+ NetworkChangeType::DISCONNECTED, network);
+}
+
+void NetworkChangeNotifierAndroid::OnNetworkMadeDefault(NetworkHandle network) {
+ NetworkChangeNotifier::NotifyObserversOfSpecificNetworkChange(
+ NetworkChangeType::MADE_DEFAULT, network);
+}
+
// static
bool NetworkChangeNotifierAndroid::Register(JNIEnv* env) {
return NetworkChangeNotifierDelegateAndroid::Register(env);
@@ -174,6 +223,8 @@ NetworkChangeNotifierAndroid::NetworkChangeNotifierAndroid(
delegate_(delegate),
dns_config_service_thread_(
new DnsConfigServiceThread(dns_config_for_testing)) {
+ CHECK_EQ(NetId::INVALID, NetworkChangeNotifier::kInvalidNetworkHandle)
+ << "kInvalidNetworkHandle doesn't match NetId::INVALID";
delegate_->AddObserver(this);
dns_config_service_thread_->StartWithOptions(
base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
diff --git a/net/android/network_change_notifier_android.h b/net/android/network_change_notifier_android.h
index 61bec53..b4870bf 100644
--- a/net/android/network_change_notifier_android.h
+++ b/net/android/network_change_notifier_android.h
@@ -52,11 +52,19 @@ class NET_EXPORT_PRIVATE NetworkChangeNotifierAndroid
void GetCurrentMaxBandwidthAndConnectionType(
double* max_bandwidth_mbps,
ConnectionType* connection_type) const override;
+ void GetCurrentConnectedNetworks(NetworkList* network_list) const override;
+ ConnectionType GetCurrentNetworkConnectionType(
+ NetworkHandle network) const override;
+ NetworkHandle GetCurrentDefaultNetwork() const override;
// NetworkChangeNotifierDelegateAndroid::Observer:
void OnConnectionTypeChanged() override;
void OnMaxBandwidthChanged(double max_bandwidth_mbps,
ConnectionType type) override;
+ void OnNetworkConnected(NetworkHandle network) override;
+ void OnNetworkSoonToDisconnect(NetworkHandle network) override;
+ void OnNetworkDisconnected(NetworkHandle network) override;
+ void OnNetworkMadeDefault(NetworkHandle network) override;
static bool Register(JNIEnv* env);
diff --git a/net/android/network_change_notifier_android_unittest.cc b/net/android/network_change_notifier_android_unittest.cc
index d3fe04d..68b6d43 100644
--- a/net/android/network_change_notifier_android_unittest.cc
+++ b/net/android/network_change_notifier_android_unittest.cc
@@ -19,9 +19,23 @@ namespace net {
namespace {
+// Types of network changes. See similarly named functions in
+// NetworkChangeNotifier::NetworkObserver for descriptions.
+enum ChangeType {
+ NONE,
+ CONNECTED,
+ SOON_TO_DISCONNECT,
+ DISCONNECTED,
+ MADE_DEFAULT,
+};
+
class NetworkChangeNotifierDelegateAndroidObserver
: public NetworkChangeNotifierDelegateAndroid::Observer {
public:
+ typedef NetworkChangeNotifier::ConnectionType ConnectionType;
+ typedef NetworkChangeNotifier::NetworkHandle NetworkHandle;
+ typedef NetworkChangeNotifier::NetworkList NetworkList;
+
NetworkChangeNotifierDelegateAndroidObserver()
: type_notifications_count_(0), max_bandwidth_notifications_count_(0) {}
@@ -34,6 +48,14 @@ class NetworkChangeNotifierDelegateAndroidObserver
max_bandwidth_notifications_count_++;
}
+ void OnNetworkConnected(NetworkHandle network) override {}
+
+ void OnNetworkSoonToDisconnect(NetworkHandle network) override {}
+
+ void OnNetworkDisconnected(NetworkHandle network) override {}
+
+ void OnNetworkMadeDefault(NetworkHandle network) override {}
+
int type_notifications_count() const { return type_notifications_count_; }
int bandwidth_notifications_count() const {
return max_bandwidth_notifications_count_;
@@ -87,6 +109,55 @@ class DNSChangeObserver : public NetworkChangeNotifier::DNSObserver {
int initial_notifications_count_;
};
+// A NetworkObserver used for verifying correct notifications are sent.
+class TestNetworkObserver : public NetworkChangeNotifier::NetworkObserver {
+ public:
+ TestNetworkObserver() { Clear(); }
+
+ void ExpectChange(ChangeType change,
+ NetworkChangeNotifier::NetworkHandle network) {
+ EXPECT_EQ(last_change_type_, change);
+ EXPECT_EQ(last_network_changed_, network);
+ Clear();
+ }
+
+ private:
+ void Clear() {
+ last_change_type_ = NONE;
+ last_network_changed_ = NetworkChangeNotifier::kInvalidNetworkHandle;
+ }
+
+ // NetworkChangeNotifier::NetworkObserver implementation:
+ void OnNetworkConnected(
+ NetworkChangeNotifier::NetworkHandle network) override {
+ ExpectChange(NONE, NetworkChangeNotifier::kInvalidNetworkHandle);
+ last_change_type_ = CONNECTED;
+ last_network_changed_ = network;
+ }
+ void OnNetworkSoonToDisconnect(
+ NetworkChangeNotifier::NetworkHandle network) override {
+ ExpectChange(NONE, NetworkChangeNotifier::kInvalidNetworkHandle);
+ last_change_type_ = SOON_TO_DISCONNECT;
+ last_network_changed_ = network;
+ }
+ void OnNetworkDisconnected(
+ NetworkChangeNotifier::NetworkHandle network) override {
+ ExpectChange(NONE, NetworkChangeNotifier::kInvalidNetworkHandle);
+ last_change_type_ = DISCONNECTED;
+ last_network_changed_ = network;
+ }
+ void OnNetworkMadeDefault(
+ NetworkChangeNotifier::NetworkHandle network) override {
+ // Cannot test for Clear()ed state as we receive CONNECTED immediately prior
+ // to MADE_DEFAULT.
+ last_change_type_ = MADE_DEFAULT;
+ last_network_changed_ = network;
+ }
+
+ ChangeType last_change_type_;
+ NetworkChangeNotifier::NetworkHandle last_network_changed_;
+};
+
} // namespace
class BaseNetworkChangeNotifierAndroidTest : public testing::Test {
@@ -135,6 +206,37 @@ class BaseNetworkChangeNotifierAndroidTest : public testing::Test {
base::MessageLoop::current()->RunUntilIdle();
}
+ void FakeNetworkChange(ChangeType change,
+ NetworkChangeNotifier::NetworkHandle network,
+ ConnectionType type) {
+ switch (change) {
+ case CONNECTED:
+ delegate_.FakeNetworkConnected(network, type);
+ break;
+ case SOON_TO_DISCONNECT:
+ delegate_.FakeNetworkSoonToBeDisconnected(network);
+ break;
+ case DISCONNECTED:
+ delegate_.FakeNetworkDisconnected(network);
+ break;
+ case MADE_DEFAULT:
+ delegate_.FakeDefaultNetwork(network, type);
+ break;
+ case NONE:
+ NOTREACHED();
+ break;
+ }
+ // See comment above.
+ base::MessageLoop::current()->RunUntilIdle();
+ }
+
+ void FakeUpdateActiveNetworkList(
+ NetworkChangeNotifier::NetworkList networks) {
+ delegate_.FakeUpdateActiveNetworkList(networks);
+ // See comment above.
+ base::MessageLoop::current()->RunUntilIdle();
+ }
+
NetworkChangeNotifierDelegateAndroid delegate_;
};
@@ -284,4 +386,99 @@ TEST_F(NetworkChangeNotifierAndroidTest, InitialSignal) {
NetworkChangeNotifier::RemoveDNSObserver(&dns_change_observer);
}
+TEST_F(NetworkChangeNotifierAndroidTest, NetworkCallbacks) {
+ TestNetworkObserver network_observer;
+ NetworkChangeNotifier::AddNetworkObserver(&network_observer);
+
+ // Test empty values
+ EXPECT_EQ(NetworkChangeNotifier::kInvalidNetworkHandle,
+ NetworkChangeNotifier::GetDefaultNetwork());
+ EXPECT_EQ(NetworkChangeNotifier::CONNECTION_UNKNOWN,
+ NetworkChangeNotifier::GetNetworkConnectionType(100));
+ NetworkChangeNotifier::NetworkList network_list;
+ NetworkChangeNotifier::GetConnectedNetworks(&network_list);
+ EXPECT_EQ(0u, network_list.size());
+ // Test connecting network
+ FakeNetworkChange(CONNECTED, 100, NetworkChangeNotifier::CONNECTION_WIFI);
+ network_observer.ExpectChange(CONNECTED, 100);
+ EXPECT_EQ(NetworkChangeNotifier::kInvalidNetworkHandle,
+ NetworkChangeNotifier::GetDefaultNetwork());
+ // Test GetConnectedNetworks()
+ NetworkChangeNotifier::GetConnectedNetworks(&network_list);
+ EXPECT_EQ(1u, network_list.size());
+ EXPECT_EQ(100, network_list[0]);
+ // Test GetNetworkConnectionType()
+ EXPECT_EQ(NetworkChangeNotifier::CONNECTION_WIFI,
+ NetworkChangeNotifier::GetNetworkConnectionType(100));
+ // Test deduplication of connecting signal
+ FakeNetworkChange(CONNECTED, 100, NetworkChangeNotifier::CONNECTION_WIFI);
+ network_observer.ExpectChange(NONE,
+ NetworkChangeNotifier::kInvalidNetworkHandle);
+ // Test connecting another network
+ FakeNetworkChange(CONNECTED, 101, NetworkChangeNotifier::CONNECTION_3G);
+ network_observer.ExpectChange(CONNECTED, 101);
+ NetworkChangeNotifier::GetConnectedNetworks(&network_list);
+ EXPECT_EQ(2u, network_list.size());
+ EXPECT_EQ(100, network_list[0]);
+ EXPECT_EQ(101, network_list[1]);
+ EXPECT_EQ(NetworkChangeNotifier::CONNECTION_WIFI,
+ NetworkChangeNotifier::GetNetworkConnectionType(100));
+ EXPECT_EQ(NetworkChangeNotifier::CONNECTION_3G,
+ NetworkChangeNotifier::GetNetworkConnectionType(101));
+ // Test lingering network
+ FakeNetworkChange(SOON_TO_DISCONNECT, 100,
+ NetworkChangeNotifier::CONNECTION_WIFI);
+ network_observer.ExpectChange(SOON_TO_DISCONNECT, 100);
+ NetworkChangeNotifier::GetConnectedNetworks(&network_list);
+ EXPECT_EQ(2u, network_list.size());
+ EXPECT_EQ(100, network_list[0]);
+ EXPECT_EQ(101, network_list[1]);
+ // Test disconnecting network
+ FakeNetworkChange(DISCONNECTED, 100, NetworkChangeNotifier::CONNECTION_WIFI);
+ network_observer.ExpectChange(DISCONNECTED, 100);
+ NetworkChangeNotifier::GetConnectedNetworks(&network_list);
+ EXPECT_EQ(1u, network_list.size());
+ EXPECT_EQ(101, network_list[0]);
+ // Test deduplication of disconnecting signal
+ FakeNetworkChange(DISCONNECTED, 100, NetworkChangeNotifier::CONNECTION_WIFI);
+ network_observer.ExpectChange(NONE,
+ NetworkChangeNotifier::kInvalidNetworkHandle);
+ // Test delay of default network signal until connect signal
+ FakeNetworkChange(MADE_DEFAULT, 100, NetworkChangeNotifier::CONNECTION_WIFI);
+ network_observer.ExpectChange(NONE,
+ NetworkChangeNotifier::kInvalidNetworkHandle);
+ FakeNetworkChange(CONNECTED, 100, NetworkChangeNotifier::CONNECTION_WIFI);
+ network_observer.ExpectChange(MADE_DEFAULT, 100);
+ EXPECT_EQ(100, NetworkChangeNotifier::GetDefaultNetwork());
+ // Test change of default
+ FakeNetworkChange(MADE_DEFAULT, 101, NetworkChangeNotifier::CONNECTION_3G);
+ network_observer.ExpectChange(MADE_DEFAULT, 101);
+ EXPECT_EQ(101, NetworkChangeNotifier::GetDefaultNetwork());
+ // Test deduplication default signal
+ FakeNetworkChange(MADE_DEFAULT, 101, NetworkChangeNotifier::CONNECTION_3G);
+ network_observer.ExpectChange(NONE,
+ NetworkChangeNotifier::kInvalidNetworkHandle);
+ // Test that networks can change type
+ FakeNetworkChange(CONNECTED, 101, NetworkChangeNotifier::CONNECTION_4G);
+ network_observer.ExpectChange(NONE,
+ NetworkChangeNotifier::kInvalidNetworkHandle);
+ EXPECT_EQ(NetworkChangeNotifier::CONNECTION_4G,
+ NetworkChangeNotifier::GetNetworkConnectionType(101));
+ // Test purging the network list
+ NetworkChangeNotifier::GetConnectedNetworks(&network_list);
+ EXPECT_EQ(2u, network_list.size());
+ EXPECT_EQ(100, network_list[0]);
+ EXPECT_EQ(101, network_list[1]);
+ network_list.erase(network_list.begin() + 1); // Remove network 101
+ FakeUpdateActiveNetworkList(network_list);
+ network_observer.ExpectChange(DISCONNECTED, 101);
+ NetworkChangeNotifier::GetConnectedNetworks(&network_list);
+ EXPECT_EQ(1u, network_list.size());
+ EXPECT_EQ(100, network_list[0]);
+ EXPECT_EQ(NetworkChangeNotifier::kInvalidNetworkHandle,
+ NetworkChangeNotifier::GetDefaultNetwork());
+
+ NetworkChangeNotifier::RemoveNetworkObserver(&network_observer);
+}
+
} // namespace net
diff --git a/net/android/network_change_notifier_delegate_android.cc b/net/android/network_change_notifier_delegate_android.cc
index be4c6b1..ea2ba93 100644
--- a/net/android/network_change_notifier_delegate_android.cc
+++ b/net/android/network_change_notifier_delegate_android.cc
@@ -4,6 +4,7 @@
#include "net/android/network_change_notifier_delegate_android.h"
+#include "base/android/jni_array.h"
#include "base/logging.h"
#include "jni/NetworkChangeNotifier_jni.h"
#include "net/android/network_change_notifier_android.h"
@@ -44,6 +45,21 @@ NetworkChangeNotifier::ConnectionSubtype ConvertConnectionSubtype(
} // namespace
+// static
+void NetworkChangeNotifierDelegateAndroid::JavaIntArrayToNetworkMap(
+ JNIEnv* env,
+ jintArray int_array,
+ NetworkMap* network_map) {
+ std::vector<int> int_list;
+ base::android::JavaIntArrayToIntVector(env, int_array, &int_list);
+ network_map->clear();
+ for (auto i = int_list.begin(); i != int_list.end(); ++i) {
+ NetworkChangeNotifier::NetworkHandle network_handle = *i;
+ CHECK(++i != int_list.end());
+ (*network_map)[network_handle] = static_cast<ConnectionType>(*i);
+ }
+}
+
jdouble GetMaxBandwidthForConnectionSubtype(JNIEnv* env,
const JavaParamRef<jclass>& caller,
jint subtype) {
@@ -67,6 +83,14 @@ NetworkChangeNotifierDelegateAndroid::NetworkChangeNotifierDelegateAndroid()
SetCurrentMaxBandwidth(
Java_NetworkChangeNotifier_getCurrentMaxBandwidthInMbps(
env, java_network_change_notifier_.obj()));
+ SetCurrentDefaultNetwork(Java_NetworkChangeNotifier_getCurrentDefaultNetId(
+ env, java_network_change_notifier_.obj()));
+ NetworkMap network_map;
+ ScopedJavaLocalRef<jintArray> networks_and_types =
+ Java_NetworkChangeNotifier_getCurrentNetworksAndTypes(
+ env, java_network_change_notifier_.obj());
+ JavaIntArrayToNetworkMap(env, networks_and_types.obj(), &network_map);
+ SetCurrentNetworksAndTypes(network_map);
}
NetworkChangeNotifierDelegateAndroid::~NetworkChangeNotifierDelegateAndroid() {
@@ -93,14 +117,60 @@ void NetworkChangeNotifierDelegateAndroid::
*max_bandwidth_mbps = connection_max_bandwidth_;
}
+NetworkChangeNotifier::ConnectionType
+NetworkChangeNotifierDelegateAndroid::GetNetworkConnectionType(
+ NetworkChangeNotifier::NetworkHandle network) const {
+ base::AutoLock auto_lock(connection_lock_);
+ auto network_entry = network_map_.find(network);
+ if (network_entry == network_map_.end())
+ return ConnectionType::CONNECTION_UNKNOWN;
+ return network_entry->second;
+}
+
+NetworkChangeNotifier::NetworkHandle
+NetworkChangeNotifierDelegateAndroid::GetCurrentDefaultNetwork() const {
+ base::AutoLock auto_lock(connection_lock_);
+ return default_network_;
+}
+
+void NetworkChangeNotifierDelegateAndroid::GetCurrentlyConnectedNetworks(
+ NetworkList* network_list) const {
+ network_list->clear();
+ base::AutoLock auto_lock(connection_lock_);
+ for (auto i : network_map_)
+ network_list->push_back(i.first);
+}
+
void NetworkChangeNotifierDelegateAndroid::NotifyConnectionTypeChanged(
JNIEnv* env,
jobject obj,
- jint new_connection_type) {
+ jint new_connection_type,
+ jint default_netid) {
DCHECK(thread_checker_.CalledOnValidThread());
const ConnectionType actual_connection_type = ConvertConnectionType(
new_connection_type);
SetCurrentConnectionType(actual_connection_type);
+ NetworkHandle default_network = default_netid;
+ if (default_network != GetCurrentDefaultNetwork()) {
+ SetCurrentDefaultNetwork(default_network);
+ bool default_exists;
+ {
+ base::AutoLock auto_lock(connection_lock_);
+ // |default_network| may be an invalid value (i.e. -1) in cases where
+ // the device is disconnected or when run on Android versions prior to L,
+ // in which case |default_exists| will correctly be false and no
+ // OnNetworkMadeDefault notification will be sent.
+ default_exists = network_map_.find(default_network) != network_map_.end();
+ }
+ // Android Lollipop had race conditions where CONNECTIVITY_ACTION intents
+ // were sent out before the network was actually made the default.
+ // Delay sending the OnNetworkMadeDefault notification until we are
+ // actually notified that the network connected in NotifyOfNetworkConnect.
+ if (default_exists) {
+ observers_->Notify(FROM_HERE, &Observer::OnNetworkMadeDefault,
+ default_network);
+ }
+ }
observers_->Notify(FROM_HERE, &Observer::OnConnectionTypeChanged);
}
@@ -121,6 +191,88 @@ void NetworkChangeNotifierDelegateAndroid::NotifyMaxBandwidthChanged(
new_max_bandwidth, GetCurrentConnectionType());
}
+void NetworkChangeNotifierDelegateAndroid::NotifyOfNetworkConnect(
+ JNIEnv* env,
+ jobject obj,
+ jint net_id,
+ jint connection_type) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ NetworkHandle network = net_id;
+ bool already_exists;
+ {
+ base::AutoLock auto_lock(connection_lock_);
+ already_exists = network_map_.find(network) != network_map_.end();
+ network_map_[network] = static_cast<ConnectionType>(connection_type);
+ }
+ // Android Lollipop would send many duplicate notifications.
+ // This was later fixed in Android Marshmallow.
+ // Deduplicate them here by avoiding sending duplicate notifications.
+ if (!already_exists) {
+ observers_->Notify(FROM_HERE, &Observer::OnNetworkConnected, network);
+ if (network == GetCurrentDefaultNetwork()) {
+ observers_->Notify(FROM_HERE, &Observer::OnNetworkMadeDefault, network);
+ }
+ }
+}
+
+void NetworkChangeNotifierDelegateAndroid::NotifyOfNetworkSoonToDisconnect(
+ JNIEnv* env,
+ jobject obj,
+ jint net_id) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ NetworkHandle network = net_id;
+ {
+ base::AutoLock auto_lock(connection_lock_);
+ if (network_map_.find(network) == network_map_.end())
+ return;
+ }
+ observers_->Notify(FROM_HERE, &Observer::OnNetworkSoonToDisconnect, network);
+}
+
+void NetworkChangeNotifierDelegateAndroid::NotifyOfNetworkDisconnect(
+ JNIEnv* env,
+ jobject obj,
+ jint net_id) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ NetworkHandle network = net_id;
+ {
+ base::AutoLock auto_lock(connection_lock_);
+ if (network == default_network_)
+ default_network_ = NetworkChangeNotifier::kInvalidNetworkHandle;
+ if (network_map_.erase(network) == 0)
+ return;
+ }
+ observers_->Notify(FROM_HERE, &Observer::OnNetworkDisconnected, network);
+}
+
+void NetworkChangeNotifierDelegateAndroid::NotifyUpdateActiveNetworkList(
+ JNIEnv* env,
+ jobject obj,
+ jintArray active_networks) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ NetworkList active_network_list;
+ base::android::JavaIntArrayToIntVector(env, active_networks,
+ &active_network_list);
+ NetworkList disconnected_networks;
+ {
+ base::AutoLock auto_lock(connection_lock_);
+ for (auto i : network_map_) {
+ bool found = false;
+ for (auto j : active_network_list) {
+ if (j == i.first) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ disconnected_networks.push_back(i.first);
+ }
+ }
+ }
+ for (auto disconnected_network : disconnected_networks)
+ NotifyOfNetworkDisconnect(env, obj, disconnected_network);
+}
+
void NetworkChangeNotifierDelegateAndroid::AddObserver(
Observer* observer) {
observers_->AddObserver(observer);
@@ -148,6 +300,18 @@ void NetworkChangeNotifierDelegateAndroid::SetCurrentMaxBandwidth(
connection_max_bandwidth_ = max_bandwidth;
}
+void NetworkChangeNotifierDelegateAndroid::SetCurrentDefaultNetwork(
+ NetworkHandle default_network) {
+ base::AutoLock auto_lock(connection_lock_);
+ default_network_ = default_network;
+}
+
+void NetworkChangeNotifierDelegateAndroid::SetCurrentNetworksAndTypes(
+ NetworkMap network_map) {
+ base::AutoLock auto_lock(connection_lock_);
+ network_map_ = network_map;
+}
+
void NetworkChangeNotifierDelegateAndroid::SetOnline() {
JNIEnv* env = base::android::AttachCurrentThread();
Java_NetworkChangeNotifier_forceConnectivityState(env, true);
@@ -158,4 +322,37 @@ void NetworkChangeNotifierDelegateAndroid::SetOffline() {
Java_NetworkChangeNotifier_forceConnectivityState(env, false);
}
+void NetworkChangeNotifierDelegateAndroid::FakeNetworkConnected(
+ NetworkChangeNotifier::NetworkHandle network,
+ ConnectionType type) {
+ JNIEnv* env = base::android::AttachCurrentThread();
+ Java_NetworkChangeNotifier_fakeNetworkConnected(env, network, type);
+}
+
+void NetworkChangeNotifierDelegateAndroid::FakeNetworkSoonToBeDisconnected(
+ NetworkChangeNotifier::NetworkHandle network) {
+ JNIEnv* env = base::android::AttachCurrentThread();
+ Java_NetworkChangeNotifier_fakeNetworkSoonToBeDisconnected(env, network);
+}
+
+void NetworkChangeNotifierDelegateAndroid::FakeNetworkDisconnected(
+ NetworkChangeNotifier::NetworkHandle network) {
+ JNIEnv* env = base::android::AttachCurrentThread();
+ Java_NetworkChangeNotifier_fakeNetworkDisconnected(env, network);
+}
+
+void NetworkChangeNotifierDelegateAndroid::FakeUpdateActiveNetworkList(
+ NetworkChangeNotifier::NetworkList networks) {
+ JNIEnv* env = base::android::AttachCurrentThread();
+ Java_NetworkChangeNotifier_fakeUpdateActiveNetworkList(
+ env, base::android::ToJavaIntArray(env, networks).obj());
+}
+
+void NetworkChangeNotifierDelegateAndroid::FakeDefaultNetwork(
+ NetworkChangeNotifier::NetworkHandle network,
+ ConnectionType type) {
+ JNIEnv* env = base::android::AttachCurrentThread();
+ Java_NetworkChangeNotifier_fakeDefaultNetwork(env, network, type);
+}
+
} // namespace net
diff --git a/net/android/network_change_notifier_delegate_android.h b/net/android/network_change_notifier_delegate_android.h
index e1f874a..d6312b7 100644
--- a/net/android/network_change_notifier_delegate_android.h
+++ b/net/android/network_change_notifier_delegate_android.h
@@ -5,6 +5,8 @@
#ifndef NET_ANDROID_NETWORK_CHANGE_NOTIFIER_DELEGATE_ANDROID_H_
#define NET_ANDROID_NETWORK_CHANGE_NOTIFIER_DELEGATE_ANDROID_H_
+#include <string>
+
#include "base/android/jni_android.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
@@ -23,13 +25,15 @@ namespace net {
class NET_EXPORT_PRIVATE NetworkChangeNotifierDelegateAndroid {
public:
typedef NetworkChangeNotifier::ConnectionType ConnectionType;
+ typedef NetworkChangeNotifier::NetworkHandle NetworkHandle;
+ typedef NetworkChangeNotifier::NetworkList NetworkList;
// Observer interface implemented by NetworkChangeNotifierAndroid which
// subscribes to network change notifications fired by the delegate (and
// initiated by the Java side).
- class Observer {
+ class Observer : public NetworkChangeNotifier::NetworkObserver {
public:
- virtual ~Observer() {}
+ ~Observer() override {}
// Updates the current connection type.
virtual void OnConnectionTypeChanged() = 0;
@@ -42,16 +46,17 @@ class NET_EXPORT_PRIVATE NetworkChangeNotifierDelegateAndroid {
NetworkChangeNotifierDelegateAndroid();
~NetworkChangeNotifierDelegateAndroid();
- // Called from NetworkChangeNotifierAndroid.java on the JNI thread whenever
+ // Called from NetworkChangeNotifier.java on the JNI thread whenever
// the connection type changes. This updates the current connection type seen
// by this class and forwards the notification to the observers that
// subscribed through AddObserver().
void NotifyConnectionTypeChanged(JNIEnv* env,
jobject obj,
- jint new_connection_type);
+ jint new_connection_type,
+ jint default_netid);
jint GetConnectionType(JNIEnv* env, jobject obj) const;
- // Called from NetworkChangeNotifierAndroid.java on the JNI thread whenever
+ // Called from NetworkChangeNotifier.java on the JNI thread whenever
// the maximum bandwidth of the connection changes. This updates the current
// max bandwidth seen by this class and forwards the notification to the
// observers that subscribed through AddObserver().
@@ -59,18 +64,39 @@ class NET_EXPORT_PRIVATE NetworkChangeNotifierDelegateAndroid {
jobject obj,
jdouble new_max_bandwidth);
+ // Called from NetworkChangeNotifier.java on the JNI thread to push
+ // down notifications of network connectivity events. These functions in
+ // turn:
+ // 1) Update |network_map_| and |default_network_|.
+ // 2) Push notifications to NetworkChangeNotifier which in turn pushes
+ // notifications to its NetworkObservers. Note that these functions
+ // perform valuable transformations on the signals like deduplicating.
+ // For descriptions of what individual calls mean, see
+ // NetworkChangeNotifierAutoDetect.Observer functions of the same names.
+ void NotifyOfNetworkConnect(JNIEnv* env,
+ jobject obj,
+ jint net_id,
+ jint connection_type);
+ void NotifyOfNetworkSoonToDisconnect(JNIEnv* env, jobject obj, jint net_id);
+ void NotifyOfNetworkDisconnect(JNIEnv* env, jobject obj, jint net_id);
+ void NotifyUpdateActiveNetworkList(JNIEnv* env,
+ jobject obj,
+ jintArray active_networks);
+
// These methods can be called on any thread. Note that the provided observer
// will be notified on the thread AddObserver() is called on.
void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);
- // Can be called from any thread.
+ // These methods are simply implementations of NetworkChangeNotifier APIs of
+ // the same name. They can be called from any thread.
ConnectionType GetCurrentConnectionType() const;
-
- // Can be called from any thread.
void GetCurrentMaxBandwidthAndConnectionType(
double* max_bandwidth_mbps,
ConnectionType* connection_type) const;
+ ConnectionType GetNetworkConnectionType(NetworkHandle network) const;
+ NetworkHandle GetCurrentDefaultNetwork() const;
+ void GetCurrentlyConnectedNetworks(NetworkList* network_list) const;
// Initializes JNI bindings.
static bool Register(JNIEnv* env);
@@ -78,20 +104,39 @@ class NET_EXPORT_PRIVATE NetworkChangeNotifierDelegateAndroid {
private:
friend class BaseNetworkChangeNotifierAndroidTest;
+ // Map of active connected networks and their connection type.
+ typedef std::map<NetworkHandle, ConnectionType> NetworkMap;
+
+ // Converts a Java int[] into a NetworkMap. Expects int[] to contain
+ // repeated instances of: NetworkHandle, ConnectionType
+ static void JavaIntArrayToNetworkMap(JNIEnv* env,
+ jintArray int_array,
+ NetworkMap* network_map);
+
+ // Setters that grab appropriate lock.
void SetCurrentConnectionType(ConnectionType connection_type);
void SetCurrentMaxBandwidth(double max_bandwidth);
+ void SetCurrentDefaultNetwork(NetworkHandle default_network);
+ void SetCurrentNetworksAndTypes(NetworkMap network_map);
// Methods calling the Java side exposed for testing.
void SetOnline();
void SetOffline();
+ void FakeNetworkConnected(NetworkHandle network, ConnectionType type);
+ void FakeNetworkSoonToBeDisconnected(NetworkHandle network);
+ void FakeNetworkDisconnected(NetworkHandle network);
+ void FakeUpdateActiveNetworkList(NetworkList networks);
+ void FakeDefaultNetwork(NetworkHandle network, ConnectionType type);
base::ThreadChecker thread_checker_;
scoped_refptr<base::ObserverListThreadSafe<Observer>> observers_;
- scoped_refptr<base::SingleThreadTaskRunner> jni_task_runner_;
base::android::ScopedJavaGlobalRef<jobject> java_network_change_notifier_;
+
mutable base::Lock connection_lock_; // Protects the state below.
ConnectionType connection_type_;
double connection_max_bandwidth_;
+ NetworkHandle default_network_;
+ NetworkMap network_map_;
DISALLOW_COPY_AND_ASSIGN(NetworkChangeNotifierDelegateAndroid);
};
diff --git a/net/base/network_change_notifier.cc b/net/base/network_change_notifier.cc
index b86b79c..fb480e9 100644
--- a/net/base/network_change_notifier.cc
+++ b/net/base/network_change_notifier.cc
@@ -56,6 +56,9 @@ class MockNetworkChangeNotifier : public NetworkChangeNotifier {
// static
bool NetworkChangeNotifier::test_notifications_only_ = false;
+// static
+const NetworkChangeNotifier::NetworkHandle
+ NetworkChangeNotifier::kInvalidNetworkHandle = -1;
// The main observer class that records UMAs for network events.
class HistogramWatcher
@@ -631,6 +634,32 @@ double NetworkChangeNotifier::GetMaxBandwidthForConnectionSubtype(
}
// static
+void NetworkChangeNotifier::GetConnectedNetworks(NetworkList* network_list) {
+ if (g_network_change_notifier) {
+ g_network_change_notifier->GetCurrentConnectedNetworks(network_list);
+ } else {
+ network_list->clear();
+ }
+}
+
+// static
+NetworkChangeNotifier::ConnectionType
+NetworkChangeNotifier::GetNetworkConnectionType(NetworkHandle network) {
+ return g_network_change_notifier
+ ? g_network_change_notifier->GetCurrentNetworkConnectionType(
+ network)
+ : CONNECTION_UNKNOWN;
+}
+
+// static
+NetworkChangeNotifier::NetworkHandle
+NetworkChangeNotifier::GetDefaultNetwork() {
+ return g_network_change_notifier
+ ? g_network_change_notifier->GetCurrentDefaultNetwork()
+ : kInvalidNetworkHandle;
+}
+
+// static
void NetworkChangeNotifier::GetDnsConfig(DnsConfig* config) {
if (!g_network_change_notifier) {
*config = DnsConfig();
@@ -808,6 +837,12 @@ void NetworkChangeNotifier::AddMaxBandwidthObserver(
}
}
+void NetworkChangeNotifier::AddNetworkObserver(NetworkObserver* observer) {
+ if (g_network_change_notifier) {
+ g_network_change_notifier->network_observer_list_->AddObserver(observer);
+ }
+}
+
void NetworkChangeNotifier::RemoveIPAddressObserver(
IPAddressObserver* observer) {
if (g_network_change_notifier) {
@@ -847,6 +882,12 @@ void NetworkChangeNotifier::RemoveMaxBandwidthObserver(
}
}
+void NetworkChangeNotifier::RemoveNetworkObserver(NetworkObserver* observer) {
+ if (g_network_change_notifier) {
+ g_network_change_notifier->network_observer_list_->RemoveObserver(observer);
+ }
+}
+
// static
void NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests() {
if (g_network_change_notifier)
@@ -908,6 +949,8 @@ NetworkChangeNotifier::NetworkChangeNotifier(
max_bandwidth_observer_list_(new base::ObserverListThreadSafe<
MaxBandwidthObserver>(
base::ObserverListBase<MaxBandwidthObserver>::NOTIFY_EXISTING_ONLY)),
+ network_observer_list_(new base::ObserverListThreadSafe<NetworkObserver>(
+ base::ObserverListBase<NetworkObserver>::NOTIFY_EXISTING_ONLY)),
network_state_(new NetworkState()),
network_change_calculator_(new NetworkChangeCalculator(params)) {
DCHECK(!g_network_change_notifier);
@@ -935,6 +978,22 @@ void NetworkChangeNotifier::GetCurrentMaxBandwidthAndConnectionType(
: GetMaxBandwidthForConnectionSubtype(SUBTYPE_UNKNOWN);
}
+void NetworkChangeNotifier::GetCurrentConnectedNetworks(
+ NetworkList* network_list) const {
+ network_list->clear();
+}
+
+NetworkChangeNotifier::ConnectionType
+NetworkChangeNotifier::GetCurrentNetworkConnectionType(
+ NetworkHandle network) const {
+ return CONNECTION_UNKNOWN;
+}
+
+NetworkChangeNotifier::NetworkHandle
+NetworkChangeNotifier::GetCurrentDefaultNetwork() const {
+ return kInvalidNetworkHandle;
+}
+
// static
void NetworkChangeNotifier::NotifyObserversOfIPAddressChange() {
if (g_network_change_notifier &&
@@ -989,6 +1048,17 @@ void NetworkChangeNotifier::NotifyObserversOfInitialDNSConfigRead() {
}
// static
+void NetworkChangeNotifier::NotifyObserversOfSpecificNetworkChange(
+ NetworkChangeType type,
+ NetworkHandle network) {
+ if (g_network_change_notifier &&
+ !NetworkChangeNotifier::test_notifications_only_) {
+ g_network_change_notifier->NotifyObserversOfSpecificNetworkChangeImpl(
+ type, network);
+ }
+}
+
+// static
void NetworkChangeNotifier::SetDnsConfig(const DnsConfig& config) {
if (!g_network_change_notifier)
return;
@@ -1044,6 +1114,29 @@ void NetworkChangeNotifier::NotifyObserversOfMaxBandwidthChangeImpl(
max_bandwidth_mbps, type);
}
+void NetworkChangeNotifier::NotifyObserversOfSpecificNetworkChangeImpl(
+ NetworkChangeType type,
+ NetworkHandle network) {
+ switch (type) {
+ case CONNECTED:
+ network_observer_list_->Notify(
+ FROM_HERE, &NetworkObserver::OnNetworkConnected, network);
+ break;
+ case DISCONNECTED:
+ network_observer_list_->Notify(
+ FROM_HERE, &NetworkObserver::OnNetworkDisconnected, network);
+ break;
+ case SOON_TO_DISCONNECT:
+ network_observer_list_->Notify(
+ FROM_HERE, &NetworkObserver::OnNetworkSoonToDisconnect, network);
+ break;
+ case MADE_DEFAULT:
+ network_observer_list_->Notify(
+ FROM_HERE, &NetworkObserver::OnNetworkMadeDefault, network);
+ break;
+ }
+}
+
NetworkChangeNotifier::DisableForTest::DisableForTest()
: network_change_notifier_(g_network_change_notifier) {
DCHECK(g_network_change_notifier);
diff --git a/net/base/network_change_notifier.h b/net/base/network_change_notifier.h
index 143abdc..4290331 100644
--- a/net/base/network_change_notifier.h
+++ b/net/base/network_change_notifier.h
@@ -8,6 +8,7 @@
#include <vector>
#include "base/basictypes.h"
+#include "base/macros.h"
#include "base/observer_list_threadsafe.h"
#include "base/time/time.h"
#include "net/base/net_export.h"
@@ -202,12 +203,49 @@ class NET_EXPORT NetworkChangeNotifier {
DISALLOW_COPY_AND_ASSIGN(MaxBandwidthObserver);
};
- virtual ~NetworkChangeNotifier();
+ // Opaque handle for device-wide connection to a particular network. For
+ // example an association with a particular WiFi network with a particular
+ // SSID or a connection to particular cellular network.
+ // The meaning of this handle is target-dependent. On Android NetworkHandles
+ // are equivalent to the framework's concept of NetIDs (e.g. Network.netId).
+ typedef int32_t NetworkHandle;
+
+ // A list of networks.
+ typedef std::vector<NetworkHandle> NetworkList;
+
+ // An interface that when implemented and added via AddNeworkObserver(),
+ // provides notifications when networks come and go.
+ // Only implemented for Android (Lollipop and newer), no callbacks issued when
+ // unimplemented.
+ class NET_EXPORT NetworkObserver {
+ public:
+ // Called when device connects to |network|. For example device associates
+ // with a WiFi access point. This does not imply the network has Internet
+ // access as it may well be behind a captive portal.
+ virtual void OnNetworkConnected(NetworkHandle network) = 0;
+ // Called when device disconnects from |network|.
+ virtual void OnNetworkDisconnected(NetworkHandle network) = 0;
+ // Called when device determines the connection to |network| is no longer
+ // preferred, for example when a device transitions from cellular to WiFi
+ // it might deem the cellular connection no longer preferred. The device
+ // will disconnect from |network| in a period of time (30s on Android),
+ // allowing network communications via |network| to wrap up.
+ virtual void OnNetworkSoonToDisconnect(NetworkHandle network) = 0;
+ // Called when |network| is made the default network for communication.
+ virtual void OnNetworkMadeDefault(NetworkHandle network) = 0;
- // See the description of NetworkChangeNotifier::GetConnectionType().
- // Implementations must be thread-safe. Implementations must also be
- // cheap as it is called often.
- virtual ConnectionType GetCurrentConnectionType() const = 0;
+ protected:
+ NetworkObserver() {}
+ virtual ~NetworkObserver() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NetworkObserver);
+ };
+
+ // An invalid NetworkHandle.
+ static const NetworkHandle kInvalidNetworkHandle;
+
+ virtual ~NetworkChangeNotifier();
// Replaces the default class factory instance of NetworkChangeNotifier class.
// The method will take over the ownership of |factory| object.
@@ -253,6 +291,27 @@ class NET_EXPORT NetworkChangeNotifier {
// TODO(jkarlin): Rename to GetMaxBandwidthMbpsForConnectionSubtype.
static double GetMaxBandwidthForConnectionSubtype(ConnectionSubtype subtype);
+ // Sets |network_list| to a list of all networks that are currently connected.
+ // Only implemented for Android (Lollipop and newer), leaves |network_list|
+ // empty when unimplemented.
+ static void GetConnectedNetworks(NetworkList* network_list);
+
+ // Returns the type of connection |network| uses. Note that this may vary
+ // slightly over time (e.g. CONNECTION_2G to CONNECTION_3G). If |network|
+ // is no longer connected, it will return CONNECTION_UNKNOWN.
+ // Only implemented for Android (Lollipop and newer), returns
+ // CONNECTION_UNKNOWN when unimplemented.
+ static ConnectionType GetNetworkConnectionType(NetworkHandle network);
+
+ // Returns the device's current default network connection. This is the
+ // network used for newly created socket communication for sockets that are
+ // not explicitly bound to a particular network (e.g. via
+ // DatagramClientSocket.BindToNetwork). Returns |kInvalidNetworkHandle| if
+ // there is no default connected network.
+ // Only implemented for Android (Lollipop and newer), returns
+ // |kInvalidNetworkHandle| when unimplemented.
+ static NetworkHandle GetDefaultNetwork();
+
// Retrieve the last read DnsConfig. This could be expensive if the system has
// a large HOSTS file.
static void GetDnsConfig(DnsConfig* config);
@@ -300,6 +359,7 @@ class NET_EXPORT NetworkChangeNotifier {
static void AddDNSObserver(DNSObserver* observer);
static void AddNetworkChangeObserver(NetworkChangeObserver* observer);
static void AddMaxBandwidthObserver(MaxBandwidthObserver* observer);
+ static void AddNetworkObserver(NetworkObserver* observer);
// Unregisters |observer| from receiving notifications. This must be called
// on the same thread on which AddObserver() was called. Like AddObserver(),
@@ -313,6 +373,7 @@ class NET_EXPORT NetworkChangeNotifier {
static void RemoveDNSObserver(DNSObserver* observer);
static void RemoveNetworkChangeObserver(NetworkChangeObserver* observer);
static void RemoveMaxBandwidthObserver(MaxBandwidthObserver* observer);
+ static void RemoveNetworkObserver(NetworkObserver* observer);
// Allow unit tests to trigger notifications.
static void NotifyObserversOfIPAddressChangeForTests();
@@ -370,6 +431,15 @@ class NET_EXPORT NetworkChangeNotifier {
};
protected:
+ // Types of network changes specified to
+ // NotifyObserversOfSpecificNetworkChange.
+ enum NetworkChangeType {
+ CONNECTED,
+ DISCONNECTED,
+ SOON_TO_DISCONNECT,
+ MADE_DEFAULT
+ };
+
// NetworkChanged signal is calculated from the IPAddressChanged and
// ConnectionTypeChanged signals. Delay parameters control how long to delay
// producing NetworkChanged signal after particular input signals so as to
@@ -403,12 +473,18 @@ class NET_EXPORT NetworkChangeNotifier {
GetAddressTrackerInternal() const;
#endif
- // See the description of NetworkChangeNotifier::GetMaxBandwidth().
+ // These are the actual implementations of the static queryable APIs.
+ // See the description of the corresponding functions named without "Current".
// Implementations must be thread-safe. Implementations must also be
- // cheap as it is called often.
+ // cheap as they are called often.
+ virtual ConnectionType GetCurrentConnectionType() const = 0;
virtual void GetCurrentMaxBandwidthAndConnectionType(
double* max_bandwidth_mbps,
ConnectionType* connection_type) const;
+ virtual void GetCurrentConnectedNetworks(NetworkList* network_list) const;
+ virtual ConnectionType GetCurrentNetworkConnectionType(
+ NetworkHandle network) const;
+ virtual NetworkHandle GetCurrentDefaultNetwork() const;
// Broadcasts a notification to all registered observers. Note that this
// happens asynchronously, even for observers on the current thread, even in
@@ -420,6 +496,8 @@ class NET_EXPORT NetworkChangeNotifier {
static void NotifyObserversOfNetworkChange(ConnectionType type);
static void NotifyObserversOfMaxBandwidthChange(double max_bandwidth_mbps,
ConnectionType type);
+ static void NotifyObserversOfSpecificNetworkChange(NetworkChangeType type,
+ NetworkHandle network);
// Stores |config| in NetworkState and notifies OnDNSChanged observers.
static void SetDnsConfig(const DnsConfig& config);
@@ -443,6 +521,8 @@ class NET_EXPORT NetworkChangeNotifier {
void NotifyObserversOfNetworkChangeImpl(ConnectionType type);
void NotifyObserversOfMaxBandwidthChangeImpl(double max_bandwidth_mbps,
ConnectionType type);
+ void NotifyObserversOfSpecificNetworkChangeImpl(NetworkChangeType type,
+ NetworkHandle network);
const scoped_refptr<base::ObserverListThreadSafe<IPAddressObserver>>
ip_address_observer_list_;
@@ -454,6 +534,8 @@ class NET_EXPORT NetworkChangeNotifier {
network_change_observer_list_;
const scoped_refptr<base::ObserverListThreadSafe<MaxBandwidthObserver>>
max_bandwidth_observer_list_;
+ const scoped_refptr<base::ObserverListThreadSafe<NetworkObserver>>
+ network_observer_list_;
// The current network state. Hosts DnsConfig, exposed via GetDnsConfig.
scoped_ptr<NetworkState> network_state_;
diff --git a/net/dns/address_sorter_posix_unittest.cc b/net/dns/address_sorter_posix_unittest.cc
index 88a135f..ab63ac4 100644
--- a/net/dns/address_sorter_posix_unittest.cc
+++ b/net/dns/address_sorter_posix_unittest.cc
@@ -57,6 +57,10 @@ class TestUDPClientSocket : public DatagramClientSocket {
*address = local_endpoint_;
return OK;
}
+ int BindToNetwork(NetworkChangeNotifier::NetworkHandle network) override {
+ NOTIMPLEMENTED();
+ return OK;
+ }
int Connect(const IPEndPoint& remote) override {
if (connected_)
diff --git a/net/net.gyp b/net/net.gyp
index 2b2b343..6c7217b 100644
--- a/net/net.gyp
+++ b/net/net.gyp
@@ -1389,6 +1389,7 @@
'cert_verify_status_android_java',
'certificate_mime_types_java',
'network_change_notifier_types_java',
+ 'network_change_notifier_android_types_java',
'net_errors_java',
'private_key_types_java',
'remote_android_keystore_aidl',
@@ -1485,6 +1486,14 @@
'includes': [ '../build/android/java_cpp_enum.gypi' ],
},
{
+ 'target_name': 'network_change_notifier_android_types_java',
+ 'type': 'none',
+ 'variables': {
+ 'source_file': 'android/network_change_notifier_android.cc',
+ },
+ 'includes': [ '../build/android/java_cpp_enum.gypi' ],
+ },
+ {
'target_name': 'private_key_types_java',
'type': 'none',
'variables': {
diff --git a/net/socket/socket_test_util.cc b/net/socket/socket_test_util.cc
index 1436b14..19a6722 100644
--- a/net/socket/socket_test_util.cc
+++ b/net/socket/socket_test_util.cc
@@ -1285,6 +1285,11 @@ int DeterministicMockUDPClientSocket::CompleteRead() {
return helper_.CompleteRead();
}
+int DeterministicMockUDPClientSocket::BindToNetwork(
+ NetworkChangeNotifier::NetworkHandle network) {
+ return ERR_NOT_IMPLEMENTED;
+}
+
int DeterministicMockUDPClientSocket::Connect(const IPEndPoint& address) {
if (connected_)
return OK;
@@ -1651,6 +1656,11 @@ const BoundNetLog& MockUDPClientSocket::NetLog() const {
return net_log_;
}
+int MockUDPClientSocket::BindToNetwork(
+ NetworkChangeNotifier::NetworkHandle network) {
+ return ERR_NOT_IMPLEMENTED;
+}
+
int MockUDPClientSocket::Connect(const IPEndPoint& address) {
connected_ = true;
peer_addr_ = address;
diff --git a/net/socket/socket_test_util.h b/net/socket/socket_test_util.h
index 6b966df..b576df1 100644
--- a/net/socket/socket_test_util.h
+++ b/net/socket/socket_test_util.h
@@ -823,6 +823,7 @@ class DeterministicMockUDPClientSocket
const BoundNetLog& NetLog() const override;
// DatagramClientSocket implementation.
+ int BindToNetwork(NetworkChangeNotifier::NetworkHandle network) override;
int Connect(const IPEndPoint& address) override;
void set_source_port(uint16 port) { source_port_ = port; }
@@ -945,6 +946,7 @@ class MockUDPClientSocket : public DatagramClientSocket, public AsyncSocket {
const BoundNetLog& NetLog() const override;
// DatagramClientSocket implementation.
+ int BindToNetwork(NetworkChangeNotifier::NetworkHandle network) override;
int Connect(const IPEndPoint& address) override;
// AsyncSocket implementation.
diff --git a/net/tools/net_watcher/net_watcher.cc b/net/tools/net_watcher/net_watcher.cc
index ca25040..1b22804 100644
--- a/net/tools/net_watcher/net_watcher.cc
+++ b/net/tools/net_watcher/net_watcher.cc
@@ -199,7 +199,7 @@ int main(int argc, char* argv[]) {
LOG(INFO) << "Initial connection type: "
<< ConnectionTypeToString(
- network_change_notifier->GetCurrentConnectionType());
+ net::NetworkChangeNotifier::GetConnectionType());
{
net::ProxyConfig config;
diff --git a/net/udp/datagram_client_socket.h b/net/udp/datagram_client_socket.h
index c2c2cba..ac4632e 100644
--- a/net/udp/datagram_client_socket.h
+++ b/net/udp/datagram_client_socket.h
@@ -5,6 +5,7 @@
#ifndef NET_UDP_DATAGRAM_CLIENT_SOCKET_H_
#define NET_UDP_DATAGRAM_CLIENT_SOCKET_H_
+#include "net/base/network_change_notifier.h"
#include "net/socket/socket.h"
#include "net/udp/datagram_socket.h"
@@ -17,6 +18,13 @@ class NET_EXPORT_PRIVATE DatagramClientSocket : public DatagramSocket,
public:
~DatagramClientSocket() override {}
+ // Binds this socket to |network|. All data traffic on the socket will be sent
+ // and received via |network|. Must be called before Connect(). This call will
+ // fail if |network| has disconnected. Communication using this socket will
+ // fail if |network| disconnects.
+ // Returns a net error code.
+ virtual int BindToNetwork(NetworkChangeNotifier::NetworkHandle network) = 0;
+
// Initialize this socket as a client socket to server at |address|.
// Returns a network error code.
virtual int Connect(const IPEndPoint& address) = 0;
diff --git a/net/udp/udp_client_socket.cc b/net/udp/udp_client_socket.cc
index 5b7639f..ca1f9e2 100644
--- a/net/udp/udp_client_socket.cc
+++ b/net/udp/udp_client_socket.cc
@@ -19,6 +19,11 @@ UDPClientSocket::UDPClientSocket(DatagramSocket::BindType bind_type,
UDPClientSocket::~UDPClientSocket() {
}
+int UDPClientSocket::BindToNetwork(
+ NetworkChangeNotifier::NetworkHandle network) {
+ return socket_.BindToNetwork(network);
+}
+
int UDPClientSocket::Connect(const IPEndPoint& address) {
int rv = socket_.Open(address.GetFamily());
if (rv != OK)
diff --git a/net/udp/udp_client_socket.h b/net/udp/udp_client_socket.h
index 50a1b29..60fa720 100644
--- a/net/udp/udp_client_socket.h
+++ b/net/udp/udp_client_socket.h
@@ -24,6 +24,7 @@ class NET_EXPORT_PRIVATE UDPClientSocket : public DatagramClientSocket {
~UDPClientSocket() override;
// DatagramClientSocket implementation.
+ int BindToNetwork(NetworkChangeNotifier::NetworkHandle network) override;
int Connect(const IPEndPoint& address) override;
int Read(IOBuffer* buf,
int buf_len,
diff --git a/net/udp/udp_socket_posix.cc b/net/udp/udp_socket_posix.cc
index 3b60bf2..f747798 100644
--- a/net/udp/udp_socket_posix.cc
+++ b/net/udp/udp_socket_posix.cc
@@ -28,6 +28,11 @@
#include "net/socket/socket_descriptor.h"
#include "net/udp/udp_net_log_parameters.h"
+#if defined(OS_ANDROID)
+#include "base/android/build_info.h"
+#include "base/native_library.h"
+#include "base/strings/utf_string_conversions.h"
+#endif
namespace net {
@@ -319,6 +324,42 @@ int UDPSocketPosix::Bind(const IPEndPoint& address) {
return rv;
}
+int UDPSocketPosix::BindToNetwork(
+ NetworkChangeNotifier::NetworkHandle network) {
+#if defined(OS_ANDROID)
+ DCHECK_NE(socket_, kInvalidSocket);
+ DCHECK(CalledOnValidThread());
+ DCHECK(!is_connected());
+ // Android prior to Lollipop didn't have support for binding sockets to
+ // networks.
+ if (base::android::BuildInfo::GetInstance()->sdk_int() <
+ base::android::SDK_VERSION_LOLLIPOP) {
+ return ERR_NOT_IMPLEMENTED;
+ }
+ // NOTE(pauljensen): This does rely on Android implementation details, but
+ // these details are unlikely to change.
+ typedef int (*SetNetworkForSocket)(unsigned netId, int socketFd);
+ static SetNetworkForSocket setNetworkForSocket;
+ // This is racy, but all racers should come out with the same answer so it
+ // shouldn't matter.
+ if (setNetworkForSocket == nullptr) {
+ // Android's netd client library should always be loaded in our address
+ // space as it shims libc functions like connect().
+ base::FilePath file(base::FilePath::FromUTF16Unsafe(
+ base::GetNativeLibraryName(base::ASCIIToUTF16("netd_client"))));
+ base::NativeLibrary lib = base::LoadNativeLibrary(file, nullptr);
+ setNetworkForSocket = reinterpret_cast<SetNetworkForSocket>(
+ base::GetFunctionPointerFromNativeLibrary(lib, "setNetworkForSocket"));
+ }
+ if (setNetworkForSocket == nullptr)
+ return ERR_NOT_IMPLEMENTED;
+ return MapSystemError(setNetworkForSocket(network, socket_));
+#else
+ NOTIMPLEMENTED();
+ return ERR_NOT_IMPLEMENTED;
+#endif
+}
+
int UDPSocketPosix::SetReceiveBufferSize(int32 size) {
DCHECK_NE(socket_, kInvalidSocket);
DCHECK(CalledOnValidThread());
diff --git a/net/udp/udp_socket_posix.h b/net/udp/udp_socket_posix.h
index e27c2f4..eb18daa 100644
--- a/net/udp/udp_socket_posix.h
+++ b/net/udp/udp_socket_posix.h
@@ -34,6 +34,13 @@ class NET_EXPORT UDPSocketPosix : public base::NonThreadSafe {
// Returns a net error code.
int Open(AddressFamily address_family);
+ // Binds this socket to |network|. All data traffic on the socket will be sent
+ // and received via |network|. Must be called before Connect(). This call will
+ // fail if |network| has disconnected. Communication using this socket will
+ // fail if |network| disconnects.
+ // Returns a net error code.
+ int BindToNetwork(NetworkChangeNotifier::NetworkHandle network);
+
// Connects the socket to connect with a certain |address|.
// Should be called after Open().
// Returns a net error code.
diff --git a/net/udp/udp_socket_win.cc b/net/udp/udp_socket_win.cc
index f9c1d20..c7ba423 100644
--- a/net/udp/udp_socket_win.cc
+++ b/net/udp/udp_socket_win.cc
@@ -482,6 +482,11 @@ int UDPSocketWin::Bind(const IPEndPoint& address) {
return rv;
}
+int UDPSocketWin::BindToNetwork(NetworkChangeNotifier::NetworkHandle network) {
+ NOTIMPLEMENTED();
+ return ERR_NOT_IMPLEMENTED;
+}
+
int UDPSocketWin::SetReceiveBufferSize(int32 size) {
DCHECK_NE(socket_, INVALID_SOCKET);
DCHECK(CalledOnValidThread());
diff --git a/net/udp/udp_socket_win.h b/net/udp/udp_socket_win.h
index 24e1bad..d01e67b 100644
--- a/net/udp/udp_socket_win.h
+++ b/net/udp/udp_socket_win.h
@@ -40,6 +40,13 @@ class NET_EXPORT UDPSocketWin
// Returns a net error code.
int Open(AddressFamily address_family);
+ // Binds this socket to |network|. All data traffic on the socket will be sent
+ // and received via |network|. Must be called before Connect(). This call will
+ // fail if |network| has disconnected. Communication using this socket will
+ // fail if |network| disconnects.
+ // Returns a net error code.
+ int BindToNetwork(NetworkChangeNotifier::NetworkHandle network);
+
// Connects the socket to connect with a certain |address|.
// Should be called after Open().
// Returns a net error code.