Get the adapter type information from Android OS.

BUG=

Review URL: https://codereview.webrtc.org/1594673002

Cr-Commit-Position: refs/heads/master@{#11463}
diff --git a/talk/app/webrtc/androidtests/src/org/webrtc/NetworkMonitorTest.java b/talk/app/webrtc/androidtests/src/org/webrtc/NetworkMonitorTest.java
index 8c0a794..3bdbbdd 100644
--- a/talk/app/webrtc/androidtests/src/org/webrtc/NetworkMonitorTest.java
+++ b/talk/app/webrtc/androidtests/src/org/webrtc/NetworkMonitorTest.java
@@ -142,6 +142,9 @@
 
     @Override
     public void onNetworkConnect(NetworkInformation networkInfo) {}
+
+    @Override
+    public void onNetworkDisconnect(int networkHandle) {}
   }
 
   private static final Object lock = new Object();
diff --git a/talk/app/webrtc/java/android/org/webrtc/NetworkMonitor.java b/talk/app/webrtc/java/android/org/webrtc/NetworkMonitor.java
index 0f3df42..427070e 100644
--- a/talk/app/webrtc/java/android/org/webrtc/NetworkMonitor.java
+++ b/talk/app/webrtc/java/android/org/webrtc/NetworkMonitor.java
@@ -154,19 +154,27 @@
     if (autoDetector == null) {
       autoDetector = new NetworkMonitorAutoDetect(
         new NetworkMonitorAutoDetect.Observer() {
+
           @Override
           public void onConnectionTypeChanged(ConnectionType newConnectionType) {
             updateCurrentConnectionType(newConnectionType);
           }
+
           @Override
           public void onNetworkConnect(NetworkInformation networkInfo) {
-            updateNetworkInformation(networkInfo);
+            notifyObserversOfNetworkConnect(networkInfo);
+          }
+
+          @Override
+          public void onNetworkDisconnect(int networkHandle) {
+            notifyObserversOfNetworkDisconnect(networkHandle);
           }
         },
         applicationContext);
       final NetworkMonitorAutoDetect.NetworkState networkState =
           autoDetector.getCurrentNetworkState();
       updateCurrentConnectionType(autoDetector.getConnectionType(networkState));
+      updateActiveNetworkList();
     }
   }
 
@@ -187,12 +195,28 @@
     }
   }
 
-  private void updateNetworkInformation(NetworkInformation networkInfo) {
+  private void notifyObserversOfNetworkConnect(NetworkInformation networkInfo) {
     for (long nativeObserver : nativeNetworkObservers) {
       nativeNotifyOfNetworkConnect(nativeObserver, networkInfo);
     }
   }
 
+  private void notifyObserversOfNetworkDisconnect(int networkHandle) {
+    for (long nativeObserver : nativeNetworkObservers) {
+      nativeNotifyOfNetworkDisconnect(nativeObserver, networkHandle);
+    }
+  }
+
+  private void updateActiveNetworkList() {
+    NetworkInformation[] networkInfos = autoDetector.getActiveNetworkList();
+    if (networkInfos.length == 0) {
+      return;
+    }
+    for (long nativeObserver : nativeNetworkObservers) {
+      nativeNotifyOfActiveNetworkList(nativeObserver, networkInfos);
+    }
+  }
+
   /**
    * Adds an observer for any connection type changes.
    */
@@ -224,11 +248,11 @@
         && connectionType != ConnectionType.CONNECTION_NONE;
   }
 
-  private native long nativeCreateNetworkMonitor();
-
   private native void nativeNotifyConnectionTypeChanged(long nativePtr);
-
   private native void nativeNotifyOfNetworkConnect(long nativePtr, NetworkInformation networkInfo);
+  private native void nativeNotifyOfNetworkDisconnect(long nativePtr, int networkHandle);
+  private native void nativeNotifyOfActiveNetworkList(long nativePtr,
+                                                      NetworkInformation[] networkInfos);
 
   // For testing only.
   static void resetInstanceForTests(Context context) {
diff --git a/talk/app/webrtc/java/android/org/webrtc/NetworkMonitorAutoDetect.java b/talk/app/webrtc/java/android/org/webrtc/NetworkMonitorAutoDetect.java
index baadd27..e3d6d0c 100644
--- a/talk/app/webrtc/java/android/org/webrtc/NetworkMonitorAutoDetect.java
+++ b/talk/app/webrtc/java/android/org/webrtc/NetworkMonitorAutoDetect.java
@@ -28,6 +28,8 @@
 package org.webrtc;
 
 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+
 
 import org.webrtc.Logging;
 
@@ -120,6 +122,54 @@
       return subtype;
     }
   }
