Merge "Update documentation for AES-GCM-ESP keymat length"
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 0ccebc1..6bf1425 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -619,6 +619,35 @@
      */
     public static final int NETID_UNSET = 0;
 
+    /**
+     * Private DNS Mode values.
+     *
+     * The "private_dns_mode" global setting stores a String value which is
+     * expected to be one of the following.
+     */
+
+    /**
+     * @hide
+     */
+    public static final String PRIVATE_DNS_MODE_OFF = "off";
+    /**
+     * @hide
+     */
+    public static final String PRIVATE_DNS_MODE_OPPORTUNISTIC = "opportunistic";
+    /**
+     * @hide
+     */
+    public static final String PRIVATE_DNS_MODE_PROVIDER_HOSTNAME = "hostname";
+    /**
+     * The default Private DNS mode.
+     *
+     * This may change from release to release or may become dependent upon
+     * the capabilities of the underlying platform.
+     *
+     * @hide
+     */
+    public static final String PRIVATE_DNS_DEFAULT_MODE = PRIVATE_DNS_MODE_OPPORTUNISTIC;
+
     private final IConnectivityManager mService;
     /**
      * A kludge to facilitate static access where a Context pointer isn't available, like in the
diff --git a/core/java/android/net/MacAddress.java b/core/java/android/net/MacAddress.java
new file mode 100644
index 0000000..e76d17d
--- /dev/null
+++ b/core/java/android/net/MacAddress.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.Arrays;
+
+/**
+ * @hide
+ */
+public final class MacAddress {
+
+    // TODO: add isLocallyAssigned().
+    // TODO: add getRandomAddress() factory method.
+
+    private static final int ETHER_ADDR_LEN = 6;
+    private static final byte FF = (byte) 0xff;
+    @VisibleForTesting
+    static final byte[] ETHER_ADDR_BROADCAST = { FF, FF, FF, FF, FF, FF };
+
+    public enum MacAddressType {
+        UNICAST,
+        MULTICAST,
+        BROADCAST;
+    }
+
+    /** Return true if the given byte array is not null and has the length of a mac address. */
+    public static boolean isMacAddress(byte[] addr) {
+        return addr != null && addr.length == ETHER_ADDR_LEN;
+    }
+
+    /**
+     * Return the MacAddressType of the mac address represented by the given byte array,
+     * or null if the given byte array does not represent an mac address. */
+    public static MacAddressType macAddressType(byte[] addr) {
+        if (!isMacAddress(addr)) {
+            return null;
+        }
+        if (Arrays.equals(addr, ETHER_ADDR_BROADCAST)) {
+            return MacAddressType.BROADCAST;
+        }
+        if ((addr[0] & 0x01) == 1) {
+            return MacAddressType.MULTICAST;
+        }
+        return MacAddressType.UNICAST;
+    }
+}
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 25c96d1..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;
@@ -2109,9 +2113,14 @@
                         final boolean valid =
                                 (msg.arg1 == NetworkMonitor.NETWORK_TEST_RESULT_VALID);
                         final boolean wasValidated = nai.lastValidated;
+                        final boolean wasDefault = isDefaultNetwork(nai);
                         if (DBG) log(nai.name() + " validation " + (valid ? "passed" : "failed") +
                                 (msg.obj == null ? "" : " with redirect to " + (String)msg.obj));
                         if (valid != nai.lastValidated) {
+                            if (wasDefault) {
+                                metricsLogger().defaultNetworkMetrics().logDefaultNetworkValidity(
+                                        SystemClock.elapsedRealtime(), valid);
+                            }
                             final int oldScore = nai.getCurrentScore();
                             nai.lastValidated = valid;
                             nai.everValidated |= valid;
@@ -2283,7 +2292,8 @@
                 // Let rematchAllNetworksAndRequests() below record a new default network event
                 // if there is a fallback. Taken together, the two form a X -> 0, 0 -> Y sequence
                 // whose timestamps tell how long it takes to recover a default network.
-                metricsLogger().defaultNetworkMetrics().logDefaultNetworkEvent(null, nai);
+                long now = SystemClock.elapsedRealtime();
+                metricsLogger().defaultNetworkMetrics().logDefaultNetworkEvent(now, null, nai);
             }
             notifyIfacesChangedForNetworkStats();
             // TODO - we shouldn't send CALLBACK_LOST to requests that can be satisfied
