From 023f3ef0296511f12897c503d6fc2ed063712474 Mon Sep 17 00:00:00 2001 From: honghaiz Date: Mon, 19 Oct 2015 09:39:32 -0700 Subject: [PATCH] Create network change notifier and pass the event to NetworkManager BUG= Review URL: https://codereview.webrtc.org/1391703003 Cr-Commit-Position: refs/heads/master@{#10325} --- .../webrtc/androidtests/AndroidManifest.xml | 1 + .../src/org/webrtc/NetworkMonitorTest.java | 288 ++++++++++++ .../android/org/webrtc/NetworkMonitor.java | 228 ++++++++++ .../org/webrtc/NetworkMonitorAutoDetect.java | 424 ++++++++++++++++++ .../java/jni/androidnetworkmonitor_jni.cc | 85 ++++ .../java/jni/androidnetworkmonitor_jni.h | 67 +++ .../webrtc/java/jni/classreferenceholder.cc | 1 + .../app/webrtc/java/jni/peerconnection_jni.cc | 39 +- .../src/org/webrtc/PeerConnectionFactory.java | 1 + talk/app/webrtc/peerconnectioninterface.h | 2 + talk/libjingle.gyp | 4 + webrtc/base/BUILD.gn | 2 + webrtc/base/base.gyp | 2 + webrtc/base/network.cc | 39 +- webrtc/base/network.h | 18 +- webrtc/base/network_unittest.cc | 52 +++ webrtc/base/networkmonitor.cc | 62 +++ webrtc/base/networkmonitor.h | 91 ++++ .../examples/androidapp/AndroidManifest.xml | 1 + .../apprtc/test/PeerConnectionClientTest.java | 1 + 20 files changed, 1400 insertions(+), 8 deletions(-) create mode 100644 talk/app/webrtc/androidtests/src/org/webrtc/NetworkMonitorTest.java create mode 100644 talk/app/webrtc/java/android/org/webrtc/NetworkMonitor.java create mode 100644 talk/app/webrtc/java/android/org/webrtc/NetworkMonitorAutoDetect.java create mode 100644 talk/app/webrtc/java/jni/androidnetworkmonitor_jni.cc create mode 100644 talk/app/webrtc/java/jni/androidnetworkmonitor_jni.h create mode 100644 webrtc/base/networkmonitor.cc create mode 100644 webrtc/base/networkmonitor.h diff --git a/talk/app/webrtc/androidtests/AndroidManifest.xml b/talk/app/webrtc/androidtests/AndroidManifest.xml index ef6beb8f23..75b6d615d3 100644 --- a/talk/app/webrtc/androidtests/AndroidManifest.xml +++ b/talk/app/webrtc/androidtests/AndroidManifest.xml @@ -14,6 +14,7 @@ + = Build.VERSION_CODES.LOLLIPOP) { + Network[] networks = delegate.getAllNetworks(); + if (networks.length >= 1) { + delegate.getNetworkState(networks[0]); + delegate.hasInternetCapability(networks[0]); + } + delegate.getDefaultNetId(); + } + } + + /** + * Tests that NetworkMonitorAutoDetect 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 + @SmallTest + public void testQueryableAPIsDoNotCrash() { + NetworkMonitorAutoDetect.Observer observer = new TestNetworkMonitorAutoDetectObserver(); + NetworkMonitorAutoDetect ncn = + new NetworkMonitorAutoDetect(observer, getInstrumentation().getTargetContext()); + ncn.getDefaultNetId(); + } +} diff --git a/talk/app/webrtc/java/android/org/webrtc/NetworkMonitor.java b/talk/app/webrtc/java/android/org/webrtc/NetworkMonitor.java new file mode 100644 index 0000000000..581a223c19 --- /dev/null +++ b/talk/app/webrtc/java/android/org/webrtc/NetworkMonitor.java @@ -0,0 +1,228 @@ +/* + * libjingle + * Copyright 2015 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.webrtc; + +import static org.webrtc.NetworkMonitorAutoDetect.ConnectionType; +import static org.webrtc.NetworkMonitorAutoDetect.INVALID_NET_ID; + +import android.content.Context; +import android.util.Log; + +import java.util.ArrayList; + +/** + * Borrowed from Chromium's src/net/android/java/src/org/chromium/net/NetworkChangeNotifier.java + * + * Triggers updates to the underlying network state from OS networking events. + * + * WARNING: This class is not thread-safe. + */ +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(ConnectionType connectionType); + } + + private static final String TAG = "NetworkMonitor"; + private static NetworkMonitor instance; + + private final Context applicationContext; + + // Native observers of the connection type changes. + private final ArrayList nativeNetworkObservers; + // Java observers of the connection type changes. + private final ArrayList networkObservers; + + // Object that detects the connection type changes. + private NetworkMonitorAutoDetect autoDetector; + + private ConnectionType currentConnectionType = ConnectionType.CONNECTION_UNKNOWN; + + private NetworkMonitor(Context context) { + assertIsTrue(context != null); + applicationContext = + context.getApplicationContext() == null ? context : context.getApplicationContext(); + + nativeNetworkObservers = new ArrayList(); + networkObservers = new ArrayList(); + } + + /** + * Initializes the singleton once. + * Called from the native code. + */ + public static NetworkMonitor init(Context context) { + if (!isInitialized()) { + instance = new NetworkMonitor(context); + } + return instance; + } + + public static boolean isInitialized() { + return instance != null; + } + + /** + * Returns the singleton instance. + */ + public static NetworkMonitor getInstance() { + return instance; + } + + /** + * Enables auto detection of the current network state based on notifications from the system. + * Note that passing true here requires the embedding app have the platform ACCESS_NETWORK_STATE + * permission. + * + * @param shouldAutoDetect true if the NetworkMonitor should listen for system changes in + * network connectivity. + */ + public static void setAutoDetectConnectivityState(boolean shouldAutoDetect) { + getInstance().setAutoDetectConnectivityStateInternal(shouldAutoDetect); + } + + private static void assertIsTrue(boolean condition) { + if (!condition) { + throw new AssertionError("Expected to be true"); + } + } + + // Called by the native code. + private void startMonitoring(long nativeObserver) { + Log.d(TAG, "Start monitoring from native observer " + nativeObserver); + nativeNetworkObservers.add(nativeObserver); + setAutoDetectConnectivityStateInternal(true); + } + + // Called by the native code. + private void stopMonitoring(long nativeObserver) { + Log.d(TAG, "Stop monitoring from native observer " + nativeObserver); + setAutoDetectConnectivityStateInternal(false); + nativeNetworkObservers.remove(nativeObserver); + } + + private ConnectionType getCurrentConnectionType() { + return currentConnectionType; + } + + private int getCurrentDefaultNetId() { + return autoDetector == null ? INVALID_NET_ID : autoDetector.getDefaultNetId(); + } + + private void destroyAutoDetector() { + if (autoDetector != null) { + autoDetector.destroy(); + autoDetector = null; + } + } + + private void setAutoDetectConnectivityStateInternal(boolean shouldAutoDetect) { + if (!shouldAutoDetect) { + destroyAutoDetector(); + return; + } + if (autoDetector == null) { + autoDetector = new NetworkMonitorAutoDetect( + new NetworkMonitorAutoDetect.Observer() { + @Override + public void onConnectionTypeChanged(ConnectionType newConnectionType) { + updateCurrentConnectionType(newConnectionType); + } + }, + applicationContext); + final NetworkMonitorAutoDetect.NetworkState networkState = + autoDetector.getCurrentNetworkState(); + updateCurrentConnectionType(autoDetector.getCurrentConnectionType(networkState)); + } + } + + private void updateCurrentConnectionType(ConnectionType newConnectionType) { + currentConnectionType = newConnectionType; + notifyObserversOfConnectionTypeChange(newConnectionType); + } + + /** + * Alerts all observers of a connection change. + */ + private void notifyObserversOfConnectionTypeChange(ConnectionType newConnectionType) { + for (long nativeObserver : nativeNetworkObservers) { + nativeNotifyConnectionTypeChanged(nativeObserver); + } + for (NetworkObserver observer : networkObservers) { + observer.onConnectionTypeChanged(newConnectionType); + } + } + + /** + * Adds an observer for any connection type changes. + */ + public static void addNetworkObserver(NetworkObserver observer) { + getInstance().addNetworkObserverInternal(observer); + } + + private void addNetworkObserverInternal(NetworkObserver observer) { + networkObservers.add(observer); + } + + /** + * Removes an observer for any connection type changes. + */ + public static void removeNetworkObserver(NetworkObserver observer) { + getInstance().removeNetworkObserverInternal(observer); + } + + private void removeNetworkObserverInternal(NetworkObserver observer) { + networkObservers.remove(observer); + } + + /** + * Checks if there currently is connectivity. + */ + public static boolean isOnline() { + ConnectionType connectionType = getInstance().getCurrentConnectionType(); + return connectionType != ConnectionType.CONNECTION_UNKNOWN + && connectionType != ConnectionType.CONNECTION_NONE; + } + + private native long nativeCreateNetworkMonitor(); + + private native void nativeNotifyConnectionTypeChanged(long nativePtr); + + // For testing only. + static void resetInstanceForTests(Context context) { + instance = new NetworkMonitor(context); + } + + // For testing only. + public static NetworkMonitorAutoDetect getAutoDetectorForTest() { + return getInstance().autoDetector; + } +} diff --git a/talk/app/webrtc/java/android/org/webrtc/NetworkMonitorAutoDetect.java b/talk/app/webrtc/java/android/org/webrtc/NetworkMonitorAutoDetect.java new file mode 100644 index 0000000000..e3a7850db4 --- /dev/null +++ b/talk/app/webrtc/java/android/org/webrtc/NetworkMonitorAutoDetect.java @@ -0,0 +1,424 @@ +/* + * libjingle + * Copyright 2015 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.webrtc; + +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.Network; +import android.net.NetworkCapabilities; +import android.net.NetworkInfo; +import android.net.wifi.WifiInfo; +import android.net.wifi.WifiManager; +import android.os.Build; +import android.telephony.TelephonyManager; +import android.util.Log; + +/** + * 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. + */ +public class NetworkMonitorAutoDetect extends BroadcastReceiver { + static enum ConnectionType { + CONNECTION_UNKNOWN, + CONNECTION_ETHERNET, + CONNECTION_WIFI, + CONNECTION_4G, + CONNECTION_3G, + CONNECTION_2G, + CONNECTION_BLUETOOTH, + CONNECTION_NONE + } + + static class NetworkState { + private final boolean connected; + // Defined from ConnectivityManager.TYPE_XXX for non-mobile; for mobile, it is + // further divided into 2G, 3G, or 4G from the subtype. + private final int type; + // 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; + + public NetworkState(boolean connected, int type, int subtype) { + this.connected = connected; + this.type = type; + this.subtype = subtype; + } + + public boolean isConnected() { + return connected; + } + + public int getNetworkType() { + return type; + } + + public int getNetworkSubType() { + return subtype; + } + } + + /** Queries the ConnectivityManager for information about the current connection. */ + static class ConnectivityManagerDelegate { + private final ConnectivityManager connectivityManager; + + ConnectivityManagerDelegate(Context context) { + connectivityManager = + (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + } + + // For testing. + ConnectivityManagerDelegate() { + // All the methods below should be overridden. + connectivityManager = null; + } + + /** + * Returns connection type and status information about the current + * default network. + */ + NetworkState getNetworkState() { + return getNetworkState(connectivityManager.getActiveNetworkInfo()); + } + + /** + * Returns connection type and status information about |network|. + * Only callable on Lollipop and newer releases. + */ + @SuppressLint("NewApi") + NetworkState getNetworkState(Network network) { + return getNetworkState(connectivityManager.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 connectivityManager.getAllNetworks(); + } + + /** + * Returns the NetID of the current default network. Returns + * INVALID_NET_ID 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 = connectivityManager.getActiveNetworkInfo(); + if (defaultNetworkInfo == null) { + return INVALID_NET_ID; + } + final Network[] networks = getAllNetworks(); + int defaultNetId = INVALID_NET_ID; + for (Network network : networks) { + if (!hasInternetCapability(network)) { + continue; + } + final NetworkInfo networkInfo = connectivityManager.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 == INVALID_NET_ID; + 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 = + connectivityManager.getNetworkCapabilities(network); + return capabilities != null && capabilities.hasCapability(NET_CAPABILITY_INTERNET); + } + } + + /** Queries the WifiManager for SSID of the current Wifi connection. */ + static class WifiManagerDelegate { + private final Context context; + private final WifiManager wifiManager; + private final boolean hasWifiPermission; + + WifiManagerDelegate(Context context) { + this.context = context; + + hasWifiPermission = context.getPackageManager().checkPermission( + permission.ACCESS_WIFI_STATE, context.getPackageName()) + == PackageManager.PERMISSION_GRANTED; + wifiManager = hasWifiPermission + ? (WifiManager) context.getSystemService(Context.WIFI_SERVICE) : null; + } + + // For testing. + WifiManagerDelegate() { + // All the methods below should be overridden. + context = null; + wifiManager = null; + hasWifiPermission = false; + } + + String getWifiSSID() { + final Intent intent = context.registerReceiver(null, + new IntentFilter(WifiManager.NETWORK_STATE_CHANGED_ACTION)); + if (intent != null) { + final WifiInfo wifiInfo = intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO); + if (wifiInfo != null) { + final String ssid = wifiInfo.getSSID(); + if (ssid != null) { + return ssid; + } + } + } + return ""; + } + + boolean getHasWifiPermission() { + return hasWifiPermission; + } + } + + static final int INVALID_NET_ID = -1; + private static final String TAG = "NetworkMonitorAutoDetect"; + private static final int UNKNOWN_LINK_SPEED = -1; + private final IntentFilter intentFilter; + + // Observer for the connection type change. + private final Observer observer; + + private final Context context; + // connectivityManagerDelegates and wifiManagerDelegate are only non-final for testing. + private ConnectivityManagerDelegate connectivityManagerDelegate; + private WifiManagerDelegate wifiManagerDelegate; + private boolean isRegistered; + private 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); + } + + /** + * Constructs a NetworkMonitorAutoDetect. Should only be called on UI thread. + */ + public NetworkMonitorAutoDetect(Observer observer, Context context) { + this.observer = observer; + this.context = context; + connectivityManagerDelegate = new ConnectivityManagerDelegate(context); + wifiManagerDelegate = new WifiManagerDelegate(context); + + final NetworkState networkState = connectivityManagerDelegate.getNetworkState(); + connectionType = getCurrentConnectionType(networkState); + wifiSSID = getCurrentWifiSSID(networkState); + intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION); + registerReceiver(); + } + + /** + * Allows overriding the ConnectivityManagerDelegate for tests. + */ + void setConnectivityManagerDelegateForTests(ConnectivityManagerDelegate delegate) { + connectivityManagerDelegate = delegate; + } + + /** + * Allows overriding the WifiManagerDelegate for tests. + */ + void setWifiManagerDelegateForTests(WifiManagerDelegate delegate) { + wifiManagerDelegate = delegate; + } + + /** + * Returns whether the object has registered to receive network connectivity intents. + * Visible for testing. + */ + boolean isReceiverRegisteredForTesting() { + return isRegistered; + } + + public void destroy() { + unregisterReceiver(); + } + + /** + * Registers a BroadcastReceiver in the given context. + */ + private void registerReceiver() { + if (!isRegistered) { + isRegistered = true; + context.registerReceiver(this, intentFilter); + } + } + + /** + * Unregisters the BroadcastReceiver in the given context. + */ + private void unregisterReceiver() { + if (isRegistered) { + isRegistered = false; + context.unregisterReceiver(this); + } + } + + public NetworkState getCurrentNetworkState() { + return connectivityManagerDelegate.getNetworkState(); + } + + /** + * Returns NetID of device's current default connected network used for + * communication. + * Only implemented on Lollipop and newer releases, returns INVALID_NET_ID + * when not implemented. + */ + public int getDefaultNetId() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + return INVALID_NET_ID; + } + return connectivityManagerDelegate.getDefaultNetId(); + } + + public ConnectionType getCurrentConnectionType(NetworkState networkState) { + if (!networkState.isConnected()) { + return ConnectionType.CONNECTION_NONE; + } + + switch (networkState.getNetworkType()) { + case ConnectivityManager.TYPE_ETHERNET: + return ConnectionType.CONNECTION_ETHERNET; + case ConnectivityManager.TYPE_WIFI: + return ConnectionType.CONNECTION_WIFI; + case ConnectivityManager.TYPE_WIMAX: + return ConnectionType.CONNECTION_4G; + case ConnectivityManager.TYPE_BLUETOOTH: + return ConnectionType.CONNECTION_BLUETOOTH; + case ConnectivityManager.TYPE_MOBILE: + // Use information from TelephonyManager to classify the connection. + switch (networkState.getNetworkSubType()) { + case TelephonyManager.NETWORK_TYPE_GPRS: + case TelephonyManager.NETWORK_TYPE_EDGE: + case TelephonyManager.NETWORK_TYPE_CDMA: + case TelephonyManager.NETWORK_TYPE_1xRTT: + case TelephonyManager.NETWORK_TYPE_IDEN: + return ConnectionType.CONNECTION_2G; + case TelephonyManager.NETWORK_TYPE_UMTS: + case TelephonyManager.NETWORK_TYPE_EVDO_0: + case TelephonyManager.NETWORK_TYPE_EVDO_A: + case TelephonyManager.NETWORK_TYPE_HSDPA: + case TelephonyManager.NETWORK_TYPE_HSUPA: + case TelephonyManager.NETWORK_TYPE_HSPA: + case TelephonyManager.NETWORK_TYPE_EVDO_B: + case TelephonyManager.NETWORK_TYPE_EHRPD: + case TelephonyManager.NETWORK_TYPE_HSPAP: + return ConnectionType.CONNECTION_3G; + case TelephonyManager.NETWORK_TYPE_LTE: + return ConnectionType.CONNECTION_4G; + default: + return ConnectionType.CONNECTION_UNKNOWN; + } + default: + return ConnectionType.CONNECTION_UNKNOWN; + } + } + + private String getCurrentWifiSSID(NetworkState networkState) { + if (getCurrentConnectionType(networkState) != ConnectionType.CONNECTION_WIFI) return ""; + return wifiManagerDelegate.getWifiSSID(); + } + + // BroadcastReceiver + @Override + public void onReceive(Context context, Intent intent) { + final NetworkState networkState = getCurrentNetworkState(); + if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) { + connectionTypeChanged(networkState); + } + } + + private void connectionTypeChanged(NetworkState networkState) { + ConnectionType newConnectionType = getCurrentConnectionType(networkState); + String newWifiSSID = getCurrentWifiSSID(networkState); + if (newConnectionType == connectionType && newWifiSSID.equals(wifiSSID)) return; + + connectionType = newConnectionType; + wifiSSID = newWifiSSID; + Log.d(TAG, "Network connectivity changed, type is: " + connectionType); + observer.onConnectionTypeChanged(newConnectionType); + } + + /** + * 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(honghaiz): When we update to Android M SDK, use Network.getNetworkHandle(). + return Integer.parseInt(network.toString()); + } +} diff --git a/talk/app/webrtc/java/jni/androidnetworkmonitor_jni.cc b/talk/app/webrtc/java/jni/androidnetworkmonitor_jni.cc new file mode 100644 index 0000000000..f7a8c07c60 --- /dev/null +++ b/talk/app/webrtc/java/jni/androidnetworkmonitor_jni.cc @@ -0,0 +1,85 @@ +/* + * libjingle + * Copyright 2015 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "talk/app/webrtc/java/jni/androidnetworkmonitor_jni.h" + +#include "webrtc/base/common.h" +#include "talk/app/webrtc/java/jni/classreferenceholder.h" +#include "talk/app/webrtc/java/jni/jni_helpers.h" + +namespace webrtc_jni { +jobject AndroidNetworkMonitor::application_context_ = nullptr; + +// static +void AndroidNetworkMonitor::SetAndroidContext(JNIEnv* jni, jobject context) { + if (application_context_) { + jni->DeleteGlobalRef(application_context_); + } + application_context_ = NewGlobalRef(jni, context); +} + +AndroidNetworkMonitor::AndroidNetworkMonitor() + : j_network_monitor_class_(jni(), + FindClass(jni(), "org/webrtc/NetworkMonitor")), + j_network_monitor_( + jni(), + jni()->CallStaticObjectMethod( + *j_network_monitor_class_, + GetStaticMethodID( + jni(), + *j_network_monitor_class_, + "init", + "(Landroid/content/Context;)Lorg/webrtc/NetworkMonitor;"), + application_context_)) { + ASSERT(application_context_ != nullptr); + CHECK_EXCEPTION(jni()) << "Error during NetworkMonitor.init"; +} + +void AndroidNetworkMonitor::Start() { + RTC_CHECK(thread_checker_.CalledOnValidThread()); + jmethodID m = + GetMethodID(jni(), *j_network_monitor_class_, "startMonitoring", "(J)V"); + jni()->CallVoidMethod(*j_network_monitor_, m, jlongFromPointer(this)); + CHECK_EXCEPTION(jni()) << "Error during NetworkMonitor.startMonitoring"; +} + +void AndroidNetworkMonitor::Stop() { + RTC_CHECK(thread_checker_.CalledOnValidThread()); + jmethodID m = + GetMethodID(jni(), *j_network_monitor_class_, "stopMonitoring", "(J)V"); + jni()->CallVoidMethod(*j_network_monitor_, m, jlongFromPointer(this)); + CHECK_EXCEPTION(jni()) << "Error during NetworkMonitor.stopMonitoring"; +} + +JOW(void, NetworkMonitor_nativeNotifyConnectionTypeChanged)( + JNIEnv* jni, jobject j_monitor, jlong j_native_monitor) { + rtc::NetworkMonitorInterface* network_monitor = + reinterpret_cast(j_native_monitor); + network_monitor->OnNetworksChanged(); +} + +} // namespace webrtc_jni diff --git a/talk/app/webrtc/java/jni/androidnetworkmonitor_jni.h b/talk/app/webrtc/java/jni/androidnetworkmonitor_jni.h new file mode 100644 index 0000000000..3f5110c2c4 --- /dev/null +++ b/talk/app/webrtc/java/jni/androidnetworkmonitor_jni.h @@ -0,0 +1,67 @@ +/* + * libjingle + * Copyright 2015 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TALK_APP_WEBRTC_JAVA_JNI_ANDROIDNETWORKMONITOR_JNI_H_ +#define TALK_APP_WEBRTC_JAVA_JNI_ANDROIDNETWORKMONITOR_JNI_H_ + +#include "webrtc/base/networkmonitor.h" + +#include "webrtc/base/thread_checker.h" +#include "talk/app/webrtc/java/jni/jni_helpers.h" + +namespace webrtc_jni { + +class AndroidNetworkMonitor : public rtc::NetworkMonitorBase { + public: + AndroidNetworkMonitor(); + + static void SetAndroidContext(JNIEnv* jni, jobject context); + + void Start() override; + void Stop() override; + + private: + JNIEnv* jni() { return AttachCurrentThreadIfNeeded(); } + + ScopedGlobalRef j_network_monitor_class_; + ScopedGlobalRef j_network_monitor_; + rtc::ThreadChecker thread_checker_; + static jobject application_context_; +}; + +class AndroidNetworkMonitorFactory : public rtc::NetworkMonitorFactory { + public: + AndroidNetworkMonitorFactory() {} + + rtc::NetworkMonitorInterface* CreateNetworkMonitor() override { + return new AndroidNetworkMonitor(); + } +}; + +} // namespace webrtc_jni + +#endif // TALK_APP_WEBRTC_JAVA_JNI_ANDROIDNETWORKMONITOR_JNI_H_ diff --git a/talk/app/webrtc/java/jni/classreferenceholder.cc b/talk/app/webrtc/java/jni/classreferenceholder.cc index d99aa1a661..7efea06a14 100644 --- a/talk/app/webrtc/java/jni/classreferenceholder.cc +++ b/talk/app/webrtc/java/jni/classreferenceholder.cc @@ -78,6 +78,7 @@ ClassReferenceHolder::ClassReferenceHolder(JNIEnv* jni) { LoadClass(jni, "org/webrtc/VideoCapturerAndroid"); LoadClass(jni, "org/webrtc/VideoCapturerAndroid$NativeObserver"); LoadClass(jni, "org/webrtc/EglBase"); + LoadClass(jni, "org/webrtc/NetworkMonitor"); LoadClass(jni, "org/webrtc/MediaCodecVideoEncoder"); LoadClass(jni, "org/webrtc/MediaCodecVideoEncoder$OutputBufferInfo"); LoadClass(jni, "org/webrtc/MediaCodecVideoEncoder$VideoCodecType"); diff --git a/talk/app/webrtc/java/jni/peerconnection_jni.cc b/talk/app/webrtc/java/jni/peerconnection_jni.cc index c3ccb8ab8f..1db8621725 100644 --- a/talk/app/webrtc/java/jni/peerconnection_jni.cc +++ b/talk/app/webrtc/java/jni/peerconnection_jni.cc @@ -76,6 +76,7 @@ #include "webrtc/base/checks.h" #include "webrtc/base/logging.h" #include "webrtc/base/logsinks.h" +#include "webrtc/base/networkmonitor.h" #include "webrtc/base/messagequeue.h" #include "webrtc/base/ssladapter.h" #include "webrtc/base/stringutils.h" @@ -88,6 +89,7 @@ #include "talk/app/webrtc/java/jni/androidmediadecoder_jni.h" #include "talk/app/webrtc/java/jni/androidmediaencoder_jni.h" #include "talk/app/webrtc/java/jni/androidvideocapturer_jni.h" +#include "talk/app/webrtc/java/jni/androidnetworkmonitor_jni.h" #include "webrtc/modules/video_render/video_render_internal.h" #include "webrtc/system_wrappers/interface/logcat_trace_context.h" using webrtc::LogcatTraceContext; @@ -1023,6 +1025,7 @@ JOW(jboolean, PeerConnectionFactory_initializeAndroidGlobals)( jboolean video_hw_acceleration) { bool failure = false; video_hw_acceleration_enabled = video_hw_acceleration; + AndroidNetworkMonitor::SetAndroidContext(jni, context); if (!factory_static_initialized) { if (initialize_video) { failure |= webrtc::SetRenderAndroidVM(GetJVM()); @@ -1063,18 +1066,29 @@ class OwnedFactoryAndThreads { Thread* signaling_thread, WebRtcVideoEncoderFactory* encoder_factory, WebRtcVideoDecoderFactory* decoder_factory, + rtc::NetworkMonitorFactory* network_monitor_factory, PeerConnectionFactoryInterface* factory) : worker_thread_(worker_thread), signaling_thread_(signaling_thread), encoder_factory_(encoder_factory), decoder_factory_(decoder_factory), + network_monitor_factory_(network_monitor_factory), factory_(factory) {} - ~OwnedFactoryAndThreads() { CHECK_RELEASE(factory_); } + ~OwnedFactoryAndThreads() { + CHECK_RELEASE(factory_); + if (network_monitor_factory_ != nullptr) { + rtc::NetworkMonitorFactory::ReleaseFactory(network_monitor_factory_); + } + } PeerConnectionFactoryInterface* factory() { return factory_; } WebRtcVideoEncoderFactory* encoder_factory() { return encoder_factory_; } WebRtcVideoDecoderFactory* decoder_factory() { return decoder_factory_; } + rtc::NetworkMonitorFactory* network_monitor_factory() { + return network_monitor_factory_; + } + void clear_network_monitor_factory() { network_monitor_factory_ = nullptr; } void InvokeJavaCallbacksOnFactoryThreads(); private: @@ -1084,6 +1098,7 @@ class OwnedFactoryAndThreads { const scoped_ptr signaling_thread_; WebRtcVideoEncoderFactory* encoder_factory_; WebRtcVideoDecoderFactory* decoder_factory_; + rtc::NetworkMonitorFactory* network_monitor_factory_; PeerConnectionFactoryInterface* factory_; // Const after ctor except dtor. }; @@ -1132,11 +1147,15 @@ JOW(jlong, PeerConnectionFactory_nativeCreatePeerConnectionFactory)( << "Failed to start threads"; WebRtcVideoEncoderFactory* encoder_factory = nullptr; WebRtcVideoDecoderFactory* decoder_factory = nullptr; + rtc::NetworkMonitorFactory* network_monitor_factory = nullptr; + #if defined(ANDROID) && !defined(WEBRTC_CHROMIUM_BUILD) if (video_hw_acceleration_enabled) { encoder_factory = new MediaCodecVideoEncoderFactory(); decoder_factory = new MediaCodecVideoDecoderFactory(); } + network_monitor_factory = new AndroidNetworkMonitorFactory(); + rtc::NetworkMonitorFactory::SetFactory(network_monitor_factory); #endif rtc::scoped_refptr factory( webrtc::CreatePeerConnectionFactory(worker_thread, @@ -1149,7 +1168,7 @@ JOW(jlong, PeerConnectionFactory_nativeCreatePeerConnectionFactory)( OwnedFactoryAndThreads* owned_factory = new OwnedFactoryAndThreads( worker_thread, signaling_thread, encoder_factory, decoder_factory, - factory.release()); + network_monitor_factory, factory.release()); owned_factory->InvokeJavaCallbacksOnFactoryThreads(); return jlongFromPointer(owned_factory); } @@ -1247,13 +1266,29 @@ JOW(void, PeerConnectionFactory_nativeSetOptions)( bool disable_encryption = jni->GetBooleanField(options, disable_encryption_field); + jfieldID disable_network_monitor_field = + jni->GetFieldID(options_class, "disableNetworkMonitor", "Z"); + bool disable_network_monitor = + jni->GetBooleanField(options, disable_network_monitor_field); + PeerConnectionFactoryInterface::Options options_to_set; // This doesn't necessarily match the c++ version of this struct; feel free // to add more parameters as necessary. options_to_set.network_ignore_mask = network_ignore_mask; options_to_set.disable_encryption = disable_encryption; + options_to_set.disable_network_monitor = disable_network_monitor; factory->SetOptions(options_to_set); + + if (disable_network_monitor) { + OwnedFactoryAndThreads* owner = + reinterpret_cast(native_factory); + if (owner->network_monitor_factory()) { + rtc::NetworkMonitorFactory::ReleaseFactory( + owner->network_monitor_factory()); + owner->clear_network_monitor_factory(); + } + } } JOW(void, PeerConnectionFactory_nativeSetVideoHwAccelerationOptions)( diff --git a/talk/app/webrtc/java/src/org/webrtc/PeerConnectionFactory.java b/talk/app/webrtc/java/src/org/webrtc/PeerConnectionFactory.java index 0460624aa7..83999ece98 100644 --- a/talk/app/webrtc/java/src/org/webrtc/PeerConnectionFactory.java +++ b/talk/app/webrtc/java/src/org/webrtc/PeerConnectionFactory.java @@ -55,6 +55,7 @@ public class PeerConnectionFactory { public int networkIgnoreMask; public boolean disableEncryption; + public boolean disableNetworkMonitor; } // |context| is an android.content.Context object, but we keep it untyped here diff --git a/talk/app/webrtc/peerconnectioninterface.h b/talk/app/webrtc/peerconnectioninterface.h index f61f2f6c5c..a5d98ffa74 100644 --- a/talk/app/webrtc/peerconnectioninterface.h +++ b/talk/app/webrtc/peerconnectioninterface.h @@ -541,11 +541,13 @@ class PeerConnectionFactoryInterface : public rtc::RefCountInterface { Options() : disable_encryption(false), disable_sctp_data_channels(false), + disable_network_monitor(false), network_ignore_mask(rtc::kDefaultNetworkIgnoreMask), ssl_max_version(rtc::SSL_PROTOCOL_DTLS_10) { } bool disable_encryption; bool disable_sctp_data_channels; + bool disable_network_monitor; // Sets the network types to ignore. For instance, calling this with // ADAPTER_TYPE_ETHERNET | ADAPTER_TYPE_LOOPBACK will ignore Ethernet and diff --git a/talk/libjingle.gyp b/talk/libjingle.gyp index fdf06312d6..81d723a0d9 100755 --- a/talk/libjingle.gyp +++ b/talk/libjingle.gyp @@ -104,6 +104,8 @@ 'app/webrtc/java/jni/androidmediadecoder_jni.h', 'app/webrtc/java/jni/androidmediaencoder_jni.cc', 'app/webrtc/java/jni/androidmediaencoder_jni.h', + 'app/webrtc/java/jni/androidnetworkmonitor_jni.cc', + 'app/webrtc/java/jni/androidnetworkmonitor_jni.h', 'app/webrtc/java/jni/surfacetexturehelper_jni.cc', 'app/webrtc/java/jni/surfacetexturehelper_jni.h', ] @@ -156,6 +158,8 @@ 'app/webrtc/java/android/org/webrtc/GlShader.java', 'app/webrtc/java/android/org/webrtc/GlUtil.java', 'app/webrtc/java/android/org/webrtc/GlTextureFrameBuffer.java', + 'app/webrtc/java/android/org/webrtc/NetworkMonitor.java', + 'app/webrtc/java/android/org/webrtc/NetworkMonitorAutoDetect.java', 'app/webrtc/java/android/org/webrtc/RendererCommon.java', 'app/webrtc/java/android/org/webrtc/SurfaceTextureHelper.java', 'app/webrtc/java/android/org/webrtc/SurfaceViewRenderer.java', diff --git a/webrtc/base/BUILD.gn b/webrtc/base/BUILD.gn index 198078272a..79e204ed98 100644 --- a/webrtc/base/BUILD.gn +++ b/webrtc/base/BUILD.gn @@ -237,6 +237,8 @@ static_library("rtc_base") { "nethelpers.h", "network.cc", "network.h", + "networkmonitor.cc", + "networkmonitor.h", "nullsocketserver.h", "pathutils.cc", "pathutils.h", diff --git a/webrtc/base/base.gyp b/webrtc/base/base.gyp index 9992d9dfc6..d57fe11aed 100644 --- a/webrtc/base/base.gyp +++ b/webrtc/base/base.gyp @@ -219,6 +219,8 @@ 'nethelpers.h', 'network.cc', 'network.h', + 'networkmonitor.cc', + 'networkmonitor.h', 'nullsocketserver.h', 'openssl.h', 'openssladapter.cc', diff --git a/webrtc/base/network.cc b/webrtc/base/network.cc index bc714e30f4..879c1e4529 100644 --- a/webrtc/base/network.cc +++ b/webrtc/base/network.cc @@ -48,6 +48,7 @@ #include #include "webrtc/base/logging.h" +#include "webrtc/base/networkmonitor.h" #include "webrtc/base/scoped_ptr.h" #include "webrtc/base/socket.h" // includes something that makes windows happy #include "webrtc/base/stream.h" @@ -329,6 +330,11 @@ BasicNetworkManager::BasicNetworkManager() BasicNetworkManager::~BasicNetworkManager() { } +void BasicNetworkManager::OnNetworksChanged() { + LOG(LS_VERBOSE) << "Network change was observed at the network manager"; + UpdateNetworksOnce(); +} + #if defined(__native_client__) bool BasicNetworkManager::CreateNetworks(bool include_ignored, @@ -663,6 +669,7 @@ void BasicNetworkManager::StartUpdating() { thread_->Post(this, kSignalNetworksMessage); } else { thread_->Post(this, kUpdateNetworksMessage); + StartNetworkMonitor(); } ++start_count_; } @@ -676,13 +683,36 @@ void BasicNetworkManager::StopUpdating() { if (!start_count_) { thread_->Clear(this); sent_first_update_ = false; + StopNetworkMonitor(); } } +void BasicNetworkManager::StartNetworkMonitor() { + NetworkMonitorFactory* factory = NetworkMonitorFactory::GetFactory(); + if (factory == nullptr) { + return; + } + network_monitor_.reset(factory->CreateNetworkMonitor()); + if (!network_monitor_) { + return; + } + network_monitor_->SignalNetworksChanged.connect( + this, &BasicNetworkManager::OnNetworksChanged); + network_monitor_->Start(); +} + +void BasicNetworkManager::StopNetworkMonitor() { + if (!network_monitor_) { + return; + } + network_monitor_->Stop(); + network_monitor_.reset(); +} + void BasicNetworkManager::OnMessage(Message* msg) { switch (msg->message_id) { - case kUpdateNetworksMessage: { - DoUpdateNetworks(); + case kUpdateNetworksMessage: { + UpdateNetworksContinually(); break; } case kSignalNetworksMessage: { @@ -694,7 +724,7 @@ void BasicNetworkManager::OnMessage(Message* msg) { } } -void BasicNetworkManager::DoUpdateNetworks() { +void BasicNetworkManager::UpdateNetworksOnce() { if (!start_count_) return; @@ -711,7 +741,10 @@ void BasicNetworkManager::DoUpdateNetworks() { sent_first_update_ = true; } } +} +void BasicNetworkManager::UpdateNetworksContinually() { + UpdateNetworksOnce(); thread_->PostDelayed(kNetworksUpdateIntervalMs, this, kUpdateNetworksMessage); } diff --git a/webrtc/base/network.h b/webrtc/base/network.h index f365a78772..ab3a88dc7d 100644 --- a/webrtc/base/network.h +++ b/webrtc/base/network.h @@ -29,6 +29,7 @@ struct ifaddrs; namespace rtc { class Network; +class NetworkMonitorInterface; class Thread; enum AdapterType { @@ -147,7 +148,6 @@ class NetworkManagerBase : public NetworkManager { private: friend class NetworkTest; - void DoUpdateNetworks(); EnumerationPermission enumeration_permission_; @@ -164,7 +164,8 @@ class NetworkManagerBase : public NetworkManager { // Basic implementation of the NetworkManager interface that gets list // of networks using OS APIs. class BasicNetworkManager : public NetworkManagerBase, - public MessageHandler { + public MessageHandler, + public sigslot::has_slots<> { public: BasicNetworkManager(); ~BasicNetworkManager() override; @@ -222,7 +223,17 @@ class BasicNetworkManager : public NetworkManagerBase, private: friend class NetworkTest; - void DoUpdateNetworks(); + // Creates a network monitor and listens for network updates. + void StartNetworkMonitor(); + // Stops and removes the network monitor. + void StopNetworkMonitor(); + // Called when it receives updates from the network monitor. + void OnNetworksChanged(); + + // Updates the networks and reschedules the next update. + void UpdateNetworksContinually(); + // Only updates the networks; does not reschedule the next update. + void UpdateNetworksOnce(); Thread* thread_; bool sent_first_update_; @@ -230,6 +241,7 @@ class BasicNetworkManager : public NetworkManagerBase, std::vector network_ignore_list_; int network_ignore_mask_; bool ignore_non_default_routes_; + scoped_ptr network_monitor_; }; // Represents a Unix-type network interface, with a name and single address. diff --git a/webrtc/base/network_unittest.cc b/webrtc/base/network_unittest.cc index 6d920e0a50..436222127e 100644 --- a/webrtc/base/network_unittest.cc +++ b/webrtc/base/network_unittest.cc @@ -10,6 +10,7 @@ #include "webrtc/base/network.h" +#include "webrtc/base/networkmonitor.h" #include #if defined(WEBRTC_POSIX) #include @@ -26,6 +27,20 @@ namespace rtc { +class FakeNetworkMonitor : public NetworkMonitorBase { + public: + void Start() override {} + void Stop() override {} +}; + +class FakeNetworkMonitorFactory : public NetworkMonitorFactory { + public: + FakeNetworkMonitorFactory() {} + NetworkMonitorInterface* CreateNetworkMonitor() { + return new FakeNetworkMonitor(); + } +}; + class NetworkTest : public testing::Test, public sigslot::has_slots<> { public: NetworkTest() : callback_called_(false) {} @@ -55,6 +70,18 @@ class NetworkTest : public testing::Test, public sigslot::has_slots<> { return list; } + NetworkMonitorInterface* GetNetworkMonitor( + BasicNetworkManager& network_manager) { + return network_manager.network_monitor_.get(); + } + void ClearNetworks(BasicNetworkManager& network_manager) { + for (const auto& kv : network_manager.networks_map_) { + delete kv.second; + } + network_manager.networks_.clear(); + network_manager.networks_map_.clear(); + } + #if defined(WEBRTC_POSIX) // Separated from CreateNetworks for tests. static void CallConvertIfAddrs(const BasicNetworkManager& network_manager, @@ -790,4 +817,29 @@ TEST_F(NetworkTest, TestIPv6Selection) { EXPECT_EQ(ipv6_network.GetBestIP(), static_cast(ip)); } +TEST_F(NetworkTest, TestNetworkMonitoring) { + BasicNetworkManager manager; + manager.SignalNetworksChanged.connect(static_cast(this), + &NetworkTest::OnNetworksChanged); + FakeNetworkMonitorFactory* factory = new FakeNetworkMonitorFactory(); + NetworkMonitorFactory::SetFactory(factory); + manager.StartUpdating(); + NetworkMonitorInterface* network_monitor = GetNetworkMonitor(manager); + EXPECT_TRUE_WAIT(callback_called_, 1000); + callback_called_ = false; + + // Clear the networks so that there will be network changes below. + ClearNetworks(manager); + // Network manager is started, so the callback is called when the network + // monitor fires the network-change event. + network_monitor->OnNetworksChanged(); + EXPECT_TRUE_WAIT(callback_called_, 1000); + + // Network manager is stopped; the network monitor is removed. + manager.StopUpdating(); + EXPECT_TRUE(GetNetworkMonitor(manager) == nullptr); + + NetworkMonitorFactory::ReleaseFactory(factory); +} + } // namespace rtc diff --git a/webrtc/base/networkmonitor.cc b/webrtc/base/networkmonitor.cc new file mode 100644 index 0000000000..92bf0592b5 --- /dev/null +++ b/webrtc/base/networkmonitor.cc @@ -0,0 +1,62 @@ +/* + * Copyright 2015 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. + */ + +#include "webrtc/base/networkmonitor.h" + +#include "webrtc/base/common.h" + +namespace { +const uint32_t UPDATE_NETWORKS_MESSAGE = 1; + +// This is set by NetworkMonitorFactory::SetFactory and the caller of +// NetworkMonitorFactory::SetFactory must be responsible for calling +// ReleaseFactory to destroy the factory. +rtc::NetworkMonitorFactory* network_monitor_factory = nullptr; +} // namespace + +namespace rtc { +NetworkMonitorInterface::NetworkMonitorInterface() {} + +NetworkMonitorInterface::~NetworkMonitorInterface() {} + +NetworkMonitorBase::NetworkMonitorBase() : thread_(Thread::Current()) {} +NetworkMonitorBase::~NetworkMonitorBase() {} + +void NetworkMonitorBase::OnNetworksChanged() { + LOG(LS_VERBOSE) << "Network change is received at the network monitor"; + thread_->Post(this, UPDATE_NETWORKS_MESSAGE); +} + +void NetworkMonitorBase::OnMessage(Message* msg) { + ASSERT(msg->message_id == UPDATE_NETWORKS_MESSAGE); + SignalNetworksChanged(); +} + +NetworkMonitorFactory::NetworkMonitorFactory() {} +NetworkMonitorFactory::~NetworkMonitorFactory() {} + +void NetworkMonitorFactory::SetFactory(NetworkMonitorFactory* factory) { + if (network_monitor_factory != nullptr) { + delete network_monitor_factory; + } + network_monitor_factory = factory; +} + +void NetworkMonitorFactory::ReleaseFactory(NetworkMonitorFactory* factory) { + if (factory == network_monitor_factory) { + SetFactory(nullptr); + } +} + +NetworkMonitorFactory* NetworkMonitorFactory::GetFactory() { + return network_monitor_factory; +} + +} // namespace rtc diff --git a/webrtc/base/networkmonitor.h b/webrtc/base/networkmonitor.h new file mode 100644 index 0000000000..c45c817040 --- /dev/null +++ b/webrtc/base/networkmonitor.h @@ -0,0 +1,91 @@ +/* + * Copyright 2015 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. + */ + +#ifndef WEBRTC_BASE_NETWORKMONITOR_H_ +#define WEBRTC_BASE_NETWORKMONITOR_H_ + +#include "webrtc/base/logging.h" +#include "webrtc/base/scoped_ptr.h" +#include "webrtc/base/sigslot.h" +#include "webrtc/base/thread.h" + +namespace rtc { +/* + * Receives network-change events via |OnNetworksChanged| and signals the + * networks changed event. + * + * Threading consideration: + * It is expected that all upstream operations (from native to Java) are + * performed from the worker thread. This includes creating, starting and + * stopping the monitor. This avoids the potential race condition when creating + * the singleton Java NetworkMonitor class. Downstream operations can be from + * any thread, but this class will forward all the downstream operations onto + * the worker thread. + * + * Memory consideration: + * NetworkMonitor is owned by the caller (NetworkManager). The global network + * monitor factory is owned by the factory itself but needs to be released from + * the factory creator. + */ +// Generic network monitor interface. It starts and stops monitoring network +// changes, and fires the SignalNetworksChanged event when networks change. +class NetworkMonitorInterface { + public: + NetworkMonitorInterface(); + virtual ~NetworkMonitorInterface(); + + sigslot::signal0<> SignalNetworksChanged; + + virtual void Start() = 0; + virtual void Stop() = 0; + + // Implementations should call this method on the base when networks change, + // and the base will fire SignalNetworksChanged on the right thread. + virtual void OnNetworksChanged() = 0; +}; + +class NetworkMonitorBase : public NetworkMonitorInterface, + public MessageHandler, + public sigslot::has_slots<> { + public: + NetworkMonitorBase(); + ~NetworkMonitorBase() override; + + void OnNetworksChanged() override; + + void OnMessage(Message* msg) override; + + private: + Thread* thread_; +}; + +/* + * NetworkMonitorFactory creates NetworkMonitors. + */ +class NetworkMonitorFactory { + public: + // This is not thread-safe; it should be called once (or once per audio/video + // call) during the call initialization. + static void SetFactory(NetworkMonitorFactory* factory); + + static void ReleaseFactory(NetworkMonitorFactory* factory); + static NetworkMonitorFactory* GetFactory(); + + virtual NetworkMonitorInterface* CreateNetworkMonitor() = 0; + + virtual ~NetworkMonitorFactory(); + + protected: + NetworkMonitorFactory(); +}; + +} // namespace rtc + +#endif // WEBRTC_BASE_NETWORKMONITOR_H_ diff --git a/webrtc/examples/androidapp/AndroidManifest.xml b/webrtc/examples/androidapp/AndroidManifest.xml index 631660a2dc..6a91cfdeed 100644 --- a/webrtc/examples/androidapp/AndroidManifest.xml +++ b/webrtc/examples/androidapp/AndroidManifest.xml @@ -14,6 +14,7 @@ +