+  /**
+   * The methods in this class get called when the network changes if the callback
+   * is registered with a proper network request. It is only available in Android Lollipop
+   * and above.
+   */
+  @SuppressLint("NewApi")
+  private class SimpleNetworkCallback extends NetworkCallback {
+
+    @Override
+    public void onAvailable(Network network) {
+      NetworkInformation networkInformation = connectivityManagerDelegate.networkToInfo(network);
+      Logging.d(TAG, "Network " + networkInformation.name + " with handle " +
+                networkInformation.handle + " is connected ");
+      observer.onNetworkConnect(networkInformation);
+    }
+
+    @Override
+    public void onCapabilitiesChanged(
+        Network network, NetworkCapabilities networkCapabilities) {
+      // A capabilities change may indicate the ConnectionType has changed,
+      // so forward the new NetworkInformation along to observer.
+      NetworkInformation networkInformation = connectivityManagerDelegate.networkToInfo(network);
+      observer.onNetworkConnect(networkInformation);
+    }
+
+    @Override
+    public void onLinkPropertiesChanged(Network network, LinkProperties linkProperties) {
+      // A link property change may indicate the IP address changes.
+      // so forward the new NetworkInformation to the observer.
+      NetworkInformation networkInformation = connectivityManagerDelegate.networkToInfo(network);
+      observer.onNetworkConnect(networkInformation);
+    }
+
+    @Override
+    public void onLosing(Network network, int maxMsToLive) {
+      // Tell the network is going to lose in MaxMsToLive milliseconds.
+      // We may use this signal later.
+      Logging.d(TAG, "Network with handle " + networkToNetId(network) +
+                " is about to lose in " + maxMsToLive + "ms");
+    }
+
+    @Override
+    public void onLost(Network network) {
+      int handle = networkToNetId(network);
+      Logging.d(TAG, "Network with handle " + handle + " is disconnected");
+      observer.onNetworkDisconnect(handle);
+    }
+  }
 
   /** Queries the ConnectivityManager for information about the current connection. */
   static class ConnectivityManagerDelegate {
@@ -128,7 +178,6 @@
      *  gracefully below.
      */
     private final ConnectivityManager connectivityManager;
-    private NetworkCallback networkCallback;
 
     ConnectivityManagerDelegate(Context context) {
       connectivityManager =
@@ -186,6 +235,18 @@
       return connectivityManager.getAllNetworks();
     }
 
+    NetworkInformation[] getActiveNetworkList() {
+      if (!supportNetworkCallback()) {
+        return new NetworkInformation[0];
+      }
+      Network[] networks = getAllNetworks();
+      NetworkInformation[] netInfos = new NetworkInformation[networks.length];
+      for (int i = 0; i < networks.length; ++i) {
+        netInfos[i] = networkToInfo(networks[i]);
+      }
+      return netInfos;
+    }
+
     /**
      * Returns the NetID of the current default network. Returns
      * INVALID_NET_ID if no current default network connected.
@@ -193,7 +254,7 @@
      */
     @SuppressLint("NewApi")
     int getDefaultNetId() {
-      if (connectivityManager == null) {
+      if (!supportNetworkCallback()) {
         return INVALID_NET_ID;
       }
       // Android Lollipop had no API to get the default network; only an
@@ -226,6 +287,17 @@
       return defaultNetId;
     }
 
+    @SuppressLint("NewApi")
+    private NetworkInformation networkToInfo(Network network) {
+      LinkProperties linkProperties = connectivityManager.getLinkProperties(network);
+      NetworkInformation networkInformation = new NetworkInformation(
+          linkProperties.getInterfaceName(),
+          getConnectionType(getNetworkState(network)),
+          networkToNetId(network),
+          getIPAddresses(linkProperties));
+      return networkInformation;
+    }
+
     /**
      * Returns true if {@code network} can provide Internet access. Can be used to
      * ignore specialized networks (e.g. IMS, FOTA).
@@ -240,30 +312,19 @@
       return capabilities != null && capabilities.hasCapability(NET_CAPABILITY_INTERNET);
     }
 
+    /** Only callable on Lollipop and newer releases. */
     @SuppressLint("NewApi")
-    public void requestMobileNetwork(final Observer observer) {
-      if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP ||
-          connectivityManager == null) {
-        return;
-      }
-      networkCallback = new NetworkCallback() {
-        @Override
-        public void onAvailable(Network network) {
-          super.onAvailable(network);
-          LinkProperties linkProperties = connectivityManager.getLinkProperties(network);
-          NetworkInformation networkInformation = new NetworkInformation(
-            linkProperties.getInterfaceName(),
-            getConnectionType(getNetworkState(network)),
-            networkToNetId(network),
-            getIPAddresses(linkProperties));
-          Logging.d(TAG, "Network " + networkInformation.name + " is connected ");
-          observer.onNetworkConnect(networkInformation);
-        }
-      };
-      Logging.d(TAG, "Requesting cellular network");
+    public void registerNetworkCallback(NetworkCallback networkCallback) {
+      connectivityManager.registerNetworkCallback(
+          new NetworkRequest.Builder().addCapability(NET_CAPABILITY_INTERNET).build(),
+          networkCallback);
+    }
+
+    /** Only callable on Lollipop and newer releases. */
+    @SuppressLint("NewApi")
+    public void requestMobileNetwork(NetworkCallback networkCallback) {
       NetworkRequest.Builder builder = new NetworkRequest.Builder();
-      builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
-      builder.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
+      builder.addCapability(NET_CAPABILITY_INTERNET).addTransportType(TRANSPORT_CELLULAR);
       connectivityManager.requestNetwork(builder.build(), networkCallback);
     }
 
@@ -279,15 +340,16 @@
     }
 
     @SuppressLint("NewApi")
-    public void releaseCallback() {
-      if (networkCallback != null) {
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
-          connectivityManager.unregisterNetworkCallback(networkCallback);
-        }
-        networkCallback = null;
+    public void releaseCallback(NetworkCallback networkCallback) {
+      if (supportNetworkCallback()) {
+        Logging.d(TAG, "Unregister network callback");
+        connectivityManager.unregisterNetworkCallback(networkCallback);
       }
     }
 
+    public boolean supportNetworkCallback() {
+      return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && connectivityManager != null;
+    }
   }
 
 
@@ -323,15 +385,20 @@
 
   static final int INVALID_NET_ID = -1;
   private static final String TAG = "NetworkMonitorAutoDetect";
-  private final IntentFilter intentFilter;
 
   // Observer for the connection type change.
   private final Observer observer;
-
+  private final IntentFilter intentFilter;
   private final Context context;
-  // connectivityManagerDelegates and wifiManagerDelegate are only non-final for testing.
+  // Used to request mobile network. It does not do anything except for keeping
+  // the callback for releasing the request.
+  private final NetworkCallback mobileNetworkCallback;
+  // Used to receive updates on all networks.
+  private final NetworkCallback allNetworkCallback;
+  // connectivityManagerDelegate and wifiManagerDelegate are only non-final for testing.
   private ConnectivityManagerDelegate connectivityManagerDelegate;
   private WifiManagerDelegate wifiManagerDelegate;
+
   private boolean isRegistered;
   private ConnectionType connectionType;
   private String wifiSSID;
@@ -345,11 +412,13 @@
      */
     public void onConnectionTypeChanged(ConnectionType newConnectionType);
     public void onNetworkConnect(NetworkInformation networkInfo);
+    public void onNetworkDisconnect(int networkHandle);
   }
 
   /**
    * Constructs a NetworkMonitorAutoDetect. Should only be called on UI thread.
    */