@@ -4664,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
@@ -4681,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) {
@@ -5013,7 +5047,7 @@
             makeDefault(newNetwork);
             // Log 0 -> X and Y -> X default network transitions, where X is the new default.
             metricsLogger().defaultNetworkMetrics().logDefaultNetworkEvent(
-                    newNetwork, oldDefaultNetwork);
+                    now, newNetwork, oldDefaultNetwork);
             // Have a new default network, release the transition wakelock in
             scheduleReleaseNetworkTransitionWakelock();
         }
@@ -5214,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/MacAddressTest.java b/tests/net/java/android/net/MacAddressTest.java
new file mode 100644
index 0000000..3fa9c3a
--- /dev/null
+++ b/tests/net/java/android/net/MacAddressTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+
+import android.net.MacAddress.MacAddressType;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import java.util.Arrays;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class MacAddressTest {
+
+    static class AddrTypeTestCase {
+        byte[] addr;
+        MacAddressType expected;
+
+        static AddrTypeTestCase of(MacAddressType expected, int... addr) {
+            AddrTypeTestCase t = new AddrTypeTestCase();
+            t.expected = expected;
+            t.addr = toByteArray(addr);
+            return t;
+        }
+    }
+
+    @Test
+    public void testMacAddrTypes() {
+        AddrTypeTestCase[] testcases = {
+            AddrTypeTestCase.of(null),
+            AddrTypeTestCase.of(null, 0),
+            AddrTypeTestCase.of(null, 1, 2, 3, 4, 5),
+            AddrTypeTestCase.of(null, 1, 2, 3, 4, 5, 6, 7),
+            AddrTypeTestCase.of(MacAddressType.UNICAST, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0),
+            AddrTypeTestCase.of(MacAddressType.BROADCAST, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
+            AddrTypeTestCase.of(MacAddressType.MULTICAST, 1, 2, 3, 4, 5, 6),
+            AddrTypeTestCase.of(MacAddressType.MULTICAST, 11, 22, 33, 44, 55, 66),
+            AddrTypeTestCase.of(MacAddressType.MULTICAST, 33, 33, 0xaa, 0xbb, 0xcc, 0xdd)
+        };
+
+        for (AddrTypeTestCase t : testcases) {
+            MacAddressType got = MacAddress.macAddressType(t.addr);
+            String msg = String.format("expected type of %s to be %s, but got %s",
+                    Arrays.toString(t.addr), t.expected, got);
+            assertEquals(msg, t.expected, got);
+        }
+    }
+
+    static byte[] toByteArray(int[] in) {
+        byte[] out = new byte[in.length];
+        for (int i = 0; i < in.length; i++) {
+            out[i] = (byte) in[i];
+        }
+        return out;
+    }
+}
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/connectivity/IpConnectivityEventBuilderTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
index ad6ebf9..a65bb24 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
@@ -198,37 +198,33 @@
 
     @Test
     public void testDefaultNetworkEventSerialization() {
-        DefaultNetworkEvent ev = new DefaultNetworkEvent();
+        DefaultNetworkEvent ev = new DefaultNetworkEvent(1001);
         ev.netId = 102;
-        ev.prevNetId = 101;
-        ev.transportTypes = new int[]{1, 2, 3};
-        ev.prevIPv4 = true;
-        ev.prevIPv6 = true;
+        ev.transports = 2;
+        ev.previousTransports = 4;
+        ev.ipv4 = true;
+        ev.initialScore = 20;
+        ev.finalScore = 60;
+        ev.durationMs = 54;
+        ev.validatedMs = 27;
 
         String want = String.join("\n",
                 "dropped_events: 0",
                 "events <",
                 "  if_name: \"\"",
-                "  link_layer: 0",
+                "  link_layer: 4",
                 "  network_id: 102",
                 "  time_ms: 0",
-                "  transports: 0",
+                "  transports: 2",
                 "  default_network_event <",
-                "    default_network_duration_ms: 0",
-                "    final_score: 0",
-                "    initial_score: 0",
-                "    ip_support: 0",
-                "    network_id <",
-                "      network_id: 102",
-                "    >",
+                "    default_network_duration_ms: 54",
+                "    final_score: 60",
+                "    initial_score: 20",
+                "    ip_support: 1",
                 "    no_default_network_duration_ms: 0",
-                "    previous_network_id <",
-                "      network_id: 101",
-                "    >",
-                "    previous_network_ip_support: 3",
-                "    transport_types: 1",
-                "    transport_types: 2",
-                "    transport_types: 3",
+                "    previous_default_network_link_layer: 1",
+                "    previous_network_ip_support: 0",
+                "    validation_duration_ms: 27",
                 "  >",
                 ">",
                 "version: 2\n");
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
index 6c1decc..b48ff8d 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
@@ -188,119 +188,99 @@
             {makeNai(102, 50, true, true, cell), makeNai(103, 20, true, false, wifi)},
         };
 
+        long timeMs = mService.mDefaultNetworkMetrics.creationTimeMs;
+        long durationMs = 1001;
         for (NetworkAgentInfo[] pair : defaultNetworks) {
-            mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(pair[1], pair[0]);
+            timeMs += durationMs;
+            durationMs += durationMs;
+            mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(timeMs, pair[1], pair[0]);
         }
 
         String want = String.join("\n",
                 "dropped_events: 0",
                 "events <",
                 "  if_name: \"\"",
-                "  link_layer: 0",
-                "  network_id: 100",
-                "  time_ms: 0",
-                "  transports: 0",
-                "  default_network_event <",
-                "    default_network_duration_ms: 0",
-                "    final_score: 0",
-                "    initial_score: 0",
-                "    ip_support: 0",
-                "    network_id <",
-                "      network_id: 100",
-                "    >",
-                "    no_default_network_duration_ms: 0",
-                "    previous_network_id <",
-                "      network_id: 0",
-                "    >",
-                "    previous_network_ip_support: 0",
-                "    transport_types: 0",
-                "  >",
-                ">",
-                "events <",
-                "  if_name: \"\"",
-                "  link_layer: 0",
-                "  network_id: 101",
-                "  time_ms: 0",
-                "  transports: 0",
-                "  default_network_event <",
-                "    default_network_duration_ms: 0",
-                "    final_score: 0",
-                "    initial_score: 0",
-                "    ip_support: 0",
-                "    network_id <",
-                "      network_id: 101",
-                "    >",
-                "    no_default_network_duration_ms: 0",
-                "    previous_network_id <",
-                "      network_id: 100",
-                "    >",
-                "    previous_network_ip_support: 3",
-                "    transport_types: 1",
-                "  >",
-                ">",
-                "events <",
-                "  if_name: \"\"",
-                "  link_layer: 0",
+                "  link_layer: 5",
                 "  network_id: 0",
                 "  time_ms: 0",
                 "  transports: 0",
                 "  default_network_event <",
-                "    default_network_duration_ms: 0",
+                "    default_network_duration_ms: 1001",
                 "    final_score: 0",
                 "    initial_score: 0",
                 "    ip_support: 0",
-                "    network_id <",
-                "      network_id: 0",
-                "    >",
                 "    no_default_network_duration_ms: 0",
-                "    previous_network_id <",
-                "      network_id: 101",
-                "    >",
-                "    previous_network_ip_support: 1",
+                "    previous_default_network_link_layer: 0",
+                "    previous_network_ip_support: 0",
+                "    validation_duration_ms: 0",
                 "  >",
                 ">",
                 "events <",
                 "  if_name: \"\"",
-                "  link_layer: 0",
+                "  link_layer: 2",
+                "  network_id: 100",
+                "  time_ms: 0",
+                "  transports: 1",
+                "  default_network_event <",
+                "    default_network_duration_ms: 2002",
+                "    final_score: 50",
+                "    initial_score: 10",
+                "    ip_support: 3",
+                "    no_default_network_duration_ms: 0",
+                "    previous_default_network_link_layer: 0",
+                "    previous_network_ip_support: 0",
+                "    validation_duration_ms: 2002",
+                "  >",
+                ">",
+                "events <",
+                "  if_name: \"\"",
+                "  link_layer: 4",
+                "  network_id: 101",
+                "  time_ms: 0",
+                "  transports: 2",
+                "  default_network_event <",
+                "    default_network_duration_ms: 4004",
+                "    final_score: 60",
+                "    initial_score: 20",
+                "    ip_support: 1",
+                "    no_default_network_duration_ms: 0",
+                "    previous_default_network_link_layer: 2",
+                "    previous_network_ip_support: 0",
+                "    validation_duration_ms: 4004",
+                "  >",
+                ">",
+                "events <",
+                "  if_name: \"\"",
+                "  link_layer: 5",
+                "  network_id: 0",
+                "  time_ms: 0",
+                "  transports: 0",
+                "  default_network_event <",
+                "    default_network_duration_ms: 8008",
+                "    final_score: 0",
+                "    initial_score: 0",
+                "    ip_support: 0",
+                "    no_default_network_duration_ms: 0",
+                "    previous_default_network_link_layer: 4",
+                "    previous_network_ip_support: 0",
+                "    validation_duration_ms: 0",
+                "  >",
+                ">",
+                "events <",
+                "  if_name: \"\"",
+                "  link_layer: 2",
                 "  network_id: 102",
                 "  time_ms: 0",
-                "  transports: 0",
+                "  transports: 1",
                 "  default_network_event <",
-                "    default_network_duration_ms: 0",
-                "    final_score: 0",
-                "    initial_score: 0",
-                "    ip_support: 0",
-                "    network_id <",
-                "      network_id: 102",
-                "    >",
+                "    default_network_duration_ms: 16016",
+                "    final_score: 50",
+                "    initial_score: 10",
+                "    ip_support: 3",
                 "    no_default_network_duration_ms: 0",
-                "    previous_network_id <",
-                "      network_id: 0",
-                "    >",
+                "    previous_default_network_link_layer: 4",
                 "    previous_network_ip_support: 0",
-                "    transport_types: 0",
-                "  >",
-                ">",
-                "events <",
-                "  if_name: \"\"",
-                "  link_layer: 0",
-                "  network_id: 103",
-                "  time_ms: 0",
-                "  transports: 0",
-                "  default_network_event <",
-                "    default_network_duration_ms: 0",
-                "    final_score: 0",
-                "    initial_score: 0",
-                "    ip_support: 0",
-                "    network_id <",
-                "      network_id: 103",
-                "    >",
-                "    no_default_network_duration_ms: 0",
-                "    previous_network_id <",
-                "      network_id: 102",
-                "    >",
-                "    previous_network_ip_support: 3",
-                "    transport_types: 1",
+                "    validation_duration_ms: 16016",
                 "  >",
                 ">",
                 "version: 2\n");
@@ -379,12 +359,13 @@
         wakeupEvent("wlan0", 10008);
         wakeupEvent("rmnet0", 1000);
 
+        long timeMs = mService.mDefaultNetworkMetrics.creationTimeMs;
         final long cell = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_CELLULAR});
         final long wifi = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_WIFI});
         NetworkAgentInfo cellNai = makeNai(100, 50, false, true, cell);
         NetworkAgentInfo wifiNai = makeNai(101, 60, true, false, wifi);
