Merge "DefaultNetworkEvent metrics: rehaul"
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
index 3c868c3..903b602 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -16,14 +16,14 @@
 
 package android.net;
 
-import android.os.Parcelable;
 import android.os.Parcel;
+import android.os.Parcelable;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.OsConstants;
 
-import libcore.net.http.Dns;
-import libcore.net.http.HttpURLConnectionFactory;
+import com.android.okhttp.internalandroidapi.Dns;
+import com.android.okhttp.internalandroidapi.HttpURLConnectionFactory;
 
 import java.io.FileDescriptor;
 import java.io.IOException;
@@ -34,11 +34,12 @@
 import java.net.Socket;
 import java.net.SocketAddress;
 import java.net.SocketException;
-import java.net.UnknownHostException;
 import java.net.URL;
 import java.net.URLConnection;
+import java.net.UnknownHostException;
 import java.util.Arrays;
 import java.util.concurrent.TimeUnit;
+
 import javax.net.SocketFactory;
 
 /**
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index db12dd9..ee75fd4 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -17,6 +17,7 @@
 package android.net;
 
 import android.annotation.IntDef;
+import android.net.ConnectivityManager.NetworkCallback;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -30,15 +31,24 @@
 import java.util.StringJoiner;
 
 /**
- * This class represents the capabilities of a network.  This is used both to specify
- * needs to {@link ConnectivityManager} and when inspecting a network.
- *
- * Note that this replaces the old {@link ConnectivityManager#TYPE_MOBILE} method
- * of network selection.  Rather than indicate a need for Wi-Fi because an application
- * needs high bandwidth and risk obsolescence when a new, fast network appears (like LTE),
- * the application should specify it needs high bandwidth.  Similarly if an application
- * needs an unmetered network for a bulk transfer it can specify that rather than assuming
- * all cellular based connections are metered and all Wi-Fi based connections are not.
+ * Representation of the capabilities of a network. This object serves two
+ * purposes:
+ * <ul>
+ * <li>An expression of the current capabilities of an active network, typically
+ * expressed through
+ * {@link NetworkCallback#onCapabilitiesChanged(Network, NetworkCapabilities)}
+ * or {@link ConnectivityManager#getNetworkCapabilities(Network)}.
+ * <li>An expression of the future capabilities of a desired network, typically
+ * expressed through {@link NetworkRequest}.
+ * </ul>
+ * <p>
+ * This replaces the old {@link ConnectivityManager#TYPE_MOBILE} method of
+ * network selection. Rather than indicate a need for Wi-Fi because an
+ * application needs high bandwidth and risk obsolescence when a new, fast
+ * network appears (like LTE), the application should specify it needs high
+ * bandwidth. Similarly if an application needs an unmetered network for a bulk
+ * transfer it can specify that rather than assuming all cellular based
+ * connections are metered and all Wi-Fi based connections are not.
  */
 public final class NetworkCapabilities implements Parcelable {
     private static final String TAG = "NetworkCapabilities";
@@ -101,6 +111,7 @@
             NET_CAPABILITY_NOT_VPN,
             NET_CAPABILITY_VALIDATED,
             NET_CAPABILITY_CAPTIVE_PORTAL,
+            NET_CAPABILITY_NOT_ROAMING,
             NET_CAPABILITY_FOREGROUND,
     })
     public @interface NetCapability { }
@@ -218,11 +229,16 @@
     public static final int NET_CAPABILITY_CAPTIVE_PORTAL = 17;
 
     /**
+     * Indicates that this network is not roaming.
+     */
+    public static final int NET_CAPABILITY_NOT_ROAMING = 18;
+
+    /**
      * Indicates that this network is available for use by apps, and not a network that is being
      * kept up in the background to facilitate fast network switching.
      * @hide
      */
-    public static final int NET_CAPABILITY_FOREGROUND = 18;
+    public static final int NET_CAPABILITY_FOREGROUND = 19;
 
     private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS;
     private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_FOREGROUND;
@@ -237,6 +253,7 @@
             (1 << NET_CAPABILITY_TRUSTED) |
             (1 << NET_CAPABILITY_VALIDATED) |
             (1 << NET_CAPABILITY_CAPTIVE_PORTAL) |