+  @SuppressLint("NewApi")
   public NetworkMonitorAutoDetect(Observer observer, Context context) {
     this.observer = observer;
     this.context = context;
@@ -360,7 +429,17 @@
     connectionType = getConnectionType(networkState);
     wifiSSID = getWifiSSID(networkState);
     intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
+
     registerReceiver();
+    if (connectivityManagerDelegate.supportNetworkCallback()) {
+      mobileNetworkCallback = new NetworkCallback();
+      connectivityManagerDelegate.requestMobileNetwork(mobileNetworkCallback);
+      allNetworkCallback = new SimpleNetworkCallback();
+      connectivityManagerDelegate.registerNetworkCallback(allNetworkCallback);
+    } else {
+      mobileNetworkCallback = null;
+      allNetworkCallback = null;
+    }
   }
 
   /**
@@ -385,7 +464,17 @@
     return isRegistered;
   }
 
+  NetworkInformation[] getActiveNetworkList() {
+    return connectivityManagerDelegate.getActiveNetworkList();
+  }
+
   public void destroy() {
+    if (allNetworkCallback != null) {
+      connectivityManagerDelegate.releaseCallback(allNetworkCallback);
+    }
+    if (mobileNetworkCallback != null) {
+      connectivityManagerDelegate.releaseCallback(mobileNetworkCallback);
+    }
     unregisterReceiver();
   }
 
@@ -393,22 +482,20 @@
    * Registers a BroadcastReceiver in the given context.
    */
   private void registerReceiver() {
-    if (!isRegistered) {
-      isRegistered = true;
-      context.registerReceiver(this, intentFilter);
-      connectivityManagerDelegate.requestMobileNetwork(observer);
-    }
+    if (isRegistered) return;
+
+    isRegistered = true;
+    context.registerReceiver(this, intentFilter);
   }
 
   /**
    * Unregisters the BroadcastReceiver in the given context.
    */
   private void unregisterReceiver() {
-    if (isRegistered) {
-      isRegistered = false;
-      context.unregisterReceiver(this);
-      connectivityManagerDelegate.releaseCallback();
-    }
+    if (!isRegistered) return;
+
+    isRegistered = false;
+    context.unregisterReceiver(this);
   }
 
   public NetworkState getCurrentNetworkState() {
@@ -422,9 +509,6 @@
    * when not implemented.
    */
   public int getDefaultNetId() {
-    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
-      return INVALID_NET_ID;
-    }
     return connectivityManagerDelegate.getDefaultNetId();
   }
 