-        mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(cellNai, null);
-        mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(wifiNai, cellNai);
+        mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(timeMs + 200, cellNai, null);
+        mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(timeMs + 300, wifiNai, cellNai);
 
         String want = String.join("\n",
                 "dropped_events: 0",
@@ -473,46 +454,36 @@
                 ">",
                 "events <",
                 "  if_name: \"\"",
-                "  link_layer: 0",
-                "  network_id: 100",
+                "  link_layer: 5",
+                "  network_id: 0",
                 "  time_ms: 0",
                 "  transports: 0",
                 "  default_network_event <",
-                "    default_network_duration_ms: 0",
+                "    default_network_duration_ms: 200",
                 "    final_score: 0",
                 "    initial_score: 0",
                 "    ip_support: 0",
-                "    network_id <",
-                "      network_id: 100",
-                "    >",
                 "    no_default_network_duration_ms: 0",
-                "    previous_network_id <",
-                "      network_id: 0",
-                "    >",
+                "    previous_default_network_link_layer: 0",
                 "    previous_network_ip_support: 0",
-                "    transport_types: 0",
+                "    validation_duration_ms: 0",
                 "  >",
                 ">",
                 "events <",
                 "  if_name: \"\"",
-                "  link_layer: 0",
-                "  network_id: 101",
+                "  link_layer: 2",
+                "  network_id: 100",
                 "  time_ms: 0",
-                "  transports: 0",
+                "  transports: 1",
                 "  default_network_event <",
-                "    default_network_duration_ms: 0",
-                "    final_score: 0",
-                "    initial_score: 0",
-                "    ip_support: 0",
-                "    network_id <",
-                "      network_id: 101",
-                "    >",
+                "    default_network_duration_ms: 100",
+                "    final_score: 50",
+                "    initial_score: 50",
+                "    ip_support: 2",
                 "    no_default_network_duration_ms: 0",
-                "    previous_network_id <",
-                "      network_id: 100",
-                "    >",
-                "    previous_network_ip_support: 2",
-                "    transport_types: 1",
+                "    previous_default_network_link_layer: 0",
+                "    previous_network_ip_support: 0",
+                "    validation_duration_ms: 100",
                 "  >",
                 ">",
                 "events <",
diff --git a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java b/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
index 6723601..83194d9 100644
--- a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
+++ b/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
@@ -82,9 +82,8 @@
     public void testWakeupEventLogging() throws Exception {
         final int BUFFER_LENGTH = NetdEventListenerService.WAKEUP_EVENT_BUFFER_LENGTH;
 
-        // Assert no events
-        String[] events1 = listNetdEvent();
-        assertEquals(new String[]{""}, events1);
+        // Baseline without any event
+        String[] baseline = listNetdEvent();
 
         long now = System.currentTimeMillis();
         String prefix = "iface:wlan0";
@@ -93,7 +92,7 @@
             mNetdEventListenerService.onWakeupEvent(prefix, uid, uid, now);
         }
 
-        String[] events2 = listNetdEvent();
+        String[] events2 = remove(listNetdEvent(), baseline);
         int expectedLength2 = uids.length + 1; // +1 for the WakeupStats line
         assertEquals(expectedLength2, events2.length);
         assertContains(events2[0], "WakeupStats");
@@ -111,7 +110,7 @@
             mNetdEventListenerService.onWakeupEvent(prefix, uid, uid, ts);
         }
 