+            (1 << NET_CAPABILITY_NOT_ROAMING) |
             (1 << NET_CAPABILITY_FOREGROUND);
 
     /**
@@ -316,6 +333,21 @@
     }
 
     /**
+     * Sets (or clears) the given capability on this {@link NetworkCapabilities}
+     * instance.
+     *
+     * @hide
+     */
+    public NetworkCapabilities setCapability(@NetCapability int capability, boolean value) {
+        if (value) {
+            addCapability(capability);
+        } else {
+            removeCapability(capability);
+        }
+        return this;
+    }
+
+    /**
      * Gets all the capabilities set on this {@code NetworkCapability} instance.
      *
      * @return an array of capability values for this instance.
@@ -326,6 +358,15 @@
     }
 
     /**
+     * Sets all the capabilities set on this {@code NetworkCapability} instance.
+     *
+     * @hide
+     */
+    public void setCapabilities(@NetCapability int[] capabilities) {
+        mNetworkCapabilities = BitUtils.packBits(capabilities);
+    }
+
+    /**
      * Tests for the presence of a capabilitity on this instance.
      *
      * @param capability the capabilities to be tested for.
@@ -515,6 +556,21 @@
     }
 
     /**
+     * Sets (or clears) the given transport on this {@link NetworkCapabilities}
+     * instance.
+     *
+     * @hide
+     */
+    public NetworkCapabilities setTransportType(@Transport int transportType, boolean value) {
+        if (value) {
+            addTransportType(transportType);
+        } else {
+            removeTransportType(transportType);
+        }
+        return this;
+    }
+
+    /**
      * Gets all the transports set on this {@code NetworkCapability} instance.
      *
      * @return an array of transport type values for this instance.
@@ -525,6 +581,15 @@
     }
 
     /**
+     * Sets all the transports set on this {@code NetworkCapability} instance.
+     *
+     * @hide
+     */
+    public void setTransportTypes(@Transport int[] transportTypes) {
+        mTransportTypes = BitUtils.packBits(transportTypes);
+    }
+
+    /**
      * Tests for the presence of a transport on this instance.
      *
      * @param transportType the transport type to be tested for.
@@ -549,12 +614,18 @@
     }
 
     /**
+     * Value indicating that link bandwidth is unspecified.
+     * @hide
+     */
+    public static final int LINK_BANDWIDTH_UNSPECIFIED = 0;
+
+    /**
      * Passive link bandwidth.  This is a rough guide of the expected peak bandwidth
      * for the first hop on the given transport.  It is not measured, but may take into account
      * link parameters (Radio technology, allocated channels, etc).
      */
-    private int mLinkUpBandwidthKbps;
-    private int mLinkDownBandwidthKbps;
+    private int mLinkUpBandwidthKbps = LINK_BANDWIDTH_UNSPECIFIED;
+    private int mLinkDownBandwidthKbps = LINK_BANDWIDTH_UNSPECIFIED;
 
     /**
      * Sets the upstream bandwidth for this network in Kbps.  This always only refers to
@@ -571,8 +642,9 @@
      * @param upKbps the estimated first hop upstream (device to network) bandwidth.
      * @hide
      */
