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}
diff --git a/talk/app/webrtc/androidtests/AndroidManifest.xml b/talk/app/webrtc/androidtests/AndroidManifest.xml
index ef6beb8..75b6d61 100644
--- a/talk/app/webrtc/androidtests/AndroidManifest.xml
+++ b/talk/app/webrtc/androidtests/AndroidManifest.xml
@@ -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"
diff --git a/talk/app/webrtc/androidtests/src/org/webrtc/NetworkMonitorTest.java b/talk/app/webrtc/androidtests/src/org/webrtc/NetworkMonitorTest.java
new file mode 100644
index 0000000..98a2363
--- /dev/null
+++ b/talk/app/webrtc/androidtests/src/org/webrtc/NetworkMonitorTest.java
@@ -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();
+  }
+}
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 0000000..581a223
--- /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<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;
+  }
+}
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 0000000..e3a7850
--- /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 0000000..f7a8c07
--- /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<rtc::NetworkMonitorInterface*>(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 0000000..3f5110c
--- /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<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_
diff --git a/talk/app/webrtc/java/jni/classreferenceholder.cc b/talk/app/webrtc/java/jni/classreferenceholder.cc
index d99aa1a..7efea06 100644
--- a/talk/app/webrtc/java/jni/classreferenceholder.cc
+++ b/talk/app/webrtc/java/jni/classreferenceholder.cc
@@ -78,6 +78,7 @@
   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 c3ccb8a..1db8621 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 @@
     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 @@
                          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 @@
   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 @@
       << "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 @@
   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 @@
   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)(
diff --git a/talk/app/webrtc/java/src/org/webrtc/PeerConnectionFactory.java b/talk/app/webrtc/java/src/org/webrtc/PeerConnectionFactory.java
index 0460624..83999ec 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 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 f61f2f6..a5d98ff 100644
--- a/talk/app/webrtc/peerconnectioninterface.h
+++ b/talk/app/webrtc/peerconnectioninterface.h
@@ -541,11 +541,13 @@
     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 fdf0631..81d723a 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 1980782..79e204e 100644
--- a/webrtc/base/BUILD.gn
+++ b/webrtc/base/BUILD.gn
@@ -237,6 +237,8 @@
     "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 9992d9d..d57fe11 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 bc714e3..879c1e4 100644
--- a/webrtc/base/network.cc
+++ b/webrtc/base/network.cc
@@ -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() {
 }
 
+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 @@
       thread_->Post(this, kSignalNetworksMessage);
   } else {
     thread_->Post(this, kUpdateNetworksMessage);
+    StartNetworkMonitor();
   }
   ++start_count_;
 }
@@ -676,13 +683,36 @@
   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::DoUpdateNetworks() {
+void BasicNetworkManager::UpdateNetworksOnce() {
   if (!start_count_)
     return;
 
@@ -711,7 +741,10 @@
       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 f365a78..ab3a88d 100644
--- a/webrtc/base/network.h
+++ b/webrtc/base/network.h
@@ -29,6 +29,7 @@
 namespace rtc {
 
 class Network;
+class NetworkMonitorInterface;
 class Thread;
 
 enum AdapterType {
@@ -147,7 +148,6 @@
 
  private:
   friend class NetworkTest;
-  void DoUpdateNetworks();
 
   EnumerationPermission enumeration_permission_;
 
@@ -164,7 +164,8 @@
 // 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 @@
  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 @@
   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.
diff --git a/webrtc/base/network_unittest.cc b/webrtc/base/network_unittest.cc
index 6d920e0..4362221 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 <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 @@
     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 @@
   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
diff --git a/webrtc/base/networkmonitor.cc b/webrtc/base/networkmonitor.cc
new file mode 100644
index 0000000..92bf059
--- /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 0000000..c45c817
--- /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 631660a..6a91cfd 100644
--- a/webrtc/examples/androidapp/AndroidManifest.xml
+++ b/webrtc/examples/androidapp/AndroidManifest.xml
@@ -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"
diff --git a/webrtc/examples/androidtests/src/org/appspot/apprtc/test/PeerConnectionClientTest.java b/webrtc/examples/androidtests/src/org/appspot/apprtc/test/PeerConnectionClientTest.java
index 4024140..5a5034b 100644
--- a/webrtc/examples/androidtests/src/org/appspot/apprtc/test/PeerConnectionClientTest.java
+++ b/webrtc/examples/androidtests/src/org/appspot/apprtc/test/PeerConnectionClientTest.java
@@ -236,6 +236,7 @@
     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);