diff --git a/talk/app/webrtc/java/jni/androidnetworkmonitor_jni.cc b/talk/app/webrtc/java/jni/androidnetworkmonitor_jni.cc
index 1716c19..ca0c782 100644
--- a/talk/app/webrtc/java/jni/androidnetworkmonitor_jni.cc
+++ b/talk/app/webrtc/java/jni/androidnetworkmonitor_jni.cc
@@ -71,6 +71,29 @@
   return NetworkType::NETWORK_UNKNOWN;
 }
 
+static rtc::AdapterType AdapterTypeFromNetworkType(NetworkType network_type) {
+  switch (network_type) {
+    case NETWORK_UNKNOWN:
+      RTC_DCHECK(false) << "Unknown network type ";
+      return rtc::ADAPTER_TYPE_UNKNOWN;
+    case NETWORK_ETHERNET:
+      return rtc::ADAPTER_TYPE_ETHERNET;
+    case NETWORK_WIFI:
+      return rtc::ADAPTER_TYPE_WIFI;
+    case NETWORK_4G:
+    case NETWORK_3G:
+    case NETWORK_2G:
+      return rtc::ADAPTER_TYPE_CELLULAR;
+    case NETWORK_BLUETOOTH:
+      // There is no corresponding mapping for bluetooth networks.
+      // Map it to VPN for now.
+      return rtc::ADAPTER_TYPE_VPN;
+    default:
+      RTC_DCHECK(false) << "Invalid network type " << network_type;
+      return rtc::ADAPTER_TYPE_UNKNOWN;
+  }
+}
+
 static rtc::IPAddress GetIPAddressFromJava(JNIEnv* jni, jobject j_ip_address) {
   jclass j_ip_address_class = GetObjectClass(jni, j_ip_address);
   jfieldID j_address_id = GetFieldID(jni, j_ip_address_class, "address", "[B");
@@ -186,7 +209,7 @@
   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";
+  CHECK_EXCEPTION(jni()) << "Error during CallVoidMethod";
 }
 
 void AndroidNetworkMonitor::Stop() {
@@ -207,21 +230,24 @@
   jni()->CallVoidMethod(*j_network_monitor_, m, jlongFromPointer(this));
   CHECK_EXCEPTION(jni()) << "Error during NetworkMonitor.stopMonitoring";
 
-  network_info_by_address_.clear();
+  network_handle_by_address_.clear();
+  network_info_by_handle_.clear();
 }
 
 int AndroidNetworkMonitor::BindSocketToNetwork(int socket_fd,
                                                const rtc::IPAddress& address) {
   RTC_CHECK(thread_checker_.CalledOnValidThread());
-  auto it = network_info_by_address_.find(address);
-  if (it == network_info_by_address_.end()) {
-    return rtc::NETWORK_BIND_ADDRESS_NOT_FOUND;
-  }
   // Android prior to Lollipop didn't have support for binding sockets to
   // networks. However, in that case it should not have reached here because
-  // |network_info_by_address_| should only be populated in Android Lollipop
+  // |network_handle_by_address_| should only be populated in Android Lollipop
   // and above.
-  NetworkInformation network = it->second;
+  // TODO(honghaiz): Add a check for Android version here so that it won't try
+  // to look for handle if the Android version is before Lollipop.
+  auto iter = network_handle_by_address_.find(address);
+  if (iter == network_handle_by_address_.end()) {
+    return rtc::NETWORK_BIND_ADDRESS_NOT_FOUND;
+  }
+  NetworkHandle network_handle = iter->second;
 
   // NOTE: This does rely on Android implementation details, but
   // these details are unlikely to change.
@@ -244,7 +270,7 @@
     LOG(LS_ERROR) << "Symbol setNetworkForSocket not found ";
     return rtc::NETWORK_BIND_NOT_IMPLEMENTED;
   }