-    public void setLinkUpstreamBandwidthKbps(int upKbps) {
+    public NetworkCapabilities setLinkUpstreamBandwidthKbps(int upKbps) {
         mLinkUpBandwidthKbps = upKbps;
+        return this;
     }
 
     /**
@@ -600,8 +672,9 @@
      * @param downKbps the estimated first hop downstream (network to device) bandwidth.
      * @hide
      */
-    public void setLinkDownstreamBandwidthKbps(int downKbps) {
+    public NetworkCapabilities setLinkDownstreamBandwidthKbps(int downKbps) {
         mLinkDownBandwidthKbps = downKbps;
+        return this;
     }
 
     /**
@@ -628,6 +701,20 @@
         return (this.mLinkUpBandwidthKbps == nc.mLinkUpBandwidthKbps &&
                 this.mLinkDownBandwidthKbps == nc.mLinkDownBandwidthKbps);
     }
+    /** @hide */
+    public static int minBandwidth(int a, int b) {
+        if (a == LINK_BANDWIDTH_UNSPECIFIED)  {
+            return b;
+        } else if (b == LINK_BANDWIDTH_UNSPECIFIED) {
+            return a;
+        } else {
+            return Math.min(a, b);
+        }
+    }
+    /** @hide */
+    public static int maxBandwidth(int a, int b) {
+        return Math.max(a, b);
+    }
 
     private NetworkSpecifier mNetworkSpecifier = null;
 
@@ -708,8 +795,9 @@
      * @param signalStrength the bearer-specific signal strength.
      * @hide
      */
-    public void setSignalStrength(int signalStrength) {
+    public NetworkCapabilities setSignalStrength(int signalStrength) {
         mSignalStrength = signalStrength;
+        return this;
     }
 
     /**
@@ -968,6 +1056,7 @@
             case NET_CAPABILITY_NOT_VPN:        return "NOT_VPN";
             case NET_CAPABILITY_VALIDATED:      return "VALIDATED";
             case NET_CAPABILITY_CAPTIVE_PORTAL: return "CAPTIVE_PORTAL";
+            case NET_CAPABILITY_NOT_ROAMING:    return "NOT_ROAMING";
             case NET_CAPABILITY_FOREGROUND:     return "FOREGROUND";
             default:                            return Integer.toString(capability);
         }
diff --git a/core/java/android/net/NetworkInfo.java b/core/java/android/net/NetworkInfo.java
index 84c32be..d554938 100644
--- a/core/java/android/net/NetworkInfo.java
+++ b/core/java/android/net/NetworkInfo.java
@@ -307,11 +307,17 @@
     }
 
     /**
-     * Indicates whether the device is currently roaming on this network.
-     * When {@code true}, it suggests that use of data on this network
-     * may incur extra costs.
+     * Indicates whether the device is currently roaming on this network. When
+     * {@code true}, it suggests that use of data on this network may incur
+     * extra costs.
+     *
      * @return {@code true} if roaming is in effect, {@code false} otherwise.
+     * @deprecated Callers should switch to checking
+     *             {@link NetworkCapabilities#NET_CAPABILITY_NOT_ROAMING}
+     *             instead, since that handles more complex situations, such as
+     *             VPNs.
      */
+    @Deprecated
     public boolean isRoaming() {
         synchronized (this) {
             return mIsRoaming;
@@ -320,6 +326,7 @@
 
     /** {@hide} */
     @VisibleForTesting
+    @Deprecated
     public void setRoaming(boolean isRoaming) {
         synchronized (this) {
             mIsRoaming = isRoaming;
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index a05e1cc..664c2ab 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -29,7 +29,10 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
+import static android.net.NetworkCapabilities.TRANSPORT_VPN;
+
 import static com.android.internal.util.Preconditions.checkNotNull;
 
 import android.annotation.Nullable;
@@ -71,6 +74,7 @@
 import android.net.RouteInfo;
 import android.net.UidRange;
 import android.net.Uri;
+import android.net.VpnService;
 import android.net.metrics.IpConnectivityLog;
 import android.net.metrics.NetworkEvent;
 import android.net.util.MultinetworkPolicyTracker;
@@ -4670,10 +4674,12 @@
             }
         }
 
-        final NetworkCapabilities prevNc = nai.networkCapabilities;
+        final NetworkCapabilities prevNc;
         synchronized (nai) {
+            prevNc = nai.networkCapabilities;
             nai.networkCapabilities = networkCapabilities;
         }
+
         if (nai.getCurrentScore() == oldScore &&
                 networkCapabilities.equalRequestableCapabilities(prevNc)) {
             // If the requestable capabilities haven't changed, and the score hasn't changed, then
@@ -4687,6 +4693,28 @@
             rematchAllNetworksAndRequests(nai, oldScore);
             notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED);
         }
+
+        // Report changes that are interesting for network statistics tracking.
+        if (prevNc != null) {
+            final boolean meteredChanged = prevNc.hasCapability(NET_CAPABILITY_NOT_METERED) !=
+                    networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED);
+            final boolean roamingChanged = prevNc.hasCapability(NET_CAPABILITY_NOT_ROAMING) !=
+                    networkCapabilities.hasCapability(NET_CAPABILITY_NOT_ROAMING);
+            if (meteredChanged || roamingChanged) {
+                notifyIfacesChangedForNetworkStats();
+            }
+        }
+
+        if (!networkCapabilities.hasTransport(TRANSPORT_VPN)) {
+            // Tell VPNs about updated capabilities, since they may need to
+            // bubble those changes through.
+            synchronized (mVpns) {
+                for (int i = 0; i < mVpns.size(); i++) {
+                    final Vpn vpn = mVpns.valueAt(i);
+                    vpn.updateCapabilities();
+                }
+            }
+        }
     }
 
     public void handleUpdateLinkProperties(NetworkAgentInfo nai, LinkProperties newLp) {
@@ -5220,14 +5248,6 @@
         }
         notifyLockdownVpn(networkAgent);
 
-        if (oldInfo != null && oldInfo.getState() == state) {
-            if (oldInfo.isRoaming() != newInfo.isRoaming()) {
-                if (VDBG) log("roaming status changed, notifying NetworkStatsService");
-                notifyIfacesChangedForNetworkStats();
-            } else if (VDBG) log("ignoring duplicate network state non-change");
-            // In either case, no further work should be needed.
-            return;
-        }
         if (DBG) {
             log(networkAgent.name() + " EVENT_NETWORK_INFO_CHANGED, going from " +
                     (oldInfo == null ? "null" : oldInfo.getState()) +
diff --git a/tests/net/java/android/net/NetworkCapabilitiesTest.java b/tests/net/java/android/net/NetworkCapabilitiesTest.java
index 7346f9f..cd2d098 100644
--- a/tests/net/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/java/android/net/NetworkCapabilitiesTest.java
@@ -16,6 +16,7 @@
 
 package android.net;
 
+import static android.net.NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_EIMS;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
@@ -26,13 +27,12 @@
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 import static android.net.NetworkCapabilities.UNRESTRICTED_CAPABILITIES;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
 
-
-import android.net.NetworkCapabilities;
 import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
 
@@ -159,4 +159,25 @@
         assertNotEquals("", nc1.describeImmutableDifferences(nc2));
         assertEquals("", nc1.describeImmutableDifferences(nc1));
     }
+
+    @Test
+    public void testLinkBandwidthUtils() {
+        assertEquals(LINK_BANDWIDTH_UNSPECIFIED, NetworkCapabilities
+                .minBandwidth(LINK_BANDWIDTH_UNSPECIFIED, LINK_BANDWIDTH_UNSPECIFIED));
+        assertEquals(10, NetworkCapabilities
+                .minBandwidth(LINK_BANDWIDTH_UNSPECIFIED, 10));
+        assertEquals(10, NetworkCapabilities
+                .minBandwidth(10, LINK_BANDWIDTH_UNSPECIFIED));
+        assertEquals(10, NetworkCapabilities
+                .minBandwidth(10, 20));
+
+        assertEquals(LINK_BANDWIDTH_UNSPECIFIED, NetworkCapabilities
+                .maxBandwidth(LINK_BANDWIDTH_UNSPECIFIED, LINK_BANDWIDTH_UNSPECIFIED));
+        assertEquals(10, NetworkCapabilities
+                .maxBandwidth(LINK_BANDWIDTH_UNSPECIFIED, 10));
+        assertEquals(10, NetworkCapabilities
+                .maxBandwidth(10, LINK_BANDWIDTH_UNSPECIFIED));
+        assertEquals(20, NetworkCapabilities
+                .maxBandwidth(10, 20));
+    }
 }
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 27a29b6..113cd37 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -23,21 +23,44 @@
 import static android.net.ConnectivityManager.TYPE_MOBILE_MMS;
 import static android.net.ConnectivityManager.TYPE_NONE;
 import static android.net.ConnectivityManager.TYPE_WIFI;
-import static android.net.ConnectivityManager.getNetworkTypeName;
-import static android.net.NetworkCapabilities.*;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_EIMS;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_FOTA;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_IA;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_IMS;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_RCS;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_SUPL;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_WIFI_P2P;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_XCAP;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE;
+
+import static com.android.internal.util.TestUtils.waitForIdleHandler;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
-import static com.android.internal.util.TestUtils.waitForIdleHandler;
-import static org.mockito.Mockito.anyBoolean;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.app.NotificationManager;
@@ -45,7 +68,6 @@
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
-import android.content.ContextWrapper;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.res.Resources;
@@ -64,7 +86,6 @@
 import android.net.Network;
 import android.net.NetworkAgent;
 import android.net.NetworkCapabilities;
-import android.net.NetworkConfig;
 import android.net.NetworkFactory;
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.DetailedState;
@@ -79,13 +100,9 @@
 import android.os.ConditionVariable;
 import android.os.Handler;
 import android.os.HandlerThread;
-import android.os.IBinder;
 import android.os.INetworkManagementService;
 import android.os.Looper;
 import android.os.Message;
-import android.os.MessageQueue;
-import android.os.Messenger;
-import android.os.MessageQueue.IdleHandler;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.Process;
@@ -96,10 +113,8 @@
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.test.mock.MockContentResolver;
-import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.Log;
-import android.util.LogPrinter;
 
 import com.android.internal.util.WakeupMessage;
 import com.android.internal.util.test.BroadcastInterceptingContext;
@@ -109,7 +124,6 @@
 import com.android.server.connectivity.MockableSystemProperties;
 import com.android.server.connectivity.NetworkAgentInfo;
 import com.android.server.connectivity.NetworkMonitor;
-import com.android.server.connectivity.NetworkMonitor.CaptivePortalProbeResult;
 import com.android.server.net.NetworkPinner;
 import com.android.server.net.NetworkPolicyManagerInternal;
 
@@ -160,6 +174,7 @@
 
     @Mock IpConnectivityMetrics.Logger mMetricsService;
     @Mock DefaultNetworkMetrics mDefaultNetworkMetrics;
+    @Mock INetworkStatsService mStatsService;
 
     // This class exists to test bindProcessToNetwork and getBoundNetworkForProcess. These methods
     // do not go through ConnectivityService but talk to netd directly, so they don't automatically
@@ -858,7 +873,7 @@
                 NetworkPolicyManagerInternal.class, mock(NetworkPolicyManagerInternal.class));
         mService = new WrappedConnectivityService(mServiceContext,
                 mock(INetworkManagementService.class),
-                mock(INetworkStatsService.class),
+                mStatsService,
                 mock(INetworkPolicyManager.class),
                 mock(IpConnectivityLog.class));
 
@@ -3440,6 +3455,40 @@
         mCm.unregisterNetworkCallback(networkCallback);
     }
 
