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}
This commit is contained in:
honghaiz
2015-10-19 09:39:32 -07:00
committed by Commit bot
parent 0dbf0090a9
commit 023f3ef029
20 changed files with 1400 additions and 8 deletions

View File

@ -14,6 +14,7 @@
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<instrumentation
android:name="android.test.InstrumentationTestRunner"

View File

@ -0,0 +1,288 @@
/*
* 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.ConnectivityManagerDelegate;
import static org.webrtc.NetworkMonitorAutoDetect.INVALID_NET_ID;
import static org.webrtc.NetworkMonitorAutoDetect.NetworkState;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.telephony.TelephonyManager;
import android.test.ActivityTestCase;
import android.test.UiThreadTest;
import android.test.suitebuilder.annotation.MediumTest;
import android.test.suitebuilder.annotation.SmallTest;
/**
* Tests for org.webrtc.NetworkMonitor.
*/
@SuppressLint("NewApi")
public class NetworkMonitorTest extends ActivityTestCase {
/**
* Listens for alerts fired by the NetworkMonitor when network status changes.
*/
private static class NetworkMonitorTestObserver
implements NetworkMonitor.NetworkObserver {
private boolean receivedNotification = false;
@Override
public void onConnectionTypeChanged(ConnectionType connectionType) {
receivedNotification = true;
}
public boolean hasReceivedNotification() {
return receivedNotification;
}
public void resetHasReceivedNotification() {
receivedNotification = false;
}
}
/**
* Mocks out calls to the ConnectivityManager.
*/
private static class MockConnectivityManagerDelegate extends ConnectivityManagerDelegate {
private boolean activeNetworkExists;
private int networkType;
private int networkSubtype;
@Override
public NetworkState getNetworkState() {
return new NetworkState(activeNetworkExists, networkType, networkSubtype);
}
// Dummy implementations to avoid NullPointerExceptions in default implementations:
@Override
public int getDefaultNetId() {
return INVALID_NET_ID;
}
@Override
public Network[] getAllNetworks() {
return new Network[0];
}
@Override
public NetworkState getNetworkState(Network network) {
return new NetworkState(false, -1, -1);
}
public void setActiveNetworkExists(boolean networkExists) {
activeNetworkExists = networkExists;
}
public void setNetworkType(int networkType) {
this.networkType = networkType;
}
public void setNetworkSubtype(int networkSubtype) {
this.networkSubtype = networkSubtype;
}
}
/**
* Mocks out calls to the WifiManager.
*/
private static class MockWifiManagerDelegate
extends NetworkMonitorAutoDetect.WifiManagerDelegate {
private String wifiSSID;
@Override
public String getWifiSSID() {
return wifiSSID;
}
public void setWifiSSID(String wifiSSID) {
this.wifiSSID = wifiSSID;
}
}
// A dummy NetworkMonitorAutoDetect.Observer.
private static class TestNetworkMonitorAutoDetectObserver
implements NetworkMonitorAutoDetect.Observer {
@Override
public void onConnectionTypeChanged(ConnectionType newConnectionType) {}
}
private static final Object lock = new Object();
private static Handler uiThreadHandler = null;
private NetworkMonitorAutoDetect receiver;
private MockConnectivityManagerDelegate connectivityDelegate;
private MockWifiManagerDelegate wifiDelegate;
private static Handler getUiThreadHandler() {
synchronized (lock) {
if (uiThreadHandler == null ) {
uiThreadHandler = new Handler(Looper.getMainLooper());
}
return uiThreadHandler;
}
}
/**
* Helper method to create a network monitor and delegates for testing.
*/
private void createTestMonitor() {
Context context = getInstrumentation().getTargetContext();
NetworkMonitor.resetInstanceForTests(context);
NetworkMonitor.setAutoDetectConnectivityState(true);
receiver = NetworkMonitor.getAutoDetectorForTest();
assertNotNull(receiver);
connectivityDelegate = new MockConnectivityManagerDelegate();
connectivityDelegate.setActiveNetworkExists(true);
receiver.setConnectivityManagerDelegateForTests(connectivityDelegate);
wifiDelegate = new MockWifiManagerDelegate();
receiver.setWifiManagerDelegateForTests(wifiDelegate);
wifiDelegate.setWifiSSID("foo");
}
private NetworkMonitorAutoDetect.ConnectionType getCurrentConnectionType() {
final NetworkMonitorAutoDetect.NetworkState networkState =
receiver.getCurrentNetworkState();
return receiver.getCurrentConnectionType(networkState);
}
@Override
protected void setUp() throws Exception {
super.setUp();
getUiThreadHandler().post(new Runnable() {
public void run() {
createTestMonitor();
}
});
}
/**
* Tests that the receiver registers for connectivity intents during construction.
*/
@UiThreadTest
@SmallTest
public void testNetworkMonitorRegistersInConstructor() throws InterruptedException {
Context context = getInstrumentation().getTargetContext();
NetworkMonitorAutoDetect.Observer observer = new TestNetworkMonitorAutoDetectObserver();
NetworkMonitorAutoDetect receiver = new NetworkMonitorAutoDetect(observer, context);
assertTrue(receiver.isReceiverRegisteredForTesting());
}
/**
* Tests that when there is an intent indicating a change in network connectivity, it sends a
* notification to Java observers.
*/
@UiThreadTest
@MediumTest
public void testNetworkMonitorJavaObservers() throws InterruptedException {
// Initialize the NetworkMonitor with a connection.
Intent connectivityIntent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
receiver.onReceive(getInstrumentation().getTargetContext(), connectivityIntent);
// We shouldn't be re-notified if the connection hasn't actually changed.
NetworkMonitorTestObserver observer = new NetworkMonitorTestObserver();
NetworkMonitor.addNetworkObserver(observer);
receiver.onReceive(getInstrumentation().getTargetContext(), connectivityIntent);
assertFalse(observer.hasReceivedNotification());
// We shouldn't be notified if we're connected to non-Wifi and the Wifi SSID changes.
wifiDelegate.setWifiSSID("bar");
receiver.onReceive(getInstrumentation().getTargetContext(), connectivityIntent);
assertFalse(observer.hasReceivedNotification());
// We should be notified when we change to Wifi.
connectivityDelegate.setNetworkType(ConnectivityManager.TYPE_WIFI);
receiver.onReceive(getInstrumentation().getTargetContext(), connectivityIntent);
assertTrue(observer.hasReceivedNotification());
observer.resetHasReceivedNotification();
// We should be notified when the Wifi SSID changes.
wifiDelegate.setWifiSSID("foo");
receiver.onReceive(getInstrumentation().getTargetContext(), connectivityIntent);
assertTrue(observer.hasReceivedNotification());
observer.resetHasReceivedNotification();
// We shouldn't be re-notified if the Wifi SSID hasn't actually changed.
receiver.onReceive(getInstrumentation().getTargetContext(), connectivityIntent);
assertFalse(observer.hasReceivedNotification());
// Mimic that connectivity has been lost and ensure that the observer gets the notification.
connectivityDelegate.setActiveNetworkExists(false);
Intent noConnectivityIntent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
receiver.onReceive(getInstrumentation().getTargetContext(), noConnectivityIntent);
assertTrue(observer.hasReceivedNotification());
}
/**
* Tests that ConnectivityManagerDelegate doesn't crash. This test cannot rely on having any
* active network connections so it cannot usefully check results, but it can at least check
* that the functions don't crash.
*/
@UiThreadTest
@SmallTest
public void testConnectivityManagerDelegateDoesNotCrash() {
ConnectivityManagerDelegate delegate =
new ConnectivityManagerDelegate(getInstrumentation().getTargetContext());
delegate.getNetworkState();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Network[] networks = delegate.getAllNetworks();
if (networks.length >= 1) {
delegate.getNetworkState(networks[0]);
delegate.hasInternetCapability(networks[0]);
}
delegate.getDefaultNetId();
}
}
/**
* 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();
}
}

View File

@ -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<Long> nativeNetworkObservers;
// Java observers of the connection type changes.
private final ArrayList<NetworkObserver> 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<Long>();
networkObservers = new ArrayList<NetworkObserver>();
}
/**
* 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;
}
}

View File

@ -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());
}
}

View File

@ -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<rtc::NetworkMonitorInterface*>(j_native_monitor);
network_monitor->OnNetworksChanged();
}
} // namespace webrtc_jni

View File

@ -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<jclass> j_network_monitor_class_;
ScopedGlobalRef<jobject> 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_

View File

@ -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");

View File

@ -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<Thread> 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<PeerConnectionFactoryInterface> 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<OwnedFactoryAndThreads*>(native_factory);
if (owner->network_monitor_factory()) {
rtc::NetworkMonitorFactory::ReleaseFactory(
owner->network_monitor_factory());
owner->clear_network_monitor_factory();
}
}
}
JOW(void, PeerConnectionFactory_nativeSetVideoHwAccelerationOptions)(

View File

@ -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

View File

@ -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

View File

@ -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',

View File

@ -237,6 +237,8 @@ static_library("rtc_base") {
"nethelpers.h",
"network.cc",
"network.h",
"networkmonitor.cc",
"networkmonitor.h",
"nullsocketserver.h",
"pathutils.cc",
"pathutils.h",

View File

@ -219,6 +219,8 @@
'nethelpers.h',
'network.cc',
'network.h',
'networkmonitor.cc',
'networkmonitor.h',
'nullsocketserver.h',
'openssl.h',
'openssladapter.cc',

View File

@ -48,6 +48,7 @@
#include <algorithm>
#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);
}

View File

@ -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<std::string> network_ignore_list_;
int network_ignore_mask_;
bool ignore_non_default_routes_;
scoped_ptr<NetworkMonitorInterface> network_monitor_;
};
// Represents a Unix-type network interface, with a name and single address.

View File

@ -10,6 +10,7 @@
#include "webrtc/base/network.h"
#include "webrtc/base/networkmonitor.h"
#include <vector>
#if defined(WEBRTC_POSIX)
#include <sys/types.h>
@ -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<IPAddress>(ip));
}
TEST_F(NetworkTest, TestNetworkMonitoring) {
BasicNetworkManager manager;
manager.SignalNetworksChanged.connect(static_cast<NetworkTest*>(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

View File

@ -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

View File

@ -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_

View File

@ -14,6 +14,7 @@
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application android:label="@string/app_name"
android:icon="@drawable/ic_launcher"

View File

@ -236,6 +236,7 @@ public class PeerConnectionClientTest extends InstrumentationTestCase
PeerConnectionClient client = PeerConnectionClient.getInstance();
PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();
options.networkIgnoreMask = 0;
options.disableNetworkMonitor = true;
client.setPeerConnectionFactoryOptions(options);
client.createPeerConnectionFactory(
getInstrumentation().getContext(), peerConnectionParameters, this);