-        String[] events3 = listNetdEvent();
+        String[] events3 = remove(listNetdEvent(), baseline);
         int expectedLength3 = BUFFER_LENGTH + 1; // +1 for the WakeupStats line
         assertEquals(expectedLength3, events3.length);
         assertContains(events2[0], "WakeupStats");
@@ -126,7 +125,7 @@
         uid = 45678;
         mNetdEventListenerService.onWakeupEvent(prefix, uid, uid, now);
 
-        String[] events4 = listNetdEvent();
+        String[] events4 = remove(listNetdEvent(), baseline);
         String lastEvent = events4[events4.length - 1];
         assertContains(lastEvent, "WakeupEvent");
         assertContains(lastEvent, "wlan0");
@@ -423,7 +422,7 @@
         final PrintWriter pw = new PrintWriter(new FileOutputStream("/dev/null"));
         new Thread(() -> {
             while (System.currentTimeMillis() < stop) {
-                mNetdEventListenerService.dump(pw);
+                mNetdEventListenerService.list(pw);
             }
         }).start();
     }
@@ -461,4 +460,16 @@
     static void assertContains(String got, String want) {
         assertTrue(got + " did not contain \"" + want + "\"", got.contains(want));
     }
+
+    static <T> T[] remove(T[] array, T[] filtered) {
+        List<T> c = Arrays.asList(filtered);
+        int next = 0;
+        for (int i = 0; i < array.length; i++) {
+            if (c.contains(array[i])) {
+                continue;
+            }
+            array[next++] = array[i];
+        }
+        return Arrays.copyOf(array, next);
+    }
 }
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);
     }