+    @Test
+    public void testStatsIfacesChanged() throws Exception {
+        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+
+        // Simple connection should have updated ifaces
+        mCellNetworkAgent.connect(false);
+        waitForIdle();
+        verify(mStatsService, atLeastOnce()).forceUpdateIfaces();
+        reset(mStatsService);
+
+        // Metered change should update ifaces
+        mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
+        waitForIdle();
+        verify(mStatsService, atLeastOnce()).forceUpdateIfaces();
+        reset(mStatsService);
+
+        mCellNetworkAgent.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
+        waitForIdle();
+        verify(mStatsService, atLeastOnce()).forceUpdateIfaces();
+        reset(mStatsService);
+
+        // Captive portal change shouldn't update ifaces
+        mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL);
+        waitForIdle();
+        verify(mStatsService, never()).forceUpdateIfaces();
+        reset(mStatsService);
+
+        // Roaming change should update ifaces
+        mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING);
+        waitForIdle();
+        verify(mStatsService, atLeastOnce()).forceUpdateIfaces();
+        reset(mStatsService);
+    }
+
     private void checkDirectlyConnectedRoutes(Object callbackObj,
             Collection<LinkAddress> linkAddresses, Collection<RouteInfo> otherRoutes) {
         assertTrue(callbackObj instanceof LinkProperties);
diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
index b4b8094..9e97d84 100644
--- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -206,7 +206,7 @@
                         eq(CRYPT_KEY),
                         anyInt(),
                         eq(""),
-                        isNull(),
+                        eq(new byte[] {}),
                         eq(0),
                         anyInt(),
                         anyInt(),
@@ -227,7 +227,7 @@
                         eq(CRYPT_KEY),
                         anyInt(),
                         eq(""),
-                        isNull(),
+                        eq(new byte[] {}),
                         eq(0),
                         anyInt(),
                         anyInt(),
@@ -256,10 +256,10 @@
                         anyLong(),
                         eq(TEST_SPI_OUT),
                         eq(""),
-                        isNull(),
+                        eq(new byte[] {}),
                         eq(0),
                         eq(""),
-                        isNull(),
+                        eq(new byte[] {}),
                         eq(0),
                         eq(IpSecAlgorithm.AUTH_CRYPT_AES_GCM),
                         eq(CRYPT_KEY),
@@ -277,10 +277,10 @@
                         anyLong(),
                         eq(TEST_SPI_IN),
                         eq(""),
-                        isNull(),
+                        eq(new byte[] {}),
                         eq(0),
                         eq(""),
-                        isNull(),
+                        eq(new byte[] {}),
                         eq(0),
                         eq(IpSecAlgorithm.AUTH_CRYPT_AES_GCM),
                         eq(CRYPT_KEY),
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index fe396c3..c29363c 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -20,11 +20,30 @@
 import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE;
 import static android.content.pm.UserInfo.FLAG_PRIMARY;
 import static android.content.pm.UserInfo.FLAG_RESTRICTED;
+import static android.net.NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_VPN;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.AdditionalMatchers.*;
-import static org.mockito.Mockito.*;
+import static org.mockito.AdditionalMatchers.aryEq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.annotation.UserIdInt;
 import android.app.AppOpsManager;
@@ -36,6 +55,9 @@
 import android.content.pm.ServiceInfo;
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
 import android.net.NetworkInfo.DetailedState;
 import android.net.UidRange;
 import android.net.VpnService;
@@ -45,17 +67,17 @@
 import android.os.Looper;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.support.test.runner.AndroidJUnit4;
 import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 
 import com.android.internal.R;
 import com.android.internal.net.VpnConfig;
 
-import org.junit.runner.RunWith;
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mockito.Answers;
 import org.mockito.InOrder;
 import org.mockito.Mock;
@@ -64,6 +86,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
 
@@ -114,6 +137,7 @@
     @Mock private AppOpsManager mAppOps;
     @Mock private NotificationManager mNotificationManager;
     @Mock private Vpn.SystemServices mSystemServices;
+    @Mock private ConnectivityManager mConnectivityManager;
 
     @Before
     public void setUp() throws Exception {
@@ -127,6 +151,8 @@
         when(mContext.getSystemService(eq(Context.APP_OPS_SERVICE))).thenReturn(mAppOps);
         when(mContext.getSystemService(eq(Context.NOTIFICATION_SERVICE)))
                 .thenReturn(mNotificationManager);
+        when(mContext.getSystemService(eq(Context.CONNECTIVITY_SERVICE)))
+                .thenReturn(mConnectivityManager);
         when(mContext.getString(R.string.config_customVpnAlwaysOnDisconnectedDialogComponent))
                 .thenReturn(Resources.getSystem().getString(
                         R.string.config_customVpnAlwaysOnDisconnectedDialogComponent));
@@ -397,6 +423,66 @@
         order.verify(mNotificationManager).cancelAsUser(anyString(), anyInt(), eq(userHandle));
     }
 
+    @Test
+    public void testCapabilities() {
+        final Vpn vpn = createVpn(primaryUser.id);
+        setMockedUsers(primaryUser);
+
+        final Network mobile = new Network(1);
+        final Network wifi = new Network(2);
+
+        final Map<Network, NetworkCapabilities> networks = new HashMap<>();
+        networks.put(mobile, new NetworkCapabilities()
+                .addTransportType(TRANSPORT_CELLULAR)
+                .addCapability(NET_CAPABILITY_INTERNET)
+                .addCapability(NET_CAPABILITY_NOT_METERED)
+                .setLinkDownstreamBandwidthKbps(10));
+        networks.put(wifi, new NetworkCapabilities()
+                .addTransportType(TRANSPORT_WIFI)
+                .addCapability(NET_CAPABILITY_INTERNET)
+                .addCapability(NET_CAPABILITY_NOT_ROAMING)
+                .setLinkUpstreamBandwidthKbps(20));
+        setMockedNetworks(networks);
+
+        final NetworkCapabilities caps = new NetworkCapabilities();
+
+        Vpn.updateCapabilities(mConnectivityManager, new Network[] { }, caps);
+        assertTrue(caps.hasTransport(TRANSPORT_VPN));
+        assertFalse(caps.hasTransport(TRANSPORT_CELLULAR));
+        assertFalse(caps.hasTransport(TRANSPORT_WIFI));
+        assertEquals(LINK_BANDWIDTH_UNSPECIFIED, caps.getLinkDownstreamBandwidthKbps());
+        assertEquals(LINK_BANDWIDTH_UNSPECIFIED, caps.getLinkUpstreamBandwidthKbps());
+        assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_METERED));
+        assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
+
+        Vpn.updateCapabilities(mConnectivityManager, new Network[] { mobile }, caps);
+        assertTrue(caps.hasTransport(TRANSPORT_VPN));
+        assertTrue(caps.hasTransport(TRANSPORT_CELLULAR));
+        assertFalse(caps.hasTransport(TRANSPORT_WIFI));
+        assertEquals(10, caps.getLinkDownstreamBandwidthKbps());
+        assertEquals(LINK_BANDWIDTH_UNSPECIFIED, caps.getLinkUpstreamBandwidthKbps());
+        assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_METERED));
+        assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
+
+        Vpn.updateCapabilities(mConnectivityManager, new Network[] { wifi }, caps);
+        assertTrue(caps.hasTransport(TRANSPORT_VPN));
+        assertFalse(caps.hasTransport(TRANSPORT_CELLULAR));
+        assertTrue(caps.hasTransport(TRANSPORT_WIFI));
+        assertEquals(LINK_BANDWIDTH_UNSPECIFIED, caps.getLinkDownstreamBandwidthKbps());
+        assertEquals(20, caps.getLinkUpstreamBandwidthKbps());
+        assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_METERED));
+        assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
+
+        Vpn.updateCapabilities(mConnectivityManager, new Network[] { mobile, wifi }, caps);
+        assertTrue(caps.hasTransport(TRANSPORT_VPN));
+        assertTrue(caps.hasTransport(TRANSPORT_CELLULAR));
+        assertTrue(caps.hasTransport(TRANSPORT_WIFI));
+        assertEquals(10, caps.getLinkDownstreamBandwidthKbps());
+        assertEquals(20, caps.getLinkUpstreamBandwidthKbps());
+        assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_METERED));
+        assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
+    }
+
     /**
      * Mock some methods of vpn object.
      */
