From de2ed7dc18d5bed8198a6a3ef8a828c3d7556f0e Mon Sep 17 00:00:00 2001 From: Qingsi Wang Date: Fri, 27 Apr 2018 14:25:37 -0700 Subject: [PATCH] Support VPN adapter type in WebRTC Android. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The VPN adapter type is not effectively supported in WebRTC Android for 1) the network monitor may not obtain the VPN adapter type from the OS, e.g. via NetworkInfo.getType, 2) and VPN adapter type is replaced by the adapter type of an underlying network by the network monitor in the current implementation. Specifically, WebRTC Android would previously classify VPNs as either type ADAPTER_TYPE_UNKNOWN, or the type of the currently active network (which we assume the VPN is using). In this CL, VPNs are classified as ADAPTER_TYPE_VPN whenever possible, and the underlying network type, if available from the VPN, is separately stored and used to prioritize ICE candidates in network path selection. This allows ADAPTER_TYPE_VPN to be used in networkIgnoreMask to ignore VPNs when gathering ICE candidates. Bug: webrtc:9168 Change-Id: I9513c76a114ba967437b699e71223a4a2f13f34a Reviewed-on: https://webrtc-review.googlesource.com/70960 Commit-Queue: Qingsi Wang Reviewed-by: Taylor Brandstetter Reviewed-by: Sami Kalliomäki Cr-Commit-Position: refs/heads/master@{#23061} --- rtc_base/network.cc | 42 +++++- rtc_base/network.h | 36 +++-- rtc_base/networkmonitor.cc | 5 + rtc_base/networkmonitor.h | 5 + .../org/webrtc/NetworkMonitorAutoDetect.java | 138 ++++++++++++++---- .../src/org/webrtc/NetworkMonitorTest.java | 15 +- sdk/android/src/jni/androidnetworkmonitor.cc | 26 +++- sdk/android/src/jni/androidnetworkmonitor.h | 5 + 8 files changed, 225 insertions(+), 47 deletions(-) diff --git a/rtc_base/network.cc b/rtc_base/network.cc index 454a7780ec..6daa7c38d9 100644 --- a/rtc_base/network.cc +++ b/rtc_base/network.cc @@ -111,6 +111,24 @@ std::string AdapterTypeToString(AdapterType type) { } } +uint16_t ComputeNetworkCostByType(int type) { + switch (type) { + case rtc::ADAPTER_TYPE_ETHERNET: + case rtc::ADAPTER_TYPE_LOOPBACK: + return kNetworkCostMin; + case rtc::ADAPTER_TYPE_WIFI: + return kNetworkCostLow; + case rtc::ADAPTER_TYPE_CELLULAR: + return kNetworkCostHigh; + case rtc::ADAPTER_TYPE_VPN: + // The cost of a VPN should be computed using its underlying network type. + RTC_NOTREACHED(); + return kNetworkCostUnknown; + default: + return kNetworkCostUnknown; + } +} + #if !defined(__native_client__) bool IsIgnoredIPv6(const InterfaceAddress& ip) { if (ip.family() != AF_INET6) { @@ -476,6 +494,7 @@ void BasicNetworkManager::ConvertIfAddrs(struct ifaddrs* interfaces, } AdapterType adapter_type = ADAPTER_TYPE_UNKNOWN; + AdapterType vpn_underlying_adapter_type = ADAPTER_TYPE_UNKNOWN; if (cursor->ifa_flags & IFF_LOOPBACK) { adapter_type = ADAPTER_TYPE_LOOPBACK; } else { @@ -488,6 +507,11 @@ void BasicNetworkManager::ConvertIfAddrs(struct ifaddrs* interfaces, adapter_type = GetAdapterTypeFromName(cursor->ifa_name); } } + + if (adapter_type == ADAPTER_TYPE_VPN && network_monitor_) { + vpn_underlying_adapter_type = + network_monitor_->GetVpnUnderlyingAdapterType(cursor->ifa_name); + } int prefix_length = CountIPMaskBits(mask); prefix = TruncateIP(ip, prefix_length); std::string key = MakeNetworkKey(std::string(cursor->ifa_name), @@ -502,6 +526,7 @@ void BasicNetworkManager::ConvertIfAddrs(struct ifaddrs* interfaces, network->set_scope_id(scope_id); network->AddIP(ip); network->set_ignored(IsIgnoredNetwork(*network)); + network->set_underlying_type_for_vpn(vpn_underlying_adapter_type); if (include_ignored || !network->ignored()) { current_networks[key] = network.get(); networks->push_back(network.release()); @@ -511,6 +536,8 @@ void BasicNetworkManager::ConvertIfAddrs(struct ifaddrs* interfaces, existing_network->AddIP(ip); if (adapter_type != ADAPTER_TYPE_UNKNOWN) { existing_network->set_type(adapter_type); + existing_network->set_underlying_type_for_vpn( + vpn_underlying_adapter_type); } } } @@ -971,13 +998,22 @@ IPAddress Network::GetBestIP() const { return static_cast(selected_ip); } +uint16_t Network::GetCost() const { + AdapterType type = IsVpn() ? underlying_type_for_vpn_ : type_; + return ComputeNetworkCostByType(type); +} + std::string Network::ToString() const { std::stringstream ss; // Print out the first space-terminated token of the network desc, plus // the IP address. - ss << "Net[" << description_.substr(0, description_.find(' ')) - << ":" << prefix_.ToSensitiveString() << "/" << prefix_length_ - << ":" << AdapterTypeToString(type_) << "]"; + ss << "Net[" << description_.substr(0, description_.find(' ')) << ":" + << prefix_.ToSensitiveString() << "/" << prefix_length_ << ":" + << AdapterTypeToString(type_); + if (IsVpn()) { + ss << "/" << AdapterTypeToString(underlying_type_for_vpn_); + } + ss << "]"; return ss.str(); } diff --git a/rtc_base/network.h b/rtc_base/network.h index 2b0f377a3f..49f500c297 100644 --- a/rtc_base/network.h +++ b/rtc_base/network.h @@ -293,7 +293,7 @@ class Network { AdapterType type); Network(const Network&); ~Network(); - + // This signal is fired whenever type() or underlying_type_for_vpn() changes. sigslot::signal1 SignalTypeChanged; const DefaultLocalAddressProvider* default_local_address_provider() { @@ -367,28 +367,37 @@ class Network { void set_ignored(bool ignored) { ignored_ = ignored; } AdapterType type() const { return type_; } + // When type() is ADAPTER_TYPE_VPN, this returns the type of the underlying + // network interface used by the VPN, typically the preferred network type + // (see for example, the method setUnderlyingNetworks(android.net.Network[]) + // on https://developer.android.com/reference/android/net/VpnService.html). + // When this information is unavailable from the OS, ADAPTER_TYPE_UNKNOWN is + // returned. + AdapterType underlying_type_for_vpn() const { + return underlying_type_for_vpn_; + } void set_type(AdapterType type) { if (type_ == type) { return; } type_ = type; + if (type != ADAPTER_TYPE_VPN) { + underlying_type_for_vpn_ = ADAPTER_TYPE_UNKNOWN; + } SignalTypeChanged(this); } - uint16_t GetCost() const { - switch (type_) { - case rtc::ADAPTER_TYPE_ETHERNET: - case rtc::ADAPTER_TYPE_LOOPBACK: - return kNetworkCostMin; - case rtc::ADAPTER_TYPE_WIFI: - case rtc::ADAPTER_TYPE_VPN: - return kNetworkCostLow; - case rtc::ADAPTER_TYPE_CELLULAR: - return kNetworkCostHigh; - default: - return kNetworkCostUnknown; + void set_underlying_type_for_vpn(AdapterType type) { + if (underlying_type_for_vpn_ == type) { + return; } + underlying_type_for_vpn_ = type; + SignalTypeChanged(this); } + + bool IsVpn() const { return type_ == ADAPTER_TYPE_VPN; } + + uint16_t GetCost() const; // A unique id assigned by the network manager, which may be signaled // to the remote side in the candidate. uint16_t id() const { return id_; } @@ -421,6 +430,7 @@ class Network { int scope_id_; bool ignored_; AdapterType type_; + AdapterType underlying_type_for_vpn_ = ADAPTER_TYPE_UNKNOWN; int preference_; bool active_ = true; uint16_t id_ = 0; diff --git a/rtc_base/networkmonitor.cc b/rtc_base/networkmonitor.cc index 0272951351..ad6805acec 100644 --- a/rtc_base/networkmonitor.cc +++ b/rtc_base/networkmonitor.cc @@ -39,6 +39,11 @@ void NetworkMonitorBase::OnMessage(Message* msg) { SignalNetworksChanged(); } +AdapterType NetworkMonitorBase::GetVpnUnderlyingAdapterType( + const std::string& interface_name) { + return ADAPTER_TYPE_UNKNOWN; +} + NetworkMonitorFactory::NetworkMonitorFactory() {} NetworkMonitorFactory::~NetworkMonitorFactory() {} diff --git a/rtc_base/networkmonitor.h b/rtc_base/networkmonitor.h index 254b22575a..a174473002 100644 --- a/rtc_base/networkmonitor.h +++ b/rtc_base/networkmonitor.h @@ -74,6 +74,8 @@ class NetworkMonitorInterface { virtual void OnNetworksChanged() = 0; virtual AdapterType GetAdapterType(const std::string& interface_name) = 0; + virtual AdapterType GetVpnUnderlyingAdapterType( + const std::string& interface_name) = 0; }; class NetworkMonitorBase : public NetworkMonitorInterface, @@ -87,6 +89,9 @@ class NetworkMonitorBase : public NetworkMonitorInterface, void OnMessage(Message* msg) override; + AdapterType GetVpnUnderlyingAdapterType( + const std::string& interface_name) override; + protected: Thread* worker_thread() { return worker_thread_; } diff --git a/sdk/android/api/org/webrtc/NetworkMonitorAutoDetect.java b/sdk/android/api/org/webrtc/NetworkMonitorAutoDetect.java index 55a025715d..112d7cae78 100644 --- a/sdk/android/api/org/webrtc/NetworkMonitorAutoDetect.java +++ b/sdk/android/api/org/webrtc/NetworkMonitorAutoDetect.java @@ -55,6 +55,7 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver { CONNECTION_2G, CONNECTION_UNKNOWN_CELLULAR, CONNECTION_BLUETOOTH, + CONNECTION_VPN, CONNECTION_NONE } @@ -74,12 +75,15 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver { 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, long handle, IPAddress[] addresses) { + 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; } @@ -94,6 +98,11 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver { return type; } + @CalledByNative("NetworkInformation") + private ConnectionType getUnderlyingConnectionTypeForVpn() { + return underlyingTypeForVpn; + } + @CalledByNative("NetworkInformation") private long getHandle() { return handle; @@ -113,11 +122,18 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver { // Defined from NetworkInfo.subtype, which is one of the TelephonyManager.NETWORK_TYPE_XXXs. // Will be useful to find the maximum bandwidth. private final int subtype; + // When the type is TYPE_VPN, the following two fields specify the similar type and subtype as + // above for the underlying network that is used by the VPN. + private final int underlyingNetworkTypeForVpn; + private final int underlyingNetworkSubtypeForVpn; - public NetworkState(boolean connected, int type, int subtype) { + public NetworkState(boolean connected, int type, int subtype, int underlyingNetworkTypeForVpn, + int underlyingNetworkSubtypeForVpn) { this.connected = connected; this.type = type; this.subtype = subtype; + this.underlyingNetworkTypeForVpn = underlyingNetworkTypeForVpn; + this.underlyingNetworkSubtypeForVpn = underlyingNetworkSubtypeForVpn; } public boolean isConnected() { @@ -131,6 +147,14 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver { public int getNetworkSubType() { return subtype; } + + public int getUnderlyingNetworkTypeForVpn() { + return underlyingNetworkTypeForVpn; + } + + public int getUnderlyingNetworkSubtypeForVpn() { + return underlyingNetworkSubtypeForVpn; + } } /** * The methods in this class get called when the network changes if the callback @@ -208,7 +232,7 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver { */ NetworkState getNetworkState() { if (connectivityManager == null) { - return new NetworkState(false, -1, -1); + return new NetworkState(false, -1, -1, -1, -1); } return getNetworkState(connectivityManager.getActiveNetworkInfo()); } @@ -220,19 +244,65 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver { @SuppressLint("NewApi") NetworkState getNetworkState(Network network) { if (connectivityManager == null) { - return new NetworkState(false, -1, -1); + return new NetworkState(false, -1, -1, -1, -1); } - return getNetworkState(connectivityManager.getNetworkInfo(network)); + NetworkInfo networkInfo = connectivityManager.getNetworkInfo(network); + // The general logic of handling a VPN in this method is as follows. getNetworkInfo will + // return the info of the network with the same id as in |network| when it is registered via + // ConnectivityManager.registerNetworkAgent in Android. |networkInfo| may or may not indicate + // the type TYPE_VPN if |network| is a VPN. To reliably detect the VPN interface, we need to + // query the network capability as below in the case when networkInfo.getType() is not + // TYPE_VPN. On the other hand when networkInfo.getType() is TYPE_VPN, the only solution so + // far to obtain the underlying network information is to query the active network interface. + // However, the active network interface may not be used for the VPN, for example, if the VPN + // is restricted to WiFi by the implementation but the WiFi interface is currently turned + // off and the active interface is the Cell. Using directly the result from + // getActiveNetworkInfo may thus give the wrong interface information, and one should note + // that getActiveNetworkInfo would return the default network interface if the VPN does not + // specify its underlying networks in the implementation. Therefore, we need further compare + // |network| to the active network. If they are not the same network, we will have to fall + // back to report an unknown network. + + // When |network| is in fact a VPN after querying its capability but |networkInfo| is not of + // type TYPE_VPN, |networkInfo| contains the info for the underlying network, and we return a + // NetworkState constructed from it. + if (networkInfo.getType() != ConnectivityManager.TYPE_VPN + && connectivityManager.getNetworkCapabilities(network).hasTransport( + NetworkCapabilities.TRANSPORT_VPN)) { + return new NetworkState(networkInfo.isConnected(), ConnectivityManager.TYPE_VPN, -1, + networkInfo.getType(), networkInfo.getSubtype()); + } + + // When |networkInfo| is of type TYPE_VPN, which implies |network| is a VPN, we return the + // NetworkState of the active network via getActiveNetworkInfo(), if |network| is the active + // network that supports the VPN. Otherwise, NetworkState of an unknown network with type -1 + // will be returned. + if (networkInfo.getType() == ConnectivityManager.TYPE_VPN + && network.equals(connectivityManager.getActiveNetwork())) { + // If a VPN network is in place, we can find the underlying network type via querying the + // active network info thanks to + // https://android.googlesource.com/platform/frameworks/base/+/d6a7980d + NetworkInfo underlyingActiveNetworkInfo = connectivityManager.getActiveNetworkInfo(); + // We use the NetworkInfo of the underlying network if it is not of TYPE_VPN itself. + if (underlyingActiveNetworkInfo.getType() != ConnectivityManager.TYPE_VPN) { + return new NetworkState(networkInfo.isConnected(), ConnectivityManager.TYPE_VPN, -1, + underlyingActiveNetworkInfo.getType(), underlyingActiveNetworkInfo.getSubtype()); + } + } + + return getNetworkState(networkInfo); } /** - * Returns connection type and status information gleaned from networkInfo. + * Returns connection type and status information gleaned from networkInfo. Note that to obtain + * the complete information about a VPN including the type of the underlying network, one should + * use the above method getNetworkState with a Network object. */ - NetworkState getNetworkState(@Nullable NetworkInfo networkInfo) { + private NetworkState getNetworkState(@Nullable NetworkInfo networkInfo) { if (networkInfo == null || !networkInfo.isConnected()) { - return new NetworkState(false, -1, -1); + return new NetworkState(false, -1, -1, -1, -1); } - return new NetworkState(true, networkInfo.getType(), networkInfo.getSubtype()); + return new NetworkState(true, networkInfo.getType(), networkInfo.getSubtype(), -1, -1); } /** @@ -322,12 +392,6 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver { } NetworkState networkState = getNetworkState(network); - if (networkState.connected && networkState.getNetworkType() == ConnectivityManager.TYPE_VPN) { - // If a VPN network is in place, we can find the underlying network type via querying the - // active network info thanks to - // https://android.googlesource.com/platform/frameworks/base/+/d6a7980d - networkState = getNetworkState(); - } ConnectionType connectionType = getConnectionType(networkState); if (connectionType == ConnectionType.CONNECTION_NONE) { // This may not be an error. The OS may signal a network event with connection type @@ -344,10 +408,14 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver { + " 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 + // unknown. + ConnectionType underlyingConnectionTypeForVpn = + getUnderlyingConnectionTypeForVpn(networkState); - NetworkInformation networkInformation = - new NetworkInformation(linkProperties.getInterfaceName(), connectionType, - networkToNetId(network), getIPAddresses(linkProperties)); + NetworkInformation networkInformation = new NetworkInformation( + linkProperties.getInterfaceName(), connectionType, underlyingConnectionTypeForVpn, + networkToNetId(network), getIPAddresses(linkProperties)); return networkInformation; } @@ -504,11 +572,8 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver { } wifiP2pNetworkInfo = - new NetworkInformation( - wifiP2pGroup.getInterface(), - ConnectionType.CONNECTION_WIFI, - WIFI_P2P_NETWORK_HANDLE, - ipAddresses); + new NetworkInformation(wifiP2pGroup.getInterface(), ConnectionType.CONNECTION_WIFI, + ConnectionType.CONNECTION_NONE, WIFI_P2P_NETWORK_HANDLE, ipAddresses); observer.onNetworkConnect(wifiP2pNetworkInfo); } @@ -683,12 +748,13 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver { return connectivityManagerDelegate.getDefaultNetId(); } - public static ConnectionType getConnectionType(NetworkState networkState) { - if (!networkState.isConnected()) { + private static ConnectionType getConnectionType( + boolean isConnected, int networkType, int networkSubtype) { + if (!isConnected) { return ConnectionType.CONNECTION_NONE; } - switch (networkState.getNetworkType()) { + switch (networkType) { case ConnectivityManager.TYPE_ETHERNET: return ConnectionType.CONNECTION_ETHERNET; case ConnectivityManager.TYPE_WIFI: @@ -699,7 +765,7 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver { return ConnectionType.CONNECTION_BLUETOOTH; case ConnectivityManager.TYPE_MOBILE: // Use information from TelephonyManager to classify the connection. - switch (networkState.getNetworkSubType()) { + switch (networkSubtype) { case TelephonyManager.NETWORK_TYPE_GPRS: case TelephonyManager.NETWORK_TYPE_EDGE: case TelephonyManager.NETWORK_TYPE_CDMA: @@ -721,11 +787,27 @@ public class NetworkMonitorAutoDetect extends BroadcastReceiver { default: return ConnectionType.CONNECTION_UNKNOWN_CELLULAR; } + case ConnectivityManager.TYPE_VPN: + return ConnectionType.CONNECTION_VPN; default: return ConnectionType.CONNECTION_UNKNOWN; } } + public static ConnectionType getConnectionType(NetworkState networkState) { + return getConnectionType(networkState.isConnected(), networkState.getNetworkType(), + networkState.getNetworkSubType()); + } + + private static ConnectionType getUnderlyingConnectionTypeForVpn(NetworkState networkState) { + if (networkState.getNetworkType() != ConnectivityManager.TYPE_VPN) { + return ConnectionType.CONNECTION_NONE; + } + return getConnectionType(networkState.isConnected(), + networkState.getUnderlyingNetworkTypeForVpn(), + networkState.getUnderlyingNetworkSubtypeForVpn()); + } + private String getWifiSSID(NetworkState networkState) { if (getConnectionType(networkState) != ConnectionType.CONNECTION_WIFI) return ""; diff --git a/sdk/android/instrumentationtests/src/org/webrtc/NetworkMonitorTest.java b/sdk/android/instrumentationtests/src/org/webrtc/NetworkMonitorTest.java index a5b27f86f5..e4a0e6ae12 100644 --- a/sdk/android/instrumentationtests/src/org/webrtc/NetworkMonitorTest.java +++ b/sdk/android/instrumentationtests/src/org/webrtc/NetworkMonitorTest.java @@ -81,10 +81,13 @@ public class NetworkMonitorTest { private boolean activeNetworkExists; private int networkType; private int networkSubtype; + private int underlyingNetworkTypeForVpn; + private int underlyingNetworkSubtypeForVpn; @Override public NetworkState getNetworkState() { - return new NetworkState(activeNetworkExists, networkType, networkSubtype); + return new NetworkState(activeNetworkExists, networkType, networkSubtype, + underlyingNetworkTypeForVpn, underlyingNetworkSubtypeForVpn); } // Dummy implementations to avoid NullPointerExceptions in default implementations: @@ -101,7 +104,7 @@ public class NetworkMonitorTest { @Override public NetworkState getNetworkState(Network network) { - return new NetworkState(false, -1, -1); + return new NetworkState(false, -1, -1, -1, -1); } public void setActiveNetworkExists(boolean networkExists) { @@ -115,6 +118,14 @@ public class NetworkMonitorTest { public void setNetworkSubtype(int networkSubtype) { this.networkSubtype = networkSubtype; } + + public void setUnderlyingNetworkType(int underlyingNetworkTypeForVpn) { + this.underlyingNetworkTypeForVpn = underlyingNetworkTypeForVpn; + } + + public void setUnderlyingNetworkSubype(int underlyingNetworkSubtypeForVpn) { + this.underlyingNetworkSubtypeForVpn = underlyingNetworkSubtypeForVpn; + } } /** diff --git a/sdk/android/src/jni/androidnetworkmonitor.cc b/sdk/android/src/jni/androidnetworkmonitor.cc index 84a545a14e..cfd51e851f 100644 --- a/sdk/android/src/jni/androidnetworkmonitor.cc +++ b/sdk/android/src/jni/androidnetworkmonitor.cc @@ -60,6 +60,9 @@ static NetworkType GetNetworkTypeFromJava( if (enum_name == "CONNECTION_BLUETOOTH") { return NetworkType::NETWORK_BLUETOOTH; } + if (enum_name == "CONNECTION_VPN") { + return NetworkType::NETWORK_VPN; + } if (enum_name == "CONNECTION_NONE") { return NetworkType::NETWORK_NONE; } @@ -80,6 +83,7 @@ static rtc::AdapterType AdapterTypeFromNetworkType(NetworkType network_type) { case NETWORK_2G: case NETWORK_UNKNOWN_CELLULAR: return rtc::ADAPTER_TYPE_CELLULAR; + case NETWORK_VPN: case NETWORK_BLUETOOTH: // There is no corresponding mapping for bluetooth networks. // Map it to VPN for now. @@ -123,6 +127,9 @@ static NetworkInformation GetNetworkInformationFromJava( Java_NetworkInformation_getHandle(jni, j_network_info)); network_info.type = GetNetworkTypeFromJava( jni, Java_NetworkInformation_getConnectionType(jni, j_network_info)); + network_info.underlying_type_for_vpn = GetNetworkTypeFromJava( + jni, Java_NetworkInformation_getUnderlyingConnectionTypeForVpn( + jni, j_network_info)); ScopedJavaLocalRef j_ip_addresses = Java_NetworkInformation_getIpAddresses(jni, j_network_info); network_info.ip_addresses = JavaToNativeVector( @@ -147,7 +154,11 @@ NetworkInformation& NetworkInformation::operator=(NetworkInformation&&) = std::string NetworkInformation::ToString() const { std::stringstream ss; ss << "NetInfo[name " << interface_name << "; handle " << handle << "; type " - << type << "; address"; + << type; + if (type == NETWORK_VPN) { + ss << "; underlying_type_for_vpn " << underlying_type_for_vpn; + } + ss << "; address"; for (const rtc::IPAddress address : ip_addresses) { ss << " " << address.ToString(); } @@ -316,6 +327,10 @@ void AndroidNetworkMonitor::OnNetworkConnected_w( RTC_LOG(LS_INFO) << "Network connected: " << network_info.ToString(); adapter_type_by_name_[network_info.interface_name] = AdapterTypeFromNetworkType(network_info.type); + if (network_info.type == NETWORK_VPN) { + vpn_underlying_adapter_type_by_name_[network_info.interface_name] = + AdapterTypeFromNetworkType(network_info.underlying_type_for_vpn); + } network_info_by_handle_[network_info.handle] = network_info; for (const rtc::IPAddress& address : network_info.ip_addresses) { network_handle_by_address_[address] = network_info.handle; @@ -363,6 +378,15 @@ rtc::AdapterType AndroidNetworkMonitor::GetAdapterType( return type; } +rtc::AdapterType AndroidNetworkMonitor::GetVpnUnderlyingAdapterType( + const std::string& if_name) { + auto iter = vpn_underlying_adapter_type_by_name_.find(if_name); + rtc::AdapterType type = (iter == vpn_underlying_adapter_type_by_name_.end()) + ? rtc::ADAPTER_TYPE_UNKNOWN + : iter->second; + return type; +} + AndroidNetworkMonitorFactory::AndroidNetworkMonitorFactory() : j_application_context_(nullptr) {} diff --git a/sdk/android/src/jni/androidnetworkmonitor.h b/sdk/android/src/jni/androidnetworkmonitor.h index 58b6686bf9..563d5bd5e4 100644 --- a/sdk/android/src/jni/androidnetworkmonitor.h +++ b/sdk/android/src/jni/androidnetworkmonitor.h @@ -35,6 +35,7 @@ enum NetworkType { NETWORK_2G, NETWORK_UNKNOWN_CELLULAR, NETWORK_BLUETOOTH, + NETWORK_VPN, NETWORK_NONE }; @@ -44,6 +45,7 @@ struct NetworkInformation { std::string interface_name; NetworkHandle handle; NetworkType type; + NetworkType underlying_type_for_vpn; std::vector ip_addresses; NetworkInformation(); @@ -73,6 +75,8 @@ class AndroidNetworkMonitor : public rtc::NetworkMonitorBase, int socket_fd, const rtc::IPAddress& address) override; rtc::AdapterType GetAdapterType(const std::string& if_name) override; + rtc::AdapterType GetVpnUnderlyingAdapterType( + const std::string& if_name) override; void OnNetworkConnected(const NetworkInformation& network_info); void OnNetworkDisconnected(NetworkHandle network_handle); // Always expected to be called on the network thread. @@ -100,6 +104,7 @@ class AndroidNetworkMonitor : public rtc::NetworkMonitorBase, rtc::ThreadChecker thread_checker_; bool started_ = false; std::map adapter_type_by_name_; + std::map vpn_underlying_adapter_type_by_name_; std::map network_handle_by_address_; std::map network_info_by_handle_; };