diff --git a/rtc_base/BUILD.gn b/rtc_base/BUILD.gn index 64548e6f80..55683e2338 100644 --- a/rtc_base/BUILD.gn +++ b/rtc_base/BUILD.gn @@ -1428,4 +1428,7 @@ if (is_android) { "//third_party/android_deps:com_android_support_support_annotations_java", ] } + java_cpp_enum("network_monitor_enums") { + sources = [ "network_monitor.h" ] + } } diff --git a/rtc_base/network.cc b/rtc_base/network.cc index 7b4124c464..0d518c0b4d 100644 --- a/rtc_base/network.cc +++ b/rtc_base/network.cc @@ -265,7 +265,9 @@ webrtc::MdnsResponderInterface* NetworkManager::GetMdnsResponder() const { } NetworkManagerBase::NetworkManagerBase() - : enumeration_permission_(NetworkManager::ENUMERATION_ALLOWED) {} + : enumeration_permission_(NetworkManager::ENUMERATION_ALLOWED), + signal_network_preference_change_(webrtc::field_trial::IsEnabled( + "WebRTC-SignalNetworkPreferenceChange")) {} NetworkManagerBase::~NetworkManagerBase() { for (const auto& kv : networks_map_) { @@ -382,6 +384,12 @@ void NetworkManagerBase::MergeNetworkList(const NetworkList& new_networks, if (!existing_net->active()) { *changed = true; } + if (net->network_preference() != existing_net->network_preference()) { + existing_net->set_network_preference(net->network_preference()); + if (signal_network_preference_change_) { + *changed = true; + } + } RTC_DCHECK(net->active()); if (existing_net != net) { delete net; @@ -536,6 +544,7 @@ void BasicNetworkManager::ConvertIfAddrs(struct ifaddrs* interfaces, AdapterType adapter_type = ADAPTER_TYPE_UNKNOWN; AdapterType vpn_underlying_adapter_type = ADAPTER_TYPE_UNKNOWN; + NetworkPreference network_preference = NetworkPreference::NEUTRAL; if (cursor->ifa_flags & IFF_LOOPBACK) { adapter_type = ADAPTER_TYPE_LOOPBACK; } else { @@ -543,6 +552,8 @@ void BasicNetworkManager::ConvertIfAddrs(struct ifaddrs* interfaces, // Otherwise, get the adapter type based on a few name matching rules. if (network_monitor_) { adapter_type = network_monitor_->GetAdapterType(cursor->ifa_name); + network_preference = + network_monitor_->GetNetworkPreference(cursor->ifa_name); } if (adapter_type == ADAPTER_TYPE_UNKNOWN) { adapter_type = GetAdapterTypeFromName(cursor->ifa_name); @@ -568,6 +579,7 @@ void BasicNetworkManager::ConvertIfAddrs(struct ifaddrs* interfaces, network->AddIP(ip); network->set_ignored(IsIgnoredNetwork(*network)); network->set_underlying_type_for_vpn(vpn_underlying_adapter_type); + network->set_network_preference(network_preference); if (include_ignored || !network->ignored()) { current_networks[key] = network.get(); networks->push_back(network.release()); @@ -580,6 +592,7 @@ void BasicNetworkManager::ConvertIfAddrs(struct ifaddrs* interfaces, existing_network->set_underlying_type_for_vpn( vpn_underlying_adapter_type); } + existing_network->set_network_preference(network_preference); } } } diff --git a/rtc_base/network.h b/rtc_base/network.h index 26ef628d8a..3dad521a77 100644 --- a/rtc_base/network.h +++ b/rtc_base/network.h @@ -215,6 +215,10 @@ class RTC_EXPORT NetworkManagerBase : public NetworkManager { // network id 0 because we only compare the network ids in the old and the new // best connections in the transport channel. uint16_t next_available_network_id_ = 1; + + // True if calling network_preference() with a changed value + // should result in firing the SignalNetworkChanged signal. + bool signal_network_preference_change_ = false; }; // Basic implementation of the NetworkManager interface that gets list @@ -306,9 +310,13 @@ class RTC_EXPORT Network { AdapterType type); Network(const Network&); ~Network(); + // This signal is fired whenever type() or underlying_type_for_vpn() changes. sigslot::signal1 SignalTypeChanged; + // This signal is fired whenever network preference changes. + sigslot::signal1 SignalNetworkPreferenceChanged; + const DefaultLocalAddressProvider* default_local_address_provider() { return default_local_address_provider_; } @@ -453,6 +461,17 @@ class RTC_EXPORT Network { } } + // Property set by operating system/firmware that has information + // about connection strength to e.g WIFI router or CELL base towers. + NetworkPreference network_preference() const { return network_preference_; } + void set_network_preference(NetworkPreference val) { + if (network_preference_ == val) { + return; + } + network_preference_ = val; + SignalNetworkPreferenceChanged(this); + } + // Debugging description of this network std::string ToString() const; @@ -473,6 +492,7 @@ class RTC_EXPORT Network { bool active_ = true; uint16_t id_ = 0; bool use_differentiated_cellular_costs_ = false; + NetworkPreference network_preference_ = NetworkPreference::NEUTRAL; friend class NetworkManager; }; diff --git a/rtc_base/network_monitor.h b/rtc_base/network_monitor.h index 5d44b62ca7..eb3c3d65fd 100644 --- a/rtc_base/network_monitor.h +++ b/rtc_base/network_monitor.h @@ -30,6 +30,14 @@ enum class NetworkBindingResult { NETWORK_CHANGED = -4 }; +// NetworkPreference property set by operating system/firmware that has +// information about connection strength to e.g WIFI router or CELL base towers. +// GENERATED_JAVA_ENUM_PACKAGE: org.webrtc +enum class NetworkPreference { + NEUTRAL = 0, + NOT_PREFERRED = -1, +}; + class NetworkBinderInterface { public: // Binds a socket to the network that is attached to |address| so that all @@ -78,6 +86,8 @@ class NetworkMonitorInterface { virtual AdapterType GetAdapterType(const std::string& interface_name) = 0; virtual AdapterType GetVpnUnderlyingAdapterType( const std::string& interface_name) = 0; + virtual NetworkPreference GetNetworkPreference( + const std::string& interface_name) = 0; }; class NetworkMonitorBase : public NetworkMonitorInterface, diff --git a/rtc_base/network_unittest.cc b/rtc_base/network_unittest.cc index 29a89ba1be..42fb3d9dbb 100644 --- a/rtc_base/network_unittest.cc +++ b/rtc_base/network_unittest.cc @@ -57,6 +57,9 @@ class FakeNetworkMonitor : public NetworkMonitorBase { } return ADAPTER_TYPE_UNKNOWN; } + NetworkPreference GetNetworkPreference(const std::string& if_name) override { + return NetworkPreference::NEUTRAL; + } private: bool started_ = false; diff --git a/sdk/android/BUILD.gn b/sdk/android/BUILD.gn index ac78d065fd..61037bc37b 100644 --- a/sdk/android/BUILD.gn +++ b/sdk/android/BUILD.gn @@ -276,12 +276,13 @@ if (is_android) { "api/org/webrtc/NativeLibraryLoader.java", "api/org/webrtc/NativePeerConnectionFactory.java", "api/org/webrtc/NetEqFactoryFactory.java", + "api/org/webrtc/NetworkChangeDetector.java", + "api/org/webrtc/NetworkChangeDetectorFactory.java", "api/org/webrtc/NetworkControllerFactoryFactory.java", - "api/org/webrtc/NetworkMonitor.java", # TODO(sakal): Break dependencies - # and move to base_java. - "api/org/webrtc/NetworkMonitorAutoDetect.java", # TODO(sakal): Break - # dependencies and move - # to base_java. + + # TODO(sakal): Break dependencies and move to base_java. + "api/org/webrtc/NetworkMonitor.java", + "api/org/webrtc/NetworkMonitorAutoDetect.java", "api/org/webrtc/NetworkStatePredictorFactoryFactory.java", "api/org/webrtc/PeerConnection.java", "api/org/webrtc/PeerConnectionDependencies.java", @@ -328,6 +329,7 @@ if (is_android) { srcjar_deps = [ "//api:priority_enums", "//api/video:video_frame_enums", + "//rtc_base:network_monitor_enums", ] } @@ -1171,8 +1173,8 @@ if (current_os == "linux" || is_android) { generate_jni("generated_base_jni") { sources = [ + "api/org/webrtc/NetworkChangeDetector.java", "api/org/webrtc/NetworkMonitor.java", - "api/org/webrtc/NetworkMonitorAutoDetect.java", "api/org/webrtc/RefCounted.java", "src/java/org/webrtc/Histogram.java", "src/java/org/webrtc/JniCommon.java", diff --git a/sdk/android/api/org/webrtc/NetworkChangeDetector.java b/sdk/android/api/org/webrtc/NetworkChangeDetector.java new file mode 100644 index 0000000000..a845c78445 --- /dev/null +++ b/sdk/android/api/org/webrtc/NetworkChangeDetector.java @@ -0,0 +1,116 @@ +/* + * Copyright 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +package org.webrtc; + +import android.support.annotation.Nullable; +import java.util.List; + +/** Interface for detecting network changes */ +public interface NetworkChangeDetector { + // java equivalent of c++ android_network_monitor.h / NetworkType. + public static enum ConnectionType { + CONNECTION_UNKNOWN, + CONNECTION_ETHERNET, + CONNECTION_WIFI, + CONNECTION_5G, + CONNECTION_4G, + CONNECTION_3G, + CONNECTION_2G, + CONNECTION_UNKNOWN_CELLULAR, + CONNECTION_BLUETOOTH, + CONNECTION_VPN, + CONNECTION_NONE + } + + public static class IPAddress { + public final byte[] address; + + public IPAddress(byte[] address) { + this.address = address; + } + + @CalledByNative("IPAddress") + private byte[] getAddress() { + return address; + } + } + + /** Java version of NetworkMonitor.NetworkInformation */ + public static class NetworkInformation { + public final String name; + public final ConnectionType type; + // Used to specify the underlying network type if the type is CONNECTION_VPN. + public final ConnectionType underlyingTypeForVpn; + public final long handle; + public final IPAddress[] ipAddresses; + + public NetworkInformation(String name, ConnectionType type, ConnectionType underlyingTypeForVpn, + long handle, IPAddress[] addresses) { + this.name = name; + this.type = type; + this.underlyingTypeForVpn = underlyingTypeForVpn; + this.handle = handle; + this.ipAddresses = addresses; + } + + @CalledByNative("NetworkInformation") + private IPAddress[] getIpAddresses() { + return ipAddresses; + } + + @CalledByNative("NetworkInformation") + private ConnectionType getConnectionType() { + return type; + } + + @CalledByNative("NetworkInformation") + private ConnectionType getUnderlyingConnectionTypeForVpn() { + return underlyingTypeForVpn; + } + + @CalledByNative("NetworkInformation") + private long getHandle() { + return handle; + } + + @CalledByNative("NetworkInformation") + private String getName() { + return name; + } + }; + + /** Observer interface by which observer is notified of network changes. */ + public static interface Observer { + /** Called when default network changes. */ + public void onConnectionTypeChanged(ConnectionType newConnectionType); + + public void onNetworkConnect(NetworkInformation networkInfo); + + public void onNetworkDisconnect(long networkHandle); + + /** + * Called when network preference change for a (list of) connection type(s). (e.g WIFI) is + * |NOT_PREFERRED| or |NEUTRAL|. + * + *

note: |types| is a list of ConnectionTypes, so that all cellular types can be modified in + * one call. + */ + public void onNetworkPreference(List types, @NetworkPreference int preference); + } + + public ConnectionType getCurrentConnectionType(); + + public boolean supportNetworkCallback(); + + @Nullable public List getActiveNetworkList(); + + public void destroy(); +} diff --git a/sdk/android/api/org/webrtc/NetworkChangeDetectorFactory.java b/sdk/android/api/org/webrtc/NetworkChangeDetectorFactory.java new file mode 100644 index 0000000000..14e98b2387 --- /dev/null +++ b/sdk/android/api/org/webrtc/NetworkChangeDetectorFactory.java @@ -0,0 +1,17 @@ +/* + * Copyright 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +package org.webrtc; + +import android.content.Context; + +public interface NetworkChangeDetectorFactory { + public NetworkChangeDetector create(NetworkChangeDetector.Observer observer, Context context); +} diff --git a/sdk/android/api/org/webrtc/NetworkMonitor.java b/sdk/android/api/org/webrtc/NetworkMonitor.java index 364bb4d0b5..566302b0b4 100644 --- a/sdk/android/api/org/webrtc/NetworkMonitor.java +++ b/sdk/android/api/org/webrtc/NetworkMonitor.java @@ -10,14 +10,12 @@ package org.webrtc; -import static org.webrtc.NetworkMonitorAutoDetect.INVALID_NET_ID; - import android.content.Context; import android.os.Build; import android.support.annotation.Nullable; import java.util.ArrayList; import java.util.List; -import org.webrtc.NetworkMonitorAutoDetect; +import org.webrtc.NetworkChangeDetector; /** * Borrowed from Chromium's @@ -32,7 +30,7 @@ public class NetworkMonitor { * Alerted when the connection type of the network changes. The alert is fired on the UI thread. */ public interface NetworkObserver { - public void onConnectionTypeChanged(NetworkMonitorAutoDetect.ConnectionType connectionType); + public void onConnectionTypeChanged(NetworkChangeDetector.ConnectionType connectionType); } private static final String TAG = "NetworkMonitor"; @@ -43,24 +41,43 @@ public class NetworkMonitor { static final NetworkMonitor instance = new NetworkMonitor(); } + // Factory for creating NetworkChangeDetector. + private NetworkChangeDetectorFactory networkChangeDetectorFactory = + new NetworkChangeDetectorFactory() { + @Override + public NetworkChangeDetector create( + NetworkChangeDetector.Observer observer, Context context) { + return new NetworkMonitorAutoDetect(observer, context); + } + }; + // Native observers of the connection type changes. private final ArrayList nativeNetworkObservers; // Java observers of the connection type changes. private final ArrayList networkObservers; - private final Object autoDetectLock = new Object(); + private final Object networkChangeDetectorLock = new Object(); // Object that detects the connection type changes and brings up mobile networks. - @Nullable private NetworkMonitorAutoDetect autoDetect; + @Nullable private NetworkChangeDetector networkChangeDetector; // Also guarded by autoDetectLock. private int numObservers; - private volatile NetworkMonitorAutoDetect.ConnectionType currentConnectionType; + private volatile NetworkChangeDetector.ConnectionType currentConnectionType; private NetworkMonitor() { nativeNetworkObservers = new ArrayList(); networkObservers = new ArrayList(); numObservers = 0; - currentConnectionType = NetworkMonitorAutoDetect.ConnectionType.CONNECTION_UNKNOWN; + currentConnectionType = NetworkChangeDetector.ConnectionType.CONNECTION_UNKNOWN; + } + + /** + * Set the factory that will be used to create the network change detector. + * Needs to be called before the monitoring is starts. + */ + public void setNetworkChangeDetectorFactory(NetworkChangeDetectorFactory factory) { + assertIsTrue(numObservers == 0); + this.networkChangeDetectorFactory = factory; } // TODO(sakal): Remove once downstream dependencies have been updated. @@ -85,13 +102,12 @@ public class NetworkMonitor { * CHANGE_NETWORK_STATE permission. */ public void startMonitoring(Context applicationContext) { - synchronized (autoDetectLock) { + synchronized (networkChangeDetectorLock) { ++numObservers; - if (autoDetect == null) { - autoDetect = createAutoDetect(applicationContext); + if (networkChangeDetector == null) { + networkChangeDetector = createNetworkChangeDetector(applicationContext); } - currentConnectionType = - NetworkMonitorAutoDetect.getConnectionType(autoDetect.getCurrentNetworkState()); + currentConnectionType = networkChangeDetector.getCurrentConnectionType(); } } @@ -122,12 +138,15 @@ public class NetworkMonitor { notifyObserversOfConnectionTypeChange(currentConnectionType); } - /** Stop network monitoring. If no one is monitoring networks, destroy and reset autoDetect. */ + /** + * Stop network monitoring. If no one is monitoring networks, destroy and reset + * networkChangeDetector. + */ public void stopMonitoring() { - synchronized (autoDetectLock) { + synchronized (networkChangeDetectorLock) { if (--numObservers == 0) { - autoDetect.destroy(); - autoDetect = null; + networkChangeDetector.destroy(); + networkChangeDetector = null; } } } @@ -144,8 +163,8 @@ public class NetworkMonitor { // Returns true if network binding is supported on this platform. @CalledByNative private boolean networkBindingSupported() { - synchronized (autoDetectLock) { - return autoDetect != null && autoDetect.supportNetworkCallback(); + synchronized (networkChangeDetectorLock) { + return networkChangeDetector != null && networkChangeDetector.supportNetworkCallback(); } } @@ -154,27 +173,19 @@ public class NetworkMonitor { return Build.VERSION.SDK_INT; } - private NetworkMonitorAutoDetect.ConnectionType getCurrentConnectionType() { + private NetworkChangeDetector.ConnectionType getCurrentConnectionType() { return currentConnectionType; } - private long getCurrentDefaultNetId() { - synchronized (autoDetectLock) { - return autoDetect == null ? INVALID_NET_ID : autoDetect.getDefaultNetId(); - } - } - - private NetworkMonitorAutoDetect createAutoDetect(Context appContext) { - return new NetworkMonitorAutoDetect(new NetworkMonitorAutoDetect.Observer() { - + private NetworkChangeDetector createNetworkChangeDetector(Context appContext) { + return networkChangeDetectorFactory.create(new NetworkChangeDetector.Observer() { @Override - public void onConnectionTypeChanged( - NetworkMonitorAutoDetect.ConnectionType newConnectionType) { + public void onConnectionTypeChanged(NetworkChangeDetector.ConnectionType newConnectionType) { updateCurrentConnectionType(newConnectionType); } @Override - public void onNetworkConnect(NetworkMonitorAutoDetect.NetworkInformation networkInfo) { + public void onNetworkConnect(NetworkChangeDetector.NetworkInformation networkInfo) { notifyObserversOfNetworkConnect(networkInfo); } @@ -182,18 +193,23 @@ public class NetworkMonitor { public void onNetworkDisconnect(long networkHandle) { notifyObserversOfNetworkDisconnect(networkHandle); } + + @Override + public void onNetworkPreference( + List types, int preference) { + notifyObserversOfNetworkPreference(types, preference); + } }, appContext); } - private void updateCurrentConnectionType( - NetworkMonitorAutoDetect.ConnectionType newConnectionType) { + private void updateCurrentConnectionType(NetworkChangeDetector.ConnectionType newConnectionType) { currentConnectionType = newConnectionType; notifyObserversOfConnectionTypeChange(newConnectionType); } /** Alerts all observers of a connection change. */ private void notifyObserversOfConnectionTypeChange( - NetworkMonitorAutoDetect.ConnectionType newConnectionType) { + NetworkChangeDetector.ConnectionType newConnectionType) { List nativeObservers = getNativeNetworkObserversSync(); for (Long nativeObserver : nativeObservers) { nativeNotifyConnectionTypeChanged(nativeObserver); @@ -209,7 +225,7 @@ public class NetworkMonitor { } private void notifyObserversOfNetworkConnect( - NetworkMonitorAutoDetect.NetworkInformation networkInfo) { + NetworkChangeDetector.NetworkInformation networkInfo) { List nativeObservers = getNativeNetworkObserversSync(); for (Long nativeObserver : nativeObservers) { nativeNotifyOfNetworkConnect(nativeObserver, networkInfo); @@ -223,17 +239,28 @@ public class NetworkMonitor { } } + private void notifyObserversOfNetworkPreference( + List types, int preference) { + List nativeObservers = getNativeNetworkObserversSync(); + for (NetworkChangeDetector.ConnectionType type : types) { + for (Long nativeObserver : nativeObservers) { + nativeNotifyOfNetworkPreference(nativeObserver, type, preference); + } + } + } + private void updateObserverActiveNetworkList(long nativeObserver) { - List networkInfoList; - synchronized (autoDetectLock) { - networkInfoList = (autoDetect == null) ? null : autoDetect.getActiveNetworkList(); + List networkInfoList; + synchronized (networkChangeDetectorLock) { + networkInfoList = + (networkChangeDetector == null) ? null : networkChangeDetector.getActiveNetworkList(); } if (networkInfoList == null || networkInfoList.size() == 0) { return; } - NetworkMonitorAutoDetect.NetworkInformation[] networkInfos = - new NetworkMonitorAutoDetect.NetworkInformation[networkInfoList.size()]; + NetworkChangeDetector.NetworkInformation[] networkInfos = + new NetworkChangeDetector.NetworkInformation[networkInfoList.size()]; networkInfos = networkInfoList.toArray(networkInfos); nativeNotifyOfActiveNetworkList(nativeObserver, networkInfos); } @@ -278,30 +305,35 @@ public class NetworkMonitor { /** Checks if there currently is connectivity. */ public static boolean isOnline() { - NetworkMonitorAutoDetect.ConnectionType connectionType = - getInstance().getCurrentConnectionType(); - return connectionType != NetworkMonitorAutoDetect.ConnectionType.CONNECTION_NONE; + NetworkChangeDetector.ConnectionType connectionType = getInstance().getCurrentConnectionType(); + return connectionType != NetworkChangeDetector.ConnectionType.CONNECTION_NONE; } private native void nativeNotifyConnectionTypeChanged(long nativeAndroidNetworkMonitor); + private native void nativeNotifyOfNetworkConnect( - long nativeAndroidNetworkMonitor, NetworkMonitorAutoDetect.NetworkInformation networkInfo); + long nativeAndroidNetworkMonitor, NetworkChangeDetector.NetworkInformation networkInfo); + private native void nativeNotifyOfNetworkDisconnect( long nativeAndroidNetworkMonitor, long networkHandle); + private native void nativeNotifyOfActiveNetworkList( - long nativeAndroidNetworkMonitor, NetworkMonitorAutoDetect.NetworkInformation[] networkInfos); + long nativeAndroidNetworkMonitor, NetworkChangeDetector.NetworkInformation[] networkInfos); + + private native void nativeNotifyOfNetworkPreference( + long nativeAndroidNetworkMonitor, NetworkChangeDetector.ConnectionType type, int preference); // For testing only. @Nullable - NetworkMonitorAutoDetect getNetworkMonitorAutoDetect() { - synchronized (autoDetectLock) { - return autoDetect; + NetworkChangeDetector getNetworkChangeDetector() { + synchronized (networkChangeDetectorLock) { + return networkChangeDetector; } } // For testing only. int getNumObservers() { - synchronized (autoDetectLock) { + synchronized (networkChangeDetectorLock) { return numObservers; } } @@ -309,7 +341,9 @@ public class NetworkMonitor { // For testing only. static NetworkMonitorAutoDetect createAndSetAutoDetectForTest(Context context) { NetworkMonitor networkMonitor = getInstance(); - NetworkMonitorAutoDetect autoDetect = networkMonitor.createAutoDetect(context); - return networkMonitor.autoDetect = autoDetect; + NetworkChangeDetector networkChangeDetector = + networkMonitor.createNetworkChangeDetector(context); + networkMonitor.networkChangeDetector = networkChangeDetector; + return (NetworkMonitorAutoDetect) networkChangeDetector; } } diff --git a/sdk/android/api/org/webrtc/NetworkMonitorAutoDetect.java b/sdk/android/api/org/webrtc/NetworkMonitorAutoDetect.java index dbea840710..5594c18f5b 100644 --- a/sdk/android/api/org/webrtc/NetworkMonitorAutoDetect.java +++ b/sdk/android/api/org/webrtc/NetworkMonitorAutoDetect.java @@ -41,80 +41,10 @@ import java.util.List; * Borrowed from Chromium's * src/net/android/java/src/org/chromium/net/NetworkChangeNotifierAutoDetect.java * - * Used by the NetworkMonitor to listen to platform changes in connectivity. - * Note that use of this class requires that the app have the platform - * ACCESS_NETWORK_STATE permission. + *

Used by the NetworkMonitor to listen to platform changes in connectivity. Note that use of + * this class requires that the app have the platform ACCESS_NETWORK_STATE permission. */ -public class NetworkMonitorAutoDetect extends BroadcastReceiver { - public static enum ConnectionType { - CONNECTION_UNKNOWN, - CONNECTION_ETHERNET, - CONNECTION_WIFI, - CONNECTION_5G, - CONNECTION_4G, - CONNECTION_3G, - CONNECTION_2G, - CONNECTION_UNKNOWN_CELLULAR, - CONNECTION_BLUETOOTH, - CONNECTION_VPN, - CONNECTION_NONE - } - - public static class IPAddress { - public final byte[] address; - public IPAddress(byte[] address) { - this.address = address; - } - - @CalledByNative("IPAddress") - private byte[] getAddress() { - return address; - } - } - - /** Java version of NetworkMonitor.NetworkInformation */ - public static class NetworkInformation { - public final String name; - public final ConnectionType type; - // Used to specify the underlying network type if the type is CONNECTION_VPN. - public final ConnectionType underlyingTypeForVpn; - public final long handle; - public final IPAddress[] ipAddresses; - public NetworkInformation(String name, ConnectionType type, ConnectionType underlyingTypeForVpn, - long handle, IPAddress[] addresses) { - this.name = name; - this.type = type; - this.underlyingTypeForVpn = underlyingTypeForVpn; - this.handle = handle; - this.ipAddresses = addresses; - } - - @CalledByNative("NetworkInformation") - private IPAddress[] getIpAddresses() { - return ipAddresses; - } - - @CalledByNative("NetworkInformation") - private ConnectionType getConnectionType() { - return type; - } - - @CalledByNative("NetworkInformation") - private ConnectionType getUnderlyingConnectionTypeForVpn() { - return underlyingTypeForVpn; - } - - @CalledByNative("NetworkInformation") - private long getHandle() { - return handle; - } - - @CalledByNative("NetworkInformation") - private String getName() { - return name; - } - }; - +public class NetworkMonitorAutoDetect extends BroadcastReceiver implements NetworkChangeDetector { static class NetworkState { private final boolean connected; // Defined from ConnectivityManager.TYPE_XXX for non-mobile; for mobile, it is @@ -410,8 +340,8 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver { } NetworkState networkState = getNetworkState(network); - ConnectionType connectionType = getConnectionType(networkState); - if (connectionType == ConnectionType.CONNECTION_NONE) { + NetworkChangeDetector.ConnectionType connectionType = getConnectionType(networkState); + if (connectionType == NetworkChangeDetector.ConnectionType.CONNECTION_NONE) { // This may not be an error. The OS may signal a network event with connection type // NONE when the network disconnects. Logging.d(TAG, "Network " + network.toString() + " is disconnected"); @@ -420,13 +350,14 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver { // Some android device may return a CONNECTION_UNKNOWN_CELLULAR or CONNECTION_UNKNOWN type, // which appears to be usable. Just log them here. - if (connectionType == ConnectionType.CONNECTION_UNKNOWN - || connectionType == ConnectionType.CONNECTION_UNKNOWN_CELLULAR) { + if (connectionType == NetworkChangeDetector.ConnectionType.CONNECTION_UNKNOWN + || connectionType == NetworkChangeDetector.ConnectionType.CONNECTION_UNKNOWN_CELLULAR) { Logging.d(TAG, "Network " + network.toString() + " connection type is " + connectionType + " because it has type " + networkState.getNetworkType() + " and subtype " + networkState.getNetworkSubType()); } - // ConnectionType.CONNECTION_UNKNOWN if the network is not a VPN or the underlying network is + // NetworkChangeDetector.ConnectionType.CONNECTION_UNKNOWN if the network is not a VPN or the + // underlying network is // unknown. ConnectionType underlyingConnectionTypeForVpn = getUnderlyingConnectionTypeForVpn(networkState); @@ -529,12 +460,12 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver { // (NETWORK_UNSPECIFIED) for these addresses. private static final int WIFI_P2P_NETWORK_HANDLE = 0; private final Context context; - private final Observer observer; + private final NetworkChangeDetector.Observer observer; // Network information about a WifiP2p (aka WiFi-Direct) network, or null if no such network is // connected. @Nullable private NetworkInformation wifiP2pNetworkInfo; - WifiDirectManagerDelegate(Observer observer, Context context) { + WifiDirectManagerDelegate(NetworkChangeDetector.Observer observer, Context context) { this.context = context; this.observer = observer; IntentFilter intentFilter = new IntentFilter(); @@ -599,9 +530,10 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver { ipAddresses[i] = new IPAddress(interfaceAddresses.get(i).getAddress()); } - wifiP2pNetworkInfo = - new NetworkInformation(wifiP2pGroup.getInterface(), ConnectionType.CONNECTION_WIFI, - ConnectionType.CONNECTION_NONE, WIFI_P2P_NETWORK_HANDLE, ipAddresses); + wifiP2pNetworkInfo = new NetworkInformation(wifiP2pGroup.getInterface(), + NetworkChangeDetector.ConnectionType.CONNECTION_WIFI, + NetworkChangeDetector.ConnectionType.CONNECTION_NONE, WIFI_P2P_NETWORK_HANDLE, + ipAddresses); observer.onNetworkConnect(wifiP2pNetworkInfo); } @@ -614,11 +546,11 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver { } } - static final long INVALID_NET_ID = -1; + private static final long INVALID_NET_ID = -1; private static final String TAG = "NetworkMonitorAutoDetect"; // Observer for the connection type change. - private final Observer observer; + private final NetworkChangeDetector.Observer observer; private final IntentFilter intentFilter; private final Context context; // Used to request mobile network. It does not do anything except for keeping @@ -632,26 +564,12 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver { private WifiDirectManagerDelegate wifiDirectManagerDelegate; private boolean isRegistered; - private ConnectionType connectionType; + private NetworkChangeDetector.ConnectionType connectionType; private String wifiSSID; - /** - * Observer interface by which observer is notified of network changes. - */ - public static interface Observer { - /** - * Called when default network changes. - */ - public void onConnectionTypeChanged(ConnectionType newConnectionType); - public void onNetworkConnect(NetworkInformation networkInfo); - public void onNetworkDisconnect(long networkHandle); - } - - /** - * Constructs a NetworkMonitorAutoDetect. Should only be called on UI thread. - */ + /** Constructs a NetworkMonitorAutoDetect. Should only be called on UI thread. */ @SuppressLint("NewApi") - public NetworkMonitorAutoDetect(Observer observer, Context context) { + public NetworkMonitorAutoDetect(NetworkChangeDetector.Observer observer, Context context) { this.observer = observer; this.context = context; connectivityManagerDelegate = new ConnectivityManagerDelegate(context); @@ -686,6 +604,7 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver { } } + @Override public boolean supportNetworkCallback() { return connectivityManagerDelegate.supportNetworkCallback(); } @@ -712,8 +631,9 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver { return isRegistered; } + @Override @Nullable - List getActiveNetworkList() { + public List getActiveNetworkList() { List connectivityManagerList = connectivityManagerDelegate.getActiveNetworkList(); if (connectivityManagerList == null) { @@ -727,6 +647,7 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver { return result; } + @Override public void destroy() { if (allNetworkCallback != null) { connectivityManagerDelegate.releaseCallback(allNetworkCallback); @@ -776,21 +697,21 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver { return connectivityManagerDelegate.getDefaultNetId(); } - private static ConnectionType getConnectionType( + private static NetworkChangeDetector.ConnectionType getConnectionType( boolean isConnected, int networkType, int networkSubtype) { if (!isConnected) { - return ConnectionType.CONNECTION_NONE; + return NetworkChangeDetector.ConnectionType.CONNECTION_NONE; } switch (networkType) { case ConnectivityManager.TYPE_ETHERNET: - return ConnectionType.CONNECTION_ETHERNET; + return NetworkChangeDetector.ConnectionType.CONNECTION_ETHERNET; case ConnectivityManager.TYPE_WIFI: - return ConnectionType.CONNECTION_WIFI; + return NetworkChangeDetector.ConnectionType.CONNECTION_WIFI; case ConnectivityManager.TYPE_WIMAX: - return ConnectionType.CONNECTION_4G; + return NetworkChangeDetector.ConnectionType.CONNECTION_4G; case ConnectivityManager.TYPE_BLUETOOTH: - return ConnectionType.CONNECTION_BLUETOOTH; + return NetworkChangeDetector.ConnectionType.CONNECTION_BLUETOOTH; case ConnectivityManager.TYPE_MOBILE: // Use information from TelephonyManager to classify the connection. switch (networkSubtype) { @@ -800,7 +721,7 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver { case TelephonyManager.NETWORK_TYPE_1xRTT: case TelephonyManager.NETWORK_TYPE_IDEN: case TelephonyManager.NETWORK_TYPE_GSM: - return ConnectionType.CONNECTION_2G; + return NetworkChangeDetector.ConnectionType.CONNECTION_2G; case TelephonyManager.NETWORK_TYPE_UMTS: case TelephonyManager.NETWORK_TYPE_EVDO_0: case TelephonyManager.NETWORK_TYPE_EVDO_A: @@ -811,30 +732,36 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver { case TelephonyManager.NETWORK_TYPE_EHRPD: case TelephonyManager.NETWORK_TYPE_HSPAP: case TelephonyManager.NETWORK_TYPE_TD_SCDMA: - return ConnectionType.CONNECTION_3G; + return NetworkChangeDetector.ConnectionType.CONNECTION_3G; case TelephonyManager.NETWORK_TYPE_LTE: case TelephonyManager.NETWORK_TYPE_IWLAN: - return ConnectionType.CONNECTION_4G; + return NetworkChangeDetector.ConnectionType.CONNECTION_4G; case TelephonyManager.NETWORK_TYPE_NR: - return ConnectionType.CONNECTION_5G; + return NetworkChangeDetector.ConnectionType.CONNECTION_5G; default: - return ConnectionType.CONNECTION_UNKNOWN_CELLULAR; + return NetworkChangeDetector.ConnectionType.CONNECTION_UNKNOWN_CELLULAR; } case ConnectivityManager.TYPE_VPN: - return ConnectionType.CONNECTION_VPN; + return NetworkChangeDetector.ConnectionType.CONNECTION_VPN; default: - return ConnectionType.CONNECTION_UNKNOWN; + return NetworkChangeDetector.ConnectionType.CONNECTION_UNKNOWN; } } - public static ConnectionType getConnectionType(NetworkState networkState) { + public static NetworkChangeDetector.ConnectionType getConnectionType(NetworkState networkState) { return getConnectionType(networkState.isConnected(), networkState.getNetworkType(), networkState.getNetworkSubType()); } - private static ConnectionType getUnderlyingConnectionTypeForVpn(NetworkState networkState) { + @Override + public NetworkChangeDetector.ConnectionType getCurrentConnectionType() { + return getConnectionType(getCurrentNetworkState()); + } + + private static NetworkChangeDetector.ConnectionType getUnderlyingConnectionTypeForVpn( + NetworkState networkState) { if (networkState.getNetworkType() != ConnectivityManager.TYPE_VPN) { - return ConnectionType.CONNECTION_NONE; + return NetworkChangeDetector.ConnectionType.CONNECTION_NONE; } return getConnectionType(networkState.isConnected(), networkState.getUnderlyingNetworkTypeForVpn(), @@ -842,7 +769,7 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver { } private String getWifiSSID(NetworkState networkState) { - if (getConnectionType(networkState) != ConnectionType.CONNECTION_WIFI) + if (getConnectionType(networkState) != NetworkChangeDetector.ConnectionType.CONNECTION_WIFI) return ""; return wifiManagerDelegate.getWifiSSID(); } @@ -857,7 +784,7 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver { } private void connectionTypeChanged(NetworkState networkState) { - ConnectionType newConnectionType = getConnectionType(networkState); + NetworkChangeDetector.ConnectionType newConnectionType = getConnectionType(networkState); String newWifiSSID = getWifiSSID(networkState); if (newConnectionType == connectionType && newWifiSSID.equals(wifiSSID)) return; diff --git a/sdk/android/instrumentationtests/src/org/webrtc/NetworkMonitorTest.java b/sdk/android/instrumentationtests/src/org/webrtc/NetworkMonitorTest.java index 36136ca933..5f7e07df55 100644 --- a/sdk/android/instrumentationtests/src/org/webrtc/NetworkMonitorTest.java +++ b/sdk/android/instrumentationtests/src/org/webrtc/NetworkMonitorTest.java @@ -15,7 +15,6 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import static org.webrtc.NetworkMonitorAutoDetect.INVALID_NET_ID; import android.annotation.SuppressLint; import android.content.Context; @@ -31,14 +30,15 @@ import android.support.test.annotation.UiThreadTest; import android.support.test.filters.MediumTest; import android.support.test.filters.SmallTest; import android.support.test.rule.UiThreadTestRule; +import java.util.List; import org.chromium.base.test.BaseJUnit4ClassRunner; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; -import org.webrtc.NetworkMonitorAutoDetect.ConnectionType; +import org.webrtc.NetworkChangeDetector.ConnectionType; +import org.webrtc.NetworkChangeDetector.NetworkInformation; import org.webrtc.NetworkMonitorAutoDetect.ConnectivityManagerDelegate; -import org.webrtc.NetworkMonitorAutoDetect.NetworkInformation; import org.webrtc.NetworkMonitorAutoDetect.NetworkState; /** @@ -53,6 +53,9 @@ import org.webrtc.NetworkMonitorAutoDetect.NetworkState; public class NetworkMonitorTest { @Rule public UiThreadTestRule uiThreadTestRule = new UiThreadTestRule(); + private static final long INVALID_NET_ID = -1; + private NetworkChangeDetector detector; + /** * Listens for alerts fired by the NetworkMonitor when network status changes. */ @@ -155,6 +158,10 @@ public class NetworkMonitorTest { @Override public void onNetworkDisconnect(long networkHandle) {} + + @Override + public void onNetworkPreference(List types, @NetworkPreference int preference) { + } } private static final Object lock = new Object(); @@ -179,6 +186,17 @@ public class NetworkMonitorTest { */ private void createTestMonitor() { Context context = InstrumentationRegistry.getTargetContext(); + + NetworkMonitor.getInstance().setNetworkChangeDetectorFactory( + new NetworkChangeDetectorFactory() { + @Override + public NetworkChangeDetector create( + NetworkChangeDetector.Observer observer, Context context) { + detector = new NetworkMonitorAutoDetect(observer, context); + return detector; + } + }); + receiver = NetworkMonitor.createAndSetAutoDetectForTest(context); assertNotNull(receiver); @@ -311,9 +329,9 @@ public class NetworkMonitorTest { Context context = ContextUtils.getApplicationContext(); networkMonitor.startMonitoring(context); assertEquals(1, networkMonitor.getNumObservers()); - assertNotNull(networkMonitor.getNetworkMonitorAutoDetect()); + assertEquals(detector, networkMonitor.getNetworkChangeDetector()); networkMonitor.stopMonitoring(); assertEquals(0, networkMonitor.getNumObservers()); - assertNull(networkMonitor.getNetworkMonitorAutoDetect()); + assertNull(networkMonitor.getNetworkChangeDetector()); } } diff --git a/sdk/android/src/jni/android_network_monitor.cc b/sdk/android/src/jni/android_network_monitor.cc index 69e89564e3..e41fd035d7 100644 --- a/sdk/android/src/jni/android_network_monitor.cc +++ b/sdk/android/src/jni/android_network_monitor.cc @@ -21,7 +21,7 @@ #include "rtc_base/ip_address.h" #include "rtc_base/logging.h" #include "rtc_base/strings/string_builder.h" -#include "sdk/android/generated_base_jni/NetworkMonitorAutoDetect_jni.h" +#include "sdk/android/generated_base_jni/NetworkChangeDetector_jni.h" #include "sdk/android/generated_base_jni/NetworkMonitor_jni.h" #include "sdk/android/native_api/jni/java_types.h" #include "sdk/android/src/jni/jni_helpers.h" @@ -413,6 +413,17 @@ void AndroidNetworkMonitor::OnNetworkDisconnected_w(NetworkHandle handle) { } } +void AndroidNetworkMonitor::OnNetworkPreference( + NetworkType type, + rtc::NetworkPreference preference) { + worker_thread()->Invoke(RTC_FROM_HERE, [&] { + auto adapter_type = + AdapterTypeFromNetworkType(type, surface_cellular_types_); + network_preference_by_adapter_type_[adapter_type] = preference; + }); + OnNetworksChanged(); +} + void AndroidNetworkMonitor::SetNetworkInfos( const std::vector& network_infos) { RTC_CHECK(thread_checker_.IsCurrent()); @@ -446,6 +457,29 @@ rtc::AdapterType AndroidNetworkMonitor::GetVpnUnderlyingAdapterType( return type; } +rtc::NetworkPreference AndroidNetworkMonitor::GetNetworkPreference( + const std::string& if_name) { + auto iter = adapter_type_by_name_.find(if_name); + if (iter == adapter_type_by_name_.end()) { + return rtc::NetworkPreference::NEUTRAL; + } + + rtc::AdapterType adapter_type = iter->second; + if (adapter_type == rtc::ADAPTER_TYPE_VPN) { + auto iter2 = vpn_underlying_adapter_type_by_name_.find(if_name); + if (iter2 != vpn_underlying_adapter_type_by_name_.end()) { + adapter_type = iter2->second; + } + } + + auto preference_iter = network_preference_by_adapter_type_.find(adapter_type); + if (preference_iter == network_preference_by_adapter_type_.end()) { + return rtc::NetworkPreference::NEUTRAL; + } + + return preference_iter->second; +} + AndroidNetworkMonitorFactory::AndroidNetworkMonitorFactory() : j_application_context_(nullptr) {} @@ -494,5 +528,16 @@ void AndroidNetworkMonitor::NotifyOfNetworkDisconnect( OnNetworkDisconnected(static_cast(network_handle)); } +void AndroidNetworkMonitor::NotifyOfNetworkPreference( + JNIEnv* env, + const JavaRef& j_caller, + const JavaRef& j_connection_type, + jint jpreference) { + NetworkType type = GetNetworkTypeFromJava(env, j_connection_type); + rtc::NetworkPreference preference = + static_cast(jpreference); + OnNetworkPreference(type, preference); +} + } // namespace jni } // namespace webrtc diff --git a/sdk/android/src/jni/android_network_monitor.h b/sdk/android/src/jni/android_network_monitor.h index 815b72d64d..d1008f2665 100644 --- a/sdk/android/src/jni/android_network_monitor.h +++ b/sdk/android/src/jni/android_network_monitor.h @@ -27,7 +27,7 @@ namespace jni { typedef int64_t NetworkHandle; -// c++ equivalent of java NetworkMonitorAutoDetect.ConnectionType. +// c++ equivalent of java NetworkChangeDetector.ConnectionType. enum NetworkType { NETWORK_UNKNOWN, NETWORK_ETHERNET, @@ -80,8 +80,13 @@ class AndroidNetworkMonitor : public rtc::NetworkMonitorBase, rtc::AdapterType GetAdapterType(const std::string& if_name) override; rtc::AdapterType GetVpnUnderlyingAdapterType( const std::string& if_name) override; + rtc::NetworkPreference GetNetworkPreference( + const std::string& if_name) override; + void OnNetworkConnected(const NetworkInformation& network_info); void OnNetworkDisconnected(NetworkHandle network_handle); + void OnNetworkPreference(NetworkType type, rtc::NetworkPreference preference); + // Always expected to be called on the network thread. void SetNetworkInfos(const std::vector& network_infos); @@ -96,6 +101,10 @@ class AndroidNetworkMonitor : public rtc::NetworkMonitorBase, void NotifyOfActiveNetworkList(JNIEnv* env, const JavaRef& j_caller, const JavaRef& j_network_infos); + void NotifyOfNetworkPreference(JNIEnv* env, + const JavaRef& j_caller, + const JavaRef& j_connection_type, + jint preference); // Visible for testing. absl::optional FindNetworkHandleFromAddress( @@ -114,6 +123,8 @@ class AndroidNetworkMonitor : public rtc::NetworkMonitorBase, std::map vpn_underlying_adapter_type_by_name_; std::map network_handle_by_address_; std::map network_info_by_handle_; + std::map + network_preference_by_adapter_type_; bool find_network_handle_without_ipv6_temporary_part_; bool surface_cellular_types_; };