@@ -463,4 +549,11 @@
         } catch (Exception e) {
         }
     }
+
+    private void setMockedNetworks(final Map<Network, NetworkCapabilities> networks) {
+        doAnswer(invocation -> {
+            final Network network = (Network) invocation.getArguments()[0];
+            return networks.get(network);
+        }).when(mConnectivityManager).getNetworkCapabilities(any());
+    }
 }
diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
index fa99795..375b418 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -1169,9 +1169,8 @@
         final LinkProperties prop = new LinkProperties();
         prop.setInterfaceName(TEST_IFACE);
         final NetworkCapabilities capabilities = new NetworkCapabilities();
-        if (!isMetered) {
-            capabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
-        }
+        capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, !isMetered);
+        capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true);
         return new NetworkState(info, prop, capabilities, null, null, TEST_SSID);
     }
 
@@ -1187,6 +1186,8 @@
         final LinkProperties prop = new LinkProperties();
         prop.setInterfaceName(TEST_IFACE);
         final NetworkCapabilities capabilities = new NetworkCapabilities();
+        capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false);
+        capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, !isRoaming);
         return new NetworkState(info, prop, capabilities, null, subscriberId, null);
     }
 
@@ -1196,6 +1197,8 @@
         final LinkProperties prop = new LinkProperties();
         prop.setInterfaceName(iface);
         final NetworkCapabilities capabilities = new NetworkCapabilities();
+        capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false);
+        capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true);
         return new NetworkState(info, prop, capabilities, null, null, null);
     }