-  int rv = setNetworkForSocket(network.handle, socket_fd);
+  int rv = setNetworkForSocket(network_handle, socket_fd);
   // If |network| has since disconnected, |rv| will be ENONET.  Surface this as
   // ERR_NETWORK_CHANGED, rather than MapSystemError(ENONET) which gives back
   // the less descriptive ERR_FAILED.
@@ -257,20 +283,58 @@
   return rtc::NETWORK_BIND_FAILURE;
 }
 
-void AndroidNetworkMonitor::OnNetworkAvailable(
+void AndroidNetworkMonitor::OnNetworkConnected(
     const NetworkInformation& network_info) {
+  LOG(LS_INFO) << "Network connected: " << network_info.ToString();
   worker_thread()->Invoke<void>(rtc::Bind(
-      &AndroidNetworkMonitor::OnNetworkAvailable_w, this, network_info));
+      &AndroidNetworkMonitor::OnNetworkConnected_w, this, network_info));
 }
 
-void AndroidNetworkMonitor::OnNetworkAvailable_w(
+void AndroidNetworkMonitor::OnNetworkConnected_w(
     const NetworkInformation& network_info) {
-  LOG(LS_INFO) << "Network available: " << network_info.ToString();
-  for (rtc::IPAddress address : network_info.ip_addresses) {
-    network_info_by_address_[address] = network_info;
+  adapter_type_by_name_[network_info.interface_name] =
+      AdapterTypeFromNetworkType(network_info.type);
+  network_info_by_handle_[network_info.handle] = network_info;
+  for (const rtc::IPAddress& address : network_info.ip_addresses) {
+    network_handle_by_address_[address] = network_info.handle;
   }
 }
 
+void AndroidNetworkMonitor::OnNetworkDisconnected(NetworkHandle handle) {
+  LOG(LS_INFO) << "Network disconnected for handle " << handle;
+  worker_thread()->Invoke<void>(
+      rtc::Bind(&AndroidNetworkMonitor::OnNetworkDisconnected_w, this, handle));
+}
+
+void AndroidNetworkMonitor::OnNetworkDisconnected_w(NetworkHandle handle) {
+  auto iter = network_info_by_handle_.find(handle);
+  if (iter != network_info_by_handle_.end()) {
+    for (const rtc::IPAddress& address : iter->second.ip_addresses) {
+      network_handle_by_address_.erase(address);
+    }
+    network_info_by_handle_.erase(iter);
+  }
+}
+
+void AndroidNetworkMonitor::SetNetworkInfos(
+    const std::vector<NetworkInformation>& network_infos) {
+  RTC_CHECK(thread_checker_.CalledOnValidThread());
+  network_handle_by_address_.clear();
+  network_info_by_handle_.clear();
+  for (NetworkInformation network : network_infos) {
+    OnNetworkConnected_w(network);
+  }
+}
+
+rtc::AdapterType AndroidNetworkMonitor::GetAdapterType(
+    const std::string& if_name) {
+  auto iter = adapter_type_by_name_.find(if_name);
+  if (iter == adapter_type_by_name_.end()) {
+    return rtc::ADAPTER_TYPE_UNKNOWN;
+  }
+  return iter->second;
+}
+
 rtc::NetworkMonitorInterface*
 AndroidNetworkMonitorFactory::CreateNetworkMonitor() {
   return new AndroidNetworkMonitor();
@@ -283,6 +347,21 @@
   network_monitor->OnNetworksChanged();
 }
 
+JOW(void, NetworkMonitor_nativeNotifyOfActiveNetworkList)(
+    JNIEnv* jni, jobject j_monitor, jlong j_native_monitor,
+    jobjectArray j_network_infos) {
+  AndroidNetworkMonitor* network_monitor =
+      reinterpret_cast<AndroidNetworkMonitor*>(j_native_monitor);
+  std::vector<NetworkInformation> network_infos;
+  size_t num_networks = jni->GetArrayLength(j_network_infos);
+  for (size_t i = 0; i < num_networks; ++i) {
+    jobject j_network_info = jni->GetObjectArrayElement(j_network_infos, i);
+    CHECK_EXCEPTION(jni) << "Error during GetObjectArrayElement";
+    network_infos.push_back(GetNetworkInformationFromJava(jni, j_network_info));
+  }
+  network_monitor->SetNetworkInfos(network_infos);
+}
+
 JOW(void, NetworkMonitor_nativeNotifyOfNetworkConnect)(
     JNIEnv* jni, jobject j_monitor, jlong j_native_monitor,
     jobject j_network_info) {
@@ -290,7 +369,16 @@
       reinterpret_cast<AndroidNetworkMonitor*>(j_native_monitor);
   NetworkInformation network_info =
       GetNetworkInformationFromJava(jni, j_network_info);
-  network_monitor->OnNetworkAvailable(network_info);
+  network_monitor->OnNetworkConnected(network_info);
+}
+
+JOW(void, NetworkMonitor_nativeNotifyOfNetworkDisconnect)(
+    JNIEnv* jni, jobject j_monitor, jlong j_native_monitor,
+    jint network_handle) {
+  AndroidNetworkMonitor* network_monitor =
+      reinterpret_cast<AndroidNetworkMonitor*>(j_native_monitor);
+  network_monitor->OnNetworkDisconnected(
+      static_cast<NetworkHandle>(network_handle));
 }
 
 }  // namespace webrtc_jni
diff --git a/talk/app/webrtc/java/jni/androidnetworkmonitor_jni.h b/talk/app/webrtc/java/jni/androidnetworkmonitor_jni.h
index 17de360..a5c8490 100644
--- a/talk/app/webrtc/java/jni/androidnetworkmonitor_jni.h
+++ b/talk/app/webrtc/java/jni/androidnetworkmonitor_jni.h
@@ -75,19 +75,25 @@
 
   int BindSocketToNetwork(int socket_fd,
                           const rtc::IPAddress& address) override;
-  void OnNetworkAvailable(const NetworkInformation& network_info);
+  rtc::AdapterType GetAdapterType(const std::string& if_name) override;
+  void OnNetworkConnected(const NetworkInformation& network_info);
+  void OnNetworkDisconnected(NetworkHandle network_handle);
+  void SetNetworkInfos(const std::vector<NetworkInformation>& network_infos);
 
  private:
   JNIEnv* jni() { return AttachCurrentThreadIfNeeded(); }
 
-  void OnNetworkAvailable_w(const NetworkInformation& network_info);
+  void OnNetworkConnected_w(const NetworkInformation& network_info);
+  void OnNetworkDisconnected_w(NetworkHandle network_handle);
 
   ScopedGlobalRef<jclass> j_network_monitor_class_;
   ScopedGlobalRef<jobject> j_network_monitor_;
   rtc::ThreadChecker thread_checker_;
   static jobject application_context_;
   bool started_ = false;
-  std::map<rtc::IPAddress, NetworkInformation> network_info_by_address_;
+  std::map<std::string, rtc::AdapterType> adapter_type_by_name_;
+  std::map<rtc::IPAddress, NetworkHandle> network_handle_by_address_;
+  std::map<NetworkHandle, NetworkInformation> network_info_by_handle_;
 };
 
 class AndroidNetworkMonitorFactory : public rtc::NetworkMonitorFactory {
diff --git a/webrtc/base/network.cc b/webrtc/base/network.cc
index 3c38e5b..6b04f89 100644
--- a/webrtc/base/network.cc
+++ b/webrtc/base/network.cc
@@ -441,6 +441,8 @@
       AdapterType adapter_type = ADAPTER_TYPE_UNKNOWN;
       if (cursor->ifa_flags & IFF_LOOPBACK) {
         adapter_type = ADAPTER_TYPE_LOOPBACK;
+      } else if (network_monitor_) {
+        adapter_type = network_monitor_->GetAdapterType(cursor->ifa_name);
       }
 #if defined(WEBRTC_IOS)
       // Cell networks are pdp_ipN on iOS.
diff --git a/webrtc/base/network.h b/webrtc/base/network.h
index 2f2e1b3..10ce6f0 100644
--- a/webrtc/base/network.h
+++ b/webrtc/base/network.h
@@ -18,6 +18,7 @@
 
 #include "webrtc/base/basictypes.h"
 #include "webrtc/base/ipaddress.h"
+#include "webrtc/base/networkmonitor.h"
 #include "webrtc/base/messagehandler.h"
 #include "webrtc/base/scoped_ptr.h"
 #include "webrtc/base/sigslot.h"
@@ -36,15 +37,6 @@
 class NetworkMonitorInterface;
 class Thread;
 
-enum AdapterType {
-  // This enum resembles the one in Chromium net::ConnectionType.
-  ADAPTER_TYPE_UNKNOWN = 0,
-  ADAPTER_TYPE_ETHERNET = 1 << 0,
-  ADAPTER_TYPE_WIFI = 1 << 1,
-  ADAPTER_TYPE_CELLULAR = 1 << 2,
-  ADAPTER_TYPE_VPN = 1 << 3,
-  ADAPTER_TYPE_LOOPBACK = 1 << 4
-};
 
 // By default, ignore loopback interfaces on the host.
 const int kDefaultNetworkIgnoreMask = ADAPTER_TYPE_LOOPBACK;
diff --git a/webrtc/base/network_unittest.cc b/webrtc/base/network_unittest.cc
index 43ccb34..0f7d6db 100644
--- a/webrtc/base/network_unittest.cc
+++ b/webrtc/base/network_unittest.cc
@@ -32,6 +32,9 @@
   void Start() override { started_ = true; }
   void Stop() override { started_ = false; }
   bool started() { return started_; }
+  AdapterType GetAdapterType(const std::string& if_name) override {
+    return ADAPTER_TYPE_UNKNOWN;
+  }
 
  private:
   bool started_ = false;
diff --git a/webrtc/base/networkmonitor.h b/webrtc/base/networkmonitor.h
index dd15f6e..d9d6cc4 100644
--- a/webrtc/base/networkmonitor.h
+++ b/webrtc/base/networkmonitor.h
@@ -29,6 +29,16 @@
   NETWORK_BIND_NETWORK_CHANGED = -4
 };
 
+enum AdapterType {
+  // This enum resembles the one in Chromium net::ConnectionType.
+  ADAPTER_TYPE_UNKNOWN = 0,
+  ADAPTER_TYPE_ETHERNET = 1 << 0,
+  ADAPTER_TYPE_WIFI = 1 << 1,
+  ADAPTER_TYPE_CELLULAR = 1 << 2,
+  ADAPTER_TYPE_VPN = 1 << 3,
+  ADAPTER_TYPE_LOOPBACK = 1 << 4
+};
+
 class NetworkBinderInterface {
  public:
   // Binds a socket to the network that is attached to |address| so that all
@@ -70,6 +80,8 @@
   // 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;
+
+  virtual AdapterType GetAdapterType(const std::string& interface_name) = 0;
 };
 
 class NetworkMonitorBase : public NetworkMonitorInterface,