Merge "Add PCSCF to LinkProperties."
diff --git a/core/java/android/net/ConnectionInfo.java b/core/java/android/net/ConnectionInfo.java
new file mode 100644
index 0000000..58d0e05
--- /dev/null
+++ b/core/java/android/net/ConnectionInfo.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2018 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 android.os.Parcel;
+import android.os.Parcelable;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.UnknownHostException;
+
+/**
+ * Describe a network connection including local and remote address/port of a connection and the
+ * transport protocol.
+ *
+ * @hide
+ */
+public final class ConnectionInfo implements Parcelable {
+    public final int protocol;
+    public final InetSocketAddress local;
+    public final InetSocketAddress remote;
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public ConnectionInfo(int protocol, InetSocketAddress local, InetSocketAddress remote) {
+        this.protocol = protocol;
+        this.local = local;
+        this.remote = remote;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(protocol);
+        out.writeByteArray(local.getAddress().getAddress());
+        out.writeInt(local.getPort());
+        out.writeByteArray(remote.getAddress().getAddress());
+        out.writeInt(remote.getPort());
+    }
+
+    public static final Creator<ConnectionInfo> CREATOR = new Creator<ConnectionInfo>() {
+        public ConnectionInfo createFromParcel(Parcel in) {
+            int protocol = in.readInt();
+            InetAddress localAddress;
+            try {
+                localAddress = InetAddress.getByAddress(in.createByteArray());
+            } catch (UnknownHostException e) {
+                throw new IllegalArgumentException("Invalid InetAddress");
+            }
+            int localPort = in.readInt();
+            InetAddress remoteAddress;
+            try {
+                remoteAddress = InetAddress.getByAddress(in.createByteArray());
+            } catch (UnknownHostException e) {
+                throw new IllegalArgumentException("Invalid InetAddress");
+            }
+            int remotePort = in.readInt();
+            InetSocketAddress local = new InetSocketAddress(localAddress, localPort);
+            InetSocketAddress remote = new InetSocketAddress(remoteAddress, remotePort);
+            return new ConnectionInfo(protocol, local, remote);
+        }
+
+        public ConnectionInfo[] newArray(int size) {
+            return new ConnectionInfo[size];
+        }
+    };
+}
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 7be708a..436b4a1 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -16,17 +16,19 @@
 package android.net;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
+import android.annotation.UnsupportedAppUsage;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.PackageManager;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Build.VERSION_CODES;
 import android.os.Bundle;
 import android.os.Handler;
@@ -58,6 +60,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.net.InetAddress;
+import java.net.InetSocketAddress;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -82,6 +85,7 @@
 @SystemService(Context.CONNECTIVITY_SERVICE)
 public class ConnectivityManager {
     private static final String TAG = "ConnectivityManager";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     /**
      * A change in network connectivity has occurred. A default connection has either
@@ -185,13 +189,19 @@
      * is for a network to which the connectivity manager was failing over
      * following a disconnect on another network.
      * Retrieve it with {@link android.content.Intent#getBooleanExtra(String,boolean)}.
+     *
+     * @deprecated See {@link NetworkInfo}.
      */
+    @Deprecated
     public static final String EXTRA_IS_FAILOVER = "isFailover";
     /**
      * The lookup key for a {@link NetworkInfo} object. This is supplied when
      * there is another network that it may be possible to connect to. Retrieve with
      * {@link android.content.Intent#getParcelableExtra(String)}.
+     *
+     * @deprecated See {@link NetworkInfo}.
      */
+    @Deprecated
     public static final String EXTRA_OTHER_NETWORK_INFO = "otherNetwork";
     /**
      * The lookup key for a boolean that indicates whether there is a
@@ -212,7 +222,10 @@
      * may be passed up from the lower networking layers, and its
      * meaning may be specific to a particular network type. Retrieve
      * it with {@link android.content.Intent#getStringExtra(String)}.
+     *
+     * @deprecated See {@link NetworkInfo#getExtraInfo()}.
      */
+    @Deprecated
     public static final String EXTRA_EXTRA_INFO = "extraInfo";
     /**
      * The lookup key for an int that provides information about
@@ -311,6 +324,7 @@
      * @hide
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @UnsupportedAppUsage
     public static final String INET_CONDITION_ACTION =
             "android.net.conn.INET_CONDITION_ACTION";
 
@@ -325,6 +339,7 @@
      * @hide
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @UnsupportedAppUsage
     public static final String ACTION_TETHER_STATE_CHANGED =
             "android.net.conn.TETHER_STATE_CHANGED";
 
@@ -333,6 +348,7 @@
      * gives a String[] listing all the interfaces configured for
      * tethering and currently available for tethering.
      */
+    @UnsupportedAppUsage
     public static final String EXTRA_AVAILABLE_TETHER = "availableArray";
 
     /**
@@ -347,6 +363,7 @@
      * gives a String[] listing all the interfaces currently tethered
      * (ie, has DHCPv4 support and packets potentially forwarded/NATed)
      */
+    @UnsupportedAppUsage
     public static final String EXTRA_ACTIVE_TETHER = "tetherArray";
 
     /**
@@ -355,6 +372,7 @@
      * failed.  Use {@link #getLastTetherError} to find the error code
      * for any interfaces listed here.
      */
+    @UnsupportedAppUsage
     public static final String EXTRA_ERRORED_TETHER = "erroredArray";
 
     /**
@@ -459,6 +477,7 @@
      * The absence of a connection type.
      * @hide
      */
+    @UnsupportedAppUsage
     public static final int TYPE_NONE        = -1;
 
     /**
@@ -575,6 +594,7 @@
      * {@hide}
      */
     @Deprecated
+    @UnsupportedAppUsage
     public static final int TYPE_MOBILE_FOTA = 10;
 
     /**
@@ -583,6 +603,7 @@
      * {@hide}
      */
     @Deprecated
+    @UnsupportedAppUsage
     public static final int TYPE_MOBILE_IMS  = 11;
 
     /**
@@ -591,6 +612,7 @@
      * {@hide}
      */
     @Deprecated
+    @UnsupportedAppUsage
     public static final int TYPE_MOBILE_CBS  = 12;
 
     /**
@@ -600,6 +622,7 @@
      * {@hide}
      */
     @Deprecated
+    @UnsupportedAppUsage
     public static final int TYPE_WIFI_P2P    = 13;
 
     /**
@@ -608,6 +631,7 @@
      * {@hide}
      */
     @Deprecated
+    @UnsupportedAppUsage
     public static final int TYPE_MOBILE_IA = 14;
 
     /**
@@ -617,6 +641,7 @@
      * {@hide}
      */
     @Deprecated
+    @UnsupportedAppUsage
     public static final int TYPE_MOBILE_EMERGENCY = 15;
 
     /**
@@ -625,6 +650,7 @@
      * {@hide}
      */
     @Deprecated
+    @UnsupportedAppUsage
     public static final int TYPE_PROXY = 16;
 
     /**
@@ -705,8 +731,9 @@
      *
      * @hide
      */
-    public static final String PRIVATE_DNS_DEFAULT_MODE = PRIVATE_DNS_MODE_OPPORTUNISTIC;
+    public static final String PRIVATE_DNS_DEFAULT_MODE_FALLBACK = PRIVATE_DNS_MODE_OPPORTUNISTIC;
 
+    @UnsupportedAppUsage
     private final IConnectivityManager mService;
     /**
      * A kludge to facilitate static access where a Context pointer isn't available, like in the
@@ -743,6 +770,7 @@
      * {@hide}
      */
     @Deprecated
+    @UnsupportedAppUsage
     public static String getNetworkTypeName(int type) {
         switch (type) {
           case TYPE_NONE:
@@ -797,6 +825,7 @@
      * {@hide}
      */
     @Deprecated
+    @UnsupportedAppUsage
     public static boolean isNetworkTypeMobile(int networkType) {
         switch (networkType) {
             case TYPE_MOBILE:
@@ -877,8 +906,11 @@
      *
      * @return a {@link NetworkInfo} object for the current default network
      *        or {@code null} if no default network is currently active
+     * @deprecated See {@link NetworkInfo}.
      */
+    @Deprecated
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+    @Nullable
     public NetworkInfo getActiveNetworkInfo() {
         try {
             return mService.getActiveNetworkInfo();
@@ -898,6 +930,7 @@
      *        {@code null} if no default network is currently active
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+    @Nullable
     public Network getActiveNetwork() {
         try {
             return mService.getActiveNetwork();
@@ -919,6 +952,7 @@
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL)
+    @Nullable
     public Network getActiveNetworkForUid(int uid) {
         return getActiveNetworkForUid(uid, false);
     }
@@ -1010,6 +1044,7 @@
      * {@hide}
      */
     @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL)
+    @UnsupportedAppUsage
     public NetworkInfo getActiveNetworkInfoForUid(int uid) {
         return getActiveNetworkInfoForUid(uid, false);
     }
@@ -1043,6 +1078,7 @@
      */
     @Deprecated
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+    @Nullable
     public NetworkInfo getNetworkInfo(int networkType) {
         try {
             return mService.getNetworkInfo(networkType);
@@ -1060,9 +1096,12 @@
      * @return a {@link NetworkInfo} object for the requested
      *        network or {@code null} if the {@code Network}
      *        is not valid.
+     * @deprecated See {@link NetworkInfo}.
      */
+    @Deprecated
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
-    public NetworkInfo getNetworkInfo(Network network) {
+    @Nullable
+    public NetworkInfo getNetworkInfo(@Nullable Network network) {
         return getNetworkInfoForUid(network, Process.myUid(), false);
     }
 
@@ -1088,6 +1127,7 @@
      */
     @Deprecated
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+    @NonNull
     public NetworkInfo[] getAllNetworkInfo() {
         try {
             return mService.getAllNetworkInfo();
@@ -1107,6 +1147,7 @@
      */
     @Deprecated
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+    @UnsupportedAppUsage
     public Network getNetworkForType(int networkType) {
         try {
             return mService.getNetworkForType(networkType);
@@ -1122,6 +1163,7 @@
      * @return an array of {@link Network} objects.
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+    @NonNull
     public Network[] getAllNetworks() {
         try {
             return mService.getAllNetworks();
@@ -1135,6 +1177,7 @@
      * the Networks that applications run by the given user will use by default.
      * @hide
      */
+    @UnsupportedAppUsage
     public NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(int userId) {
         try {
             return mService.getDefaultNetworkCapabilitiesForUser(userId);
@@ -1153,6 +1196,7 @@
      * {@hide}
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+    @UnsupportedAppUsage
     public LinkProperties getActiveLinkProperties() {
         try {
             return mService.getActiveLinkProperties();
@@ -1177,6 +1221,7 @@
      */
     @Deprecated
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+    @UnsupportedAppUsage
     public LinkProperties getLinkProperties(int networkType) {
         try {
             return mService.getLinkPropertiesForType(networkType);
@@ -1193,7 +1238,8 @@
      * @return The {@link LinkProperties} for the network, or {@code null}.
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
-    public LinkProperties getLinkProperties(Network network) {
+    @Nullable
+    public LinkProperties getLinkProperties(@Nullable Network network) {
         try {
             return mService.getLinkProperties(network);
         } catch (RemoteException e) {
@@ -1209,7 +1255,8 @@
      * @return The {@link android.net.NetworkCapabilities} for the network, or {@code null}.
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
-    public NetworkCapabilities getNetworkCapabilities(Network network) {
+    @Nullable
+    public NetworkCapabilities getNetworkCapabilities(@Nullable Network network) {
         try {
             return mService.getNetworkCapabilities(network);
         } catch (RemoteException e) {
@@ -1332,6 +1379,7 @@
         return 1;
     }
 
+    @UnsupportedAppUsage
     private NetworkCapabilities networkCapabilitiesForFeature(int networkType, String feature) {
         if (networkType == TYPE_MOBILE) {
             switch (feature) {
@@ -1495,6 +1543,7 @@
         };
     }
 
+    @UnsupportedAppUsage
     private static final HashMap<NetworkCapabilities, LegacyRequest> sLegacyRequests =
             new HashMap<>();
 
@@ -1523,6 +1572,7 @@
         Log.d(TAG, "expireRequest with " + ourSeqNum + ", " + sequenceNum);
     }
 
+    @UnsupportedAppUsage
     private NetworkRequest requestNetworkForFeatureLocked(NetworkCapabilities netCap) {
         int delay = -1;
         int type = legacyTypeForNetworkCapabilities(netCap);
@@ -1552,6 +1602,7 @@
         }
     }
 
+    @UnsupportedAppUsage
     private boolean removeRequestForFeature(NetworkCapabilities netCap) {
         final LegacyRequest l;
         synchronized (sLegacyRequests) {
@@ -1619,10 +1670,13 @@
     /** @hide */
     public static class PacketKeepaliveCallback {
         /** The requested keepalive was successfully started. */
+        @UnsupportedAppUsage
         public void onStarted() {}
         /** The keepalive was successfully stopped. */
+        @UnsupportedAppUsage
         public void onStopped() {}
         /** An error occurred. */
+        @UnsupportedAppUsage
         public void onError(int error) {}
     }
 
@@ -1689,6 +1743,7 @@
             mLooper.quit();
         }
 
+        @UnsupportedAppUsage
         public void stop() {
             try {
                 mService.stopKeepalive(mNetwork, mSlot);
@@ -1744,6 +1799,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public PacketKeepalive startNattKeepalive(
             Network network, int intervalSeconds, PacketKeepaliveCallback callback,
             InetAddress srcAddr, int srcPort, InetAddress dstAddr) {
@@ -1805,6 +1861,7 @@
      *             {@link #bindProcessToNetwork} API.
      */
     @Deprecated
+    @UnsupportedAppUsage
     public boolean requestRouteToHostAddress(int networkType, InetAddress hostAddress) {
         checkLegacyRoutingApiAccess();
         try {
@@ -1848,12 +1905,14 @@
      * @hide
      */
     @Deprecated
+    @UnsupportedAppUsage
     public void setBackgroundDataSetting(boolean allowBackgroundData) {
         // ignored
     }
 
     /** {@hide} */
     @Deprecated
+    @UnsupportedAppUsage
     public NetworkQuotaInfo getActiveNetworkQuotaInfo() {
         try {
             return mService.getActiveNetworkQuotaInfo();
@@ -1867,6 +1926,7 @@
      * @deprecated Talk to TelephonyManager directly
      */
     @Deprecated
+    @UnsupportedAppUsage
     public boolean getMobileDataEnabled() {
         IBinder b = ServiceManager.getService(Context.TELEPHONY_SERVICE);
         if (b != null) {
@@ -1950,7 +2010,7 @@
      *
      * @param l Previously registered listener.
      */
-    public void removeDefaultNetworkActiveListener(OnNetworkActiveListener l) {
+    public void removeDefaultNetworkActiveListener(@NonNull OnNetworkActiveListener l) {
         INetworkActivityListener rl = mNetworkActivityListeners.get(l);
         Preconditions.checkArgument(rl != null, "Listener was not registered.");
         try {
@@ -1986,6 +2046,7 @@
     }
 
     /** {@hide} */
+    @UnsupportedAppUsage
     public static ConnectivityManager from(Context context) {
         return (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
     }
@@ -2036,6 +2097,7 @@
      * @hide
      */
     @Deprecated
+    @UnsupportedAppUsage
     private static ConnectivityManager getInstance() {
         if (getInstanceOrNull() == null) {
             throw new IllegalStateException("No ConnectivityManager yet constructed");
@@ -2052,6 +2114,7 @@
      * {@hide}
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+    @UnsupportedAppUsage
     public String[] getTetherableIfaces() {
         try {
             return mService.getTetherableIfaces();
@@ -2068,6 +2131,7 @@
      * {@hide}
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+    @UnsupportedAppUsage
     public String[] getTetheredIfaces() {
         try {
             return mService.getTetheredIfaces();
@@ -2090,6 +2154,7 @@
      * {@hide}
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+    @UnsupportedAppUsage
     public String[] getTetheringErroredIfaces() {
         try {
             return mService.getTetheringErroredIfaces();
@@ -2136,6 +2201,7 @@
      *
      * {@hide}
      */
+    @UnsupportedAppUsage
     public int tether(String iface) {
         try {
             String pkgName = mContext.getOpPackageName();
@@ -2164,6 +2230,7 @@
      *
      * {@hide}
      */
+    @UnsupportedAppUsage
     public int untether(String iface) {
         try {
             String pkgName = mContext.getOpPackageName();
@@ -2317,6 +2384,7 @@
      * {@hide}
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+    @UnsupportedAppUsage
     public String[] getTetherableUsbRegexs() {
         try {
             return mService.getTetherableUsbRegexs();
@@ -2336,6 +2404,7 @@
      * {@hide}
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+    @UnsupportedAppUsage
     public String[] getTetherableWifiRegexs() {
         try {
             return mService.getTetherableWifiRegexs();
@@ -2355,6 +2424,7 @@
      * {@hide}
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+    @UnsupportedAppUsage
     public String[] getTetherableBluetoothRegexs() {
         try {
             return mService.getTetherableBluetoothRegexs();
@@ -2380,6 +2450,7 @@
      *
      * {@hide}
      */
+    @UnsupportedAppUsage
     public int setUsbTethering(boolean enable) {
         try {
             String pkgName = mContext.getOpPackageName();
@@ -2414,6 +2485,8 @@
     public static final int TETHER_ERROR_IFACE_CFG_ERROR      = 10;
     /** {@hide} */
     public static final int TETHER_ERROR_PROVISION_FAILED     = 11;
+    /** {@hide} */
+    public static final int TETHER_ERROR_DHCPSERVER_ERROR     = 12;
 
     /**
      * Get a more detailed error code after a Tethering or Untethering
@@ -2426,6 +2499,7 @@
      * {@hide}
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+    @UnsupportedAppUsage
     public int getLastTetherError(String iface) {
         try {
             return mService.getLastTetherError(iface);
@@ -2446,6 +2520,7 @@
      * {@hide}
      */
     public void reportInetCondition(int networkType, int percentage) {
+        printStackTrace();
         try {
             mService.reportInetCondition(networkType, percentage);
         } catch (RemoteException e) {
@@ -2465,7 +2540,8 @@
      *             working and non-working connectivity.
      */
     @Deprecated
-    public void reportBadNetwork(Network network) {
+    public void reportBadNetwork(@Nullable Network network) {
+        printStackTrace();
         try {
             // One of these will be ignored because it matches system's current state.
             // The other will trigger the necessary reevaluation.
@@ -2487,7 +2563,8 @@
      * @param hasConnectivity {@code true} if the application was able to successfully access the
      *                        Internet using {@code network} or {@code false} if not.
      */
-    public void reportNetworkConnectivity(Network network, boolean hasConnectivity) {
+    public void reportNetworkConnectivity(@Nullable Network network, boolean hasConnectivity) {
+        printStackTrace();
         try {
             mService.reportNetworkConnectivity(network, hasConnectivity);
         } catch (RemoteException e) {
@@ -2560,6 +2637,7 @@
      * @return the {@link ProxyInfo} for the current HTTP proxy, or {@code null} if no
      *        HTTP proxy is active.
      */
+    @Nullable
     public ProxyInfo getDefaultProxy() {
         return getProxyForNetwork(getBoundNetworkForProcess());
     }
@@ -2579,6 +2657,7 @@
      */
     @Deprecated
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+    @UnsupportedAppUsage
     public boolean isNetworkSupported(int networkType) {
         try {
             return mService.isNetworkSupported(networkType);
@@ -2679,7 +2758,11 @@
      *
      * @hide
      */
-    @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL)
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.NETWORK_SETTINGS,
+            android.Manifest.permission.NETWORK_SETUP_WIZARD,
+            android.Manifest.permission.NETWORK_STACK})
+    @SystemApi
     public void setAirplaneMode(boolean enable) {
         try {
             mService.setAirplaneMode(enable);
@@ -2689,6 +2772,7 @@
     }
 
     /** {@hide} */
+    @UnsupportedAppUsage
     public void registerNetworkFactory(Messenger messenger, String name) {
         try {
             mService.registerNetworkFactory(messenger, name);
@@ -2698,6 +2782,7 @@
     }
 
     /** {@hide} */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public void unregisterNetworkFactory(Messenger messenger) {
         try {
             mService.unregisterNetworkFactory(messenger);
@@ -2758,10 +2843,11 @@
          * @param network The {@link Network} of the satisfying network.
          * @param networkCapabilities The {@link NetworkCapabilities} of the satisfying network.
          * @param linkProperties The {@link LinkProperties} of the satisfying network.
+         * @param blocked Whether access to the {@link Network} is blocked due to system policy.
          * @hide
          */
         public void onAvailable(Network network, NetworkCapabilities networkCapabilities,
-                LinkProperties linkProperties) {
+                LinkProperties linkProperties, boolean blocked) {
             // Internally only this method is called when a new network is available, and
             // it calls the callback in the same way and order that older versions used
             // to call so as not to change the behavior.
@@ -2772,6 +2858,7 @@
             }
             onCapabilitiesChanged(network, networkCapabilities);
             onLinkPropertiesChanged(network, linkProperties);
+            onBlockedStatusChanged(network, blocked);
         }
 
         /**
@@ -2779,7 +2866,8 @@
          * This callback may be called more than once if the {@link Network} that is
          * satisfying the request changes. This will always immediately be followed by a
          * call to {@link #onCapabilitiesChanged(Network, NetworkCapabilities)} then by a
-         * call to {@link #onLinkPropertiesChanged(Network, LinkProperties)}.
+         * call to {@link #onLinkPropertiesChanged(Network, LinkProperties)}, and a call to
+         * {@link #onBlockedStatusChanged(Network, boolean)}.
          *
          * @param network The {@link Network} of the satisfying network.
          */
@@ -2858,6 +2946,14 @@
          */
         public void onNetworkResumed(Network network) {}
 
+        /**
+         * Called when access to the specified network is blocked or unblocked.
+         *
+         * @param network The {@link Network} whose blocked status has changed.
+         * @param blocked The blocked status of this {@link Network}.
+         */
+        public void onBlockedStatusChanged(Network network, boolean blocked) {}
+
         private NetworkRequest networkRequest;
     }
 
@@ -2904,6 +3000,8 @@
     public static final int CALLBACK_SUSPENDED           = BASE + 9;
     /** @hide */
     public static final int CALLBACK_RESUMED             = BASE + 10;
+    /** @hide */
+    public static final int CALLBACK_BLK_CHANGED         = BASE + 11;
 
     /** @hide */
     public static String getCallbackName(int whichCallback) {
@@ -2918,6 +3016,7 @@
             case EXPIRE_LEGACY_REQUEST: return "EXPIRE_LEGACY_REQUEST";
             case CALLBACK_SUSPENDED:    return "CALLBACK_SUSPENDED";
             case CALLBACK_RESUMED:      return "CALLBACK_RESUMED";
+            case CALLBACK_BLK_CHANGED:  return "CALLBACK_BLK_CHANGED";
             default:
                 return Integer.toString(whichCallback);
         }
@@ -2964,7 +3063,7 @@
                 case CALLBACK_AVAILABLE: {
                     NetworkCapabilities cap = getObject(message, NetworkCapabilities.class);
                     LinkProperties lp = getObject(message, LinkProperties.class);
-                    callback.onAvailable(network, cap, lp);
+                    callback.onAvailable(network, cap, lp, message.arg1 != 0);
                     break;
                 }
                 case CALLBACK_LOSING: {
@@ -2997,6 +3096,10 @@
                     callback.onNetworkResumed(network);
                     break;
                 }
+                case CALLBACK_BLK_CHANGED: {
+                    boolean blocked = message.arg1 != 0;
+                    callback.onBlockedStatusChanged(network, blocked);
+                }
             }
         }
 
@@ -3022,6 +3125,7 @@
 
     private NetworkRequest sendRequestForNetwork(NetworkCapabilities need, NetworkCallback callback,
             int timeoutMs, int action, int legacyType, CallbackHandler handler) {
+        printStackTrace();
         checkCallbackNotNull(callback);
         Preconditions.checkArgument(action == REQUEST || need != null, "null NetworkCapabilities");
         final NetworkRequest request;
@@ -3065,8 +3169,9 @@
      *
      * @hide
      */
-    public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback,
-            int timeoutMs, int legacyType, Handler handler) {
+    public void requestNetwork(@NonNull NetworkRequest request,
+            @NonNull NetworkCallback networkCallback, int timeoutMs, int legacyType,
+            @NonNull Handler handler) {
         CallbackHandler cbHandler = new CallbackHandler(handler);
         NetworkCapabilities nc = request.networkCapabilities;
         sendRequestForNetwork(nc, networkCallback, timeoutMs, REQUEST, legacyType, cbHandler);
@@ -3103,7 +3208,8 @@
      * @throws IllegalArgumentException if {@code request} specifies any mutable
      *         {@code NetworkCapabilities}.
      */
-    public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback) {
+    public void requestNetwork(@NonNull NetworkRequest request,
+            @NonNull NetworkCallback networkCallback) {
         requestNetwork(request, networkCallback, getDefaultHandler());
     }
 
@@ -3138,8 +3244,8 @@
      * @throws IllegalArgumentException if {@code request} specifies any mutable
      *         {@code NetworkCapabilities}.
      */
-    public void requestNetwork(
-            NetworkRequest request, NetworkCallback networkCallback, Handler handler) {
+    public void requestNetwork(@NonNull NetworkRequest request,
+            @NonNull NetworkCallback networkCallback, @NonNull Handler handler) {
         int legacyType = inferLegacyTypeForNetworkCapabilities(request.networkCapabilities);
         CallbackHandler cbHandler = new CallbackHandler(handler);
         requestNetwork(request, networkCallback, 0, legacyType, cbHandler);
@@ -3173,8 +3279,8 @@
      *                  before {@link NetworkCallback#onUnavailable()} is called. The timeout must
      *                  be a positive value (i.e. >0).
      */
-    public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback,
-            int timeoutMs) {
+    public void requestNetwork(@NonNull NetworkRequest request,
+            @NonNull NetworkCallback networkCallback, int timeoutMs) {
         checkTimeout(timeoutMs);
         int legacyType = inferLegacyTypeForNetworkCapabilities(request.networkCapabilities);
         requestNetwork(request, networkCallback, timeoutMs, legacyType, getDefaultHandler());
@@ -3207,8 +3313,8 @@
      * @param timeoutMs The time in milliseconds to attempt looking for a suitable network
      *                  before {@link NetworkCallback#onUnavailable} is called.
      */
-    public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback,
-            Handler handler, int timeoutMs) {
+    public void requestNetwork(@NonNull NetworkRequest request,
+            @NonNull NetworkCallback networkCallback, @NonNull Handler handler, int timeoutMs) {
         checkTimeout(timeoutMs);
         int legacyType = inferLegacyTypeForNetworkCapabilities(request.networkCapabilities);
         CallbackHandler cbHandler = new CallbackHandler(handler);
@@ -3280,7 +3386,9 @@
      *         {@link NetworkCapabilities#NET_CAPABILITY_VALIDATED} or
      *         {@link NetworkCapabilities#NET_CAPABILITY_CAPTIVE_PORTAL}.
      */
-    public void requestNetwork(NetworkRequest request, PendingIntent operation) {
+    public void requestNetwork(@NonNull NetworkRequest request,
+            @NonNull PendingIntent operation) {
+        printStackTrace();
         checkPendingIntentNotNull(operation);
         try {
             mService.pendingRequestForNetwork(request.networkCapabilities, operation);
@@ -3303,7 +3411,8 @@
      *                  {@link #requestNetwork(NetworkRequest, android.app.PendingIntent)} with the
      *                  corresponding NetworkRequest you'd like to remove. Cannot be null.
      */
-    public void releaseNetworkRequest(PendingIntent operation) {
+    public void releaseNetworkRequest(@NonNull PendingIntent operation) {
+        printStackTrace();
         checkPendingIntentNotNull(operation);
         try {
             mService.releasePendingNetworkRequest(operation);
@@ -3335,7 +3444,8 @@
      *                        The callback is invoked on the default internal Handler.
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
-    public void registerNetworkCallback(NetworkRequest request, NetworkCallback networkCallback) {
+    public void registerNetworkCallback(@NonNull NetworkRequest request,
+            @NonNull NetworkCallback networkCallback) {
         registerNetworkCallback(request, networkCallback, getDefaultHandler());
     }
 
@@ -3350,8 +3460,8 @@
      * @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
-    public void registerNetworkCallback(
-            NetworkRequest request, NetworkCallback networkCallback, Handler handler) {
+    public void registerNetworkCallback(@NonNull NetworkRequest request,
+            @NonNull NetworkCallback networkCallback, @NonNull Handler handler) {
         CallbackHandler cbHandler = new CallbackHandler(handler);
         NetworkCapabilities nc = request.networkCapabilities;
         sendRequestForNetwork(nc, networkCallback, 0, LISTEN, TYPE_NONE, cbHandler);
@@ -3387,7 +3497,9 @@
      *                  comes from {@link PendingIntent#getBroadcast}. Cannot be null.
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
-    public void registerNetworkCallback(NetworkRequest request, PendingIntent operation) {
+    public void registerNetworkCallback(@NonNull NetworkRequest request,
+            @NonNull PendingIntent operation) {
+        printStackTrace();
         checkPendingIntentNotNull(operation);
         try {
             mService.pendingListenForNetwork(request.networkCapabilities, operation);
@@ -3408,7 +3520,7 @@
      *                        The callback is invoked on the default internal Handler.
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
-    public void registerDefaultNetworkCallback(NetworkCallback networkCallback) {
+    public void registerDefaultNetworkCallback(@NonNull NetworkCallback networkCallback) {
         registerDefaultNetworkCallback(networkCallback, getDefaultHandler());
     }
 
@@ -3422,7 +3534,8 @@
      * @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
-    public void registerDefaultNetworkCallback(NetworkCallback networkCallback, Handler handler) {
+    public void registerDefaultNetworkCallback(@NonNull NetworkCallback networkCallback,
+            @NonNull Handler handler) {
         // This works because if the NetworkCapabilities are null,
         // ConnectivityService takes them from the default request.
         //
@@ -3447,7 +3560,7 @@
      * @param network {@link Network} specifying which network you're interested.
      * @return {@code true} on success, {@code false} if the {@link Network} is no longer valid.
      */
-    public boolean requestBandwidthUpdate(Network network) {
+    public boolean requestBandwidthUpdate(@NonNull Network network) {
         try {
             return mService.requestBandwidthUpdate(network);
         } catch (RemoteException e) {
@@ -3468,7 +3581,8 @@
      *
      * @param networkCallback The {@link NetworkCallback} used when making the request.
      */
-    public void unregisterNetworkCallback(NetworkCallback networkCallback) {
+    public void unregisterNetworkCallback(@NonNull NetworkCallback networkCallback) {
+        printStackTrace();
         checkCallbackNotNull(networkCallback);
         final List<NetworkRequest> reqs = new ArrayList<>();
         // Find all requests associated to this callback and stop callback triggers immediately.
@@ -3506,7 +3620,7 @@
      *                  {@link #registerNetworkCallback(NetworkRequest, android.app.PendingIntent)}.
      *                  Cannot be null.
      */
-    public void unregisterNetworkCallback(PendingIntent operation) {
+    public void unregisterNetworkCallback(@NonNull PendingIntent operation) {
         checkPendingIntentNotNull(operation);
         releaseNetworkRequest(operation);
     }
@@ -3628,7 +3742,7 @@
      * @return a bitwise OR of zero or more of the  {@code MULTIPATH_PREFERENCE_*} constants.
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
-    public @MultipathPreference int getMultipathPreference(Network network) {
+    public @MultipathPreference int getMultipathPreference(@Nullable Network network) {
         try {
             return mService.getMultipathPreference(network);
         } catch (RemoteException e) {
@@ -3666,7 +3780,7 @@
      *                the current binding.
      * @return {@code true} on success, {@code false} if the {@link Network} is no longer valid.
      */
-    public boolean bindProcessToNetwork(Network network) {
+    public boolean bindProcessToNetwork(@Nullable Network network) {
         // Forcing callers to call through non-static function ensures ConnectivityManager
         // instantiated.
         return setProcessDefaultNetwork(network);
@@ -3694,7 +3808,7 @@
      *             is a direct replacement.
      */
     @Deprecated
-    public static boolean setProcessDefaultNetwork(Network network) {
+    public static boolean setProcessDefaultNetwork(@Nullable Network network) {
         int netId = (network == null) ? NETID_UNSET : network.netId;
         if (netId == NetworkUtils.getBoundNetworkForProcess()) {
             return true;
@@ -3725,6 +3839,7 @@
      *
      * @return {@code Network} to which this process is bound, or {@code null}.
      */
+    @Nullable
     public Network getBoundNetworkForProcess() {
         // Forcing callers to call thru non-static function ensures ConnectivityManager
         // instantiated.
@@ -3741,6 +3856,7 @@
      *             {@code getBoundNetworkForProcess} is a direct replacement.
      */
     @Deprecated
+    @Nullable
     public static Network getProcessDefaultNetwork() {
         int netId = NetworkUtils.getBoundNetworkForProcess();
         if (netId == NETID_UNSET) return null;
@@ -3749,8 +3865,9 @@
 
     private void unsupportedStartingFrom(int version) {
         if (Process.myUid() == Process.SYSTEM_UID) {
-            // The getApplicationInfo() call we make below is not supported in system context, and
-            // we want to allow the system to use these APIs anyway.
+            // The getApplicationInfo() call we make below is not supported in system context. Let
+            // the call through here, and rely on the fact that ConnectivityService will refuse to
+            // allow the system to use these APIs anyway.
             return;
         }
 
@@ -3767,11 +3884,6 @@
     // functions by accessing ConnectivityService directly. However, it should be clear that doing
     // so is unsupported and may break in the future. http://b/22728205
     private void checkLegacyRoutingApiAccess() {
-        if (mContext.checkCallingOrSelfPermission("com.android.permission.INJECT_OMADM_SETTINGS")
-                == PackageManager.PERMISSION_GRANTED) {
-            return;
-        }
-
         unsupportedStartingFrom(VERSION_CODES.M);
     }
 
@@ -3786,9 +3898,10 @@
      * @deprecated This is strictly for legacy usage to support {@link #startUsingNetworkFeature}.
      */
     @Deprecated
+    @UnsupportedAppUsage
     public static boolean setProcessDefaultNetworkForHostResolution(Network network) {
         return NetworkUtils.bindProcessToNetworkForHostResolution(
-                network == null ? NETID_UNSET : network.netId);
+                (network == null) ? NETID_UNSET : network.getNetIdForResolv());
     }
 
     /**
@@ -3862,4 +3975,58 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * The network watchlist is a list of domains and IP addresses that are associated with
+     * potentially harmful apps. This method returns the SHA-256 of the watchlist config file
+     * currently used by the system for validation purposes.
+     *
+     * @return Hash of network watchlist config file. Null if config does not exist.
+     */
+    @Nullable
+    public byte[] getNetworkWatchlistConfigHash() {
+        try {
+            return mService.getNetworkWatchlistConfigHash();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Unable to get watchlist config hash");
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the {@code uid} of the owner of a network connection.
+     *
+     * @param protocol The protocol of the connection. Only {@code IPPROTO_TCP} and
+     * {@code IPPROTO_UDP} currently supported.
+     * @param local The local {@link InetSocketAddress} of a connection.
+     * @param remote The remote {@link InetSocketAddress} of a connection.
+     *
+     * @return {@code uid} if the connection is found and the app has permission to observe it
+     * (e.g., if it is associated with the calling VPN app's tunnel) or
+     * {@link android.os.Process#INVALID_UID} if the connection is not found.
+     */
+    public int getConnectionOwnerUid(int protocol, @NonNull InetSocketAddress local,
+            @NonNull InetSocketAddress remote) {
+        ConnectionInfo connectionInfo = new ConnectionInfo(protocol, local, remote);
+        try {
+            return mService.getConnectionOwnerUid(connectionInfo);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    private void printStackTrace() {
+        if (DEBUG) {
+            final StackTraceElement[] callStack = Thread.currentThread().getStackTrace();
+            final StringBuffer sb = new StringBuffer();
+            for (int i = 3; i < callStack.length; i++) {
+                final String stackTrace = callStack[i].toString();
+                if (stackTrace == null || stackTrace.contains("android.os")) {
+                    break;
+                }
+                sb.append(" [").append(stackTrace).append("]");
+            }
+            Log.d(TAG, "StackLog:" + sb.toString());
+        }
+    }
 }
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index a6fe738..e7d441d 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -17,6 +17,7 @@
 package android.net;
 
 import android.app.PendingIntent;
+import android.net.ConnectionInfo;
 import android.net.LinkProperties;
 import android.net.Network;
 import android.net.NetworkCapabilities;
@@ -180,4 +181,8 @@
     void stopKeepalive(in Network network, int slot);
 
     String getCaptivePortalServerUrl();
+
+    byte[] getNetworkWatchlistConfigHash();
+
+    int getConnectionOwnerUid(in ConnectionInfo connectionInfo);
 }
diff --git a/core/java/android/net/InetAddresses.java b/core/java/android/net/InetAddresses.java
new file mode 100644
index 0000000..8e6c69a
--- /dev/null
+++ b/core/java/android/net/InetAddresses.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2018 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 libcore.net.InetAddressUtils;
+
+import java.net.InetAddress;
+
+/**
+ * Utility methods for {@link InetAddress} implementations.
+ */
+public class InetAddresses {
+
+    private InetAddresses() {}
+
+    /**
+     * Checks to see if the {@code address} is a numeric address (such as {@code "192.0.2.1"} or
+     * {@code "2001:db8::1:2"}).
+     *
+     * <p>A numeric address is either an IPv4 address containing exactly 4 decimal numbers or an
+     * IPv6 numeric address. IPv4 addresses that consist of either hexadecimal or octal digits or
+     * do not have exactly 4 numbers are not treated as numeric.
+     *
+     * <p>This method will never do a DNS lookup.
+     *
+     * @param address the address to parse.
+     * @return true if the supplied address is numeric, false otherwise.
+     */
+    public static boolean isNumericAddress(String address) {
+        return InetAddressUtils.isNumericAddress(address);
+    }
+
+    /**
+     * Returns an InetAddress corresponding to the given numeric address (such
+     * as {@code "192.168.0.1"} or {@code "2001:4860:800d::68"}).
+     *
+     * <p>See {@link #isNumericAddress(String)} (String)} for a definition as to what constitutes a
+     * numeric address.
+     *
+     * <p>This method will never do a DNS lookup.
+     *
+     * @param address the address to parse, must be numeric.
+     * @return an {@link InetAddress} instance corresponding to the address.
+     * @throws IllegalArgumentException if {@code address} is not a numeric address.
+     */
+    public static InetAddress parseNumericAddress(String address) {
+        return InetAddressUtils.parseNumericAddress(address);
+    }
+}
diff --git a/core/java/android/net/IpConfiguration.java b/core/java/android/net/IpConfiguration.java
index fe69f29..3319f33 100644
--- a/core/java/android/net/IpConfiguration.java
+++ b/core/java/android/net/IpConfiguration.java
@@ -16,6 +16,7 @@
 
 package android.net;
 
+import android.annotation.UnsupportedAppUsage;
 import android.net.StaticIpConfiguration;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -32,8 +33,9 @@
     public enum IpAssignment {
         /* Use statically configured IP settings. Configuration can be accessed
          * with staticIpConfiguration */
+        @UnsupportedAppUsage
         STATIC,
-        /* Use dynamically configured IP settigns */
+        /* Use dynamically configured IP settings */
         DHCP,
         /* no IP details are assigned, this is used to indicate
          * that any existing IP settings should be retained */
@@ -47,6 +49,7 @@
     public enum ProxySettings {
         /* No proxy is to be used. Any existing proxy settings
          * should be cleared. */
+        @UnsupportedAppUsage
         NONE,
         /* Use statically configured proxy. Configuration can be accessed
          * with httpProxy. */
@@ -61,6 +64,7 @@
 
     public ProxySettings proxySettings;
 
+    @UnsupportedAppUsage
     public ProxyInfo httpProxy;
 
     private void init(IpAssignment ipAssignment,
@@ -79,6 +83,7 @@
         init(IpAssignment.UNASSIGNED, ProxySettings.UNASSIGNED, null, null);
     }
 
+    @UnsupportedAppUsage
     public IpConfiguration(IpAssignment ipAssignment,
                            ProxySettings proxySettings,
                            StaticIpConfiguration staticIpConfiguration,
diff --git a/core/java/android/net/LinkAddress.java b/core/java/android/net/LinkAddress.java
index bcfe938..b40f15a 100644
--- a/core/java/android/net/LinkAddress.java
+++ b/core/java/android/net/LinkAddress.java
@@ -25,6 +25,9 @@
 import static android.system.OsConstants.RT_SCOPE_SITE;
 import static android.system.OsConstants.RT_SCOPE_UNIVERSE;
 
+import android.annotation.SystemApi;
+import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Pair;
@@ -54,11 +57,13 @@
     /**
      * IPv4 or IPv6 address.
      */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private InetAddress address;
 
     /**
      * Prefix length.
      */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private int prefixLength;
 
     /**
@@ -112,6 +117,7 @@
      * @return true if the address is IPv6.
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean isIPv6() {
         return address instanceof Inet6Address;
     }
@@ -163,6 +169,7 @@
      * @param prefixLength The prefix length.
      * @hide
      */
+    @SystemApi
     public LinkAddress(InetAddress address, int prefixLength) {
         this(address, prefixLength, 0, 0);
         this.scope = scopeForUnicastAddress(address);
@@ -185,6 +192,7 @@
      * @param string The string to parse.
      * @hide
      */
+    @SystemApi
     public LinkAddress(String address) {
         this(address, 0, 0);
         this.scope = scopeForUnicastAddress(this.address);
@@ -255,6 +263,7 @@
      * otherwise.
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean isSameAddressAs(LinkAddress other) {
         return address.equals(other.address) && prefixLength == other.prefixLength;
     }
@@ -278,6 +287,7 @@
      * TODO: Delete all callers and remove in favour of getPrefixLength().
      * @hide
      */
+    @UnsupportedAppUsage
     public int getNetworkPrefixLength() {
         return getPrefixLength();
     }
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 7a8af58..4a466f3 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -18,6 +18,9 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
@@ -48,6 +51,7 @@
  */
 public final class LinkProperties implements Parcelable {
     // The interface described by the network link.
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private String mIfaceName;
     private ArrayList<LinkAddress> mLinkAddresses = new ArrayList<>();
     private ArrayList<InetAddress> mDnses = new ArrayList<>();
@@ -105,9 +109,13 @@
      * @hide
      */
     public enum ProvisioningChange {
+        @UnsupportedAppUsage
         STILL_NOT_PROVISIONED,
+        @UnsupportedAppUsage
         LOST_PROVISIONING,
+        @UnsupportedAppUsage
         GAINED_PROVISIONING,
+        @UnsupportedAppUsage
         STILL_PROVISIONED,
     }
 
@@ -116,6 +124,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public static ProvisioningChange compareProvisioning(
             LinkProperties before, LinkProperties after) {
         if (before.isProvisioned() && after.isProvisioned()) {
@@ -156,12 +165,14 @@
     /**
      * @hide
      */
+    @SystemApi
     public LinkProperties() {
     }
 
     /**
      * @hide
      */
+    @UnsupportedAppUsage
     public LinkProperties(LinkProperties source) {
         if (source != null) {
             mIfaceName = source.mIfaceName;
@@ -189,6 +200,7 @@
      * @param iface The name of the network interface used for this link.
      * @hide
      */
+    @SystemApi
     public void setInterfaceName(String iface) {
         mIfaceName = iface;
         ArrayList<RouteInfo> newRoutes = new ArrayList<>(mRoutes.size());
@@ -210,6 +222,7 @@
     /**
      * @hide
      */
+    @UnsupportedAppUsage
     public List<String> getAllInterfaceNames() {
         List<String> interfaceNames = new ArrayList<>(mStackedLinks.size() + 1);
         if (mIfaceName != null) interfaceNames.add(mIfaceName);
@@ -229,6 +242,7 @@
      * @return An unmodifiable {@link List} of {@link InetAddress} for this link.
      * @hide
      */
+    @UnsupportedAppUsage
     public List<InetAddress> getAddresses() {
         List<InetAddress> addresses = new ArrayList<>();
         for (LinkAddress linkAddress : mLinkAddresses) {
@@ -241,6 +255,7 @@
      * Returns all the addresses on this link and all the links stacked above it.
      * @hide
      */
+    @UnsupportedAppUsage
     public List<InetAddress> getAllAddresses() {
         List<InetAddress> addresses = new ArrayList<>();
         for (LinkAddress linkAddress : mLinkAddresses) {
@@ -268,6 +283,7 @@
      * @return true if {@code address} was added or updated, false otherwise.
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean addLinkAddress(LinkAddress address) {
         if (address == null) {
             return false;
@@ -318,6 +334,7 @@
      * Returns all the addresses on this link and all the links stacked above it.
      * @hide
      */
+    @UnsupportedAppUsage
     public List<LinkAddress> getAllLinkAddresses() {
         List<LinkAddress> addresses = new ArrayList<>(mLinkAddresses);
         for (LinkProperties stacked: mStackedLinks.values()) {
@@ -334,6 +351,7 @@
      *                  object.
      * @hide
      */
+    @SystemApi
     public void setLinkAddresses(Collection<LinkAddress> addresses) {
         mLinkAddresses.clear();
         for (LinkAddress address: addresses) {
@@ -348,6 +366,7 @@
      * @return true if the DNS server was added, false if it was already present.
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean addDnsServer(InetAddress dnsServer) {
         if (dnsServer != null && !mDnses.contains(dnsServer)) {
             mDnses.add(dnsServer);
@@ -363,6 +382,7 @@
      * @return true if the DNS server was removed, false if it did not exist.
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean removeDnsServer(InetAddress dnsServer) {
         if (dnsServer != null) {
             return mDnses.remove(dnsServer);
@@ -377,6 +397,7 @@
      * @param dnsServers The {@link Collection} of DNS servers to set in this object.
      * @hide
      */
+    @SystemApi
     public void setDnsServers(Collection<InetAddress> dnsServers) {
         mDnses.clear();
         for (InetAddress dnsServer: dnsServers) {
@@ -567,6 +588,7 @@
      *                domains to search when resolving host names on this link.
      * @hide
      */
+    @SystemApi
     public void setDomains(String domains) {
         mDomains = domains;
     }
@@ -589,6 +611,7 @@
      * @param mtu The MTU to use for this link.
      * @hide
      */
+    @SystemApi
     public void setMtu(int mtu) {
         mMtu = mtu;
     }
@@ -598,7 +621,6 @@
      * this will return 0.
      *
      * @return The mtu value set for this link.
-     * @hide
      */
     public int getMtu() {
         return mMtu;
@@ -612,6 +634,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public void setTcpBufferSizes(String tcpBufferSizes) {
         mTcpBufferSizes = tcpBufferSizes;
     }
@@ -623,6 +646,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public String getTcpBufferSizes() {
         return mTcpBufferSizes;
     }
@@ -646,6 +670,7 @@
      *
      * @hide
      */
+    @SystemApi
     public boolean addRoute(RouteInfo route) {
         if (route != null) {
             String routeIface = route.getInterface();
@@ -672,6 +697,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean removeRoute(RouteInfo route) {
         return route != null &&
                 Objects.equals(mIfaceName, route.getInterface()) &&
@@ -702,6 +728,7 @@
      * Returns all the routes on this link and all the links stacked above it.
      * @hide
      */
+    @UnsupportedAppUsage
     public List<RouteInfo> getAllRoutes() {
         List<RouteInfo> routes = new ArrayList<>(mRoutes);
         for (LinkProperties stacked: mStackedLinks.values()) {
@@ -718,6 +745,7 @@
      * @param proxy A {@link ProxyInfo} defining the HTTP Proxy to use on this link.
      * @hide
      */
+    @SystemApi
     public void setHttpProxy(ProxyInfo proxy) {
         mHttpProxy = proxy;
     }
@@ -742,6 +770,7 @@
      * @return true if the link was stacked, false otherwise.
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean addStackedLink(LinkProperties link) {
         if (link != null && link.getInterfaceName() != null) {
             mStackedLinks.put(link.getInterfaceName(), link);
@@ -772,6 +801,7 @@
      * Returns all the links stacked on top of this link.
      * @hide
      */
+    @UnsupportedAppUsage
     public @NonNull List<LinkProperties> getStackedLinks() {
         if (mStackedLinks.isEmpty()) {
             return Collections.emptyList();
@@ -787,6 +817,7 @@
      * Clears this object to its initial state.
      * @hide
      */
+    @SystemApi
     public void clear() {
         mIfaceName = null;
         mLinkAddresses.clear();
@@ -895,6 +926,7 @@
      * @return {@code true} if there is an IPv4 address, {@code false} otherwise.
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean hasIPv4Address() {
         for (LinkAddress address : mLinkAddresses) {
             if (address.getAddress() instanceof Inet4Address) {
@@ -922,6 +954,7 @@
      * @return {@code true} if there is a global preferred IPv6 address, {@code false} otherwise.
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean hasGlobalIPv6Address() {
         for (LinkAddress address : mLinkAddresses) {
           if (address.getAddress() instanceof Inet6Address && address.isGlobalPreferred()) {
@@ -937,6 +970,7 @@
      * @return {@code true} if there is an IPv4 default route, {@code false} otherwise.
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean hasIPv4DefaultRoute() {
         for (RouteInfo r : mRoutes) {
             if (r.isIPv4Default()) {
@@ -952,6 +986,7 @@
      * @return {@code true} if there is an IPv6 default route, {@code false} otherwise.
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean hasIPv6DefaultRoute() {
         for (RouteInfo r : mRoutes) {
             if (r.isIPv6Default()) {
@@ -967,6 +1002,7 @@
      * @return {@code true} if there is an IPv4 DNS server, {@code false} otherwise.
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean hasIPv4DnsServer() {
         for (InetAddress ia : mDnses) {
             if (ia instanceof Inet4Address) {
@@ -982,6 +1018,7 @@
      * @return {@code true} if there is an IPv6 DNS server, {@code false} otherwise.
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean hasIPv6DnsServer() {
         for (InetAddress ia : mDnses) {
             if (ia instanceof Inet6Address) {
@@ -1041,6 +1078,7 @@
      * @return {@code true} if the link is provisioned, {@code false} otherwise.
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean isIPv6Provisioned() {
         return (hasGlobalIPv6Address() &&
                 hasIPv6DefaultRoute() &&
@@ -1054,6 +1092,7 @@
      * @return {@code true} if the link is provisioned, {@code false} otherwise.
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean isProvisioned() {
         return (isIPv4Provisioned() || isIPv6Provisioned());
     }
@@ -1065,6 +1104,7 @@
      *         {@code false} otherwise.
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean isReachable(InetAddress ip) {
         final List<RouteInfo> allRoutes = getAllRoutes();
         // If we don't have a route to this IP address, it's not reachable.
@@ -1102,6 +1142,7 @@
      * @return {@code true} if both are identical, {@code false} otherwise.
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean isIdenticalInterfaceName(LinkProperties target) {
         return TextUtils.equals(getInterfaceName(), target.getInterfaceName());
     }
@@ -1113,6 +1154,7 @@
      * @return {@code true} if both are identical, {@code false} otherwise.
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean isIdenticalAddresses(LinkProperties target) {
         Collection<InetAddress> targetAddresses = target.getAddresses();
         Collection<InetAddress> sourceAddresses = getAddresses();
@@ -1127,6 +1169,7 @@
      * @return {@code true} if both are identical, {@code false} otherwise.
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean isIdenticalDnses(LinkProperties target) {
         Collection<InetAddress> targetDnses = target.getDnsServers();
         String targetDomains = target.getDomains();
@@ -1187,6 +1230,7 @@
      * @return {@code true} if both are identical, {@code false} otherwise.
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean isIdenticalRoutes(LinkProperties target) {
         Collection<RouteInfo> targetRoutes = target.getRoutes();
         return (mRoutes.size() == targetRoutes.size()) ?
@@ -1200,6 +1244,7 @@
      * @return {@code true} if both are identical, {@code false} otherwise.
      * @hide
      */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public boolean isIdenticalHttpProxy(LinkProperties target) {
         return getHttpProxy() == null ? target.getHttpProxy() == null :
                 getHttpProxy().equals(target.getHttpProxy());
@@ -1212,6 +1257,7 @@
      * @return {@code true} if both are identical, {@code false} otherwise.
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean isIdenticalStackedLinks(LinkProperties target) {
         if (!mStackedLinks.keySet().equals(target.mStackedLinks.keySet())) {
             return false;
diff --git a/core/java/android/net/MacAddress.java b/core/java/android/net/MacAddress.java
index 74d6470..058cb94 100644
--- a/core/java/android/net/MacAddress.java
+++ b/core/java/android/net/MacAddress.java
@@ -18,6 +18,8 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -26,6 +28,8 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.net.Inet6Address;
+import java.net.UnknownHostException;
 import java.security.SecureRandom;
 import java.util.Arrays;
 import java.util.Random;
@@ -50,6 +54,7 @@
      * The MacAddress zero MAC address.
      * @hide
      */
+    @UnsupportedAppUsage
     public static final MacAddress ALL_ZEROS_ADDRESS = new MacAddress(0);
 
     /** @hide */
@@ -391,4 +396,34 @@
         }
         return out;
     }
+
+    /**
+     * Create a link-local Inet6Address from the MAC address. The EUI-48 MAC address is converted
+     * to an EUI-64 MAC address per RFC 4291. The resulting EUI-64 is used to construct a link-local
+     * IPv6 address per RFC 4862.
+     *
+     * @return A link-local Inet6Address constructed from the MAC address.
+     * @hide
+     */
+    public @Nullable Inet6Address getLinkLocalIpv6FromEui48Mac() {
+        byte[] macEui48Bytes = toByteArray();
+        byte[] addr = new byte[16];
+
+        addr[0] = (byte) 0xfe;
+        addr[1] = (byte) 0x80;
+        addr[8] = (byte) (macEui48Bytes[0] ^ (byte) 0x02); // flip the link-local bit
+        addr[9] = macEui48Bytes[1];
+        addr[10] = macEui48Bytes[2];
+        addr[11] = (byte) 0xff;
+        addr[12] = (byte) 0xfe;
+        addr[13] = macEui48Bytes[3];
+        addr[14] = macEui48Bytes[4];
+        addr[15] = macEui48Bytes[5];
+
+        try {
+            return Inet6Address.getByAddress(null, addr, 0);
+        } catch (UnknownHostException e) {
+            return null;
+        }
+    }
 }
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
index d1cccac..bf2344d 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -16,11 +16,13 @@
 
 package android.net;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.OsConstants;
+import android.util.proto.ProtoOutputStream;
 
 import com.android.okhttp.internalandroidapi.Dns;
 import com.android.okhttp.internalandroidapi.HttpURLConnectionFactory;
@@ -58,6 +60,7 @@
     /**
      * @hide
      */
+    @UnsupportedAppUsage
     public final int netId;
 
     // Objects used to perform per-network operations such as getSocketFactory
@@ -78,6 +81,11 @@
             httpKeepAlive ? Integer.parseInt(System.getProperty("http.maxConnections", "5")) : 0;
     private static final long httpKeepAliveDurationMs =
             Long.parseLong(System.getProperty("http.keepAliveDuration", "300000"));  // 5 minutes.
+    // Value used to obfuscate network handle longs.
+    // The HANDLE_MAGIC value MUST be kept in sync with the corresponding
+    // value in the native/android/net.c NDK implementation.
+    private static final long HANDLE_MAGIC = 0xcafed00dL;
+    private static final int HANDLE_MAGIC_SIZE = 32;
 
     // A boolean to control how getAllByName()/getByName() behaves in the face
     // of Private DNS.
@@ -92,20 +100,29 @@
     // anytime and (b) receivers should be explicit about attempts to bypass
     // Private DNS so that the intent of the code is easily determined and
     // code search audits are possible.
-    private boolean mPrivateDnsBypass = false;
+    private final transient boolean mPrivateDnsBypass;
 
     /**
      * @hide
      */
+    @UnsupportedAppUsage
     public Network(int netId) {
+        this(netId, false);
+    }
+
+    /**
+     * @hide
+     */
+    public Network(int netId, boolean privateDnsBypass) {
         this.netId = netId;
+        this.mPrivateDnsBypass = privateDnsBypass;
     }
 
     /**
      * @hide
      */
     public Network(Network that) {
-        this.netId = that.netId;
+        this(that.netId, that.mPrivateDnsBypass);
     }
 
     /**
@@ -124,8 +141,7 @@
      * Operates the same as {@code InetAddress.getByName} except that host
      * resolution is done on this network.
      *
-     * @param host
-     *            the hostName to be resolved to an address or {@code null}.
+     * @param host the hostname to be resolved to an address or {@code null}.
      * @return the {@code InetAddress} instance representing the host.
      * @throws UnknownHostException
      *             if the address lookup fails.
@@ -135,14 +151,14 @@
     }
 
     /**
-     * Specify whether or not Private DNS should be bypassed when attempting
+     * Obtain a Network object for which Private DNS is to be bypassed when attempting
      * to use {@link #getAllByName(String)}/{@link #getByName(String)} methods on the given
      * instance for hostname resolution.
      *
      * @hide
      */
-    public void setPrivateDnsBypass(boolean bypass) {
-        mPrivateDnsBypass = bypass;
+    public Network getPrivateDnsBypassingCopy() {
+        return new Network(netId, true);
     }
 
     /**
@@ -394,6 +410,25 @@
     }
 
     /**
+     * Returns a {@link Network} object given a handle returned from {@link #getNetworkHandle}.
+     *
+     * @param networkHandle a handle returned from {@link #getNetworkHandle}.
+     * @return A {@link Network} object derived from {@code networkHandle}.
+     */
+    public static Network fromNetworkHandle(long networkHandle) {
+        if (networkHandle == 0) {
+            throw new IllegalArgumentException(
+                    "Network.fromNetworkHandle refusing to instantiate NETID_UNSET Network.");
+        }
+        if ((networkHandle & ((1L << HANDLE_MAGIC_SIZE) - 1)) != HANDLE_MAGIC
+                || networkHandle < 0) {
+            throw new IllegalArgumentException(
+                    "Value passed to fromNetworkHandle() is not a network handle.");
+        }
+        return new Network((int) (networkHandle >> HANDLE_MAGIC_SIZE));
+    }
+
+    /**
      * Returns a handle representing this {@code Network}, for use with the NDK API.
      */
     public long getNetworkHandle() {
@@ -415,14 +450,10 @@
         // At some future date it may be desirable to realign the handle with
         // Multiple Provisioning Domains API recommendations, as made by the
         // IETF mif working group.
-        //
-        // The handleMagic value MUST be kept in sync with the corresponding
-        // value in the native/android/net.c NDK implementation.
         if (netId == 0) {
             return 0L;  // make this zero condition obvious for debugging
         }
-        final long handleMagic = 0xcafed00dL;
-        return (((long) netId) << 32) | handleMagic;
+        return (((long) netId) << HANDLE_MAGIC_SIZE) | HANDLE_MAGIC;
     }
 
     // implement the Parcelable interface
@@ -462,4 +493,11 @@
     public String toString() {
         return Integer.toString(netId);
     }
+
+    /** @hide */
+    public void writeToProto(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+        proto.write(NetworkProto.NET_ID, netId);
+        proto.end(token);
+    }
 }
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index 52a2354..99bfc14 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -16,8 +16,10 @@
 
 package android.net;
 
+import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.net.ConnectivityManager.PacketKeepalive;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
@@ -351,6 +353,7 @@
     /**
      * Called by the bearer code when it has new NetworkInfo data.
      */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public void sendNetworkInfo(NetworkInfo networkInfo) {
         queueOrSendMessage(EVENT_NETWORK_INFO_CHANGED, new NetworkInfo(networkInfo));
     }
@@ -372,7 +375,7 @@
         if (score < 0) {
             throw new IllegalArgumentException("Score must be >= 0");
         }
-        queueOrSendMessage(EVENT_NETWORK_SCORE_CHANGED, new Integer(score));
+        queueOrSendMessage(EVENT_NETWORK_SCORE_CHANGED,  score, 0);
     }
 
     /**
@@ -387,7 +390,7 @@
      * {@link #saveAcceptUnvalidated} to respect the user's choice.
      */
     public void explicitlySelected(boolean acceptUnvalidated) {
-        queueOrSendMessage(EVENT_SET_EXPLICITLY_SELECTED, acceptUnvalidated);
+        queueOrSendMessage(EVENT_SET_EXPLICITLY_SELECTED, acceptUnvalidated ? 1 : 0, 0);
     }
 
     /**
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 83553df..1b44c92 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -17,12 +17,16 @@
 package android.net;
 
 import android.annotation.IntDef;
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
+import android.annotation.UnsupportedAppUsage;
 import android.net.ConnectivityManager.NetworkCallback;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.ArraySet;
+import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.BitUtils;
@@ -55,6 +59,7 @@
     /**
      * @hide
      */
+    @UnsupportedAppUsage
     public NetworkCapabilities() {
         clearAll();
         mNetworkCapabilities = DEFAULT_CAPABILITIES;
@@ -75,6 +80,7 @@
         mNetworkCapabilities = mTransportTypes = mUnwantedNetworkCapabilities = 0;
         mLinkUpBandwidthKbps = mLinkDownBandwidthKbps = LINK_BANDWIDTH_UNSPECIFIED;
         mNetworkSpecifier = null;
+        mTransportInfo = null;
         mSignalStrength = SIGNAL_STRENGTH_UNSPECIFIED;
         mUids = null;
         mEstablishingVpnAppUid = INVALID_UID;
@@ -91,6 +97,7 @@
         mLinkUpBandwidthKbps = nc.mLinkUpBandwidthKbps;
         mLinkDownBandwidthKbps = nc.mLinkDownBandwidthKbps;
         mNetworkSpecifier = nc.mNetworkSpecifier;
+        mTransportInfo = nc.mTransportInfo;
         mSignalStrength = nc.mSignalStrength;
         setUids(nc.mUids); // Will make the defensive copy
         mEstablishingVpnAppUid = nc.mEstablishingVpnAppUid;
@@ -102,6 +109,7 @@
      * Represents the network's capabilities.  If any are specified they will be satisfied
      * by any Network that matches all of them.
      */
+    @UnsupportedAppUsage
     private long mNetworkCapabilities;
 
     /**
@@ -264,9 +272,8 @@
     /**
      * Indicates that this network is not congested.
      * <p>
-     * When a network is congested, the device should defer network traffic that
-     * can be done at a later time without breaking developer contracts.
-     * @hide
+     * When a network is congested, applications should defer network traffic
+     * that can be done at a later time, such as uploading analytics.
      */
     public static final int NET_CAPABILITY_NOT_CONGESTED = 20;
 
@@ -371,6 +378,7 @@
      * @return This NetworkCapabilities instance, to facilitate chaining.
      * @hide
      */
+    @UnsupportedAppUsage
     public NetworkCapabilities addCapability(@NetCapability int capability) {
         checkValidCapability(capability);
         mNetworkCapabilities |= 1 << capability;
@@ -407,6 +415,7 @@
      * @return This NetworkCapabilities instance, to facilitate chaining.
      * @hide
      */
+    @UnsupportedAppUsage
     public NetworkCapabilities removeCapability(@NetCapability int capability) {
         checkValidCapability(capability);
         final long mask = ~(1 << capability);
@@ -659,6 +668,7 @@
      * @return This NetworkCapabilities instance, to facilitate chaining.
      * @hide
      */
+    @UnsupportedAppUsage
     public NetworkCapabilities addTransportType(@Transport int transportType) {
         checkValidTransportType(transportType);
         mTransportTypes |= 1 << transportType;
@@ -867,6 +877,7 @@
     }
 
     private NetworkSpecifier mNetworkSpecifier = null;
+    private TransportInfo mTransportInfo = null;
 
     /**
      * Sets the optional bearer specific network specifier.
@@ -892,16 +903,43 @@
     }
 
     /**
+     * Sets the optional transport specific information.
+     *
+     * @param transportInfo A concrete, parcelable framework class that extends
+     * {@link TransportInfo}.
+     * @return This NetworkCapabilities instance, to facilitate chaining.
+     * @hide
+     */
+    public NetworkCapabilities setTransportInfo(TransportInfo transportInfo) {
+        mTransportInfo = transportInfo;
+        return this;
+    }
+
+    /**
      * Gets the optional bearer specific network specifier.
      *
      * @return The optional {@link NetworkSpecifier} specifying the bearer specific network
      *         specifier. See {@link #setNetworkSpecifier}.
      * @hide
      */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public NetworkSpecifier getNetworkSpecifier() {
         return mNetworkSpecifier;
     }
 
+    /**
+     * Returns a transport-specific information container. The application may cast this
+     * container to a concrete sub-class based on its knowledge of the network request. The
+     * application should be able to deal with a {@code null} return value or an invalid case,
+     * e.g. use {@code instanceof} operator to verify expected type.
+     *
+     * @return A concrete implementation of the {@link TransportInfo} class or null if not
+     * available for the network.
+     */
+    @Nullable public TransportInfo getTransportInfo() {
+        return mTransportInfo;
+    }
+
     private void combineSpecifiers(NetworkCapabilities nc) {
         if (mNetworkSpecifier != null && !mNetworkSpecifier.equals(nc.mNetworkSpecifier)) {
             throw new IllegalStateException("Can't combine two networkSpecifiers");
@@ -918,6 +956,17 @@
         return Objects.equals(mNetworkSpecifier, nc.mNetworkSpecifier);
     }
 
+    private void combineTransportInfos(NetworkCapabilities nc) {
+        if (mTransportInfo != null && !mTransportInfo.equals(nc.mTransportInfo)) {
+            throw new IllegalStateException("Can't combine two TransportInfos");
+        }
+        setTransportInfo(nc.mTransportInfo);
+    }
+
+    private boolean equalsTransportInfo(NetworkCapabilities nc) {
+        return Objects.equals(mTransportInfo, nc.mTransportInfo);
+    }
+
     /**
      * Magic value that indicates no signal strength provided. A request specifying this value is
      * always satisfied.
@@ -930,6 +979,7 @@
      * Signal strength. This is a signed integer, and higher values indicate better signal.
      * The exact units are bearer-dependent. For example, Wi-Fi uses RSSI.
      */
+    @UnsupportedAppUsage
     private int mSignalStrength = SIGNAL_STRENGTH_UNSPECIFIED;
 
     /**
@@ -945,6 +995,7 @@
      * @param signalStrength the bearer-specific signal strength.
      * @hide
      */
+    @UnsupportedAppUsage
     public NetworkCapabilities setSignalStrength(int signalStrength) {
         mSignalStrength = signalStrength;
         return this;
@@ -955,6 +1006,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean hasSignalStrength() {
         return mSignalStrength > SIGNAL_STRENGTH_UNSPECIFIED;
     }
@@ -965,6 +1017,7 @@
      * @return The bearer-specific signal strength.
      * @hide
      */
+    @SystemApi
     public int getSignalStrength() {
         return mSignalStrength;
     }
@@ -1226,6 +1279,7 @@
         combineTransportTypes(nc);
         combineLinkBandwidths(nc);
         combineSpecifiers(nc);
+        combineTransportInfos(nc);
         combineSignalStrength(nc);
         combineUids(nc);
         combineSSIDs(nc);
@@ -1289,9 +1343,7 @@
 
         // Ignore NOT_METERED being added or removed as it is effectively dynamic. http://b/63326103
         // TODO: properly support NOT_METERED as a mutable and requestable capability.
-        // Ignore DUN being added or removed. http://b/65257223.
-        final long mask = ~MUTABLE_CAPABILITIES
-                & ~(1 << NET_CAPABILITY_NOT_METERED) & ~(1 << NET_CAPABILITY_DUN);
+        final long mask = ~MUTABLE_CAPABILITIES & ~(1 << NET_CAPABILITY_NOT_METERED);
         long oldImmutableCapabilities = this.mNetworkCapabilities & mask;
         long newImmutableCapabilities = that.mNetworkCapabilities & mask;
         if (oldImmutableCapabilities != newImmutableCapabilities) {
@@ -1337,6 +1389,7 @@
                 && equalsLinkBandwidths(that)
                 && equalsSignalStrength(that)
                 && equalsSpecifier(that)
+                && equalsTransportInfo(that)
                 && equalsUids(that)
                 && equalsSSID(that));
     }
@@ -1354,7 +1407,8 @@
                 + Objects.hashCode(mNetworkSpecifier) * 23
                 + (mSignalStrength * 29)
                 + Objects.hashCode(mUids) * 31
-                + Objects.hashCode(mSSID) * 37;
+                + Objects.hashCode(mSSID) * 37
+                + Objects.hashCode(mTransportInfo) * 41;
     }
 
     @Override
@@ -1369,6 +1423,7 @@
         dest.writeInt(mLinkUpBandwidthKbps);
         dest.writeInt(mLinkDownBandwidthKbps);
         dest.writeParcelable((Parcelable) mNetworkSpecifier, flags);
+        dest.writeParcelable((Parcelable) mTransportInfo, flags);
         dest.writeInt(mSignalStrength);
         dest.writeArraySet(mUids);
         dest.writeString(mSSID);
@@ -1386,6 +1441,7 @@
                 netCap.mLinkUpBandwidthKbps = in.readInt();
                 netCap.mLinkDownBandwidthKbps = in.readInt();
                 netCap.mNetworkSpecifier = in.readParcelable(null);
+                netCap.mTransportInfo = in.readParcelable(null);
                 netCap.mSignalStrength = in.readInt();
                 netCap.mUids = (ArraySet<UidRange>) in.readArraySet(
                         null /* ClassLoader, null for default */);
@@ -1411,7 +1467,7 @@
             appendStringRepresentationOfBitMaskToStringBuilder(sb, mNetworkCapabilities,
                     NetworkCapabilities::capabilityNameOf, "&");
         }
-        if (0 != mNetworkCapabilities) {
+        if (0 != mUnwantedNetworkCapabilities) {
             sb.append(" Unwanted: ");
             appendStringRepresentationOfBitMaskToStringBuilder(sb, mUnwantedNetworkCapabilities,
                     NetworkCapabilities::capabilityNameOf, "&");
@@ -1425,6 +1481,9 @@
         if (mNetworkSpecifier != null) {
             sb.append(" Specifier: <").append(mNetworkSpecifier).append(">");
         }
+        if (mTransportInfo != null) {
+            sb.append(" TransportInfo: <").append(mTransportInfo).append(">");
+        }
         if (hasSignalStrength()) {
             sb.append(" SignalStrength: ").append(mSignalStrength);
         }
@@ -1473,6 +1532,34 @@
         }
     }
 
+    /** @hide */
+    public void writeToProto(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+
+        for (int transport : getTransportTypes()) {
+            proto.write(NetworkCapabilitiesProto.TRANSPORTS, transport);
+        }
+
+        for (int capability : getCapabilities()) {
+            proto.write(NetworkCapabilitiesProto.CAPABILITIES, capability);
+        }
+
+        proto.write(NetworkCapabilitiesProto.LINK_UP_BANDWIDTH_KBPS, mLinkUpBandwidthKbps);
+        proto.write(NetworkCapabilitiesProto.LINK_DOWN_BANDWIDTH_KBPS, mLinkDownBandwidthKbps);
+
+        if (mNetworkSpecifier != null) {
+            proto.write(NetworkCapabilitiesProto.NETWORK_SPECIFIER, mNetworkSpecifier.toString());
+        }
+        if (mTransportInfo != null) {
+            // TODO b/120653863: write transport-specific info to proto?
+        }
+
+        proto.write(NetworkCapabilitiesProto.CAN_REPORT_SIGNAL_STRENGTH, hasSignalStrength());
+        proto.write(NetworkCapabilitiesProto.SIGNAL_STRENGTH, mSignalStrength);
+
+        proto.end(token);
+    }
+
     /**
      * @hide
      */
@@ -1521,6 +1608,7 @@
     /**
      * @hide
      */
+    @UnsupportedAppUsage
     public static String transportNamesOf(@Transport int[] types) {
         StringJoiner joiner = new StringJoiner("|");
         if (types != null) {
@@ -1554,4 +1642,14 @@
         Preconditions.checkArgument(isValidCapability(capability),
                 "NetworkCapability " + capability + "out of range");
     }
+
+    /**
+     * Check if this {@code NetworkCapability} instance is metered.
+     *
+     * @return {@code true} if {@code NET_CAPABILITY_NOT_METERED} is not set on this instance.
+     * @hide
+     */
+    public boolean isMetered() {
+        return !hasCapability(NET_CAPABILITY_NOT_METERED);
+    }
 }
diff --git a/core/java/android/net/NetworkInfo.java b/core/java/android/net/NetworkInfo.java
index 999771a..89d9961 100644
--- a/core/java/android/net/NetworkInfo.java
+++ b/core/java/android/net/NetworkInfo.java
@@ -16,6 +16,7 @@
 
 package android.net;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -27,7 +28,20 @@
  * Describes the status of a network interface.
  * <p>Use {@link ConnectivityManager#getActiveNetworkInfo()} to get an instance that represents
  * the current network connection.
+ *
+ * @deprecated Callers should instead use the {@link ConnectivityManager.NetworkCallback} API to
+ *             learn about connectivity changes, or switch to use
+ *             {@link ConnectivityManager#getNetworkCapabilities} or
+ *             {@link ConnectivityManager#getLinkProperties} to get information synchronously. Keep
+ *             in mind that while callbacks are guaranteed to be called for every event in order,
+ *             synchronous calls have no such constraints, and as such it is unadvisable to use the
+ *             synchronous methods inside the callbacks as they will often not offer a view of
+ *             networking that is consistent (that is: they may return a past or a future state with
+ *             respect to the event being processed by the callback). Instead, callers are advised
+ *             to only use the arguments of the callbacks, possibly memorizing the specific bits of
+ *             information they need to keep from one callback to another.
  */
+@Deprecated
 public class NetworkInfo implements Parcelable {
 
     /**
@@ -51,7 +65,10 @@
      * <tr><td><code>FAILED</code></td><td><code>DISCONNECTED</code></td></tr>
      * <tr><td><code>BLOCKED</code></td><td><code>DISCONNECTED</code></td></tr>
      * </table>
+     *
+     * @deprecated See {@link NetworkInfo}.
      */
+    @Deprecated
     public enum State {
         CONNECTING, CONNECTED, SUSPENDED, DISCONNECTING, DISCONNECTED, UNKNOWN
     }
@@ -60,7 +77,10 @@
      * The fine-grained state of a network connection. This level of detail
      * is probably of interest to few applications. Most should use
      * {@link android.net.NetworkInfo.State State} instead.
+     *
+     * @deprecated See {@link NetworkInfo}.
      */
+    @Deprecated
     public enum DetailedState {
         /** Ready to start data connection setup. */
         IDLE,
@@ -129,6 +149,7 @@
     /**
      * @hide
      */
+    @UnsupportedAppUsage
     public NetworkInfo(int type, int subtype, String typeName, String subtypeName) {
         if (!ConnectivityManager.isNetworkTypeValid(type)
                 && type != ConnectivityManager.TYPE_NONE) {
@@ -143,6 +164,7 @@
     }
 
     /** {@hide} */
+    @UnsupportedAppUsage
     public NetworkInfo(NetworkInfo source) {
         if (source != null) {
             synchronized (source) {
@@ -199,7 +221,9 @@
      * Return a network-type-specific integer describing the subtype
      * of the network.
      * @return the network subtype
+     * @deprecated Use {@link android.telephony.TelephonyManager#getDataNetworkType} instead.
      */
+    @Deprecated
     public int getSubtype() {
         synchronized (this) {
             return mSubtype;
@@ -209,6 +233,7 @@
     /**
      * @hide
      */
+    @UnsupportedAppUsage
     public void setSubtype(int subtype, String subtypeName) {
         synchronized (this) {
             mSubtype = subtype;
@@ -239,7 +264,9 @@
     /**
      * Return a human-readable name describing the subtype of the network.
      * @return the name of the network subtype
+     * @deprecated Use {@link android.telephony.TelephonyManager#getDataNetworkType} instead.
      */
+    @Deprecated
     public String getSubtypeName() {
         synchronized (this) {
             return mSubtypeName;
@@ -274,7 +301,15 @@
      * connections and pass data.
      * <p>Always call this before attempting to perform data transactions.
      * @return {@code true} if network connectivity exists, {@code false} otherwise.
+     * @deprecated Apps should instead use the
+     *             {@link android.net.ConnectivityManager.NetworkCallback} API to
+     *             learn about connectivity changes. See
+     *             {@link ConnectivityManager#registerDefaultNetworkCallback} and
+     *             {@link ConnectivityManager#registerNetworkCallback}. These will
+     *             give a more accurate picture of the connectivity state of
+     *             the device and let apps react more easily and quickly to changes.
      */
+    @Deprecated
     public boolean isConnected() {
         synchronized (this) {
             return mState == State.CONNECTED;
@@ -317,6 +352,7 @@
      * @hide
      */
     @Deprecated
+    @UnsupportedAppUsage
     public void setIsAvailable(boolean isAvailable) {
         synchronized (this) {
             mIsAvailable = isAvailable;
@@ -347,6 +383,7 @@
      * @hide
      */
     @Deprecated
+    @UnsupportedAppUsage
     public void setFailover(boolean isFailover) {
         synchronized (this) {
             mIsFailover = isFailover;
@@ -377,6 +414,7 @@
      */
     @VisibleForTesting
     @Deprecated
+    @UnsupportedAppUsage
     public void setRoaming(boolean isRoaming) {
         synchronized (this) {
             mIsRoaming = isRoaming;
@@ -404,7 +442,15 @@
     /**
      * Reports the current fine-grained state of the network.
      * @return the fine-grained state
+     * @deprecated Apps should instead use the
+     *             {@link android.net.ConnectivityManager.NetworkCallback} API to
+     *             learn about connectivity changes. See
+     *             {@link ConnectivityManager#registerDefaultNetworkCallback} and
+     *             {@link ConnectivityManager#registerNetworkCallback}. These will
+     *             give a more accurate picture of the connectivity state of
+     *             the device and let apps react more easily and quickly to changes.
      */
+    @Deprecated
     public DetailedState getDetailedState() {
         synchronized (this) {
             return mDetailedState;
@@ -422,6 +468,7 @@
      * @hide
      */
     @Deprecated
+    @UnsupportedAppUsage
     public void setDetailedState(DetailedState detailedState, String reason, String extraInfo) {
         synchronized (this) {
             this.mDetailedState = detailedState;
@@ -435,8 +482,10 @@
      * Set the extraInfo field.
      * @param extraInfo an optional {@code String} providing addditional network state
      * information passed up from the lower networking layers.
+     * @deprecated See {@link NetworkInfo#getExtraInfo}.
      * @hide
      */
+    @Deprecated
     public void setExtraInfo(String extraInfo) {
         synchronized (this) {
             this.mExtraInfo = extraInfo;
@@ -460,7 +509,10 @@
      * Report the extra information about the network state, if any was
      * provided by the lower networking layers.
      * @return the extra information, or null if not available
+     * @deprecated Use other services e.g. WifiManager to get additional information passed up from
+     *             the lower networking layers.
      */
+    @Deprecated
     public String getExtraInfo() {
         synchronized (this) {
             return mExtraInfo;
diff --git a/core/java/android/net/NetworkMisc.java b/core/java/android/net/NetworkMisc.java
index 69f50a2..daa2640 100644
--- a/core/java/android/net/NetworkMisc.java
+++ b/core/java/android/net/NetworkMisc.java
@@ -65,6 +65,12 @@
      */
     public String subscriberId;
 
+    /**
+     * Set to skip 464xlat. This means the device will treat the network as IPv6-only and
+     * will not attempt to detect a NAT64 via RFC 7050 DNS lookups.
+     */
+    public boolean skip464xlat;
+
     public NetworkMisc() {
     }
 
@@ -75,6 +81,7 @@
             acceptUnvalidated = nm.acceptUnvalidated;
             subscriberId = nm.subscriberId;
             provisioningNotificationDisabled = nm.provisioningNotificationDisabled;
+            skip464xlat = nm.skip464xlat;
         }
     }
 
@@ -90,6 +97,7 @@
         out.writeInt(acceptUnvalidated ? 1 : 0);
         out.writeString(subscriberId);
         out.writeInt(provisioningNotificationDisabled ? 1 : 0);
+        out.writeInt(skip464xlat ? 1 : 0);
     }
 
     public static final Creator<NetworkMisc> CREATOR = new Creator<NetworkMisc>() {
@@ -101,6 +109,7 @@
             networkMisc.acceptUnvalidated = in.readInt() != 0;
             networkMisc.subscriberId = in.readString();
             networkMisc.provisioningNotificationDisabled = in.readInt() != 0;
+            networkMisc.skip464xlat = in.readInt() != 0;
             return networkMisc;
         }
 
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index f3669fb..9508217 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -17,12 +17,16 @@
 package android.net;
 
 import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.annotation.UnsupportedAppUsage;
 import android.net.NetworkCapabilities.NetCapability;
 import android.net.NetworkCapabilities.Transport;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.Process;
 import android.text.TextUtils;
+import android.util.proto.ProtoOutputStream;
 
 import java.util.Objects;
 import java.util.Set;
@@ -37,6 +41,7 @@
      * The {@link NetworkCapabilities} that define this request.
      * @hide
      */
+    @UnsupportedAppUsage
     public final @NonNull NetworkCapabilities networkCapabilities;
 
     /**
@@ -45,6 +50,7 @@
      * the request.
      * @hide
      */
+    @UnsupportedAppUsage
     public final int requestId;
 
     /**
@@ -52,6 +58,7 @@
      * Causes CONNECTIVITY_ACTION broadcasts to be sent.
      * @hide
      */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public final int legacyType;
 
     /**
@@ -240,6 +247,7 @@
          * @return The builder to facilitate chaining.
          * @hide
          */
+        @UnsupportedAppUsage
         public Builder clearCapabilities() {
             mNetworkCapabilities.clearAll();
             return this;
@@ -338,6 +346,7 @@
          * @param signalStrength the bearer-specific signal strength.
          * @hide
          */
+        @SystemApi
         public Builder setSignalStrength(int signalStrength) {
             mNetworkCapabilities.setSignalStrength(signalStrength);
             return this;
@@ -453,6 +462,35 @@
                 ", " + networkCapabilities.toString() + " ]";
     }
 
+    private int typeToProtoEnum(Type t) {
+        switch (t) {
+            case NONE:
+                return NetworkRequestProto.TYPE_NONE;
+            case LISTEN:
+                return NetworkRequestProto.TYPE_LISTEN;
+            case TRACK_DEFAULT:
+                return NetworkRequestProto.TYPE_TRACK_DEFAULT;
+            case REQUEST:
+                return NetworkRequestProto.TYPE_REQUEST;
+            case BACKGROUND_REQUEST:
+                return NetworkRequestProto.TYPE_BACKGROUND_REQUEST;
+            default:
+                return NetworkRequestProto.TYPE_UNKNOWN;
+        }
+    }
+
+    /** @hide */
+    public void writeToProto(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+
+        proto.write(NetworkRequestProto.TYPE, typeToProtoEnum(type));
+        proto.write(NetworkRequestProto.REQUEST_ID, requestId);
+        proto.write(NetworkRequestProto.LEGACY_TYPE, legacyType);
+        networkCapabilities.writeToProto(proto, NetworkRequestProto.NETWORK_CAPABILITIES);
+
+        proto.end(token);
+    }
+
     public boolean equals(Object obj) {
         if (obj instanceof NetworkRequest == false) return false;
         NetworkRequest that = (NetworkRequest)obj;
diff --git a/core/java/android/net/NetworkState.java b/core/java/android/net/NetworkState.java
index b00cb48..97fb3fb 100644
--- a/core/java/android/net/NetworkState.java
+++ b/core/java/android/net/NetworkState.java
@@ -16,6 +16,8 @@
 
 package android.net;
 
+import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Slog;
@@ -26,11 +28,14 @@
  * @hide
  */
 public class NetworkState implements Parcelable {
+    private static final boolean SANITY_CHECK_ROAMING = false;
+
     public static final NetworkState EMPTY = new NetworkState(null, null, null, null, null, null);
 
     public final NetworkInfo networkInfo;
     public final LinkProperties linkProperties;
     public final NetworkCapabilities networkCapabilities;
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public final Network network;
     public final String subscriberId;
     public final String networkId;
@@ -47,7 +52,7 @@
 
         // This object is an atomic view of a network, so the various components
         // should always agree on roaming state.
-        if (networkInfo != null && networkCapabilities != null) {
+        if (SANITY_CHECK_ROAMING && networkInfo != null && networkCapabilities != null) {
             if (networkInfo.isRoaming() == networkCapabilities
                     .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING)) {
                 Slog.wtf("NetworkState", "Roaming state disagreement between " + networkInfo
@@ -56,6 +61,7 @@
         }
     }
 
+    @UnsupportedAppUsage
     public NetworkState(Parcel in) {
         networkInfo = in.readParcelable(null);
         linkProperties = in.readParcelable(null);
@@ -80,6 +86,7 @@
         out.writeString(networkId);
     }
 
+    @UnsupportedAppUsage
     public static final Creator<NetworkState> CREATOR = new Creator<NetworkState>() {
         @Override
         public NetworkState createFromParcel(Parcel in) {
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index 9a5d502..c0aa4a6 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -16,11 +16,14 @@
 
 package android.net;
 
+import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
 import android.os.Parcel;
 import android.util.Log;
 import android.util.Pair;
 
 import java.io.FileDescriptor;
+import java.io.IOException;
 import java.math.BigInteger;
 import java.net.Inet4Address;
 import java.net.Inet6Address;
@@ -43,6 +46,7 @@
     /**
      * Attaches a socket filter that accepts DHCP packets to the given socket.
      */
+    @UnsupportedAppUsage
     public native static void attachDhcpFilter(FileDescriptor fd) throws SocketException;
 
     /**
@@ -50,6 +54,7 @@
      * @param fd the socket's {@link FileDescriptor}.
      * @param packetType the hardware address type, one of ARPHRD_*.
      */
+    @UnsupportedAppUsage
     public native static void attachRaFilter(FileDescriptor fd, int packetType) throws SocketException;
 
     /**
@@ -60,6 +65,7 @@
      * @param fd the socket's {@link FileDescriptor}.
      * @param packetType the hardware address type, one of ARPHRD_*.
      */
+    @UnsupportedAppUsage
     public native static void attachControlPacketFilter(FileDescriptor fd, int packetType)
             throws SocketException;
 
@@ -108,6 +114,7 @@
      * this socket will go directly to the underlying network, so its traffic will not be
      * forwarded through the VPN.
      */
+    @UnsupportedAppUsage
     public static boolean protectFromVpn(FileDescriptor fd) {
         return protectFromVpn(fd.getInt$());
     }
@@ -126,51 +133,135 @@
     public native static boolean queryUserAccess(int uid, int netId);
 
     /**
-     * Convert a IPv4 address from an integer to an InetAddress.
-     * @param hostAddress an int corresponding to the IPv4 address in network byte order
+     * Add an entry into the ARP cache.
      */
+    public static void addArpEntry(Inet4Address ipv4Addr, MacAddress ethAddr, String ifname,
+            FileDescriptor fd) throws IOException {
+        addArpEntry(ethAddr.toByteArray(), ipv4Addr.getAddress(), ifname, fd);
+    }
+
+    private static native void addArpEntry(byte[] ethAddr, byte[] netAddr, String ifname,
+            FileDescriptor fd) throws IOException;
+
+    /**
+     * @see #intToInet4AddressHTL(int)
+     * @deprecated Use either {@link #intToInet4AddressHTH(int)}
+     *             or {@link #intToInet4AddressHTL(int)}
+     */
+    @Deprecated
+    @UnsupportedAppUsage
     public static InetAddress intToInetAddress(int hostAddress) {
-        byte[] addressBytes = { (byte)(0xff & hostAddress),
-                                (byte)(0xff & (hostAddress >> 8)),
-                                (byte)(0xff & (hostAddress >> 16)),
-                                (byte)(0xff & (hostAddress >> 24)) };
+        return intToInet4AddressHTL(hostAddress);
+    }
+
+    /**
+     * Convert a IPv4 address from an integer to an InetAddress (0x04030201 -> 1.2.3.4)
+     *
+     * <p>This method uses the higher-order int bytes as the lower-order IPv4 address bytes,
+     * which is an unusual convention. Consider {@link #intToInet4AddressHTH(int)} instead.
+     * @param hostAddress an int coding for an IPv4 address, where higher-order int byte is
+     *                    lower-order IPv4 address byte
+     */
+    public static Inet4Address intToInet4AddressHTL(int hostAddress) {
+        return intToInet4AddressHTH(Integer.reverseBytes(hostAddress));
+    }
+
+    /**
+     * Convert a IPv4 address from an integer to an InetAddress (0x01020304 -> 1.2.3.4)
+     * @param hostAddress an int coding for an IPv4 address
+     */
+    public static Inet4Address intToInet4AddressHTH(int hostAddress) {
+        byte[] addressBytes = { (byte) (0xff & (hostAddress >> 24)),
+                (byte) (0xff & (hostAddress >> 16)),
+                (byte) (0xff & (hostAddress >> 8)),
+                (byte) (0xff & hostAddress) };
 
         try {
-           return InetAddress.getByAddress(addressBytes);
+            return (Inet4Address) InetAddress.getByAddress(addressBytes);
         } catch (UnknownHostException e) {
-           throw new AssertionError();
+            throw new AssertionError();
         }
     }
 
     /**
-     * Convert a IPv4 address from an InetAddress to an integer
-     * @param inetAddr is an InetAddress corresponding to the IPv4 address
-     * @return the IP address as an integer in network byte order
+     * @see #inet4AddressToIntHTL(Inet4Address)
+     * @deprecated Use either {@link #inet4AddressToIntHTH(Inet4Address)}
+     *             or {@link #inet4AddressToIntHTL(Inet4Address)}
      */
+    @Deprecated
     public static int inetAddressToInt(Inet4Address inetAddr)
             throws IllegalArgumentException {
-        byte [] addr = inetAddr.getAddress();
-        return ((addr[3] & 0xff) << 24) | ((addr[2] & 0xff) << 16) |
-                ((addr[1] & 0xff) << 8) | (addr[0] & 0xff);
+        return inet4AddressToIntHTL(inetAddr);
     }
 
     /**
-     * Convert a network prefix length to an IPv4 netmask integer
-     * @param prefixLength
-     * @return the IPv4 netmask as an integer in network byte order
+     * Convert an IPv4 address from an InetAddress to an integer (1.2.3.4 -> 0x01020304)
+     *
+     * <p>This conversion can help order IP addresses: considering the ordering
+     * 192.0.2.1 < 192.0.2.2 < ..., resulting ints will follow that ordering if read as unsigned
+     * integers with {@link Integer#toUnsignedLong}.
+     * @param inetAddr is an InetAddress corresponding to the IPv4 address
+     * @return the IP address as integer
      */
+    public static int inet4AddressToIntHTH(Inet4Address inetAddr)
+            throws IllegalArgumentException {
+        byte [] addr = inetAddr.getAddress();
+        return ((addr[0] & 0xff) << 24) | ((addr[1] & 0xff) << 16)
+                | ((addr[2] & 0xff) << 8) | (addr[3] & 0xff);
+    }
+
+    /**
+     * Convert a IPv4 address from an InetAddress to an integer (1.2.3.4 -> 0x04030201)
+     *
+     * <p>This method stores the higher-order IPv4 address bytes in the lower-order int bytes,
+     * which is an unusual convention. Consider {@link #inet4AddressToIntHTH(Inet4Address)} instead.
+     * @param inetAddr is an InetAddress corresponding to the IPv4 address
+     * @return the IP address as integer
+     */
+    public static int inet4AddressToIntHTL(Inet4Address inetAddr) {
+        return Integer.reverseBytes(inet4AddressToIntHTH(inetAddr));
+    }
+
+    /**
+     * @see #prefixLengthToV4NetmaskIntHTL(int)
+     * @deprecated Use either {@link #prefixLengthToV4NetmaskIntHTH(int)}
+     *             or {@link #prefixLengthToV4NetmaskIntHTL(int)}
+     */
+    @Deprecated
+    @UnsupportedAppUsage
     public static int prefixLengthToNetmaskInt(int prefixLength)
             throws IllegalArgumentException {
+        return prefixLengthToV4NetmaskIntHTL(prefixLength);
+    }
+
+    /**
+     * Convert a network prefix length to an IPv4 netmask integer (prefixLength 17 -> 0xffff8000)
+     * @return the IPv4 netmask as an integer
+     */
+    public static int prefixLengthToV4NetmaskIntHTH(int prefixLength)
+            throws IllegalArgumentException {
         if (prefixLength < 0 || prefixLength > 32) {
             throw new IllegalArgumentException("Invalid prefix length (0 <= prefix <= 32)");
         }
-        int value = 0xffffffff << (32 - prefixLength);
-        return Integer.reverseBytes(value);
+        // (int)a << b is equivalent to a << (b & 0x1f): can't shift by 32 (-1 << 32 == -1)
+        return prefixLength == 0 ? 0 : 0xffffffff << (32 - prefixLength);
+    }
+
+    /**
+     * Convert a network prefix length to an IPv4 netmask integer (prefixLength 17 -> 0x0080ffff).
+     *
+     * <p>This method stores the higher-order IPv4 address bytes in the lower-order int bytes,
+     * which is an unusual convention. Consider {@link #prefixLengthToV4NetmaskIntHTH(int)} instead.
+     * @return the IPv4 netmask as an integer
+     */
+    public static int prefixLengthToV4NetmaskIntHTL(int prefixLength)
+            throws IllegalArgumentException {
+        return Integer.reverseBytes(prefixLengthToV4NetmaskIntHTH(prefixLength));
     }
 
     /**
      * Convert a IPv4 netmask integer to a prefix length
-     * @param netmask as an integer in network byte order
+     * @param netmask as an integer (0xff000000 for a /8 subnet)
      * @return the network prefix length
      */
     public static int netmaskIntToPrefixLength(int netmask) {
@@ -184,6 +275,7 @@
      * @throws IllegalArgumentException the specified netmask was not contiguous.
      * @hide
      */
+    @UnsupportedAppUsage
     public static int netmaskToPrefixLength(Inet4Address netmask) {
         // inetAddressToInt returns an int in *network* byte order.
         int i = Integer.reverseBytes(inetAddressToInt(netmask));
@@ -203,7 +295,10 @@
      * @param addrString
      * @return the InetAddress
      * @hide
+     * @deprecated Use {@link InetAddresses#parseNumericAddress(String)}, if possible.
      */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    @Deprecated
     public static InetAddress numericToInetAddress(String addrString)
             throws IllegalArgumentException {
         return InetAddress.parseNumericAddress(addrString);
@@ -278,6 +373,7 @@
     /**
      * Returns the implicit netmask of an IPv4 address, as was the custom before 1993.
      */
+    @UnsupportedAppUsage
     public static int getImplicitNetmask(Inet4Address address) {
         int firstByte = address.getAddress()[0] & 0xff;  // Convert to an unsigned value.
         if (firstByte < 128) {
@@ -316,6 +412,28 @@
     }
 
     /**
+     * Get a prefix mask as Inet4Address for a given prefix length.
+     *
+     * <p>For example 20 -> 255.255.240.0
+     */
+    public static Inet4Address getPrefixMaskAsInet4Address(int prefixLength)
+            throws IllegalArgumentException {
+        return intToInet4AddressHTH(prefixLengthToV4NetmaskIntHTH(prefixLength));
+    }
+
+    /**
+     * Get the broadcast address for a given prefix.
+     *
+     * <p>For example 192.168.0.1/24 -> 192.168.0.255
+     */
+    public static Inet4Address getBroadcastAddress(Inet4Address addr, int prefixLength)
+            throws IllegalArgumentException {
+        final int intBroadcastAddr = inet4AddressToIntHTH(addr)
+                | ~prefixLengthToV4NetmaskIntHTH(prefixLength);
+        return intToInet4AddressHTH(intBroadcastAddr);
+    }
+
+    /**
      * Check if IP address type is consistent between two InetAddress.
      * @return true if both are the same type.  False otherwise.
      */
@@ -368,6 +486,7 @@
      * @param addr a string representing an ip addr
      * @return a string propertly trimmed
      */
+    @UnsupportedAppUsage
     public static String trimV4AddrZeros(String addr) {
         if (addr == null) return null;
         String[] octets = addr.split("\\.");
diff --git a/core/java/android/net/ProxyInfo.java b/core/java/android/net/ProxyInfo.java
index 5f5e623..e926fda 100644
--- a/core/java/android/net/ProxyInfo.java
+++ b/core/java/android/net/ProxyInfo.java
@@ -17,6 +17,7 @@
 package android.net;
 
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
@@ -91,6 +92,7 @@
      * Create a ProxyProperties that points at a HTTP Proxy.
      * @hide
      */
+    @UnsupportedAppUsage
     public ProxyInfo(String host, int port, String exclList) {
         mHost = host;
         mPort = port;
diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java
index 90a2460..37ab9ff 100644
--- a/core/java/android/net/RouteInfo.java
+++ b/core/java/android/net/RouteInfo.java
@@ -16,6 +16,8 @@
 
 package android.net;
 
+import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -55,6 +57,7 @@
     /**
      * The gateway address for this route.
      */
+    @UnsupportedAppUsage
     private final InetAddress mGateway;
 
     /**
@@ -79,6 +82,7 @@
 
     // Derived data members.
     // TODO: remove these.
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private final boolean mIsHost;
     private final boolean mHasGateway;
 
@@ -160,6 +164,7 @@
     /**
      *  @hide
      */
+    @UnsupportedAppUsage
     public RouteInfo(IpPrefix destination, InetAddress gateway, String iface) {
         this(destination, gateway, iface, RTN_UNICAST);
     }
@@ -167,6 +172,7 @@
     /**
      * @hide
      */
+    @UnsupportedAppUsage
     public RouteInfo(LinkAddress destination, InetAddress gateway, String iface) {
         this(destination == null ? null :
                 new IpPrefix(destination.getAddress(), destination.getPrefixLength()),
@@ -197,6 +203,7 @@
      *
      * TODO: Remove this.
      */
+    @UnsupportedAppUsage
     public RouteInfo(LinkAddress destination, InetAddress gateway) {
         this(destination, gateway, null);
     }
@@ -208,6 +215,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public RouteInfo(InetAddress gateway) {
         this((IpPrefix) null, gateway, null);
     }
@@ -258,6 +266,7 @@
         }
     }
 
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private boolean isHost() {
         return (mDestination.getAddress() instanceof Inet4Address &&
                 mDestination.getPrefixLength() == 32) ||
@@ -355,6 +364,7 @@
      * @return {@code true} if a gateway is specified
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean hasGateway() {
         return mHasGateway;
     }
@@ -379,6 +389,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public static RouteInfo selectBestRoute(Collection<RouteInfo> routes, InetAddress dest) {
         if ((routes == null) || (dest == null)) return null;
 
diff --git a/core/java/android/net/StaticIpConfiguration.java b/core/java/android/net/StaticIpConfiguration.java
index 58b1b88..3aa56b9 100644
--- a/core/java/android/net/StaticIpConfiguration.java
+++ b/core/java/android/net/StaticIpConfiguration.java
@@ -16,6 +16,7 @@
 
 package android.net;
 
+import android.annotation.UnsupportedAppUsage;
 import android.net.LinkAddress;
 import android.os.Parcelable;
 import android.os.Parcel;
@@ -46,11 +47,16 @@
  * @hide
  */
 public class StaticIpConfiguration implements Parcelable {
+    @UnsupportedAppUsage
     public LinkAddress ipAddress;
+    @UnsupportedAppUsage
     public InetAddress gateway;
+    @UnsupportedAppUsage
     public final ArrayList<InetAddress> dnsServers;
+    @UnsupportedAppUsage
     public String domains;
 
+    @UnsupportedAppUsage
     public StaticIpConfiguration() {
         dnsServers = new ArrayList<InetAddress>();
     }
@@ -80,6 +86,7 @@
      * route to the gateway as well. This configuration is arguably invalid, but it used to work
      * in K and earlier, and other OSes appear to accept it.
      */
+    @UnsupportedAppUsage
     public List<RouteInfo> getRoutes(String iface) {
         List<RouteInfo> routes = new ArrayList<RouteInfo>(3);
         if (ipAddress != null) {
diff --git a/core/java/android/net/TransportInfo.java b/core/java/android/net/TransportInfo.java
new file mode 100644
index 0000000..b78d3fe
--- /dev/null
+++ b/core/java/android/net/TransportInfo.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2018 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;
+
+/**
+ * A container for transport-specific capabilities which is returned by
+ * {@link NetworkCapabilities#getTransportInfo()}. Specific networks
+ * may provide concrete implementations of this interface.
+ */
+public interface TransportInfo {
+}
diff --git a/core/java/android/net/UidRange.java b/core/java/android/net/UidRange.java
index 3164929..793c82d 100644
--- a/core/java/android/net/UidRange.java
+++ b/core/java/android/net/UidRange.java
@@ -19,17 +19,14 @@
 import static android.os.UserHandle.PER_USER_RANGE;
 
 import android.os.Parcel;
-import android.os.Parcelable;
 
 /**
  * An inclusive range of UIDs.
  *
  * @hide
  */
-public final class UidRange implements Parcelable {
-    public final int start;
-    public final int stop;
-
+public final class UidRange extends UidRangeParcel {
+    private UidRange() {}
     public UidRange(int startUid, int stopUid) {
         if (startUid < 0) throw new IllegalArgumentException("Invalid start UID.");
         if (stopUid < 0) throw new IllegalArgumentException("Invalid stop UID.");
@@ -89,26 +86,18 @@
         return start + "-" + stop;
     }
 
-    // implement the Parcelable interface
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(start);
-        dest.writeInt(stop);
-    }
+    /**
+     * DO NOT override "writeToParcel" and "readFromParcel" in this class.
+     * The parceling code is autogenerated by the superclass.
+     */
 
     public static final Creator<UidRange> CREATOR =
         new Creator<UidRange>() {
             @Override
             public UidRange createFromParcel(Parcel in) {
-                int start = in.readInt();
-                int stop = in.readInt();
-
-                return new UidRange(start, stop);
+                UidRange obj = new UidRange();
+                obj.readFromParcel(in);
+                return obj;
             }
             @Override
             public UidRange[] newArray(int size) {
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 823f1cc..9b138eb 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -323,6 +323,55 @@
     return (jboolean) !queryUserAccess(uid, netId);
 }
 
+static bool checkLenAndCopy(JNIEnv* env, const jbyteArray& addr, int len, void* dst)
+{
+    if (env->GetArrayLength(addr) != len) {
+        return false;
+    }
+    env->GetByteArrayRegion(addr, 0, len, reinterpret_cast<jbyte*>(dst));
+    return true;
+}
+
+static void android_net_utils_addArpEntry(JNIEnv *env, jobject thiz, jbyteArray ethAddr,
+        jbyteArray ipv4Addr, jstring ifname, jobject javaFd)
+{
+    struct arpreq req = {};
+    struct sockaddr_in& netAddrStruct = *reinterpret_cast<sockaddr_in*>(&req.arp_pa);
+    struct sockaddr& ethAddrStruct = req.arp_ha;
+
+    ethAddrStruct.sa_family = ARPHRD_ETHER;
+    if (!checkLenAndCopy(env, ethAddr, ETH_ALEN, ethAddrStruct.sa_data)) {
+        jniThrowException(env, "java/io/IOException", "Invalid ethAddr length");
+        return;
+    }
+
+    netAddrStruct.sin_family = AF_INET;
+    if (!checkLenAndCopy(env, ipv4Addr, sizeof(in_addr), &netAddrStruct.sin_addr)) {
+        jniThrowException(env, "java/io/IOException", "Invalid ipv4Addr length");
+        return;
+    }
+
+    int ifLen = env->GetStringLength(ifname);
+    // IFNAMSIZ includes the terminating NULL character
+    if (ifLen >= IFNAMSIZ) {
+        jniThrowException(env, "java/io/IOException", "ifname too long");
+        return;
+    }
+    env->GetStringUTFRegion(ifname, 0, ifLen, req.arp_dev);
+
+    req.arp_flags = ATF_COM;  // Completed entry (ha valid)
+    int fd = jniGetFDFromFileDescriptor(env, javaFd);
+    if (fd < 0) {
+        jniThrowExceptionFmt(env, "java/io/IOException", "Invalid file descriptor");
+        return;
+    }
+    // See also: man 7 arp
+    if (ioctl(fd, SIOCSARP, &req)) {
+        jniThrowExceptionFmt(env, "java/io/IOException", "ioctl error: %s", strerror(errno));
+        return;
+    }
+}
+
 
 // ----------------------------------------------------------------------------
 
@@ -337,6 +386,7 @@
     { "bindSocketToNetwork", "(II)I", (void*) android_net_utils_bindSocketToNetwork },
     { "protectFromVpn", "(I)Z", (void*)android_net_utils_protectFromVpn },
     { "queryUserAccess", "(II)Z", (void*)android_net_utils_queryUserAccess },
+    { "addArpEntry", "([B[BLjava/lang/String;Ljava/io/FileDescriptor;)V", (void*) android_net_utils_addArpEntry },
     { "attachDhcpFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_attachDhcpFilter },
     { "attachRaFilter", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_attachRaFilter },
     { "attachControlPacketFilter", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_attachControlPacketFilter },
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 0b24db0..66ceae4 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -35,6 +35,11 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
 import static android.net.NetworkCapabilities.TRANSPORT_VPN;
+import static android.net.NetworkPolicyManager.RULE_NONE;
+import static android.net.NetworkPolicyManager.uidRulesToString;
+import static android.os.Process.INVALID_UID;
+import static android.system.OsConstants.IPPROTO_TCP;
+import static android.system.OsConstants.IPPROTO_UDP;
 
 import static com.android.internal.util.Preconditions.checkNotNull;
 
@@ -49,10 +54,12 @@
 import android.content.IntentFilter;
 import android.content.res.Configuration;
 import android.database.ContentObserver;
+import android.net.ConnectionInfo;
 import android.net.ConnectivityManager;
 import android.net.ConnectivityManager.PacketKeepalive;
 import android.net.IConnectivityManager;
 import android.net.IIpConnectivityMetrics;
+import android.net.INetd;
 import android.net.INetdEventCallback;
 import android.net.INetworkManagementEventObserver;
 import android.net.INetworkPolicyListener;
@@ -74,7 +81,7 @@
 import android.net.NetworkSpecifier;
 import android.net.NetworkState;
 import android.net.NetworkUtils;
-import android.net.Proxy;
+import android.net.NetworkWatchlistManager;
 import android.net.ProxyInfo;
 import android.net.RouteInfo;
 import android.net.UidRange;
@@ -82,7 +89,9 @@
 import android.net.VpnService;
 import android.net.metrics.IpConnectivityLog;
 import android.net.metrics.NetworkEvent;
+import android.net.netlink.InetDiagMessage;
 import android.net.util.MultinetworkPolicyTracker;
+import android.net.util.NetdService;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
@@ -146,12 +155,12 @@
 import com.android.server.connectivity.KeepaliveTracker;
 import com.android.server.connectivity.LingerMonitor;
 import com.android.server.connectivity.MockableSystemProperties;
+import com.android.server.connectivity.MultipathPolicyTracker;
 import com.android.server.connectivity.NetworkAgentInfo;
 import com.android.server.connectivity.NetworkDiagnostics;
 import com.android.server.connectivity.NetworkMonitor;
 import com.android.server.connectivity.NetworkNotificationManager;
 import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
-import com.android.server.connectivity.PacManager;
 import com.android.server.connectivity.PermissionMonitor;
 import com.android.server.connectivity.ProxyTracker;
 import com.android.server.connectivity.Tethering;
@@ -161,6 +170,7 @@
 import com.android.server.net.BaseNetworkObserver;
 import com.android.server.net.LockdownVpnTracker;
 import com.android.server.net.NetworkPolicyManagerInternal;
+import com.android.server.utils.PriorityDump;
 
 import com.google.android.collect.Lists;
 
@@ -180,6 +190,8 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Comparator;
+import java.util.ConcurrentModificationException;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -199,9 +211,12 @@
     private static final String DIAG_ARG = "--diag";
     public static final String SHORT_ARG = "--short";
     private static final String TETHERING_ARG = "tethering";
+    private static final String NETWORK_ARG = "networks";
+    private static final String REQUEST_ARG = "requests";
 
     private static final boolean DBG = true;
-    private static final boolean VDBG = false;
+    private static final boolean DDBG = Log.isLoggable(TAG, Log.DEBUG);
+    private static final boolean VDBG = Log.isLoggable(TAG, Log.VERBOSE);
 
     private static final boolean LOGD_BLOCKED_NETWORKINFO = true;
 
@@ -246,11 +261,21 @@
     @GuardedBy("mVpns")
     private LockdownVpnTracker mLockdownTracker;
 
+    /**
+     * Stale copy of uid rules provided by NPMS. As long as they are accessed only in internal
+     * handler thread, they don't need a lock.
+     */
+    private SparseIntArray mUidRules = new SparseIntArray();
+    /** Flag indicating if background data is restricted. */
+    private boolean mRestrictBackground;
+
     final private Context mContext;
     // 0 is full bad, 100 is full good
     private int mDefaultInetConditionPublished = 0;
 
-    private INetworkManagementService mNetd;
+    private INetworkManagementService mNMS;
+    @VisibleForTesting
+    protected INetd mNetd;
     private INetworkStatsService mStatsService;
     private INetworkPolicyManager mPolicyManager;
     private NetworkPolicyManagerInternal mPolicyManagerInternal;
@@ -381,9 +406,9 @@
     private static final int EVENT_PROMPT_UNVALIDATED = 29;
 
     /**
-     * used internally to (re)configure mobile data always-on settings.
+     * used internally to (re)configure always-on networks.
      */
-    private static final int EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON = 30;
+    private static final int EVENT_CONFIGURE_ALWAYS_ON_NETWORKS = 30;
 
     /**
      * used to add a network listener with a pending intent
@@ -407,6 +432,16 @@
     // Handle private DNS validation status updates.
     private static final int EVENT_PRIVATE_DNS_VALIDATION_UPDATE = 38;
 
+    /**
+     * Used to handle onUidRulesChanged event from NetworkPolicyManagerService.
+     */
+    private static final int EVENT_UID_RULES_CHANGED = 39;
+
+    /**
+     * Used to handle onRestrictBackgroundChanged event from NetworkPolicyManagerService.
+     */
+    private static final int EVENT_DATA_SAVER_CHANGED = 40;
+
     private static String eventName(int what) {
         return sMagicDecoderRing.get(what, Integer.toString(what));
     }
@@ -497,9 +532,15 @@
 
     private final IpConnectivityLog mMetricsLog;
 
+    @GuardedBy("mBandwidthRequests")
+    private final SparseArray<Integer> mBandwidthRequests = new SparseArray(10);
+
     @VisibleForTesting
     final MultinetworkPolicyTracker mMultinetworkPolicyTracker;
 
+    @VisibleForTesting
+    final MultipathPolicyTracker mMultipathPolicyTracker;
+
     /**
      * Implements support for the legacy "one network per network type" model.
      *
@@ -689,6 +730,28 @@
     }
     private LegacyTypeTracker mLegacyTypeTracker = new LegacyTypeTracker();
 
+    /**
+     * Helper class which parses out priority arguments and dumps sections according to their
+     * priority. If priority arguments are omitted, function calls the legacy dump command.
+     */
+    private final PriorityDump.PriorityDumper mPriorityDumper = new PriorityDump.PriorityDumper() {
+        @Override
+        public void dumpHigh(FileDescriptor fd, PrintWriter pw, String[] args, boolean asProto) {
+            doDump(fd, pw, new String[] {DIAG_ARG}, asProto);
+            doDump(fd, pw, new String[] {SHORT_ARG}, asProto);
+        }
+
+        @Override
+        public void dumpNormal(FileDescriptor fd, PrintWriter pw, String[] args, boolean asProto) {
+            doDump(fd, pw, args, asProto);
+        }
+
+        @Override
+        public void dump(FileDescriptor fd, PrintWriter pw, String[] args, boolean asProto) {
+           doDump(fd, pw, args, asProto);
+        }
+    };
+
     public ConnectivityService(Context context, INetworkManagementService netManager,
             INetworkStatsService statsService, INetworkPolicyManager policyManager) {
         this(context, netManager, statsService, policyManager, new IpConnectivityLog());
@@ -711,6 +774,12 @@
         mDefaultMobileDataRequest = createDefaultInternetRequestForTransport(
                 NetworkCapabilities.TRANSPORT_CELLULAR, NetworkRequest.Type.BACKGROUND_REQUEST);
 
+        // The default WiFi request is a background request so that apps using WiFi are
+        // migrated to a better network (typically ethernet) when one comes up, instead
+        // of staying on WiFi forever.
+        mDefaultWifiRequest = createDefaultInternetRequestForTransport(
+                NetworkCapabilities.TRANSPORT_WIFI, NetworkRequest.Type.BACKGROUND_REQUEST);
+
         mHandlerThread = new HandlerThread("ConnectivityServiceThread");
         mHandlerThread.start();
         mHandler = new InternalHandler(mHandlerThread.getLooper());
@@ -722,7 +791,7 @@
         mLingerDelayMs = mSystemProperties.getInt(LINGER_DELAY_PROPERTY, DEFAULT_LINGER_DELAY_MS);
 
         mContext = checkNotNull(context, "missing Context");
-        mNetd = checkNotNull(netManager, "missing INetworkManagementService");
+        mNMS = checkNotNull(netManager, "missing INetworkManagementService");
         mStatsService = checkNotNull(statsService, "missing INetworkStatsService");
         mPolicyManager = checkNotNull(policyManager, "missing INetworkPolicyManager");
         mPolicyManagerInternal = checkNotNull(
@@ -730,9 +799,13 @@
                 "missing NetworkPolicyManagerInternal");
         mProxyTracker = new ProxyTracker(context, mHandler, EVENT_PROXY_HAS_CHANGED);
 
+        mNetd = NetdService.getInstance();
         mKeyStore = KeyStore.getInstance();
         mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
 
+        // To ensure uid rules are synchronized with Network Policy, register for
+        // NetworkPolicyManagerService events must happen prior to NetworkPolicyManagerService
+        // reading existing policy from disk.
         try {
             mPolicyManager.registerListener(mPolicyListener);
         } catch (RemoteException e) {
@@ -812,7 +885,7 @@
 
         mTethering = makeTethering();
 
-        mPermissionMonitor = new PermissionMonitor(mContext, mNetd);
+        mPermissionMonitor = new PermissionMonitor(mContext, mNMS);
 
         //set up the listener for user state for creating user VPNs
         IntentFilter intentFilter = new IntentFilter();
@@ -822,13 +895,22 @@
         intentFilter.addAction(Intent.ACTION_USER_REMOVED);
         intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
         mContext.registerReceiverAsUser(
-                mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null);
+                mIntentReceiver, UserHandle.ALL, intentFilter, null, null);
         mContext.registerReceiverAsUser(mUserPresentReceiver, UserHandle.SYSTEM,
                 new IntentFilter(Intent.ACTION_USER_PRESENT), null, null);
 
+        // Listen to package add and removal events for all users.
+        intentFilter = new IntentFilter();
+        intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+        intentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+        intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        intentFilter.addDataScheme("package");
+        mContext.registerReceiverAsUser(
+                mIntentReceiver, UserHandle.ALL, intentFilter, null, null);
+
         try {
-            mNetd.registerObserver(mTethering);
-            mNetd.registerObserver(mDataActivityObserver);
+            mNMS.registerObserver(mTethering);
+            mNMS.registerObserver(mDataActivityObserver);
         } catch (RemoteException e) {
             loge("Error registering observer :" + e);
         }
@@ -857,11 +939,14 @@
                 mContext, mHandler, () -> rematchForAvoidBadWifiUpdate());
         mMultinetworkPolicyTracker.start();
 
-        mDnsManager = new DnsManager(mContext, mNetd, mSystemProperties);
+        mMultipathPolicyTracker = new MultipathPolicyTracker(mContext, mHandler);
+
+        mDnsManager = new DnsManager(mContext, mNMS, mSystemProperties);
         registerPrivateDnsSettingsCallbacks();
     }
 
-    private Tethering makeTethering() {
+    @VisibleForTesting
+    protected Tethering makeTethering() {
         // TODO: Move other elements into @Overridden getters.
         final TetheringDependencies deps = new TetheringDependencies() {
             @Override
@@ -873,7 +958,7 @@
                 return mDefaultRequest;
             }
         };
-        return new Tethering(mContext, mNetd, mStatsService, mPolicyManager,
+        return new Tethering(mContext, mNMS, mStatsService, mPolicyManager,
                 IoThread.get().getLooper(), new MockableSystemProperties(),
                 deps);
     }
@@ -905,8 +990,8 @@
     // 2. Give FakeSettingsProvider an alternative notification mechanism and have the test use it
     //    by subclassing SettingsObserver.
     @VisibleForTesting
-    void updateMobileDataAlwaysOn() {
-        mHandler.sendEmptyMessage(EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON);
+    void updateAlwaysOnNetworks() {
+        mHandler.sendEmptyMessage(EVENT_CONFIGURE_ALWAYS_ON_NETWORKS);
     }
 
     // See FakeSettingsProvider comment above.
@@ -915,22 +1000,30 @@
         mHandler.sendEmptyMessage(EVENT_PRIVATE_DNS_SETTINGS_CHANGED);
     }
 
-    private void handleMobileDataAlwaysOn() {
+    private void handleAlwaysOnNetworkRequest(
+            NetworkRequest networkRequest, String settingName, boolean defaultValue) {
         final boolean enable = toBool(Settings.Global.getInt(
-                mContext.getContentResolver(), Settings.Global.MOBILE_DATA_ALWAYS_ON, 1));
-        final boolean isEnabled = (mNetworkRequests.get(mDefaultMobileDataRequest) != null);
+                mContext.getContentResolver(), settingName, encodeBool(defaultValue)));
+        final boolean isEnabled = (mNetworkRequests.get(networkRequest) != null);
         if (enable == isEnabled) {
             return;  // Nothing to do.
         }
 
         if (enable) {
             handleRegisterNetworkRequest(new NetworkRequestInfo(
-                    null, mDefaultMobileDataRequest, new Binder()));
+                    null, networkRequest, new Binder()));
         } else {
-            handleReleaseNetworkRequest(mDefaultMobileDataRequest, Process.SYSTEM_UID);
+            handleReleaseNetworkRequest(networkRequest, Process.SYSTEM_UID);
         }
     }
 
+    private void handleConfigureAlwaysOnNetworks() {
+        handleAlwaysOnNetworkRequest(
+                mDefaultMobileDataRequest,Settings.Global.MOBILE_DATA_ALWAYS_ON, true);
+        handleAlwaysOnNetworkRequest(mDefaultWifiRequest, Settings.Global.WIFI_ALWAYS_REQUESTED,
+                false);
+    }
+
     private void registerSettingsCallbacks() {
         // Watch for global HTTP proxy changes.
         mSettingsObserver.observe(
@@ -940,7 +1033,12 @@
         // Watch for whether or not to keep mobile data always on.
         mSettingsObserver.observe(
                 Settings.Global.getUriFor(Settings.Global.MOBILE_DATA_ALWAYS_ON),
-                EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON);
+                EVENT_CONFIGURE_ALWAYS_ON_NETWORKS);
+
+        // Watch for whether or not to keep wifi always on.
+        mSettingsObserver.observe(
+                Settings.Global.getUriFor(Settings.Global.WIFI_ALWAYS_REQUESTED),
+                EVENT_CONFIGURE_ALWAYS_ON_NETWORKS);
     }
 
     private void registerPrivateDnsSettingsCallbacks() {
@@ -1054,14 +1152,9 @@
         if (ignoreBlocked) {
             return false;
         }
-        // Networks are never blocked for system services
-        // TODO: consider moving this check to NetworkPolicyManagerInternal.isUidNetworkingBlocked.
-        if (isSystem(uid)) {
-            return false;
-        }
         synchronized (mVpns) {
             final Vpn vpn = mVpns.get(UserHandle.getUserId(uid));
-            if (vpn != null && vpn.isBlockingUid(uid)) {
+            if (vpn != null && vpn.getLockdown() && vpn.isBlockingUid(uid)) {
                 return true;
             }
         }
@@ -1088,6 +1181,17 @@
         mNetworkInfoBlockingLogs.log(action + " " + uid);
     }
 
+    private void maybeLogBlockedStatusChanged(NetworkRequestInfo nri, Network net,
+            boolean blocked) {
+        if (nri == null || net == null || !LOGD_BLOCKED_NETWORKINFO) {
+            return;
+        }
+        String action = blocked ? "BLOCKED" : "UNBLOCKED";
+        log(String.format("Blocked status changed to %s for %d(%d) on netId %d", blocked,
+                nri.mUid, nri.request.requestId, net.netId));
+        mNetworkInfoBlockingLogs.log(action + " " + nri.mUid);
+    }
+
     /**
      * Apply any relevant filters to {@link NetworkState} for the given UID. For
      * example, this may mark the network as {@link DetailedState#BLOCKED} based
@@ -1437,6 +1541,20 @@
     };
 
     /**
+     * Ensures that the system cannot call a particular method.
+     */
+    private boolean disallowedBecauseSystemCaller() {
+        // TODO: start throwing a SecurityException when GnssLocationProvider stops calling
+        // requestRouteToHost.
+        if (isSystem(Binder.getCallingUid())) {
+            log("This method exists only for app backwards compatibility"
+                    + " and must not be called by system services.");
+            return true;
+        }
+        return false;
+    }
+
+    /**
      * Ensure that a network route exists to deliver traffic to the specified
      * host via the specified network interface.
      * @param networkType the type of the network over which traffic to the
@@ -1447,6 +1565,9 @@
      */
     @Override
     public boolean requestRouteToHostAddress(int networkType, byte[] hostAddress) {
+        if (disallowedBecauseSystemCaller()) {
+            return false;
+        }
         enforceChangePermission();
         if (mProtectedNetworks.contains(networkType)) {
             enforceConnectivityInternalPermission();
@@ -1524,7 +1645,7 @@
         if (DBG) log("Adding legacy route " + bestRoute +
                 " for UID/PID " + uid + "/" + Binder.getCallingPid());
         try {
-            mNetd.addLegacyRouteForNetId(netId, bestRoute, uid);
+            mNMS.addLegacyRouteForNetId(netId, bestRoute, uid);
         } catch (Exception e) {
             // never crash - catch them all
             if (DBG) loge("Exception trying to add a route: " + e);
@@ -1548,6 +1669,24 @@
                 loge("Error parsing ip address in validation event");
             }
         }
+
+        @Override
+        public void onDnsEvent(int netId, int eventType, int returnCode, String hostname,
+                String[] ipAddresses, int ipAddressesCount, long timestamp, int uid) {
+            NetworkAgentInfo nai = getNetworkAgentInfoForNetId(netId);
+            // Netd event only allow registrants from system. Each NetworkMonitor thread is under
+            // the caller thread of registerNetworkAgent. Thus, it's not allowed to register netd
+            // event callback for certain nai. e.g. cellular. Register here to pass to
+            // NetworkMonitor instead.
+            // TODO: Move the Dns Event to NetworkMonitor. Use Binder.clearCallingIdentity() in
+            // registerNetworkAgent to have NetworkMonitor created with system process as design
+            // expectation. Also, NetdEventListenerService only allow one callback from each
+            // caller type. Need to re-factor NetdEventListenerService to allow multiple
+            // NetworkMonitor registrants.
+            if (nai != null && nai.satisfies(mDefaultRequest)) {
+                nai.networkMonitor.sendMessage(NetworkMonitor.EVENT_DNS_NOTIFICATION, returnCode);
+            }
+        }
     };
 
     @VisibleForTesting
@@ -1572,10 +1711,17 @@
     private final INetworkPolicyListener mPolicyListener = new NetworkPolicyManager.Listener() {
         @Override
         public void onUidRulesChanged(int uid, int uidRules) {
-            // TODO: notify UID when it has requested targeted updates
+            mHandler.sendMessage(mHandler.obtainMessage(EVENT_UID_RULES_CHANGED, uid, uidRules));
         }
         @Override
         public void onRestrictBackgroundChanged(boolean restrictBackground) {
+            // caller is NPMS, since we only register with them
+            if (LOGD_BLOCKED_NETWORKINFO) {
+                log("onRestrictBackgroundChanged(restrictBackground=" + restrictBackground + ")");
+            }
+            mHandler.sendMessage(mHandler.obtainMessage(
+                    EVENT_DATA_SAVER_CHANGED, restrictBackground ? 1 : 0, 0));
+
             // TODO: relocate this specific callback in Tethering.
             if (restrictBackground) {
                 log("onRestrictBackgroundChanged(true): disabling tethering");
@@ -1584,6 +1730,50 @@
         }
     };
 
+    void handleUidRulesChanged(int uid, int newRules) {
+        // skip update when we've already applied rules
+        final int oldRules = mUidRules.get(uid, RULE_NONE);
+        if (oldRules == newRules) return;
+
+        maybeNotifyNetworkBlockedForNewUidRules(uid, newRules);
+
+        if (newRules == RULE_NONE) {
+            mUidRules.delete(uid);
+        } else {
+            mUidRules.put(uid, newRules);
+        }
+    }
+
+    void handleRestrictBackgroundChanged(boolean restrictBackground) {
+        if (mRestrictBackground == restrictBackground) return;
+
+        for (final NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
+            final boolean curMetered = nai.networkCapabilities.isMetered();
+            maybeNotifyNetworkBlocked(nai, curMetered, curMetered, mRestrictBackground,
+                    restrictBackground);
+        }
+
+        mRestrictBackground = restrictBackground;
+    }
+
+    private boolean isUidNetworkingWithVpnBlocked(int uid, int uidRules, boolean isNetworkMetered,
+            boolean isBackgroundRestricted) {
+        synchronized (mVpns) {
+            final Vpn vpn = mVpns.get(UserHandle.getUserId(uid));
+            // Because the return value of this function depends on the list of UIDs the
+            // always-on VPN blocks when in lockdown mode, when the always-on VPN changes that
+            // list all state depending on the return value of this function has to be recomputed.
+            // TODO: add a trigger when the always-on VPN sets its blocked UIDs to reevaluate and
+            // send the necessary onBlockedStatusChanged callbacks.
+            if (vpn != null && vpn.getLockdown() && vpn.isBlockingUid(uid)) {
+                return true;
+            }
+        }
+
+        return mPolicyManagerInternal.isUidNetworkingBlocked(uid, uidRules,
+                isNetworkMetered, isBackgroundRestricted);
+    }
+
     /**
      * Require that the caller is either in the same user or has appropriate permission to interact
      * across users.
@@ -1600,6 +1790,16 @@
                 "ConnectivityService");
     }
 
+    private void enforceAnyPermissionOf(String... permissions) {
+        for (String permission : permissions) {
+            if (mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED) {
+                return;
+            }
+        }
+        throw new SecurityException(
+            "Requires one of the following permissions: " + String.join(", ", permissions) + ".");
+    }
+
     private void enforceInternetPermission() {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.INTERNET,
@@ -1644,6 +1844,18 @@
                 "ConnectivityService");
     }
 
+    private void enforceNetworkStackSettingsOrSetup() {
+        enforceAnyPermissionOf(
+            android.Manifest.permission.NETWORK_SETTINGS,
+            android.Manifest.permission.NETWORK_SETUP_WIZARD,
+            android.Manifest.permission.NETWORK_STACK);
+    }
+
+    private boolean checkNetworkStackPermission() {
+        return PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission(
+                android.Manifest.permission.NETWORK_STACK);
+    }
+
     private void enforceConnectivityRestrictedNetworksPermission() {
         try {
             mContext.enforceCallingOrSelfPermission(
@@ -1714,7 +1926,8 @@
 
     private void sendStickyBroadcast(Intent intent) {
         synchronized (this) {
-            if (!mSystemReady) {
+            if (!mSystemReady
+                    && intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
                 mInitialBroadcast = new Intent(intent);
             }
             intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
@@ -1753,8 +1966,9 @@
     }
 
     void systemReady() {
-        loadGlobalProxy();
+        mProxyTracker.loadGlobalProxy();
         registerNetdEventCallback();
+        mTethering.systemReady();
 
         synchronized (this) {
             mSystemReady = true;
@@ -1763,15 +1977,13 @@
                 mInitialBroadcast = null;
             }
         }
-        // load the global proxy at startup
-        mHandler.sendMessage(mHandler.obtainMessage(EVENT_APPLY_GLOBAL_HTTP_PROXY));
 
         // Try bringing up tracker, but KeyStore won't be ready yet for secondary users so wait
         // for user to unlock device too.
         updateLockdownVpn();
 
-        // Configure whether mobile data is always on.
-        mHandler.sendMessage(mHandler.obtainMessage(EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON));
+        // Create network requests for always-on networks.
+        mHandler.sendMessage(mHandler.obtainMessage(EVENT_CONFIGURE_ALWAYS_ON_NETWORKS));
 
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_SYSTEM_READY));
 
@@ -1809,7 +2021,7 @@
 
         if (timeout > 0 && iface != null && type != ConnectivityManager.TYPE_NONE) {
             try {
-                mNetd.addIdleTimer(iface, timeout, type);
+                mNMS.addIdleTimer(iface, timeout, type);
             } catch (Exception e) {
                 // You shall not crash!
                 loge("Exception in setupDataActivityTracking " + e);
@@ -1828,7 +2040,7 @@
                               caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))) {
             try {
                 // the call fails silently if no idle timer setup for this interface
-                mNetd.removeIdleTimer(iface);
+                mNMS.removeIdleTimer(iface);
             } catch (Exception e) {
                 loge("Exception in removeDataActivityTracking " + e);
             }
@@ -1836,6 +2048,18 @@
     }
 
     /**
+     * Update data activity tracking when network state is updated.
+     */
+    private void updateDataActivityTracking(NetworkAgentInfo newNetwork,
+            NetworkAgentInfo oldNetwork) {
+        if (newNetwork != null) {
+            setupDataActivityTracking(newNetwork);
+        }
+        if (oldNetwork != null) {
+            removeDataActivityTracking(oldNetwork);
+        }
+    }
+    /**
      * Reads the network specific MTU size from resources.
      * and set it on it's iface.
      */
@@ -1862,14 +2086,15 @@
         }
 
         try {
-            if (VDBG) log("Setting MTU size: " + iface + ", " + mtu);
-            mNetd.setMtu(iface, mtu);
+            if (VDBG || DDBG) log("Setting MTU size: " + iface + ", " + mtu);
+            mNMS.setMtu(iface, mtu);
         } catch (Exception e) {
             Slog.e(TAG, "exception in setMtu()" + e);
         }
     }
 
-    private static final String DEFAULT_TCP_BUFFER_SIZES = "4096,87380,110208,4096,16384,110208";
+    @VisibleForTesting
+    protected static final String DEFAULT_TCP_BUFFER_SIZES = "4096,87380,110208,4096,16384,110208";
     private static final String DEFAULT_TCP_RWND_KEY = "net.tcp.default_init_rwnd";
 
     // Overridden for testing purposes to avoid writing to SystemProperties.
@@ -1878,12 +2103,7 @@
         return new MockableSystemProperties();
     }
 
-    private void updateTcpBufferSizes(NetworkAgentInfo nai) {
-        if (isDefaultNetwork(nai) == false) {
-            return;
-        }
-
-        String tcpBufferSizes = nai.linkProperties.getTcpBufferSizes();
+    private void updateTcpBufferSizes(String tcpBufferSizes) {
         String[] values = null;
         if (tcpBufferSizes != null) {
             values = tcpBufferSizes.split(",");
@@ -1898,17 +2118,13 @@
         if (tcpBufferSizes.equals(mCurrentTcpBufferSizes)) return;
 
         try {
-            if (VDBG) Slog.d(TAG, "Setting tx/rx TCP buffers to " + tcpBufferSizes);
+            if (VDBG || DDBG) Slog.d(TAG, "Setting tx/rx TCP buffers to " + tcpBufferSizes);
 
-            final String prefix = "/sys/kernel/ipv4/tcp_";
-            FileUtils.stringToFile(prefix + "rmem_min", values[0]);
-            FileUtils.stringToFile(prefix + "rmem_def", values[1]);
-            FileUtils.stringToFile(prefix + "rmem_max", values[2]);
-            FileUtils.stringToFile(prefix + "wmem_min", values[3]);
-            FileUtils.stringToFile(prefix + "wmem_def", values[4]);
-            FileUtils.stringToFile(prefix + "wmem_max", values[5]);
+            String rmemValues = String.join(" ", values[0], values[1], values[2]);
+            String wmemValues = String.join(" ", values[3], values[4], values[5]);
+            mNetd.setTcpRWmemorySize(rmemValues, wmemValues);
             mCurrentTcpBufferSizes = tcpBufferSizes;
-        } catch (IOException e) {
+        } catch (RemoteException | ServiceSpecificException e) {
             loge("Can't set TCP buffer sizes:" + e);
         }
 
@@ -1945,7 +2161,7 @@
     private void dumpNetworkDiagnostics(IndentingPrintWriter pw) {
         final List<NetworkDiagnostics> netDiags = new ArrayList<NetworkDiagnostics>();
         final long DIAG_TIME_MS = 5000;
-        for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
+        for (NetworkAgentInfo nai : networksSortedById()) {
             // Start gathering diagnostic information.
             netDiags.add(new NetworkDiagnostics(
                     nai.network,
@@ -1962,8 +2178,13 @@
 
     @Override
     protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+        PriorityDump.dump(mPriorityDumper, fd, writer, args);
+    }
+
+    private void doDump(FileDescriptor fd, PrintWriter writer, String[] args, boolean asProto) {
         final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+        if (asProto) return;
 
         if (ArrayUtils.contains(args, DIAG_ARG)) {
             dumpNetworkDiagnostics(pw);
@@ -1971,6 +2192,12 @@
         } else if (ArrayUtils.contains(args, TETHERING_ARG)) {
             mTethering.dump(fd, pw, args);
             return;
+        } else if (ArrayUtils.contains(args, NETWORK_ARG)) {
+            dumpNetworks(pw);
+            return;
+        } else if (ArrayUtils.contains(args, REQUEST_ARG)) {
+            dumpNetworkRequests(pw);
+            return;
         }
 
         pw.print("NetworkFactories for:");
@@ -1991,37 +2218,38 @@
 
         pw.println("Current Networks:");
         pw.increaseIndent();
-        for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
-            pw.println(nai.toString());
-            pw.increaseIndent();
-            pw.println(String.format(
-                    "Requests: REQUEST:%d LISTEN:%d BACKGROUND_REQUEST:%d total:%d",
-                    nai.numForegroundNetworkRequests(),
-                    nai.numNetworkRequests() - nai.numRequestNetworkRequests(),
-                    nai.numBackgroundNetworkRequests(),
-                    nai.numNetworkRequests()));
-            pw.increaseIndent();
-            for (int i = 0; i < nai.numNetworkRequests(); i++) {
-                pw.println(nai.requestAt(i).toString());
-            }
-            pw.decreaseIndent();
-            pw.println("Lingered:");
-            pw.increaseIndent();
-            nai.dumpLingerTimers(pw);
-            pw.decreaseIndent();
-            pw.decreaseIndent();
-        }
+        dumpNetworks(pw);
         pw.decreaseIndent();
         pw.println();
 
-        pw.println("Network Requests:");
+        pw.print("Restrict background: ");
+        pw.println(mRestrictBackground);
+        pw.println();
+
+        pw.println("Status for known UIDs:");
         pw.increaseIndent();
-        for (NetworkRequestInfo nri : mNetworkRequests.values()) {
-            pw.println(nri.toString());
+        final int size = mUidRules.size();
+        for (int i = 0; i < size; i++) {
+            // Don't crash if the array is modified while dumping in bugreports.
+            try {
+                final int uid = mUidRules.keyAt(i);
+                final int uidRules = mUidRules.get(uid, RULE_NONE);
+                pw.println("UID=" + uid + " rules=" + uidRulesToString(uidRules));
+            } catch (ArrayIndexOutOfBoundsException e) {
+                pw.println("  ArrayIndexOutOfBoundsException");
+            } catch (ConcurrentModificationException e) {
+                pw.println("  ConcurrentModificationException");
+            }
         }
         pw.println();
         pw.decreaseIndent();
 
+        pw.println("Network Requests:");
+        pw.increaseIndent();
+        dumpNetworkRequests(pw);
+        pw.decreaseIndent();
+        pw.println();
+
         mLegacyTypeTracker.dump(pw);
 
         pw.println();
@@ -2033,6 +2261,9 @@
         pw.println();
         dumpAvoidBadWifiSettings(pw);
 
+        pw.println();
+        mMultipathPolicyTracker.dump(pw);
+
         if (ArrayUtils.contains(args, SHORT_ARG) == false) {
             pw.println();
             synchronized (mValidationLogs) {
@@ -2069,10 +2300,71 @@
                 pw.println("currently holding WakeLock for: " + (duration / 1000) + "s");
             }
             mWakelockLogs.reverseDump(fd, pw, args);
+
+            pw.println();
+            pw.println("bandwidth update requests (by uid):");
+            pw.increaseIndent();
+            synchronized (mBandwidthRequests) {
+                for (int i = 0; i < mBandwidthRequests.size(); i++) {
+                    pw.println("[" + mBandwidthRequests.keyAt(i)
+                            + "]: " + mBandwidthRequests.valueAt(i));
+                }
+            }
+            pw.decreaseIndent();
+
             pw.decreaseIndent();
         }
     }
 
+    private void dumpNetworks(IndentingPrintWriter pw) {
+        for (NetworkAgentInfo nai : networksSortedById()) {
+            pw.println(nai.toString());
+            pw.increaseIndent();
+            pw.println(String.format(
+                    "Requests: REQUEST:%d LISTEN:%d BACKGROUND_REQUEST:%d total:%d",
+                    nai.numForegroundNetworkRequests(),
+                    nai.numNetworkRequests() - nai.numRequestNetworkRequests(),
+                    nai.numBackgroundNetworkRequests(),
+                    nai.numNetworkRequests()));
+            pw.increaseIndent();
+            for (int i = 0; i < nai.numNetworkRequests(); i++) {
+                pw.println(nai.requestAt(i).toString());
+            }
+            pw.decreaseIndent();
+            pw.println("Lingered:");
+            pw.increaseIndent();
+            nai.dumpLingerTimers(pw);
+            pw.decreaseIndent();
+            pw.decreaseIndent();
+        }
+    }
+
+    private void dumpNetworkRequests(IndentingPrintWriter pw) {
+        for (NetworkRequestInfo nri : requestsSortedById()) {
+            pw.println(nri.toString());
+        }
+    }
+
+    /**
+     * Return an array of all current NetworkAgentInfos sorted by network id.
+     */
+    private NetworkAgentInfo[] networksSortedById() {
+        NetworkAgentInfo[] networks = new NetworkAgentInfo[0];
+        networks = mNetworkAgentInfos.values().toArray(networks);
+        Arrays.sort(networks, Comparator.comparingInt(nai -> nai.network.netId));
+        return networks;
+    }
+
+    /**
+     * Return an array of all current NetworkRequest sorted by request id.
+     */
+    private NetworkRequestInfo[] requestsSortedById() {
+        NetworkRequestInfo[] requests = new NetworkRequestInfo[0];
+        requests = mNetworkRequests.values().toArray(requests);
+        Arrays.sort(requests, Comparator.comparingInt(nri -> nri.request.requestId));
+        return requests;
+    }
+
     private boolean isLiveNetworkAgent(NetworkAgentInfo nai, int what) {
         if (nai.network == null) return false;
         final NetworkAgentInfo officialNai = getNetworkAgentInfoForNetwork(nai.network);
@@ -2141,8 +2433,7 @@
                     break;
                 }
                 case NetworkAgent.EVENT_NETWORK_SCORE_CHANGED: {
-                    Integer score = (Integer) msg.obj;
-                    if (score != null) updateNetworkScore(nai, score.intValue());
+                    updateNetworkScore(nai, msg.arg1);
                     break;
                 }
                 case NetworkAgent.EVENT_SET_EXPLICITLY_SELECTED: {
@@ -2150,7 +2441,7 @@
                         loge("ERROR: already-connected network explicitly selected.");
                     }
                     nai.networkMisc.explicitlySelected = true;
-                    nai.networkMisc.acceptUnvalidated = (boolean) msg.obj;
+                    nai.networkMisc.acceptUnvalidated = msg.arg1 == 1;
                     break;
                 }
                 case NetworkAgent.EVENT_PACKET_KEEPALIVE: {
@@ -2191,6 +2482,7 @@
                         updateCapabilities(oldScore, nai, nai.networkCapabilities);
                         // If score has changed, rebroadcast to NetworkFactories. b/17726566
                         if (oldScore != nai.getCurrentScore()) sendUpdatedScoreToFactories(nai);
+                        if (valid) handleFreshlyValidatedNetwork(nai);
                     }
                     updateInetCondition(nai);
                     // Let the NetworkAgent know the state of its network
@@ -2285,6 +2577,16 @@
                 mDefaultRequest.networkCapabilities, nai.networkCapabilities);
     }
 
+    private void handleFreshlyValidatedNetwork(NetworkAgentInfo nai) {
+        if (nai == null) return;
+        // If the Private DNS mode is opportunistic, reprogram the DNS servers
+        // in order to restart a validation pass from within netd.
+        final PrivateDnsConfig cfg = mDnsManager.getPrivateDnsConfig();
+        if (cfg.useTls && TextUtils.isEmpty(cfg.hostname)) {
+            updateDnses(nai.linkProperties, null, nai.network.netId);
+        }
+    }
+
     private void handlePrivateDnsSettingsChanged() {
         final PrivateDnsConfig cfg = mDnsManager.getPrivateDnsConfig();
 
@@ -2453,7 +2755,7 @@
         }
         nai.clearLingerState();
         if (nai.isSatisfyingRequest(mDefaultRequest.requestId)) {
-            removeDataActivityTracking(nai);
+            updateDataActivityTracking(null /* newNetwork */, nai);
             notifyLockdownVpn(nai);
             ensureNetworkTransitionWakelock(nai.name());
         }
@@ -2473,7 +2775,7 @@
             // NetworkFactories, so network traffic isn't interrupted for an unnecessarily
             // long time.
             try {
-                mNetd.removeNetwork(nai.network.netId);
+                mNMS.removeNetwork(nai.network.netId);
             } catch (Exception e) {
                 loge("Exception removing network: " + e);
             }
@@ -2651,7 +2953,7 @@
             if (nai != null) {
                 boolean wasBackgroundNetwork = nai.isBackgroundNetwork();
                 nai.removeRequest(nri.request.requestId);
-                if (VDBG) {
+                if (VDBG || DDBG) {
                     log(" Removing from current network " + nai.name() +
                             ", leaving " + nai.numNetworkRequests() + " requests.");
                 }
@@ -2671,20 +2973,6 @@
                 }
             }
 
-            // TODO: remove this code once we know that the Slog.wtf is never hit.
-            //
-            // Find all networks that are satisfying this request and remove the request
-            // from their request lists.
-            // TODO - it's my understanding that for a request there is only a single
-            // network satisfying it, so this loop is wasteful
-            for (NetworkAgentInfo otherNai : mNetworkAgentInfos.values()) {
-                if (otherNai.isSatisfyingRequest(nri.request.requestId) && otherNai != nai) {
-                    Slog.wtf(TAG, "Request " + nri.request + " satisfied by " +
-                            otherNai.name() + ", but mNetworkAgentInfos says " +
-                            (nai != null ? nai.name() : "null"));
-                }
-            }
-
             // Maintain the illusion.  When this request arrived, we might have pretended
             // that a network connected to serve it, even though the network was already
             // connected.  Now that this request has gone away, we might have to pretend
@@ -2853,7 +3141,7 @@
         pw.println("User setting:      " + description);
         pw.println("Network overrides:");
         pw.increaseIndent();
-        for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
+        for (NetworkAgentInfo nai : networksSortedById()) {
             if (nai.avoidUnvalidated) {
                 pw.println(nai.name());
             }
@@ -2888,7 +3176,7 @@
     }
 
     private void handlePromptUnvalidated(Network network) {
-        if (VDBG) log("handlePromptUnvalidated " + network);
+        if (VDBG || DDBG) log("handlePromptUnvalidated " + network);
         NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
 
         // Only prompt if the network is unvalidated and was explicitly selected by the user, and if
@@ -2921,6 +3209,11 @@
             return ConnectivityManager.MULTIPATH_PREFERENCE_UNMETERED;
         }
 
+        Integer networkPreference = mMultipathPolicyTracker.getMultipathPreference(network);
+        if (networkPreference != null) {
+            return networkPreference;
+        }
+
         return mMultinetworkPolicyTracker.getMeteredMultipathPreference();
     }
 
@@ -2938,7 +3231,7 @@
                     break;
                 }
                 case EVENT_APPLY_GLOBAL_HTTP_PROXY: {
-                    handleDeprecatedGlobalHttpProxy();
+                    mProxyTracker.loadDeprecatedGlobalHttpProxy();
                     break;
                 }
                 case EVENT_PROXY_HAS_CHANGED: {
@@ -2993,8 +3286,8 @@
                     handlePromptUnvalidated((Network) msg.obj);
                     break;
                 }
-                case EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON: {
-                    handleMobileDataAlwaysOn();
+                case EVENT_CONFIGURE_ALWAYS_ON_NETWORKS: {
+                    handleConfigureAlwaysOnNetworks();
                     break;
                 }
                 // Sent by KeepaliveTracker to process an app request on the state machine thread.
@@ -3014,6 +3307,7 @@
                     for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
                         nai.networkMonitor.systemReady = true;
                     }
+                    mMultipathPolicyTracker.start();
                     break;
                 }
                 case EVENT_REVALIDATE_NETWORK: {
@@ -3027,6 +3321,12 @@
                     handlePrivateDnsValidationUpdate(
                             (PrivateDnsValidationUpdate) msg.obj);
                     break;
+                case EVENT_UID_RULES_CHANGED:
+                    handleUidRulesChanged(msg.arg1, msg.arg2);
+                    break;
+                case EVENT_DATA_SAVER_CHANGED:
+                    handleRestrictBackgroundChanged(toBool(msg.arg1));
+                    break;
             }
         }
     }
@@ -3303,31 +3603,6 @@
         mProxyTracker.setGlobalProxy(proxyProperties);
     }
 
-    private void loadGlobalProxy() {
-        ContentResolver res = mContext.getContentResolver();
-        String host = Settings.Global.getString(res, Settings.Global.GLOBAL_HTTP_PROXY_HOST);
-        int port = Settings.Global.getInt(res, Settings.Global.GLOBAL_HTTP_PROXY_PORT, 0);
-        String exclList = Settings.Global.getString(res,
-                Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST);
-        String pacFileUrl = Settings.Global.getString(res, Settings.Global.GLOBAL_HTTP_PROXY_PAC);
-        if (!TextUtils.isEmpty(host) || !TextUtils.isEmpty(pacFileUrl)) {
-            ProxyInfo proxyProperties;
-            if (!TextUtils.isEmpty(pacFileUrl)) {
-                proxyProperties = new ProxyInfo(pacFileUrl);
-            } else {
-                proxyProperties = new ProxyInfo(host, port, exclList);
-            }
-            if (!proxyProperties.isValid()) {
-                if (DBG) log("Invalid proxy properties, ignoring: " + proxyProperties.toString());
-                return;
-            }
-
-            synchronized (mProxyTracker.mProxyLock) {
-                mProxyTracker.mGlobalProxy = proxyProperties;
-            }
-        }
-    }
-
     @Override
     @Nullable
     public ProxyInfo getGlobalProxy() {
@@ -3352,30 +3627,7 @@
         ProxyInfo oldProxyInfo = oldLp == null ? null : oldLp.getHttpProxy();
 
         if (!ProxyTracker.proxyInfoEqual(newProxyInfo, oldProxyInfo)) {
-            mProxyTracker.sendProxyBroadcast(mProxyTracker.getDefaultProxy());
-        }
-    }
-
-    private void handleDeprecatedGlobalHttpProxy() {
-        final String proxy = Settings.Global.getString(mContext.getContentResolver(),
-                Settings.Global.HTTP_PROXY);
-        if (!TextUtils.isEmpty(proxy)) {
-            String data[] = proxy.split(":");
-            if (data.length == 0) {
-                return;
-            }
-
-            final String proxyHost = data[0];
-            int proxyPort = 8080;
-            if (data.length > 1) {
-                try {
-                    proxyPort = Integer.parseInt(data[1]);
-                } catch (NumberFormatException e) {
-                    return;
-                }
-            }
-            final ProxyInfo p = new ProxyInfo(proxyHost, proxyPort, "");
-            setGlobalProxy(p);
+            mProxyTracker.sendProxyBroadcast();
         }
     }
 
@@ -3646,7 +3898,7 @@
                     Slog.w(TAG, "VPN for user " + user + " not ready yet. Skipping lockdown");
                     return false;
                 }
-                setLockdownTracker(new LockdownVpnTracker(mContext, mNetd, this, vpn, profile));
+                setLockdownTracker(new LockdownVpnTracker(mContext, mNMS, this, vpn, profile));
             } else {
                 setLockdownTracker(null);
             }
@@ -3663,6 +3915,8 @@
     private void setLockdownTracker(LockdownVpnTracker tracker) {
         // Shutdown any existing tracker
         final LockdownVpnTracker existing = mLockdownTracker;
+        // TODO: Add a trigger when the always-on VPN enable/disable to reevaluate and send the
+        // necessary onBlockedStatusChanged callbacks.
         mLockdownTracker = null;
         if (existing != null) {
             existing.shutdown();
@@ -3881,7 +4135,7 @@
 
     @Override
     public void setAirplaneMode(boolean enable) {
-        enforceConnectivityInternalPermission();
+        enforceNetworkStackSettingsOrSetup();
         final long ident = Binder.clearCallingIdentity();
         try {
             final ContentResolver cr = mContext.getContentResolver();
@@ -3901,7 +4155,7 @@
                 loge("Starting user already has a VPN");
                 return;
             }
-            userVpn = new Vpn(mHandler.getLooper(), mContext, mNetd, userId);
+            userVpn = new Vpn(mHandler.getLooper(), mContext, mNMS, userId);
             mVpns.put(userId, userVpn);
             if (mUserManager.getUserInfo(userId).isPrimary() && LockdownVpnTracker.isEnabled()) {
                 updateLockdownVpn();
@@ -3922,6 +4176,7 @@
     }
 
     private void onUserAdded(int userId) {
+        mPermissionMonitor.onUserAdded(userId);
         synchronized (mVpns) {
             final int vpnsSize = mVpns.size();
             for (int i = 0; i < vpnsSize; i++) {
@@ -3932,6 +4187,7 @@
     }
 
     private void onUserRemoved(int userId) {
+        mPermissionMonitor.onUserRemoved(userId);
         synchronized (mVpns) {
             final int vpnsSize = mVpns.size();
             for (int i = 0; i < vpnsSize; i++) {
@@ -3941,6 +4197,56 @@
         }
     }
 
+    private void onPackageAdded(String packageName, int uid) {
+        if (TextUtils.isEmpty(packageName) || uid < 0) {
+            Slog.wtf(TAG, "Invalid package in onPackageAdded: " + packageName + " | " + uid);
+            return;
+        }
+        mPermissionMonitor.onPackageAdded(packageName, uid);
+    }
+
+    private void onPackageReplaced(String packageName, int uid) {
+        if (TextUtils.isEmpty(packageName) || uid < 0) {
+            Slog.wtf(TAG, "Invalid package in onPackageReplaced: " + packageName + " | " + uid);
+            return;
+        }
+        final int userId = UserHandle.getUserId(uid);
+        synchronized (mVpns) {
+            final Vpn vpn = mVpns.get(userId);
+            if (vpn == null) {
+                return;
+            }
+            // Legacy always-on VPN won't be affected since the package name is not set.
+            if (TextUtils.equals(vpn.getAlwaysOnPackage(), packageName)) {
+                Slog.d(TAG, "Restarting always-on VPN package " + packageName + " for user "
+                        + userId);
+                vpn.startAlwaysOnVpn();
+            }
+        }
+    }
+
+    private void onPackageRemoved(String packageName, int uid, boolean isReplacing) {
+        if (TextUtils.isEmpty(packageName) || uid < 0) {
+            Slog.wtf(TAG, "Invalid package in onPackageRemoved: " + packageName + " | " + uid);
+            return;
+        }
+        mPermissionMonitor.onPackageRemoved(uid);
+
+        final int userId = UserHandle.getUserId(uid);
+        synchronized (mVpns) {
+            final Vpn vpn = mVpns.get(userId);
+            if (vpn == null) {
+                return;
+            }
+            // Legacy always-on VPN won't be affected since the package name is not set.
+            if (TextUtils.equals(vpn.getAlwaysOnPackage(), packageName) && !isReplacing) {
+                Slog.d(TAG, "Removing always-on VPN package " + packageName + " for user "
+                        + userId);
+                vpn.setAlwaysOnPackage(null, false);
+            }
+        }
+    }
+
     private void onUserUnlocked(int userId) {
         synchronized (mVpns) {
             // User present may be sent because of an unlock, which might mean an unlocked keystore.
@@ -3952,11 +4258,15 @@
         }
     }
 
-    private BroadcastReceiver mUserIntentReceiver = new BroadcastReceiver() {
+    private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
             final String action = intent.getAction();
             final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+            final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+            final Uri packageData = intent.getData();
+            final String packageName =
+                    packageData != null ? packageData.getSchemeSpecificPart() : null;
             if (userId == UserHandle.USER_NULL) return;
 
             if (Intent.ACTION_USER_STARTED.equals(action)) {
@@ -3969,6 +4279,14 @@
                 onUserRemoved(userId);
             } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
                 onUserUnlocked(userId);
+            } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
+                onPackageAdded(packageName, uid);
+            } else if (Intent.ACTION_PACKAGE_REPLACED.equals(action)) {
+                onPackageReplaced(packageName, uid);
+            } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
+                final boolean isReplacing = intent.getBooleanExtra(
+                        Intent.EXTRA_REPLACING, false);
+                onPackageRemoved(packageName, uid, isReplacing);
             }
         }
     };
@@ -4212,6 +4530,14 @@
         }
         if (nai != null) {
             nai.asyncChannel.sendMessage(android.net.NetworkAgent.CMD_REQUEST_BANDWIDTH_UPDATE);
+            synchronized (mBandwidthRequests) {
+                final int uid = Binder.getCallingUid();
+                Integer uidReqs = mBandwidthRequests.get(uid);
+                if (uidReqs == null) {
+                    uidReqs = new Integer(0);
+                }
+                mBandwidthRequests.put(uid, ++uidReqs);
+            }
             return true;
         }
         return false;
@@ -4413,6 +4739,10 @@
     // priority networks like Wi-Fi are active.
     private final NetworkRequest mDefaultMobileDataRequest;
 
+    // Request used to optionally keep wifi data active even when higher
+    // priority networks like ethernet are active.
+    private final NetworkRequest mDefaultWifiRequest;
+
     private NetworkAgentInfo getNetworkForRequest(int requestId) {
         synchronized (mNetworkForRequestId) {
             return mNetworkForRequestId.get(requestId);
@@ -4483,8 +4813,8 @@
         updateUids(nai, null, nai.networkCapabilities);
     }
 
-    private void updateLinkProperties(NetworkAgentInfo networkAgent, LinkProperties oldLp) {
-        LinkProperties newLp = new LinkProperties(networkAgent.linkProperties);
+    private void updateLinkProperties(NetworkAgentInfo networkAgent, LinkProperties newLp,
+            LinkProperties oldLp) {
         int netId = networkAgent.network.netId;
 
         // The NetworkAgentInfo does not know whether clatd is running on its network or not. Before
@@ -4499,7 +4829,9 @@
 //        for (LinkProperties lp : newLp.getStackedLinks()) {
 //            updateMtu(lp, null);
 //        }
-        updateTcpBufferSizes(networkAgent);
+        if (isDefaultNetwork(networkAgent)) {
+            updateTcpBufferSizes(newLp.getTcpBufferSizes());
+        }
 
         updateRoutes(newLp, oldLp, netId);
         updateDnses(newLp, oldLp, netId);
@@ -4509,8 +4841,6 @@
         // updateDnses will fetch the private DNS configuration from DnsManager.
         mDnsManager.updatePrivateDnsStatus(netId, newLp);
 
-        // Start or stop clat accordingly to network state.
-        networkAgent.updateClat(mNetd);
         if (isDefaultNetwork(networkAgent)) {
             handleApplyDefaultProxy(newLp.getHttpProxy());
         } else {
@@ -4521,8 +4851,12 @@
             synchronized (networkAgent) {
                 networkAgent.linkProperties = newLp;
             }
+            // Start or stop clat accordingly to network state.
+            networkAgent.updateClat(mNMS);
             notifyIfacesChangedForNetworkStats();
-            notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_IP_CHANGED);
+            if (networkAgent.everConnected) {
+                notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_IP_CHANGED);
+            }
         }
 
         mKeepaliveTracker.handleCheckKeepalivesStillValid(networkAgent);
@@ -4549,9 +4883,9 @@
         final String prefix = "iface:" + iface;
         try {
             if (add) {
-                mNetd.getNetdService().wakeupAddInterface(iface, prefix, mark, mask);
+                mNetd.wakeupAddInterface(iface, prefix, mark, mask);
             } else {
-                mNetd.getNetdService().wakeupDelInterface(iface, prefix, mark, mask);
+                mNetd.wakeupDelInterface(iface, prefix, mark, mask);
             }
         } catch (Exception e) {
             loge("Exception modifying wakeup packet monitoring: " + e);
@@ -4567,7 +4901,7 @@
         for (String iface : interfaceDiff.added) {
             try {
                 if (DBG) log("Adding iface " + iface + " to network " + netId);
-                mNetd.addInterfaceToNetwork(iface, netId);
+                mNMS.addInterfaceToNetwork(iface, netId);
                 wakeupModifyInterface(iface, caps, true);
             } catch (Exception e) {
                 loge("Exception adding interface: " + e);
@@ -4577,7 +4911,7 @@
             try {
                 if (DBG) log("Removing iface " + iface + " from network " + netId);
                 wakeupModifyInterface(iface, caps, false);
-                mNetd.removeInterfaceFromNetwork(iface, netId);
+                mNMS.removeInterfaceFromNetwork(iface, netId);
             } catch (Exception e) {
                 loge("Exception removing interface: " + e);
             }
@@ -4599,9 +4933,9 @@
         // do this twice, adding non-next-hop routes first, then routes they are dependent on
         for (RouteInfo route : routeDiff.added) {
             if (route.hasGateway()) continue;
-            if (VDBG) log("Adding Route [" + route + "] to network " + netId);
+            if (VDBG || DDBG) log("Adding Route [" + route + "] to network " + netId);
             try {
-                mNetd.addRoute(netId, route);
+                mNMS.addRoute(netId, route);
             } catch (Exception e) {
                 if ((route.getDestination().getAddress() instanceof Inet4Address) || VDBG) {
                     loge("Exception in addRoute for non-gateway: " + e);
@@ -4610,9 +4944,9 @@
         }
         for (RouteInfo route : routeDiff.added) {
             if (route.hasGateway() == false) continue;
-            if (VDBG) log("Adding Route [" + route + "] to network " + netId);
+            if (VDBG || DDBG) log("Adding Route [" + route + "] to network " + netId);
             try {
-                mNetd.addRoute(netId, route);
+                mNMS.addRoute(netId, route);
             } catch (Exception e) {
                 if ((route.getGateway() instanceof Inet4Address) || VDBG) {
                     loge("Exception in addRoute for gateway: " + e);
@@ -4621,9 +4955,9 @@
         }
 
         for (RouteInfo route : routeDiff.removed) {
-            if (VDBG) log("Removing Route [" + route + "] from network " + netId);
+            if (VDBG || DDBG) log("Removing Route [" + route + "] from network " + netId);
             try {
-                mNetd.removeRoute(netId, route);
+                mNMS.removeRoute(netId, route);
             } catch (Exception e) {
                 loge("Exception in removeRoute: " + e);
             }
@@ -4650,15 +4984,14 @@
         }
     }
 
-    private String getNetworkPermission(NetworkCapabilities nc) {
-        // TODO: make these permission strings AIDL constants instead.
+    private int getNetworkPermission(NetworkCapabilities nc) {
         if (!nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)) {
-            return NetworkManagementService.PERMISSION_SYSTEM;
+            return INetd.PERMISSION_SYSTEM;
         }
         if (!nc.hasCapability(NET_CAPABILITY_FOREGROUND)) {
-            return NetworkManagementService.PERMISSION_NETWORK;
+            return INetd.PERMISSION_NETWORK;
         }
-        return null;
+        return INetd.PERMISSION_NONE;
     }
 
     /**
@@ -4668,7 +5001,12 @@
      */
     private NetworkCapabilities mixInCapabilities(NetworkAgentInfo nai, NetworkCapabilities nc) {
         // Once a NetworkAgent is connected, complain if some immutable capabilities are removed.
+         // Don't complain for VPNs since they're not driven by requests and there is no risk of
+         // causing a connect/teardown loop.
+         // TODO: remove this altogether and make it the responsibility of the NetworkFactories to
+         // avoid connect/teardown loops.
         if (nai.everConnected &&
+                !nai.isVPN() &&
                 !nai.networkCapabilities.satisfiedByImmutableNetworkCapabilities(nc)) {
             // TODO: consider not complaining when a network agent degrades its capabilities if this
             // does not cause any request (that is not a listen) currently matching that agent to
@@ -4726,11 +5064,11 @@
 
         if (Objects.equals(nai.networkCapabilities, newNc)) return;
 
-        final String oldPermission = getNetworkPermission(nai.networkCapabilities);
-        final String newPermission = getNetworkPermission(newNc);
-        if (!Objects.equals(oldPermission, newPermission) && nai.created && !nai.isVPN()) {
+        final int oldPermission = getNetworkPermission(nai.networkCapabilities);
+        final int newPermission = getNetworkPermission(newNc);
+        if (oldPermission != newPermission && nai.created && !nai.isVPN()) {
             try {
-                mNetd.setNetworkPermission(nai.network.netId, newPermission);
+                mNMS.setNetworkPermission(nai.network.netId, newPermission);
             } catch (RemoteException e) {
                 loge("Exception in setNetworkPermission: " + e);
             }
@@ -4757,12 +5095,20 @@
             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) !=
-                    newNc.hasCapability(NET_CAPABILITY_NOT_METERED);
+            final boolean oldMetered = prevNc.isMetered();
+            final boolean newMetered = newNc.isMetered();
+            final boolean meteredChanged = oldMetered != newMetered;
+
+            if (meteredChanged) {
+                maybeNotifyNetworkBlocked(nai, oldMetered, newMetered, mRestrictBackground,
+                        mRestrictBackground);
+            }
+
             final boolean roamingChanged = prevNc.hasCapability(NET_CAPABILITY_NOT_ROAMING) !=
                     newNc.hasCapability(NET_CAPABILITY_NOT_ROAMING);
+
+            // Report changes that are interesting for network statistics tracking.
             if (meteredChanged || roamingChanged) {
                 notifyIfacesChangedForNetworkStats();
             }
@@ -4790,12 +5136,12 @@
             if (!newRanges.isEmpty()) {
                 final UidRange[] addedRangesArray = new UidRange[newRanges.size()];
                 newRanges.toArray(addedRangesArray);
-                mNetd.addVpnUidRanges(nai.network.netId, addedRangesArray);
+                mNMS.addVpnUidRanges(nai.network.netId, addedRangesArray);
             }
             if (!prevRanges.isEmpty()) {
                 final UidRange[] removedRangesArray = new UidRange[prevRanges.size()];
                 prevRanges.toArray(removedRangesArray);
-                mNetd.removeVpnUidRanges(nai.network.netId, removedRangesArray);
+                mNMS.removeVpnUidRanges(nai.network.netId, removedRangesArray);
             }
         } catch (Exception e) {
             // Never crash!
@@ -4810,18 +5156,12 @@
         }
         // newLp is already a defensive copy.
         newLp.ensureDirectlyConnectedRoutes();
-        if (VDBG) {
+        if (VDBG || DDBG) {
             log("Update of LinkProperties for " + nai.name() +
                     "; created=" + nai.created +
                     "; everConnected=" + nai.everConnected);
         }
-        LinkProperties oldLp = nai.linkProperties;
-        synchronized (nai) {
-            nai.linkProperties = newLp;
-        }
-        if (nai.everConnected) {
-            updateLinkProperties(nai, oldLp);
-        }
+        updateLinkProperties(nai, newLp, new LinkProperties(nai.linkProperties));
     }
 
     private void sendUpdatedScoreToFactories(NetworkAgentInfo nai) {
@@ -4834,7 +5174,9 @@
     }
 
     private void sendUpdatedScoreToFactories(NetworkRequest networkRequest, int score) {
-        if (VDBG) log("sending new Min Network Score(" + score + "): " + networkRequest.toString());
+        if (VDBG || DDBG){
+            log("sending new Min Network Score(" + score + "): " + networkRequest.toString());
+        }
         for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) {
             nfi.asyncChannel.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK, score, 0,
                     networkRequest);
@@ -4892,6 +5234,8 @@
             case ConnectivityManager.CALLBACK_AVAILABLE: {
                 putParcelable(bundle, new NetworkCapabilities(networkAgent.networkCapabilities));
                 putParcelable(bundle, new LinkProperties(networkAgent.linkProperties));
+                // For this notification, arg1 contains the blocked status.
+                msg.arg1 = arg1;
                 break;
             }
             case ConnectivityManager.CALLBACK_LOSING: {
@@ -4909,6 +5253,11 @@
                 putParcelable(bundle, new LinkProperties(networkAgent.linkProperties));
                 break;
             }
+            case ConnectivityManager.CALLBACK_BLK_CHANGED: {
+                maybeLogBlockedStatusChanged(nri, networkAgent.network, arg1 != 0);
+                msg.arg1 = arg1;
+                break;
+            }
         }
         msg.what = notificationType;
         msg.setData(bundle);
@@ -4964,16 +5313,16 @@
 
     private void makeDefault(NetworkAgentInfo newNetwork) {
         if (DBG) log("Switching to new default network: " + newNetwork);
-        setupDataActivityTracking(newNetwork);
+
         try {
-            mNetd.setDefaultNetId(newNetwork.network.netId);
+            mNMS.setDefaultNetId(newNetwork.network.netId);
         } catch (Exception e) {
             loge("Exception setting default network :" + e);
         }
 
         notifyLockdownVpn(newNetwork);
         handleApplyDefaultProxy(newNetwork.linkProperties.getHttpProxy());
-        updateTcpBufferSizes(newNetwork);
+        updateTcpBufferSizes(newNetwork.linkProperties.getTcpBufferSizes());
         mDnsManager.setDefaultDnsSystemProperties(newNetwork.linkProperties.getDnsServers());
         notifyIfacesChangedForNetworkStats();
     }
@@ -5040,7 +5389,7 @@
         final boolean wasBackgroundNetwork = newNetwork.isBackgroundNetwork();
         final int score = newNetwork.getCurrentScore();
 
-        if (VDBG) log("rematching " + newNetwork.name());
+        if (VDBG || DDBG) log("rematching " + newNetwork.name());
 
         // Find and migrate to this Network any NetworkRequests for
         // which this network is now the best.
@@ -5071,7 +5420,7 @@
             if (satisfies) {
                 // next check if it's better than any current network we're using for
                 // this request
-                if (VDBG) {
+                if (VDBG || DDBG) {
                     log("currentScore = " +
                             (currentNetwork != null ? currentNetwork.getCurrentScore() : 0) +
                             ", newScore = " + score);
@@ -5079,12 +5428,14 @@
                 if (currentNetwork == null || currentNetwork.getCurrentScore() < score) {
                     if (VDBG) log("rematch for " + newNetwork.name());
                     if (currentNetwork != null) {
-                        if (VDBG) log("   accepting network in place of " + currentNetwork.name());
+                        if (VDBG || DDBG){
+                            log("   accepting network in place of " + currentNetwork.name());
+                        }
                         currentNetwork.removeRequest(nri.request.requestId);
                         currentNetwork.lingerRequest(nri.request, now, mLingerDelayMs);
                         affectedNetworks.add(currentNetwork);
                     } else {
-                        if (VDBG) log("   accepting network in place of null");
+                        if (VDBG || DDBG) log("   accepting network in place of null");
                     }
                     newNetwork.unlingerRequest(nri.request);
                     setNetworkForRequest(nri.request.requestId, newNetwork);
@@ -5139,6 +5490,7 @@
             }
         }
         if (isNewDefault) {
+            updateDataActivityTracking(newNetwork, oldDefaultNetwork);
             // Notify system services that this network is up.
             makeDefault(newNetwork);
             // Log 0 -> X and Y -> X default network transitions, where X is the new default.
@@ -5361,12 +5713,11 @@
             try {
                 // This should never fail.  Specifying an already in use NetID will cause failure.
                 if (networkAgent.isVPN()) {
-                    mNetd.createVirtualNetwork(networkAgent.network.netId,
-                            !networkAgent.linkProperties.getDnsServers().isEmpty(),
+                    mNMS.createVirtualNetwork(networkAgent.network.netId,
                             (networkAgent.networkMisc == null ||
                                 !networkAgent.networkMisc.allowBypass));
                 } else {
-                    mNetd.createPhysicalNetwork(networkAgent.network.netId,
+                    mNMS.createPhysicalNetwork(networkAgent.network.netId,
                             getNetworkPermission(networkAgent.networkCapabilities));
                 }
             } catch (Exception e) {
@@ -5385,22 +5736,15 @@
             }
 
             handlePerNetworkPrivateDnsConfig(networkAgent, mDnsManager.getPrivateDnsConfig());
-            updateLinkProperties(networkAgent, null);
+            updateLinkProperties(networkAgent, new LinkProperties(networkAgent.linkProperties),
+                    null);
 
             networkAgent.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_CONNECTED);
             scheduleUnvalidatedPrompt(networkAgent);
 
             if (networkAgent.isVPN()) {
                 // Temporarily disable the default proxy (not global).
-                synchronized (mProxyTracker.mProxyLock) {
-                    if (!mProxyTracker.mDefaultProxyDisabled) {
-                        mProxyTracker.mDefaultProxyDisabled = true;
-                        if (mProxyTracker.mGlobalProxy == null
-                                && mProxyTracker.mDefaultProxy != null) {
-                            mProxyTracker.sendProxyBroadcast(null);
-                        }
-                    }
-                }
+                mProxyTracker.setDefaultProxyEnabled(false);
                 // TODO: support proxy per network.
             }
 
@@ -5422,15 +5766,7 @@
         } else if (state == NetworkInfo.State.DISCONNECTED) {
             networkAgent.asyncChannel.disconnect();
             if (networkAgent.isVPN()) {
-                synchronized (mProxyTracker.mProxyLock) {
-                    if (mProxyTracker.mDefaultProxyDisabled) {
-                        mProxyTracker.mDefaultProxyDisabled = false;
-                        if (mProxyTracker.mGlobalProxy == null
-                                && mProxyTracker.mDefaultProxy != null) {
-                            mProxyTracker.sendProxyBroadcast(mProxyTracker.mDefaultProxy);
-                        }
-                    }
-                }
+                mProxyTracker.setDefaultProxyEnabled(true);
                 updateUids(networkAgent, networkAgent.networkCapabilities, null);
             }
             disconnectAndDestroyNetwork(networkAgent);
@@ -5452,7 +5788,7 @@
     }
 
     private void updateNetworkScore(NetworkAgentInfo nai, int score) {
-        if (VDBG) log("updateNetworkScore for " + nai.name() + " to " + score);
+        if (VDBG || DDBG) log("updateNetworkScore for " + nai.name() + " to " + score);
         if (score < 0) {
             loge("updateNetworkScore for " + nai.name() + " got a negative score (" + score +
                     ").  Bumping score to min of 0");
@@ -5479,7 +5815,76 @@
             return;
         }
 
-        callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_AVAILABLE, 0);
+        final boolean metered = nai.networkCapabilities.isMetered();
+        final boolean blocked = isUidNetworkingWithVpnBlocked(nri.mUid, mUidRules.get(nri.mUid),
+                metered, mRestrictBackground);
+        callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_AVAILABLE, blocked ? 1 : 0);
+    }
+
+    /**
+     * Notify of the blocked state apps with a registered callback matching a given NAI.
+     *
+     * Unlike other callbacks, blocked status is different between each individual uid. So for
+     * any given nai, all requests need to be considered according to the uid who filed it.
+     *
+     * @param nai The target NetworkAgentInfo.
+     * @param oldMetered True if the previous network capabilities is metered.
+     * @param newRestrictBackground True if data saver is enabled.
+     */
+    private void maybeNotifyNetworkBlocked(NetworkAgentInfo nai, boolean oldMetered,
+            boolean newMetered, boolean oldRestrictBackground, boolean newRestrictBackground) {
+
+        for (int i = 0; i < nai.numNetworkRequests(); i++) {
+            NetworkRequest nr = nai.requestAt(i);
+            NetworkRequestInfo nri = mNetworkRequests.get(nr);
+            final int uidRules = mUidRules.get(nri.mUid);
+            final boolean oldBlocked, newBlocked;
+            // mVpns lock needs to be hold here to ensure that the active VPN cannot be changed
+            // between these two calls.
+            synchronized (mVpns) {
+                oldBlocked = isUidNetworkingWithVpnBlocked(nri.mUid, uidRules, oldMetered,
+                        oldRestrictBackground);
+                newBlocked = isUidNetworkingWithVpnBlocked(nri.mUid, uidRules, newMetered,
+                        newRestrictBackground);
+            }
+            if (oldBlocked != newBlocked) {
+                callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_BLK_CHANGED,
+                        encodeBool(newBlocked));
+            }
+        }
+    }
+
+    /**
+     * Notify apps with a given UID of the new blocked state according to new uid rules.
+     * @param uid The uid for which the rules changed.
+     * @param newRules The new rules to apply.
+     */
+    private void maybeNotifyNetworkBlockedForNewUidRules(int uid, int newRules) {
+        for (final NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
+            final boolean metered = nai.networkCapabilities.isMetered();
+            final boolean oldBlocked, newBlocked;
+            // TODO: Consider that doze mode or turn on/off battery saver would deliver lots of uid
+            // rules changed event. And this function actually loop through all connected nai and
+            // its requests. It seems that mVpns lock will be grabbed frequently in this case.
+            // Reduce the number of locking or optimize the use of lock are likely needed in future.
+            synchronized (mVpns) {
+                oldBlocked = isUidNetworkingWithVpnBlocked(
+                        uid, mUidRules.get(uid), metered, mRestrictBackground);
+                newBlocked = isUidNetworkingWithVpnBlocked(
+                        uid, newRules, metered, mRestrictBackground);
+            }
+            if (oldBlocked == newBlocked) {
+                return;
+            }
+            final int arg = encodeBool(newBlocked);
+            for (int i = 0; i < nai.numNetworkRequests(); i++) {
+                NetworkRequest nr = nai.requestAt(i);
+                NetworkRequestInfo nri = mNetworkRequests.get(nr);
+                if (nri != null && nri.mUid == uid) {
+                    callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_BLK_CHANGED, arg);
+                }
+            }
+        }
     }
 
     private void sendLegacyNetworkBroadcast(NetworkAgentInfo nai, DetailedState state, int type) {
@@ -5529,7 +5934,7 @@
     }
 
     protected void notifyNetworkCallbacks(NetworkAgentInfo networkAgent, int notifyType, int arg1) {
-        if (VDBG) {
+        if (VDBG || DDBG) {
             String notification = ConnectivityManager.getCallbackName(notifyType);
             log("notifyType " + notification + " for " + networkAgent.name());
         }
@@ -5696,6 +6101,17 @@
                 Settings.Global.NETWORK_AVOID_BAD_WIFI, null);
     }
 
+    @Override
+    public byte[] getNetworkWatchlistConfigHash() {
+        NetworkWatchlistManager nwm = mContext.getSystemService(NetworkWatchlistManager.class);
+        if (nwm == null) {
+            loge("Unable to get NetworkWatchlistManager");
+            return null;
+        }
+        // Redirect it to network watchlist service to access watchlist file and calculate hash.
+        return nwm.getWatchlistConfigHash();
+    }
+
     @VisibleForTesting
     public NetworkMonitor createNetworkMonitor(Context context, Handler handler,
             NetworkAgentInfo nai, NetworkRequest defaultRequest) {
@@ -5792,4 +6208,49 @@
             pw.println("    Get airplane mode.");
         }
     }
-}
\ No newline at end of file
+
+    /**
+     * Caller either needs to be an active VPN, or hold the NETWORK_STACK permission
+     * for testing.
+     */
+    private Vpn enforceActiveVpnOrNetworkStackPermission() {
+        if (checkNetworkStackPermission()) {
+            return null;
+        }
+        final int uid = Binder.getCallingUid();
+        final int user = UserHandle.getUserId(uid);
+        synchronized (mVpns) {
+            Vpn vpn = mVpns.get(user);
+            try {
+                if (vpn.getVpnInfo().ownerUid == uid) return vpn;
+            } catch (NullPointerException e) {
+                /* vpn is null, or VPN is not connected and getVpnInfo() is null. */
+            }
+        }
+        throw new SecurityException("App must either be an active VPN or have the NETWORK_STACK "
+                + "permission");
+    }
+
+    /**
+     * @param connectionInfo the connection to resolve.
+     * @return {@code uid} if the connection is found and the app has permission to observe it
+     * (e.g., if it is associated with the calling VPN app's tunnel) or {@code INVALID_UID} if the
+     * connection is not found.
+     */
+    public int getConnectionOwnerUid(ConnectionInfo connectionInfo) {
+        final Vpn vpn = enforceActiveVpnOrNetworkStackPermission();
+        if (connectionInfo.protocol != IPPROTO_TCP && connectionInfo.protocol != IPPROTO_UDP) {
+            throw new IllegalArgumentException("Unsupported protocol " + connectionInfo.protocol);
+        }
+
+        final int uid = InetDiagMessage.getConnectionOwnerUid(connectionInfo.protocol,
+                connectionInfo.local, connectionInfo.remote);
+
+        /* Filter out Uids not associated with the VPN. */
+        if (vpn != null && !vpn.appliesToUid(uid)) {
+            return INVALID_UID;
+        }
+
+        return uid;
+    }
+}
diff --git a/services/core/java/com/android/server/connectivity/DnsManager.java b/services/core/java/com/android/server/connectivity/DnsManager.java
index d51a196..b8f057d 100644
--- a/services/core/java/com/android/server/connectivity/DnsManager.java
+++ b/services/core/java/com/android/server/connectivity/DnsManager.java
@@ -16,7 +16,7 @@
 
 package com.android.server.connectivity;
 
-import static android.net.ConnectivityManager.PRIVATE_DNS_DEFAULT_MODE;
+import static android.net.ConnectivityManager.PRIVATE_DNS_DEFAULT_MODE_FALLBACK;
 import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF;
 import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
 import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
@@ -24,6 +24,7 @@
 import static android.provider.Settings.Global.DNS_RESOLVER_MAX_SAMPLES;
 import static android.provider.Settings.Global.DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS;
 import static android.provider.Settings.Global.DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT;
+import static android.provider.Settings.Global.PRIVATE_DNS_DEFAULT_MODE;
 import static android.provider.Settings.Global.PRIVATE_DNS_MODE;
 import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER;
 
@@ -34,7 +35,6 @@
 import android.net.Network;
 import android.net.NetworkUtils;
 import android.net.Uri;
-import android.net.dns.ResolvUtil;
 import android.os.Binder;
 import android.os.INetworkManagementService;
 import android.os.UserHandle;
@@ -173,17 +173,9 @@
         return new PrivateDnsConfig(useTls);
     }
 
-    public static PrivateDnsConfig tryBlockingResolveOf(Network network, String name) {
-        try {
-            final InetAddress[] ips = ResolvUtil.blockingResolveAllLocally(network, name);
-            return new PrivateDnsConfig(name, ips);
-        } catch (UnknownHostException uhe) {
-            return new PrivateDnsConfig(name, null);
-        }
-    }
-
     public static Uri[] getPrivateDnsSettingsUris() {
         return new Uri[]{
+            Settings.Global.getUriFor(PRIVATE_DNS_DEFAULT_MODE),
             Settings.Global.getUriFor(PRIVATE_DNS_MODE),
             Settings.Global.getUriFor(PRIVATE_DNS_SPECIFIER),
         };
@@ -485,8 +477,10 @@
     }
 
     private static String getPrivateDnsMode(ContentResolver cr) {
-        final String mode = getStringSetting(cr, PRIVATE_DNS_MODE);
-        return !TextUtils.isEmpty(mode) ? mode : PRIVATE_DNS_DEFAULT_MODE;
+        String mode = getStringSetting(cr, PRIVATE_DNS_MODE);
+        if (TextUtils.isEmpty(mode)) mode = getStringSetting(cr, PRIVATE_DNS_DEFAULT_MODE);
+        if (TextUtils.isEmpty(mode)) mode = PRIVATE_DNS_DEFAULT_MODE_FALLBACK;
+        return mode;
     }
 
     private static String getStringSetting(ContentResolver cr, String which) {
diff --git a/services/core/java/com/android/server/connectivity/LingerMonitor.java b/services/core/java/com/android/server/connectivity/LingerMonitor.java
index 635db19..929dfc4 100644
--- a/services/core/java/com/android/server/connectivity/LingerMonitor.java
+++ b/services/core/java/com/android/server/connectivity/LingerMonitor.java
@@ -90,6 +90,8 @@
         mNotifier = notifier;
         mDailyLimit = dailyLimit;
         mRateLimitMillis = rateLimitMillis;
+        // Ensure that (now - mLastNotificationMillis) >= rateLimitMillis at first
+        mLastNotificationMillis = -rateLimitMillis;
     }
 
     private static HashMap<String, Integer> makeTransportToNameMap() {
diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
index f523d59..6596d27 100644
--- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java
+++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
@@ -93,7 +93,9 @@
         // We only run clat on networks that don't have a native IPv4 address.
         final boolean hasIPv4Address =
                 (nai.linkProperties != null) && nai.linkProperties.hasIPv4Address();
-        return supported && connected && !hasIPv4Address;
+        final boolean skip464xlat =
+                (nai.netMisc() != null) && nai.netMisc().skip464xlat;
+        return supported && connected && !hasIPv4Address && !skip464xlat;
     }
 
     /**
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 505480e..262184b 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -266,6 +266,10 @@
         return mConnService;
     }
 
+    public NetworkMisc netMisc() {
+        return networkMisc;
+    }
+
     public Handler handler() {
         return mHandler;
     }
diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
index d578e95..420b23e 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
@@ -23,22 +23,22 @@
 import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
 import static android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
 import static android.content.pm.PackageManager.GET_PERMISSIONS;
+import static android.os.Process.INVALID_UID;
+import static android.os.Process.SYSTEM_UID;
 
-import android.content.BroadcastReceiver;
+import android.annotation.NonNull;
 import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.UserInfo;
 import android.net.Uri;
+import android.os.Build;
 import android.os.INetworkManagementService;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.text.TextUtils;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -60,14 +60,14 @@
 public class PermissionMonitor {
     private static final String TAG = "PermissionMonitor";
     private static final boolean DBG = true;
-    private static final Boolean SYSTEM = Boolean.TRUE;
-    private static final Boolean NETWORK = Boolean.FALSE;
+    protected static final Boolean SYSTEM = Boolean.TRUE;
+    protected static final Boolean NETWORK = Boolean.FALSE;
+    private static final int VERSION_Q = Build.VERSION_CODES.Q;
 
     private final Context mContext;
     private final PackageManager mPackageManager;
     private final UserManager mUserManager;
     private final INetworkManagementService mNetd;
-    private final BroadcastReceiver mIntentReceiver;
 
     // Values are User IDs.
     private final Set<Integer> mUsers = new HashSet<>();
@@ -80,26 +80,6 @@
         mPackageManager = context.getPackageManager();
         mUserManager = UserManager.get(context);
         mNetd = netd;
-        mIntentReceiver = new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                String action = intent.getAction();
-                int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
-                int appUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
-                Uri appData = intent.getData();
-                String appName = appData != null ? appData.getSchemeSpecificPart() : null;
-
-                if (Intent.ACTION_USER_ADDED.equals(action)) {
-                    onUserAdded(user);
-                } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
-                    onUserRemoved(user);
-                } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
-                    onAppAdded(appName, appUid);
-                } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
-                    onAppRemoved(appUid);
-                }
-            }
-        };
     }
 
     // Intended to be called only once at startup, after the system is ready. Installs a broadcast
@@ -107,17 +87,6 @@
     public synchronized void startMonitoring() {
         log("Monitoring");
 
-        IntentFilter intentFilter = new IntentFilter();
-        intentFilter.addAction(Intent.ACTION_USER_ADDED);
-        intentFilter.addAction(Intent.ACTION_USER_REMOVED);
-        mContext.registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, intentFilter, null, null);
-
-        intentFilter = new IntentFilter();
-        intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
-        intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
-        intentFilter.addDataScheme("package");
-        mContext.registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, intentFilter, null, null);
-
         List<PackageInfo> apps = mPackageManager.getInstalledPackages(GET_PERMISSIONS);
         if (apps == null) {
             loge("No apps");
@@ -125,7 +94,7 @@
         }
 
         for (PackageInfo app : apps) {
-            int uid = app.applicationInfo != null ? app.applicationInfo.uid : -1;
+            int uid = app.applicationInfo != null ? app.applicationInfo.uid : INVALID_UID;
             if (uid < 0) {
                 continue;
             }
@@ -155,9 +124,13 @@
     }
 
     @VisibleForTesting
-    boolean isPreinstalledSystemApp(PackageInfo app) {
-        int flags = app.applicationInfo != null ? app.applicationInfo.flags : 0;
-        return (flags & (FLAG_SYSTEM | FLAG_UPDATED_SYSTEM_APP)) != 0;
+    static boolean isVendorApp(@NonNull ApplicationInfo appInfo) {
+        return appInfo.isVendor() || appInfo.isOem() || appInfo.isProduct();
+    }
+
+    @VisibleForTesting
+    protected int getDeviceFirstSdkInt() {
+        return Build.VERSION.FIRST_SDK_INT;
     }
 
     @VisibleForTesting
@@ -177,7 +150,20 @@
     }
 
     private boolean hasRestrictedNetworkPermission(PackageInfo app) {
-        if (isPreinstalledSystemApp(app)) return true;
+        // TODO : remove this check in the future(b/31479477). All apps should just
+        // request the appropriate permission for their use case since android Q.
+        if (app.applicationInfo != null) {
+            // Backward compatibility for b/114245686, on devices that launched before Q daemons
+            // and apps running as the system UID are exempted from this check.
+            if (app.applicationInfo.uid == SYSTEM_UID && getDeviceFirstSdkInt() < VERSION_Q) {
+                return true;
+            }
+
+            if (app.applicationInfo.targetSdkVersion < VERSION_Q
+                    && isVendorApp(app.applicationInfo)) {
+                return true;
+            }
+        }
         return hasPermission(app, CONNECTIVITY_INTERNAL)
                 || hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS);
     }
@@ -186,13 +172,8 @@
         // This function defines what it means to hold the permission to use
         // background networks.
         return hasPermission(app, CHANGE_NETWORK_STATE)
-                || hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS)
-                || hasPermission(app, CONNECTIVITY_INTERNAL)
                 || hasPermission(app, NETWORK_STACK)
-                // TODO : remove this check (b/31479477). Not all preinstalled apps should
-                // have access to background networks, they should just request the appropriate
-                // permission for their use case from the list above.
-                || isPreinstalledSystemApp(app);
+                || hasRestrictedNetworkPermission(app);
     }
 
     public boolean hasUseBackgroundNetworksPermission(int uid) {
@@ -202,7 +183,9 @@
             // Only using the first package name. There may be multiple names if multiple
             // apps share the same UID, but in that case they also share permissions so
             // querying with any of the names will return the same results.
-            final PackageInfo app = mPackageManager.getPackageInfo(names[0], GET_PERMISSIONS);
+            int userId = UserHandle.getUserId(uid);
+            final PackageInfo app = mPackageManager.getPackageInfoAsUser(
+                    names[0], GET_PERMISSIONS, userId);
             return hasUseBackgroundNetworksPermission(app);
         } catch (NameNotFoundException e) {
             // App not found.
@@ -241,7 +224,14 @@
         }
     }
 
-    private synchronized void onUserAdded(int user) {
+    /**
+     * Called when a user is added. See {link #ACTION_USER_ADDED}.
+     *
+     * @param user The integer userHandle of the added user. See {@link #EXTRA_USER_HANDLE}.
+     *
+     * @hide
+     */
+    public synchronized void onUserAdded(int user) {
         if (user < 0) {
             loge("Invalid user in onUserAdded: " + user);
             return;
@@ -253,7 +243,14 @@
         update(users, mApps, true);
     }
 
-    private synchronized void onUserRemoved(int user) {
+    /**
+     * Called when an user is removed. See {link #ACTION_USER_REMOVED}.
+     *
+     * @param user The integer userHandle of the removed user. See {@link #EXTRA_USER_HANDLE}.
+     *
+     * @hide
+     */
+    public synchronized void onUserRemoved(int user) {
         if (user < 0) {
             loge("Invalid user in onUserRemoved: " + user);
             return;
@@ -265,8 +262,8 @@
         update(users, mApps, false);
     }
 
-
-    private Boolean highestPermissionForUid(Boolean currentPermission, String name) {
+    @VisibleForTesting
+    protected Boolean highestPermissionForUid(Boolean currentPermission, String name) {
         if (currentPermission == SYSTEM) {
             return currentPermission;
         }
@@ -284,33 +281,39 @@
         return currentPermission;
     }
 
-    private synchronized void onAppAdded(String appName, int appUid) {
-        if (TextUtils.isEmpty(appName) || appUid < 0) {
-            loge("Invalid app in onAppAdded: " + appName + " | " + appUid);
-            return;
-        }
-
+    /**
+     * Called when a package is added. See {link #ACTION_PACKAGE_ADDED}.
+     *
+     * @param packageName The name of the new package.
+     * @param uid The uid of the new package.
+     *
+     * @hide
+     */
+    public synchronized void onPackageAdded(String packageName, int uid) {
         // If multiple packages share a UID (cf: android:sharedUserId) and ask for different
         // permissions, don't downgrade (i.e., if it's already SYSTEM, leave it as is).
-        final Boolean permission = highestPermissionForUid(mApps.get(appUid), appName);
-        if (permission != mApps.get(appUid)) {
-            mApps.put(appUid, permission);
+        final Boolean permission = highestPermissionForUid(mApps.get(uid), packageName);
+        if (permission != mApps.get(uid)) {
+            mApps.put(uid, permission);
 
             Map<Integer, Boolean> apps = new HashMap<>();
-            apps.put(appUid, permission);
+            apps.put(uid, permission);
             update(mUsers, apps, true);
         }
     }
 
-    private synchronized void onAppRemoved(int appUid) {
-        if (appUid < 0) {
-            loge("Invalid app in onAppRemoved: " + appUid);
-            return;
-        }
+    /**
+     * Called when a package is removed. See {link #ACTION_PACKAGE_REMOVED}.
+     *
+     * @param uid containing the integer uid previously assigned to the package.
+     *
+     * @hide
+     */
+    public synchronized void onPackageRemoved(int uid) {
         Map<Integer, Boolean> apps = new HashMap<>();
 
         Boolean permission = null;
-        String[] packages = mPackageManager.getPackagesForUid(appUid);
+        String[] packages = mPackageManager.getPackagesForUid(uid);
         if (packages != null && packages.length > 0) {
             for (String name : packages) {
                 permission = highestPermissionForUid(permission, name);
@@ -322,16 +325,16 @@
                 }
             }
         }
-        if (permission == mApps.get(appUid)) {
+        if (permission == mApps.get(uid)) {
             // The permissions of this UID have not changed. Nothing to do.
             return;
         } else if (permission != null) {
-            mApps.put(appUid, permission);
-            apps.put(appUid, permission);
+            mApps.put(uid, permission);
+            apps.put(uid, permission);
             update(mUsers, apps, true);
         } else {
-            mApps.remove(appUid);
-            apps.put(appUid, NETWORK);  // doesn't matter which permission we pick here
+            mApps.remove(uid);
+            apps.put(uid, NETWORK);  // doesn't matter which permission we pick here
             update(mUsers, apps, false);
         }
     }
diff --git a/services/core/java/com/android/server/connectivity/ProxyTracker.java b/services/core/java/com/android/server/connectivity/ProxyTracker.java
index dc65e1e..fdddccd 100644
--- a/services/core/java/com/android/server/connectivity/ProxyTracker.java
+++ b/services/core/java/com/android/server/connectivity/ProxyTracker.java
@@ -16,6 +16,12 @@
 
 package com.android.server.connectivity;
 
+import static android.provider.Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST;
+import static android.provider.Settings.Global.GLOBAL_HTTP_PROXY_HOST;
+import static android.provider.Settings.Global.GLOBAL_HTTP_PROXY_PAC;
+import static android.provider.Settings.Global.GLOBAL_HTTP_PROXY_PORT;
+import static android.provider.Settings.Global.HTTP_PROXY;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.ContentResolver;
@@ -47,16 +53,14 @@
     @NonNull
     private final Context mContext;
 
-    // TODO : make this private and import as much managing logic from ConnectivityService as
-    // possible
     @NonNull
-    public final Object mProxyLock = new Object();
+    private final Object mProxyLock = new Object();
     // The global proxy is the proxy that is set device-wide, overriding any network-specific
     // proxy. Note however that proxies are hints ; the system does not enforce their use. Hence
     // this value is only for querying.
     @Nullable
     @GuardedBy("mProxyLock")
-    public ProxyInfo mGlobalProxy = null;
+    private ProxyInfo mGlobalProxy = null;
     // The default proxy is the proxy that applies to no particular network if the global proxy
     // is not set. Individual networks have their own settings that override this. This member
     // is set through setDefaultProxy, which is called when the default network changes proxies
@@ -64,10 +68,10 @@
     // when PacManager resolves the proxy.
     @Nullable
     @GuardedBy("mProxyLock")
-    public volatile ProxyInfo mDefaultProxy = null;
-    // Whether the default proxy is disabled. TODO : make this mDefaultProxyEnabled
+    private volatile ProxyInfo mDefaultProxy = null;
+    // Whether the default proxy is enabled.
     @GuardedBy("mProxyLock")
-    public boolean mDefaultProxyDisabled = false;
+    private boolean mDefaultProxyEnabled = true;
 
     // The object responsible for Proxy Auto Configuration (PAC).
     @NonNull
@@ -85,7 +89,7 @@
     @Nullable
     private static ProxyInfo canonicalizeProxyInfo(@Nullable final ProxyInfo proxy) {
         if (proxy != null && TextUtils.isEmpty(proxy.getHost())
-                && (proxy.getPacFileUrl() == null || Uri.EMPTY.equals(proxy.getPacFileUrl()))) {
+                && Uri.EMPTY.equals(proxy.getPacFileUrl())) {
             return null;
         }
         return proxy;
@@ -122,9 +126,9 @@
     public ProxyInfo getDefaultProxy() {
         // This information is already available as a world read/writable jvm property.
         synchronized (mProxyLock) {
-            final ProxyInfo ret = mGlobalProxy;
-            if ((ret == null) && !mDefaultProxyDisabled) return mDefaultProxy;
-            return ret;
+            if (mGlobalProxy != null) return mGlobalProxy;
+            if (mDefaultProxyEnabled) return mDefaultProxy;
+            return null;
         }
     }
 
@@ -142,16 +146,72 @@
     }
 
     /**
+     * Read the global proxy settings and cache them in memory.
+     */
+    public void loadGlobalProxy() {
+        ContentResolver res = mContext.getContentResolver();
+        String host = Settings.Global.getString(res, GLOBAL_HTTP_PROXY_HOST);
+        int port = Settings.Global.getInt(res, GLOBAL_HTTP_PROXY_PORT, 0);
+        String exclList = Settings.Global.getString(res, GLOBAL_HTTP_PROXY_EXCLUSION_LIST);
+        String pacFileUrl = Settings.Global.getString(res, GLOBAL_HTTP_PROXY_PAC);
+        if (!TextUtils.isEmpty(host) || !TextUtils.isEmpty(pacFileUrl)) {
+            ProxyInfo proxyProperties;
+            if (!TextUtils.isEmpty(pacFileUrl)) {
+                proxyProperties = new ProxyInfo(pacFileUrl);
+            } else {
+                proxyProperties = new ProxyInfo(host, port, exclList);
+            }
+            if (!proxyProperties.isValid()) {
+                if (DBG) Slog.d(TAG, "Invalid proxy properties, ignoring: " + proxyProperties);
+                return;
+            }
+
+            synchronized (mProxyLock) {
+                mGlobalProxy = proxyProperties;
+            }
+        }
+        loadDeprecatedGlobalHttpProxy();
+        // TODO : shouldn't this function call mPacManager.setCurrentProxyScriptUrl ?
+    }
+
+    /**
+     * Read the global proxy from the deprecated Settings.Global.HTTP_PROXY setting and apply it.
+     */
+    public void loadDeprecatedGlobalHttpProxy() {
+        final String proxy = Settings.Global.getString(mContext.getContentResolver(), HTTP_PROXY);
+        if (!TextUtils.isEmpty(proxy)) {
+            String data[] = proxy.split(":");
+            if (data.length == 0) {
+                return;
+            }
+
+            final String proxyHost = data[0];
+            int proxyPort = 8080;
+            if (data.length > 1) {
+                try {
+                    proxyPort = Integer.parseInt(data[1]);
+                } catch (NumberFormatException e) {
+                    return;
+                }
+            }
+            final ProxyInfo p = new ProxyInfo(proxyHost, proxyPort, "");
+            setGlobalProxy(p);
+        }
+    }
+
+    /**
      * Sends the system broadcast informing apps about a new proxy configuration.
      *
      * Confusingly this method also sets the PAC file URL. TODO : separate this, it has nothing
      * to do in a "sendProxyBroadcast" method.
-     * @param proxyInfo the proxy spec, or null for no proxy.
      */
-    // TODO : make the argument NonNull final and the method private
-    public void sendProxyBroadcast(@Nullable ProxyInfo proxyInfo) {
-        if (proxyInfo == null) proxyInfo = new ProxyInfo("", 0, "");
-        if (mPacManager.setCurrentProxyScriptUrl(proxyInfo)) return;
+    public void sendProxyBroadcast() {
+        final ProxyInfo defaultProxy = getDefaultProxy();
+        final ProxyInfo proxyInfo = null != defaultProxy ? defaultProxy : new ProxyInfo("", 0, "");
+        if (mPacManager.setCurrentProxyScriptUrl(proxyInfo)
+                == PacManager.ToSendOrNotToSendBroadcast.DONT_SEND_BROADCAST) {
+            return;
+        }
         if (DBG) Slog.d(TAG, "sending Proxy Broadcast for " + proxyInfo);
         Intent intent = new Intent(Proxy.PROXY_CHANGE_ACTION);
         intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING |
@@ -203,16 +263,15 @@
             final ContentResolver res = mContext.getContentResolver();
             final long token = Binder.clearCallingIdentity();
             try {
-                Settings.Global.putString(res, Settings.Global.GLOBAL_HTTP_PROXY_HOST, host);
-                Settings.Global.putInt(res, Settings.Global.GLOBAL_HTTP_PROXY_PORT, port);
-                Settings.Global.putString(res, Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST,
-                        exclList);
-                Settings.Global.putString(res, Settings.Global.GLOBAL_HTTP_PROXY_PAC, pacFileUrl);
+                Settings.Global.putString(res, GLOBAL_HTTP_PROXY_HOST, host);
+                Settings.Global.putInt(res, GLOBAL_HTTP_PROXY_PORT, port);
+                Settings.Global.putString(res, GLOBAL_HTTP_PROXY_EXCLUSION_LIST, exclList);
+                Settings.Global.putString(res, GLOBAL_HTTP_PROXY_PAC, pacFileUrl);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
 
-            sendProxyBroadcast(mGlobalProxy == null ? mDefaultProxy : proxyInfo);
+            sendProxyBroadcast();
         }
     }
 
@@ -224,10 +283,7 @@
      */
     public void setDefaultProxy(@Nullable ProxyInfo proxyInfo) {
         synchronized (mProxyLock) {
-            if (mDefaultProxy != null && mDefaultProxy.equals(proxyInfo)) {
-                return;
-            }
-            if (mDefaultProxy == proxyInfo) return; // catches repeated nulls
+            if (Objects.equals(mDefaultProxy, proxyInfo)) return;
             if (proxyInfo != null &&  !proxyInfo.isValid()) {
                 if (DBG) Slog.d(TAG, "Invalid proxy properties, ignoring: " + proxyInfo);
                 return;
@@ -242,14 +298,32 @@
                     && (!Uri.EMPTY.equals(proxyInfo.getPacFileUrl()))
                     && proxyInfo.getPacFileUrl().equals(mGlobalProxy.getPacFileUrl())) {
                 mGlobalProxy = proxyInfo;
-                sendProxyBroadcast(mGlobalProxy);
+                sendProxyBroadcast();
                 return;
             }
             mDefaultProxy = proxyInfo;
 
             if (mGlobalProxy != null) return;
-            if (!mDefaultProxyDisabled) {
-                sendProxyBroadcast(proxyInfo);
+            if (mDefaultProxyEnabled) {
+                sendProxyBroadcast();
+            }
+        }
+    }
+
+    /**
+     * Enable or disable the default proxy.
+     *
+     * This sets the flag for enabling/disabling the default proxy and sends the broadcast
+     * if applicable.
+     * @param enabled whether the default proxy should be enabled.
+     */
+    public void setDefaultProxyEnabled(final boolean enabled) {
+        synchronized (mProxyLock) {
+            if (mDefaultProxyEnabled != enabled) {
+                mDefaultProxyEnabled = enabled;
+                if (mGlobalProxy == null && mDefaultProxy != null) {
+                    sendProxyBroadcast();
+                }
             }
         }
     }
diff --git a/services/net/java/android/net/util/MultinetworkPolicyTracker.java b/services/net/java/android/net/util/MultinetworkPolicyTracker.java
index 424e40d..30c5cd9 100644
--- a/services/net/java/android/net/util/MultinetworkPolicyTracker.java
+++ b/services/net/java/android/net/util/MultinetworkPolicyTracker.java
@@ -122,6 +122,7 @@
         return mAvoidBadWifi;
     }
 
+    // TODO: move this to MultipathPolicyTracker.
     public int getMeteredMultipathPreference() {
         return mMeteredMultipathPreference;
     }
diff --git a/tests/net/java/android/net/ConnectivityManagerTest.java b/tests/net/java/android/net/ConnectivityManagerTest.java
index 03a617c..6174c6c 100644
--- a/tests/net/java/android/net/ConnectivityManagerTest.java
+++ b/tests/net/java/android/net/ConnectivityManagerTest.java
@@ -219,7 +219,7 @@
         // callback triggers
         captor.getValue().send(makeMessage(request, ConnectivityManager.CALLBACK_AVAILABLE));
         verify(callback, timeout(500).times(1)).onAvailable(any(Network.class),
-                any(NetworkCapabilities.class), any(LinkProperties.class));
+                any(NetworkCapabilities.class), any(LinkProperties.class), anyBoolean());
 
         // unregister callback
         manager.unregisterNetworkCallback(callback);
@@ -247,7 +247,7 @@
         // callback triggers
         captor.getValue().send(makeMessage(req1, ConnectivityManager.CALLBACK_AVAILABLE));
         verify(callback, timeout(100).times(1)).onAvailable(any(Network.class),
-                any(NetworkCapabilities.class), any(LinkProperties.class));
+                any(NetworkCapabilities.class), any(LinkProperties.class), anyBoolean());
 
         // unregister callback
         manager.unregisterNetworkCallback(callback);
diff --git a/tests/net/java/android/net/IpSecConfigTest.java b/tests/net/java/android/net/IpSecConfigTest.java
index 771faaf..be1a455 100644
--- a/tests/net/java/android/net/IpSecConfigTest.java
+++ b/tests/net/java/android/net/IpSecConfigTest.java
@@ -47,6 +47,7 @@
         assertNull(c.getEncryption());
         assertNull(c.getAuthentication());
         assertEquals(IpSecManager.INVALID_RESOURCE_ID, c.getSpiResourceId());
+        assertEquals(0, c.getXfrmInterfaceId());
     }
 
     private IpSecConfig getSampleConfig() {
@@ -77,6 +78,7 @@
         c.setNattKeepaliveInterval(42);
         c.setMarkValue(12);
         c.setMarkMask(23);
+        c.setXfrmInterfaceId(34);
 
         return c;
     }
diff --git a/tests/net/java/android/net/MacAddressTest.java b/tests/net/java/android/net/MacAddressTest.java
index 04266c5..8be807d 100644
--- a/tests/net/java/android/net/MacAddressTest.java
+++ b/tests/net/java/android/net/MacAddressTest.java
@@ -16,6 +16,7 @@
 
 package android.net;
 
+import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.assertFalse;
@@ -24,12 +25,13 @@
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
-import java.util.Arrays;
-import java.util.Random;
-
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.net.Inet6Address;
+import java.util.Arrays;
+import java.util.Random;
+
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class MacAddressTest {
@@ -252,6 +254,19 @@
         }
     }
 
+    /**
+     * Tests that link-local address generation from MAC is valid.
+     */
+    @Test
+    public void testLinkLocalFromMacGeneration() {
+        MacAddress mac = MacAddress.fromString("52:74:f2:b1:a8:7f");
+        byte[] inet6ll = {(byte) 0xfe, (byte) 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x74,
+            (byte) 0xf2, (byte) 0xff, (byte) 0xfe, (byte) 0xb1, (byte) 0xa8, 0x7f};
+        Inet6Address llv6 = mac.getLinkLocalIpv6FromEui48Mac();
+        assertTrue(llv6.isLinkLocalAddress());
+        assertArrayEquals(inet6ll, llv6.getAddress());
+    }
+
     static byte[] toByteArray(int... in) {
         byte[] out = new byte[in.length];
         for (int i = 0; i < in.length; i++) {
diff --git a/tests/net/java/android/net/NetworkCapabilitiesTest.java b/tests/net/java/android/net/NetworkCapabilitiesTest.java
index 2f4d69e..84f7359 100644
--- a/tests/net/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/java/android/net/NetworkCapabilitiesTest.java
@@ -19,15 +19,14 @@
 import static android.net.NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
 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_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_OEM_PAID;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PAID;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_WIFI_P2P;
 import static android.net.NetworkCapabilities.RESTRICTED_CAPABILITIES;
@@ -47,7 +46,6 @@
 import android.test.suitebuilder.annotation.SmallTest;
 import android.util.ArraySet;
 
-
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -159,14 +157,6 @@
         assertEquals("", nc1.describeImmutableDifferences(nc2));
         assertEquals("", nc1.describeImmutableDifferences(nc1));
 
-        // DUN changing (http://b/65257223)
-        nc1 = new NetworkCapabilities()
-                .addCapability(NET_CAPABILITY_DUN)
-                .addCapability(NET_CAPABILITY_INTERNET);
-        nc2 = new NetworkCapabilities().addCapability(NET_CAPABILITY_INTERNET);
-        assertEquals("", nc1.describeImmutableDifferences(nc2));
-        assertEquals("", nc1.describeImmutableDifferences(nc1));
-
         // Immutable capability changing
         nc1 = new NetworkCapabilities()
                 .addCapability(NET_CAPABILITY_INTERNET)
@@ -466,6 +456,69 @@
         assertEquals(nc1, nc2);
     }
 
+    @Test
+    public void testSetNetworkSpecifierOnMultiTransportNc() {
+        // Sequence 1: Transport + Transport + NetworkSpecifier
+        NetworkCapabilities nc1 = new NetworkCapabilities();
+        nc1.addTransportType(TRANSPORT_CELLULAR).addTransportType(TRANSPORT_WIFI);
+        try {
+            nc1.setNetworkSpecifier(new StringNetworkSpecifier("specs"));
+            fail("Cannot set NetworkSpecifier on a NetworkCapability with multiple transports!");
+        } catch (IllegalStateException expected) {
+            // empty
+        }
+
+        // Sequence 2: Transport + NetworkSpecifier + Transport
+        NetworkCapabilities nc2 = new NetworkCapabilities();
+        nc2.addTransportType(TRANSPORT_CELLULAR).setNetworkSpecifier(
+                new StringNetworkSpecifier("specs"));
+        try {
+            nc2.addTransportType(TRANSPORT_WIFI);
+            fail("Cannot set a second TransportType of a network which has a NetworkSpecifier!");
+        } catch (IllegalStateException expected) {
+            // empty
+        }
+    }
+
+    @Test
+    public void testSetTransportInfoOnMultiTransportNc() {
+        // Sequence 1: Transport + Transport + TransportInfo
+        NetworkCapabilities nc1 = new NetworkCapabilities();
+        nc1.addTransportType(TRANSPORT_CELLULAR).addTransportType(TRANSPORT_WIFI)
+                .setTransportInfo(new TransportInfo() {});
+
+        // Sequence 2: Transport + NetworkSpecifier + Transport
+        NetworkCapabilities nc2 = new NetworkCapabilities();
+        nc2.addTransportType(TRANSPORT_CELLULAR).setTransportInfo(new TransportInfo() {})
+                .addTransportType(TRANSPORT_WIFI);
+    }
+
+    @Test
+    public void testCombineTransportInfo() {
+        NetworkCapabilities nc1 = new NetworkCapabilities();
+        nc1.setTransportInfo(new TransportInfo() {
+            // empty
+        });
+        NetworkCapabilities nc2 = new NetworkCapabilities();
+        // new TransportInfo so that object is not #equals to nc1's TransportInfo (that's where
+        // combine fails)
+        nc2.setTransportInfo(new TransportInfo() {
+            // empty
+        });
+
+        try {
+            nc1.combineCapabilities(nc2);
+            fail("Should not be able to combine NetworkCabilities which contain TransportInfos");
+        } catch (IllegalStateException expected) {
+            // empty
+        }
+
+        // verify that can combine with identical TransportInfo objects
+        NetworkCapabilities nc3 = new NetworkCapabilities();
+        nc3.setTransportInfo(nc1.getTransportInfo());
+        nc1.combineCapabilities(nc3);
+    }
+
     private void assertEqualsThroughMarshalling(NetworkCapabilities netCap) {
         Parcel p = Parcel.obtain();
         netCap.writeToParcel(p, /* flags */ 0);
diff --git a/tests/net/java/android/net/NetworkStatsTest.java b/tests/net/java/android/net/NetworkStatsTest.java
index 8f18d07..2c2afd4 100644
--- a/tests/net/java/android/net/NetworkStatsTest.java
+++ b/tests/net/java/android/net/NetworkStatsTest.java
@@ -39,6 +39,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import android.net.NetworkStats.Entry;
 import android.os.Process;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.test.filters.SmallTest;
@@ -447,22 +448,58 @@
     }
 
     @Test
-    public void testWithoutUid() throws Exception {
-        final NetworkStats before = new NetworkStats(TEST_START, 3)
-                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 2L, 20L)
-                .addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 512L, 32L, 0L, 0L, 0L)
-                .addValues(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, 64L, 4L, 0L, 0L, 0L)
-                .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, 512L, 32L, 0L, 0L, 0L)
-                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 0L, 0L)
-                .addValues(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 128L, 8L, 0L, 0L, 0L);
+    public void testRemoveUids() throws Exception {
+        final NetworkStats before = new NetworkStats(TEST_START, 3);
 
-        final NetworkStats after = before.withoutUids(new int[] { 100 });
-        assertEquals(6, before.size());
-        assertEquals(2, after.size());
-        assertValues(after, 0, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                DEFAULT_NETWORK_NO, 128L, 8L, 0L, 0L, 0L);
-        assertValues(after, 1, TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
-                DEFAULT_NETWORK_NO, 128L, 8L, 0L, 0L, 0L);
+        // Test 0 item stats.
+        NetworkStats after = before.clone();
+        after.removeUids(new int[0]);
+        assertEquals(0, after.size());
+        after.removeUids(new int[] {100});
+        assertEquals(0, after.size());
+
+        // Test 1 item stats.
+        before.addValues(TEST_IFACE, 99, SET_DEFAULT, TAG_NONE, 1L, 128L, 0L, 2L, 20L);
+        after = before.clone();
+        after.removeUids(new int[0]);
+        assertEquals(1, after.size());
+        assertValues(after, 0, TEST_IFACE, 99, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO, 1L, 128L, 0L, 2L, 20L);
+        after.removeUids(new int[] {99});
+        assertEquals(0, after.size());
+
+        // Append remaining test items.
+        before.addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 2L, 64L, 0L, 2L, 20L)
+                .addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 4L, 32L, 0L, 0L, 0L)
+                .addValues(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, 8L, 16L, 0L, 0L, 0L)
+                .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, 16L, 8L, 0L, 0L, 0L)
+                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 32L, 4L, 0L, 0L, 0L)
+                .addValues(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 64L, 2L, 0L, 0L, 0L);
+        assertEquals(7, before.size());
+
+        // Test remove with empty uid list.
+        after = before.clone();
+        after.removeUids(new int[0]);
+        assertValues(after.getTotalIncludingTags(null), 127L, 254L, 0L, 4L, 40L);
+
+        // Test remove uids don't exist in stats.
+        after.removeUids(new int[] {98, 0, Integer.MIN_VALUE, Integer.MAX_VALUE});
+        assertValues(after.getTotalIncludingTags(null), 127L, 254L, 0L, 4L, 40L);
+
+        // Test remove all uids.
+        after.removeUids(new int[] {99, 100, 100, 101});
+        assertEquals(0, after.size());
+
+        // Test remove in the middle.
+        after = before.clone();
+        after.removeUids(new int[] {100});
+        assertEquals(3, after.size());
+        assertValues(after, 0, TEST_IFACE, 99, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO, 1L, 128L, 0L, 2L, 20L);
+        assertValues(after, 1, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO, 32L, 4L, 0L, 0L, 0L);
+        assertValues(after, 2, TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO, 64L, 2L, 0L, 0L, 0L);
     }
 
     @Test
@@ -785,7 +822,36 @@
         ArrayMap<String, String> stackedIface = new ArrayMap<>();
         stackedIface.put(v4Iface, baseIface);
 
-        NetworkStats.Entry otherEntry = new NetworkStats.Entry(
+        // Ipv4 traffic sent/received by an app on stacked interface.
+        final NetworkStats.Entry appEntry = new NetworkStats.Entry(
+                v4Iface, appUid, SET_DEFAULT, TAG_NONE,
+                30501490  /* rxBytes */,
+                22401 /* rxPackets */,
+                876235 /* txBytes */,
+                13805 /* txPackets */,
+                0 /* operations */);
+
+        // Traffic measured for the root uid on the base interface if eBPF is in use.
+        final NetworkStats.Entry ebpfRootUidEntry = new NetworkStats.Entry(
+                baseIface, rootUid, SET_DEFAULT, TAG_NONE,
+                163577 /* rxBytes */,
+                187 /* rxPackets */,
+                17607 /* txBytes */,
+                97 /* txPackets */,
+                0 /* operations */);
+
+        // Traffic measured for the root uid on the base interface if xt_qtaguid is in use.
+        // Incorrectly includes appEntry's bytes and packets, plus IPv4-IPv6 translation
+        // overhead (20 bytes per packet), in rx direction.
+        final NetworkStats.Entry xtRootUidEntry = new NetworkStats.Entry(
+                baseIface, rootUid, SET_DEFAULT, TAG_NONE,
+                31113087 /* rxBytes */,
+                22588 /* rxPackets */,
+                17607 /* txBytes */,
+                97 /* txPackets */,
+                0 /* operations */);
+
+        final NetworkStats.Entry otherEntry = new NetworkStats.Entry(
                 otherIface, appUid, SET_DEFAULT, TAG_NONE,
                 2600  /* rxBytes */,
                 2 /* rxPackets */,
@@ -793,39 +859,41 @@
                 3 /* txPackets */,
                 0 /* operations */);
 
-        NetworkStats stats = new NetworkStats(TEST_START, 3)
-                .addValues(v4Iface, appUid, SET_DEFAULT, TAG_NONE,
-                        30501490  /* rxBytes */,
-                        22401 /* rxPackets */,
-                        876235 /* txBytes */,
-                        13805 /* txPackets */,
-                        0 /* operations */)
-                .addValues(baseIface, rootUid, SET_DEFAULT, TAG_NONE,
-                        31113087,
-                        22588,
-                        1169942,
-                        13902,
-                        0)
+        final NetworkStats statsXt = new NetworkStats(TEST_START, 3)
+                .addValues(appEntry)
+                .addValues(xtRootUidEntry)
                 .addValues(otherEntry);
 
-        stats.apply464xlatAdjustments(stackedIface);
+        final NetworkStats statsEbpf = new NetworkStats(TEST_START, 3)
+                .addValues(appEntry)
+                .addValues(ebpfRootUidEntry)
+                .addValues(otherEntry);
 
-        assertEquals(3, stats.size());
-        assertValues(stats, 0, v4Iface, appUid, SET_DEFAULT, TAG_NONE,
-                METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO,
+        statsXt.apply464xlatAdjustments(stackedIface, false);
+        statsEbpf.apply464xlatAdjustments(stackedIface, true);
+
+        assertEquals(3, statsXt.size());
+        assertEquals(3, statsEbpf.size());
+        final NetworkStats.Entry expectedAppUid = new NetworkStats.Entry(
+                v4Iface, appUid, SET_DEFAULT, TAG_NONE,
                 30949510,
                 22401,
                 1152335,
                 13805,
                 0);
-        assertValues(stats, 1, baseIface, 0, SET_DEFAULT, TAG_NONE,
-                METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO,
+        final NetworkStats.Entry expectedRootUid = new NetworkStats.Entry(
+                baseIface, 0, SET_DEFAULT, TAG_NONE,
                 163577,
                 187,
                 17607,
                 97,
                 0);
-        assertEquals(otherEntry, stats.getValues(2, null));
+        assertEquals(expectedAppUid, statsXt.getValues(0, null));
+        assertEquals(expectedRootUid, statsXt.getValues(1, null));
+        assertEquals(otherEntry, statsXt.getValues(2, null));
+        assertEquals(expectedAppUid, statsEbpf.getValues(0, null));
+        assertEquals(expectedRootUid, statsEbpf.getValues(1, null));
+        assertEquals(otherEntry, statsEbpf.getValues(2, null));
     }
 
     @Test
@@ -850,7 +918,7 @@
                 .addValues(secondEntry);
 
         // Empty map: no adjustment
-        stats.apply464xlatAdjustments(new ArrayMap<>());
+        stats.apply464xlatAdjustments(new ArrayMap<>(), false);
 
         assertEquals(2, stats.size());
         assertEquals(firstEntry, stats.getValues(0, null));
diff --git a/tests/net/java/android/net/NetworkUtilsTest.java b/tests/net/java/android/net/NetworkUtilsTest.java
index a5ee8e3..3452819 100644
--- a/tests/net/java/android/net/NetworkUtilsTest.java
+++ b/tests/net/java/android/net/NetworkUtilsTest.java
@@ -16,17 +16,34 @@
 
 package android.net;
 
-import android.net.NetworkUtils;
-import android.test.suitebuilder.annotation.SmallTest;
+import static android.net.NetworkUtils.getImplicitNetmask;
+import static android.net.NetworkUtils.inet4AddressToIntHTH;
+import static android.net.NetworkUtils.inet4AddressToIntHTL;
+import static android.net.NetworkUtils.intToInet4AddressHTH;
+import static android.net.NetworkUtils.intToInet4AddressHTL;
+import static android.net.NetworkUtils.netmaskToPrefixLength;
+import static android.net.NetworkUtils.prefixLengthToV4NetmaskIntHTH;
+import static android.net.NetworkUtils.prefixLengthToV4NetmaskIntHTL;
+import static android.net.NetworkUtils.getBroadcastAddress;
+import static android.net.NetworkUtils.getPrefixMaskAsInet4Address;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.junit.Assert.fail;
+
+import android.support.test.runner.AndroidJUnit4;
 
 import java.math.BigInteger;
 import java.net.Inet4Address;
 import java.net.InetAddress;
 import java.util.TreeSet;
 
-import junit.framework.TestCase;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
-public class NetworkUtilsTest extends TestCase {
+@RunWith(AndroidJUnit4.class)
+@android.support.test.filters.SmallTest
+public class NetworkUtilsTest {
 
     private InetAddress Address(String addr) {
         return InetAddress.parseNumericAddress(addr);
@@ -36,41 +53,125 @@
         return (Inet4Address) Address(addr);
     }
 
-    @SmallTest
+    @Test
     public void testGetImplicitNetmask() {
-        assertEquals(8, NetworkUtils.getImplicitNetmask(IPv4Address("4.2.2.2")));
-        assertEquals(8, NetworkUtils.getImplicitNetmask(IPv4Address("10.5.6.7")));
-        assertEquals(16, NetworkUtils.getImplicitNetmask(IPv4Address("173.194.72.105")));
-        assertEquals(16, NetworkUtils.getImplicitNetmask(IPv4Address("172.23.68.145")));
-        assertEquals(24, NetworkUtils.getImplicitNetmask(IPv4Address("192.0.2.1")));
-        assertEquals(24, NetworkUtils.getImplicitNetmask(IPv4Address("192.168.5.1")));
-        assertEquals(32, NetworkUtils.getImplicitNetmask(IPv4Address("224.0.0.1")));
-        assertEquals(32, NetworkUtils.getImplicitNetmask(IPv4Address("255.6.7.8")));
+        assertEquals(8, getImplicitNetmask(IPv4Address("4.2.2.2")));
+        assertEquals(8, getImplicitNetmask(IPv4Address("10.5.6.7")));
+        assertEquals(16, getImplicitNetmask(IPv4Address("173.194.72.105")));
+        assertEquals(16, getImplicitNetmask(IPv4Address("172.23.68.145")));
+        assertEquals(24, getImplicitNetmask(IPv4Address("192.0.2.1")));
+        assertEquals(24, getImplicitNetmask(IPv4Address("192.168.5.1")));
+        assertEquals(32, getImplicitNetmask(IPv4Address("224.0.0.1")));
+        assertEquals(32, getImplicitNetmask(IPv4Address("255.6.7.8")));
     }
 
     private void assertInvalidNetworkMask(Inet4Address addr) {
         try {
-            NetworkUtils.netmaskToPrefixLength(addr);
+            netmaskToPrefixLength(addr);
             fail("Invalid netmask " + addr.getHostAddress() + " did not cause exception");
         } catch (IllegalArgumentException expected) {
         }
     }
 
-    @SmallTest
+    @Test
+    public void testInet4AddressToIntHTL() {
+        assertEquals(0, inet4AddressToIntHTL(IPv4Address("0.0.0.0")));
+        assertEquals(0x000080ff, inet4AddressToIntHTL(IPv4Address("255.128.0.0")));
+        assertEquals(0x0080ff0a, inet4AddressToIntHTL(IPv4Address("10.255.128.0")));
+        assertEquals(0x00feff0a, inet4AddressToIntHTL(IPv4Address("10.255.254.0")));
+        assertEquals(0xfeffa8c0, inet4AddressToIntHTL(IPv4Address("192.168.255.254")));
+        assertEquals(0xffffa8c0, inet4AddressToIntHTL(IPv4Address("192.168.255.255")));
+    }
+
+    @Test
+    public void testIntToInet4AddressHTL() {
+        assertEquals(IPv4Address("0.0.0.0"), intToInet4AddressHTL(0));
+        assertEquals(IPv4Address("255.128.0.0"), intToInet4AddressHTL(0x000080ff));
+        assertEquals(IPv4Address("10.255.128.0"), intToInet4AddressHTL(0x0080ff0a));
+        assertEquals(IPv4Address("10.255.254.0"), intToInet4AddressHTL(0x00feff0a));
+        assertEquals(IPv4Address("192.168.255.254"), intToInet4AddressHTL(0xfeffa8c0));
+        assertEquals(IPv4Address("192.168.255.255"), intToInet4AddressHTL(0xffffa8c0));
+    }
+
+    @Test
+    public void testInet4AddressToIntHTH() {
+        assertEquals(0, inet4AddressToIntHTH(IPv4Address("0.0.0.0")));
+        assertEquals(0xff800000, inet4AddressToIntHTH(IPv4Address("255.128.0.0")));
+        assertEquals(0x0aff8000, inet4AddressToIntHTH(IPv4Address("10.255.128.0")));
+        assertEquals(0x0afffe00, inet4AddressToIntHTH(IPv4Address("10.255.254.0")));
+        assertEquals(0xc0a8fffe, inet4AddressToIntHTH(IPv4Address("192.168.255.254")));
+        assertEquals(0xc0a8ffff, inet4AddressToIntHTH(IPv4Address("192.168.255.255")));
+    }
+
+    @Test
+    public void testIntToInet4AddressHTH() {
+        assertEquals(IPv4Address("0.0.0.0"), intToInet4AddressHTH(0));
+        assertEquals(IPv4Address("255.128.0.0"), intToInet4AddressHTH(0xff800000));
+        assertEquals(IPv4Address("10.255.128.0"), intToInet4AddressHTH(0x0aff8000));
+        assertEquals(IPv4Address("10.255.254.0"), intToInet4AddressHTH(0x0afffe00));
+        assertEquals(IPv4Address("192.168.255.254"), intToInet4AddressHTH(0xc0a8fffe));
+        assertEquals(IPv4Address("192.168.255.255"), intToInet4AddressHTH(0xc0a8ffff));
+    }
+
+    @Test
     public void testNetmaskToPrefixLength() {
-        assertEquals(0, NetworkUtils.netmaskToPrefixLength(IPv4Address("0.0.0.0")));
-        assertEquals(9, NetworkUtils.netmaskToPrefixLength(IPv4Address("255.128.0.0")));
-        assertEquals(17, NetworkUtils.netmaskToPrefixLength(IPv4Address("255.255.128.0")));
-        assertEquals(23, NetworkUtils.netmaskToPrefixLength(IPv4Address("255.255.254.0")));
-        assertEquals(31, NetworkUtils.netmaskToPrefixLength(IPv4Address("255.255.255.254")));
-        assertEquals(32, NetworkUtils.netmaskToPrefixLength(IPv4Address("255.255.255.255")));
+        assertEquals(0, netmaskToPrefixLength(IPv4Address("0.0.0.0")));
+        assertEquals(9, netmaskToPrefixLength(IPv4Address("255.128.0.0")));
+        assertEquals(17, netmaskToPrefixLength(IPv4Address("255.255.128.0")));
+        assertEquals(23, netmaskToPrefixLength(IPv4Address("255.255.254.0")));
+        assertEquals(31, netmaskToPrefixLength(IPv4Address("255.255.255.254")));
+        assertEquals(32, netmaskToPrefixLength(IPv4Address("255.255.255.255")));
 
         assertInvalidNetworkMask(IPv4Address("0.0.0.1"));
         assertInvalidNetworkMask(IPv4Address("255.255.255.253"));
         assertInvalidNetworkMask(IPv4Address("255.255.0.255"));
     }
 
-    @SmallTest
+    @Test
+    public void testPrefixLengthToV4NetmaskIntHTL() {
+        assertEquals(0, prefixLengthToV4NetmaskIntHTL(0));
+        assertEquals(0x000080ff /* 255.128.0.0 */, prefixLengthToV4NetmaskIntHTL(9));
+        assertEquals(0x0080ffff /* 255.255.128.0 */, prefixLengthToV4NetmaskIntHTL(17));
+        assertEquals(0x00feffff /* 255.255.254.0 */, prefixLengthToV4NetmaskIntHTL(23));
+        assertEquals(0xfeffffff /* 255.255.255.254 */, prefixLengthToV4NetmaskIntHTL(31));
+        assertEquals(0xffffffff /* 255.255.255.255 */, prefixLengthToV4NetmaskIntHTL(32));
+    }
+
+    @Test
+    public void testPrefixLengthToV4NetmaskIntHTH() {
+        assertEquals(0, prefixLengthToV4NetmaskIntHTH(0));
+        assertEquals(0xff800000 /* 255.128.0.0 */, prefixLengthToV4NetmaskIntHTH(9));
+        assertEquals(0xffff8000 /* 255.255.128.0 */, prefixLengthToV4NetmaskIntHTH(17));
+        assertEquals(0xfffffe00 /* 255.255.254.0 */, prefixLengthToV4NetmaskIntHTH(23));
+        assertEquals(0xfffffffe /* 255.255.255.254 */, prefixLengthToV4NetmaskIntHTH(31));
+        assertEquals(0xffffffff /* 255.255.255.255 */, prefixLengthToV4NetmaskIntHTH(32));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testPrefixLengthToV4NetmaskIntHTH_NegativeLength() {
+        prefixLengthToV4NetmaskIntHTH(-1);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testPrefixLengthToV4NetmaskIntHTH_LengthTooLarge() {
+        prefixLengthToV4NetmaskIntHTH(33);
+    }
+
+    private void checkAddressMasking(String expectedAddr, String addr, int prefixLength) {
+        final int prefix = prefixLengthToV4NetmaskIntHTH(prefixLength);
+        final int addrInt = inet4AddressToIntHTH(IPv4Address(addr));
+        assertEquals(IPv4Address(expectedAddr), intToInet4AddressHTH(prefix & addrInt));
+    }
+
+    @Test
+    public void testPrefixLengthToV4NetmaskIntHTH_MaskAddr() {
+        checkAddressMasking("192.168.0.0", "192.168.128.1", 16);
+        checkAddressMasking("255.240.0.0", "255.255.255.255", 12);
+        checkAddressMasking("255.255.255.255", "255.255.255.255", 32);
+        checkAddressMasking("0.0.0.0", "255.255.255.255", 0);
+    }
+
+    @Test
     public void testRoutedIPv4AddressCount() {
         final TreeSet<IpPrefix> set = new TreeSet<>(IpPrefix.lengthComparator());
         // No routes routes to no addresses.
@@ -118,7 +219,7 @@
         assertEquals(7l - 4 + 4 + 16 + 65536, NetworkUtils.routedIPv4AddressCount(set));
     }
 
-    @SmallTest
+    @Test
     public void testRoutedIPv6AddressCount() {
         final TreeSet<IpPrefix> set = new TreeSet<>(IpPrefix.lengthComparator());
         // No routes routes to no addresses.
@@ -166,4 +267,44 @@
         assertEquals(BigInteger.valueOf(7l - 4 + 4 + 16 + 65536),
                 NetworkUtils.routedIPv6AddressCount(set));
     }
+
+    @Test
+    public void testGetPrefixMaskAsAddress() {
+        assertEquals("255.255.240.0", getPrefixMaskAsInet4Address(20).getHostAddress());
+        assertEquals("255.0.0.0", getPrefixMaskAsInet4Address(8).getHostAddress());
+        assertEquals("0.0.0.0", getPrefixMaskAsInet4Address(0).getHostAddress());
+        assertEquals("255.255.255.255", getPrefixMaskAsInet4Address(32).getHostAddress());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testGetPrefixMaskAsAddress_PrefixTooLarge() {
+        getPrefixMaskAsInet4Address(33);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testGetPrefixMaskAsAddress_NegativePrefix() {
+        getPrefixMaskAsInet4Address(-1);
+    }
+
+    @Test
+    public void testGetBroadcastAddress() {
+        assertEquals("192.168.15.255",
+                getBroadcastAddress(IPv4Address("192.168.0.123"), 20).getHostAddress());
+        assertEquals("192.255.255.255",
+                getBroadcastAddress(IPv4Address("192.168.0.123"), 8).getHostAddress());
+        assertEquals("192.168.0.123",
+                getBroadcastAddress(IPv4Address("192.168.0.123"), 32).getHostAddress());
+        assertEquals("255.255.255.255",
+                getBroadcastAddress(IPv4Address("192.168.0.123"), 0).getHostAddress());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testGetBroadcastAddress_PrefixTooLarge() {
+        getBroadcastAddress(IPv4Address("192.168.0.123"), 33);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testGetBroadcastAddress_NegativePrefix() {
+        getBroadcastAddress(IPv4Address("192.168.0.123"), -1);
+    }
 }
diff --git a/tests/net/java/com/android/internal/net/NetworkStatsFactoryTest.java b/tests/net/java/com/android/internal/net/NetworkStatsFactoryTest.java
index 788924b..90bf7b1 100644
--- a/tests/net/java/com/android/internal/net/NetworkStatsFactoryTest.java
+++ b/tests/net/java/com/android/internal/net/NetworkStatsFactoryTest.java
@@ -159,7 +159,7 @@
         assertStatsEntry(stats, "v4-wlan0", 1000, SET_DEFAULT, 0x0, 30812L, 2310L);
         assertStatsEntry(stats, "v4-wlan0", 10102, SET_DEFAULT, 0x0, 10022L, 3330L);
         assertStatsEntry(stats, "v4-wlan0", 10060, SET_DEFAULT, 0x0, 9532772L, 254112L);
-        assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, 15229L, 5766L);
+        assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, 15229L, 0L);
         assertStatsEntry(stats, "wlan0", 1000, SET_DEFAULT, 0x0, 6126L, 2013L);
         assertStatsEntry(stats, "wlan0", 10013, SET_DEFAULT, 0x0, 0L, 144L);
         assertStatsEntry(stats, "wlan0", 10018, SET_DEFAULT, 0x0, 5980263L, 167667L);
@@ -170,6 +170,8 @@
         assertStatsEntry(stats, "dummy0", 0, SET_DEFAULT, 0x0, 0L, 168L);
         assertStatsEntry(stats, "lo", 0, SET_DEFAULT, 0x0, 1288L, 1288L);
 
+        assertNoStatsEntry(stats, "wlan0", 1029, SET_DEFAULT, 0x0);
+
         NetworkStatsFactory.clearStackedIfaces();
     }
 
@@ -191,12 +193,12 @@
         // Stats snapshot before the download
         stats = parseDetailedStats(R.raw.xt_qtaguid_with_clat_100mb_download_before);
         assertStatsEntry(stats, "v4-wlan0", 10106, SET_FOREGROUND, 0x0, appRxBytesBefore, 5199872L);
-        assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, rootRxBytesBefore, 647888L);
+        assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, rootRxBytesBefore, 0L);
 
         // Stats snapshot after the download
         stats = parseDetailedStats(R.raw.xt_qtaguid_with_clat_100mb_download_after);
         assertStatsEntry(stats, "v4-wlan0", 10106, SET_FOREGROUND, 0x0, appRxBytesAfter, 7867488L);
-        assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, rootRxBytesAfter, 647587L);
+        assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, rootRxBytesAfter, 0L);
 
         NetworkStatsFactory.clearStackedIfaces();
     }
@@ -252,6 +254,15 @@
         assertEquals("unexpected txBytes", txBytes, entry.txBytes);
     }
 
+    private static void assertNoStatsEntry(NetworkStats stats, String iface, int uid, int set,
+            int tag) {
+        final int i = stats.findIndex(iface, uid, set, tag, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO);
+        if (i >= 0) {
+            fail("unexpected NetworkStats entry at " + i);
+        }
+    }
+
     private static void assertStatsEntry(NetworkStats stats, String iface, int uid, int set,
             int tag, long rxBytes, long rxPackets, long txBytes, long txPackets) {
         final int i = stats.findIndex(iface, uid, set, tag, METERED_NO, ROAMING_NO,
diff --git a/tests/net/java/com/android/internal/util/RingBufferTest.java b/tests/net/java/com/android/internal/util/RingBufferTest.java
index 7a23443..90a373a 100644
--- a/tests/net/java/com/android/internal/util/RingBufferTest.java
+++ b/tests/net/java/com/android/internal/util/RingBufferTest.java
@@ -17,6 +17,7 @@
 package com.android.internal.util;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.fail;
 
 import android.support.test.filters.SmallTest;
@@ -129,6 +130,55 @@
         assertArraysEqual(expected2, buffer.toArray());
     }
 
+    @Test
+    public void testGetNextSlot() {
+        int capacity = 100;
+        RingBuffer<DummyClass1> buffer = new RingBuffer<>(DummyClass1.class, capacity);
+
+        final DummyClass1[] actual = new DummyClass1[capacity];
+        final DummyClass1[] expected = new DummyClass1[capacity];
+        for (int i = 0; i < capacity; ++i) {
+            final DummyClass1 obj = buffer.getNextSlot();
+            obj.x = capacity * i;
+            actual[i] = obj;
+            expected[i] = new DummyClass1();
+            expected[i].x = capacity * i;
+        }
+        assertArraysEqual(expected, buffer.toArray());
+
+        for (int i = 0; i < capacity; ++i) {
+            if (actual[i] != buffer.getNextSlot()) {
+                fail("getNextSlot() should re-use objects if available");
+            }
+        }
+
+        RingBuffer<DummyClass2> buffer2 = new RingBuffer<>(DummyClass2.class, capacity);
+        assertNull("getNextSlot() should return null if the object can't be initiated "
+                + "(No nullary constructor)", buffer2.getNextSlot());
+
+        RingBuffer<DummyClass3> buffer3 = new RingBuffer<>(DummyClass3.class, capacity);
+        assertNull("getNextSlot() should return null if the object can't be initiated "
+                + "(Inaccessible class)", buffer3.getNextSlot());
+    }
+
+    public static final class DummyClass1 {
+        int x;
+
+        public boolean equals(Object o) {
+            if (o instanceof DummyClass1) {
+                final DummyClass1 other = (DummyClass1) o;
+                return other.x == this.x;
+            }
+            return false;
+        }
+    }
+
+    public static final class DummyClass2 {
+        public DummyClass2(int x) {}
+    }
+
+    private static final class DummyClass3 {}
+
     static <T> void assertArraysEqual(T[] expected, T[] got) {
         if (expected.length != got.length) {
             fail(Arrays.toString(expected) + " and " + Arrays.toString(got)
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 5e5ba4d..71529fd 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -51,6 +51,10 @@
 import static android.net.NetworkCapabilities.TRANSPORT_VPN;
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE;
+import static android.net.NetworkPolicyManager.RULE_ALLOW_METERED;
+import static android.net.NetworkPolicyManager.RULE_NONE;
+import static android.net.NetworkPolicyManager.RULE_REJECT_ALL;
+import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
 
 import static com.android.internal.util.TestUtils.waitForIdleHandler;
 import static com.android.internal.util.TestUtils.waitForIdleLooper;
@@ -62,9 +66,7 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
-import static org.mockito.Matchers.anyBoolean;
 import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.eq;
@@ -81,11 +83,11 @@
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
+import android.content.ContentProvider;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.net.CaptivePortal;
 import android.net.ConnectivityManager;
@@ -94,6 +96,8 @@
 import android.net.ConnectivityManager.PacketKeepaliveCallback;
 import android.net.ConnectivityManager.TooManyRequestsException;
 import android.net.ConnectivityThread;
+import android.net.INetd;
+import android.net.INetworkPolicyListener;
 import android.net.INetworkPolicyManager;
 import android.net.INetworkStatsService;
 import android.net.InterfaceConfiguration;
@@ -114,7 +118,6 @@
 import android.net.RouteInfo;
 import android.net.StringNetworkSpecifier;
 import android.net.UidRange;
-import android.net.VpnService;
 import android.net.captiveportal.CaptivePortalProbeResult;
 import android.net.metrics.IpConnectivityLog;
 import android.net.util.MultinetworkPolicyTracker;
@@ -135,7 +138,6 @@
 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;
 
@@ -152,6 +154,7 @@
 import com.android.server.connectivity.Nat464Xlat;
 import com.android.server.connectivity.NetworkAgentInfo;
 import com.android.server.connectivity.NetworkMonitor;
+import com.android.server.connectivity.Tethering;
 import com.android.server.connectivity.Vpn;
 import com.android.server.net.NetworkPinner;
 import com.android.server.net.NetworkPolicyManagerInternal;
@@ -196,7 +199,16 @@
     private static final String TAG = "ConnectivityServiceTest";
 
     private static final int TIMEOUT_MS = 500;
-    private static final int TEST_LINGER_DELAY_MS = 120;
+    private static final int TEST_LINGER_DELAY_MS = 250;
+    // Chosen to be less than the linger timeout. This ensures that we can distinguish between a
+    // LOST callback that arrives immediately and a LOST callback that arrives after the linger
+    // timeout. For this, our assertions should run fast enough to leave less than
+    // (mService.mLingerDelayMs - TEST_CALLBACK_TIMEOUT_MS) between the time callbacks are
+    // supposedly fired, and the time we call expectCallback.
+    private final static int TEST_CALLBACK_TIMEOUT_MS = 200;
+    // Chosen to be less than TEST_CALLBACK_TIMEOUT_MS. This ensures that requests have time to
+    // complete before callbacks are verified.
+    private final static int TEST_REQUEST_TIMEOUT_MS = 150;
 
     private static final String CLAT_PREFIX = "v4-";
     private static final String MOBILE_IFNAME = "test_rmnet_data0";
@@ -210,11 +222,14 @@
     private MockNetworkAgent mEthernetNetworkAgent;
     private MockVpn mMockVpn;
     private Context mContext;
+    private INetworkPolicyListener mPolicyListener;
 
     @Mock IpConnectivityMetrics.Logger mMetricsService;
     @Mock DefaultNetworkMetrics mDefaultNetworkMetrics;
     @Mock INetworkManagementService mNetworkManagementService;
     @Mock INetworkStatsService mStatsService;
+    @Mock INetworkPolicyManager mNpm;
+    @Mock INetd mMockNetd;
 
     private ArgumentCaptor<String[]> mStringArrayCaptor = ArgumentCaptor.forClass(String[].class);
 
@@ -244,7 +259,7 @@
         @Spy private Resources mResources;
         private final LinkedBlockingQueue<Intent> mStartedActivities = new LinkedBlockingQueue<>();
 
-        MockContext(Context base) {
+        MockContext(Context base, ContentProvider settingsProvider) {
             super(base);
 
             mResources = spy(base.getResources());
@@ -256,7 +271,7 @@
                     });
 
             mContentResolver = new MockContentResolver();
-            mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+            mContentResolver.addProvider(Settings.AUTHORITY, settingsProvider);
         }
 
         @Override
@@ -876,7 +891,7 @@
                 NetworkAgentInfo networkAgentInfo, NetworkRequest defaultRequest,
                 IpConnectivityLog log) {
             super(context, handler, networkAgentInfo, defaultRequest, log,
-                    NetworkMonitor.NetworkMonitorSettings.DEFAULT);
+                    NetworkMonitor.Dependencies.DEFAULT);
             connectivityHandler = handler;
         }
 
@@ -913,8 +928,9 @@
 
         public WrappedConnectivityService(Context context, INetworkManagementService netManager,
                 INetworkStatsService statsService, INetworkPolicyManager policyManager,
-                IpConnectivityLog log) {
+                IpConnectivityLog log, INetd netd) {
             super(context, netManager, statsService, policyManager, log);
+            mNetd = netd;
             mLingerDelayMs = TEST_LINGER_DELAY_MS;
         }
 
@@ -929,6 +945,11 @@
         }
 
         @Override
+        protected Tethering makeTethering() {
+            return mock(Tethering.class);
+        }
+
+        @Override
         protected int reserveNetId() {
             while (true) {
                 final int netId = super.reserveNetId();
@@ -1018,6 +1039,20 @@
         public void waitForIdle() {
             waitForIdle(TIMEOUT_MS);
         }
+
+        public void setUidRulesChanged(int uidRules) {
+            try {
+                mPolicyListener.onUidRulesChanged(Process.myUid(), uidRules);
+            } catch (RemoteException ignored) {
+            }
+        }
+
+        public void setRestrictBackgroundChanged(boolean restrictBackground) {
+            try {
+                mPolicyListener.onRestrictBackgroundChanged(restrictBackground);
+            } catch (RemoteException ignored) {
+            }
+        }
     }
 
     /**
@@ -1044,16 +1079,27 @@
             Looper.prepare();
         }
 
-        mServiceContext = new MockContext(InstrumentationRegistry.getContext());
+        FakeSettingsProvider.clearSettingsProvider();
+        mServiceContext = new MockContext(InstrumentationRegistry.getContext(),
+                new FakeSettingsProvider());
         LocalServices.removeServiceForTest(NetworkPolicyManagerInternal.class);
         LocalServices.addService(
                 NetworkPolicyManagerInternal.class, mock(NetworkPolicyManagerInternal.class));
+
         mService = new WrappedConnectivityService(mServiceContext,
                 mNetworkManagementService,
                 mStatsService,
-                mock(INetworkPolicyManager.class),
-                mock(IpConnectivityLog.class));
+                mNpm,
+                mock(IpConnectivityLog.class),
+                mMockNetd);
 
+        final ArgumentCaptor<INetworkPolicyListener> policyListenerCaptor =
+                ArgumentCaptor.forClass(INetworkPolicyListener.class);
+        verify(mNpm).registerListener(policyListenerCaptor.capture());
+        mPolicyListener = policyListenerCaptor.getValue();
+
+        // Create local CM before sending system ready so that we can answer
+        // getSystemService() correctly.
         mCm = new WrappedConnectivityManager(InstrumentationRegistry.getContext(), mService);
         mService.systemReady();
         mService.mockVpn(Process.myUid());
@@ -1061,13 +1107,13 @@
 
         // Ensure that the default setting for Captive Portals is used for most tests
         setCaptivePortalMode(Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT);
-        setMobileDataAlwaysOn(false);
+        setAlwaysOnNetworks(false);
         setPrivateDnsSettings(PRIVATE_DNS_MODE_OFF, "ignored.example.com");
     }
 
     @After
     public void tearDown() throws Exception {
-        setMobileDataAlwaysOn(false);
+        setAlwaysOnNetworks(false);
         if (mCellNetworkAgent != null) {
             mCellNetworkAgent.disconnect();
             mCellNetworkAgent = null;
@@ -1080,6 +1126,7 @@
             mEthernetNetworkAgent.disconnect();
             mEthernetNetworkAgent = null;
         }
+        FakeSettingsProvider.clearSettingsProvider();
     }
 
     private static int transportToLegacyType(int transport) {
@@ -1431,7 +1478,8 @@
         RESUMED,
         LOSING,
         LOST,
-        UNAVAILABLE
+        UNAVAILABLE,
+        BLOCKED_STATUS
     }
 
     private static class CallbackInfo {
@@ -1463,11 +1511,6 @@
      * received. assertNoCallback may be called at any time.
      */
     private class TestNetworkCallback extends NetworkCallback {
-        // Chosen to be much less than the linger timeout. This ensures that we can distinguish
-        // between a LOST callback that arrives immediately and a LOST callback that arrives after
-        // the linger timeout.
-        private final static int TIMEOUT_MS = 100;
-
         private final LinkedBlockingQueue<CallbackInfo> mCallbacks = new LinkedBlockingQueue<>();
         private Network mLastAvailableNetwork;
 
@@ -1517,6 +1560,11 @@
             setLastCallback(CallbackState.LOST, network, null);
         }
 
+        @Override
+        public void onBlockedStatusChanged(Network network, boolean blocked) {
+            setLastCallback(CallbackState.BLOCKED_STATUS, network, blocked);
+        }
+
         public Network getLastAvailableNetwork() {
             return mLastAvailableNetwork;
         }
@@ -1543,20 +1591,20 @@
             if (state == CallbackState.LOSING) {
                 String msg = String.format(
                         "Invalid linger time value %d, must be between %d and %d",
-                        actual.arg, 0, TEST_LINGER_DELAY_MS);
+                        actual.arg, 0, mService.mLingerDelayMs);
                 int maxMsToLive = (Integer) actual.arg;
-                assertTrue(msg, 0 <= maxMsToLive && maxMsToLive <= TEST_LINGER_DELAY_MS);
+                assertTrue(msg, 0 <= maxMsToLive && maxMsToLive <= mService.mLingerDelayMs);
             }
 
             return actual;
         }
 
         CallbackInfo expectCallback(CallbackState state, MockNetworkAgent agent) {
-            return expectCallback(state, agent, TIMEOUT_MS);
+            return expectCallback(state, agent, TEST_CALLBACK_TIMEOUT_MS);
         }
 
         CallbackInfo expectCallbackLike(Predicate<CallbackInfo> fn) {
-            return expectCallbackLike(fn, TIMEOUT_MS);
+            return expectCallbackLike(fn, TEST_CALLBACK_TIMEOUT_MS);
         }
 
         CallbackInfo expectCallbackLike(Predicate<CallbackInfo> fn, int timeoutMs) {
@@ -1577,6 +1625,7 @@
         // - onSuspended, iff the network was suspended when the callbacks fire.
         // - onCapabilitiesChanged.
         // - onLinkPropertiesChanged.
+        // - onBlockedStatusChanged.
         //
         // @param agent the network to expect the callbacks on.
         // @param expectSuspended whether to expect a SUSPENDED callback.
@@ -1584,7 +1633,7 @@
         //        onCapabilitiesChanged callback.
         // @param timeoutMs how long to wait for the callbacks.
         void expectAvailableCallbacks(MockNetworkAgent agent, boolean expectSuspended,
-                boolean expectValidated, int timeoutMs) {
+                boolean expectValidated, boolean expectBlocked, int timeoutMs) {
             expectCallback(CallbackState.AVAILABLE, agent, timeoutMs);
             if (expectSuspended) {
                 expectCallback(CallbackState.SUSPENDED, agent, timeoutMs);
@@ -1595,19 +1644,28 @@
                 expectCapabilitiesWithout(NET_CAPABILITY_VALIDATED, agent, timeoutMs);
             }
             expectCallback(CallbackState.LINK_PROPERTIES, agent, timeoutMs);
+            expectBlockedStatusCallback(expectBlocked, agent);
         }
 
         // Expects the available callbacks (validated), plus onSuspended.
         void expectAvailableAndSuspendedCallbacks(MockNetworkAgent agent, boolean expectValidated) {
-            expectAvailableCallbacks(agent, true, expectValidated, TIMEOUT_MS);
+            expectAvailableCallbacks(agent, true, expectValidated, false, TEST_CALLBACK_TIMEOUT_MS);
         }
 
         void expectAvailableCallbacksValidated(MockNetworkAgent agent) {
-            expectAvailableCallbacks(agent, false, true, TIMEOUT_MS);
+            expectAvailableCallbacks(agent, false, true, false, TEST_CALLBACK_TIMEOUT_MS);
+        }
+
+        void expectAvailableCallbacksValidatedAndBlocked(MockNetworkAgent agent) {
+            expectAvailableCallbacks(agent, false, true, true, TEST_CALLBACK_TIMEOUT_MS);
         }
 
         void expectAvailableCallbacksUnvalidated(MockNetworkAgent agent) {
-            expectAvailableCallbacks(agent, false, false, TIMEOUT_MS);
+            expectAvailableCallbacks(agent, false, false, false, TEST_CALLBACK_TIMEOUT_MS);
+        }
+
+        void expectAvailableCallbacksUnvalidatedAndBlocked(MockNetworkAgent agent) {
+            expectAvailableCallbacks(agent, false, false, true, TEST_CALLBACK_TIMEOUT_MS);
         }
 
         // Expects the available callbacks (where the onCapabilitiesChanged must contain the
@@ -1615,9 +1673,12 @@
         // one we just sent.
         // TODO: this is likely a bug. Fix it and remove this method.
         void expectAvailableDoubleValidatedCallbacks(MockNetworkAgent agent) {
-            expectCallback(CallbackState.AVAILABLE, agent, TIMEOUT_MS);
+            expectCallback(CallbackState.AVAILABLE, agent, TEST_CALLBACK_TIMEOUT_MS);
             NetworkCapabilities nc1 = expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, agent);
-            expectCallback(CallbackState.LINK_PROPERTIES, agent, TIMEOUT_MS);
+            expectCallback(CallbackState.LINK_PROPERTIES, agent, TEST_CALLBACK_TIMEOUT_MS);
+            // Implicitly check the network is allowed to use.
+            // TODO: should we need to consider if network is in blocked status in this case?
+            expectBlockedStatusCallback(false, agent);
             NetworkCapabilities nc2 = expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, agent);
             assertEquals(nc1, nc2);
         }
@@ -1631,7 +1692,7 @@
         }
 
         NetworkCapabilities expectCapabilitiesWith(int capability, MockNetworkAgent agent) {
-            return expectCapabilitiesWith(capability, agent, TIMEOUT_MS);
+            return expectCapabilitiesWith(capability, agent, TEST_CALLBACK_TIMEOUT_MS);
         }
 
         NetworkCapabilities expectCapabilitiesWith(int capability, MockNetworkAgent agent,
@@ -1643,7 +1704,7 @@
         }
 
         NetworkCapabilities expectCapabilitiesWithout(int capability, MockNetworkAgent agent) {
-            return expectCapabilitiesWithout(capability, agent, TIMEOUT_MS);
+            return expectCapabilitiesWithout(capability, agent, TEST_CALLBACK_TIMEOUT_MS);
         }
 
         NetworkCapabilities expectCapabilitiesWithout(int capability, MockNetworkAgent agent,
@@ -1660,6 +1721,12 @@
                     fn.test((NetworkCapabilities) cbi.arg));
         }
 
+        void expectBlockedStatusCallback(boolean expectBlocked, MockNetworkAgent agent) {
+            CallbackInfo cbi = expectCallback(CallbackState.BLOCKED_STATUS, agent);
+            boolean actualBlocked = (boolean) cbi.arg;
+            assertEquals(expectBlocked, actualBlocked);
+        }
+
         void assertNoCallback() {
             waitForIdle();
             CallbackInfo c = mCallbacks.peek();
@@ -1767,6 +1834,12 @@
 
     @Test
     public void testMultipleLingering() {
+        // This test would be flaky with the default 120ms timer: that is short enough that
+        // lingered networks are torn down before assertions can be run. We don't want to mock the
+        // lingering timer to keep the WakeupMessage logic realistic: this has already proven useful
+        // in detecting races.
+        mService.mLingerDelayMs = 300;
+
         NetworkRequest request = new NetworkRequest.Builder()
                 .clearCapabilities().addCapability(NET_CAPABILITY_NOT_METERED)
                 .build();
@@ -1984,7 +2057,7 @@
 
         // Let linger run its course.
         callback.assertNoCallback();
-        final int lingerTimeoutMs = TEST_LINGER_DELAY_MS + TEST_LINGER_DELAY_MS / 4;
+        final int lingerTimeoutMs = mService.mLingerDelayMs + mService.mLingerDelayMs / 4;
         callback.expectCallback(CallbackState.LOST, mCellNetworkAgent, lingerTimeoutMs);
 
         // Register a TRACK_DEFAULT request and check that it does not affect lingering.
@@ -2016,7 +2089,7 @@
 
     @Test
     public void testNetworkGoesIntoBackgroundAfterLinger() {
-        setMobileDataAlwaysOn(true);
+        setAlwaysOnNetworks(true);
         NetworkRequest request = new NetworkRequest.Builder()
                 .clearCapabilities()
                 .build();
@@ -2761,10 +2834,10 @@
         Settings.Global.putInt(cr, Settings.Global.CAPTIVE_PORTAL_MODE, mode);
     }
 
-    private void setMobileDataAlwaysOn(boolean enable) {
+    private void setAlwaysOnNetworks(boolean enable) {
         ContentResolver cr = mServiceContext.getContentResolver();
         Settings.Global.putInt(cr, Settings.Global.MOBILE_DATA_ALWAYS_ON, enable ? 1 : 0);
-        mService.updateMobileDataAlwaysOn();
+        mService.updateAlwaysOnNetworks();
         waitForIdle();
     }
 
@@ -2786,7 +2859,7 @@
     public void testBackgroundNetworks() throws Exception {
         // Create a background request. We can't do this ourselves because ConnectivityService
         // doesn't have an API for it. So just turn on mobile data always on.
-        setMobileDataAlwaysOn(true);
+        setAlwaysOnNetworks(true);
         final NetworkRequest request = new NetworkRequest.Builder().build();
         final NetworkRequest fgRequest = new NetworkRequest.Builder()
                 .addCapability(NET_CAPABILITY_FOREGROUND).build();
@@ -2984,7 +3057,7 @@
 
         // Turn on mobile data always on. The factory starts looking again.
         testFactory.expectAddRequests(1);
-        setMobileDataAlwaysOn(true);
+        setAlwaysOnNetworks(true);
         testFactory.waitForNetworkRequests(2);
         assertTrue(testFactory.getMyStartRequested());
 
@@ -3004,7 +3077,7 @@
 
         // Turn off mobile data always on and expect the request to disappear...
         testFactory.expectRemoveRequests(1);
-        setMobileDataAlwaysOn(false);
+        setAlwaysOnNetworks(false);
         testFactory.waitForNetworkRequests(1);
 
         // ...  and cell data to be torn down.
@@ -3208,12 +3281,12 @@
         NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
                 NetworkCapabilities.TRANSPORT_WIFI).build();
         final TestNetworkCallback networkCallback = new TestNetworkCallback();
-        final int timeoutMs = 150;
-        mCm.requestNetwork(nr, networkCallback, timeoutMs);
+        mCm.requestNetwork(nr, networkCallback, TEST_REQUEST_TIMEOUT_MS);
 
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(false);
-        networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, false, timeoutMs);
+        networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, false, false,
+                TEST_CALLBACK_TIMEOUT_MS);
 
         // pass timeout and validate that UNAVAILABLE is not called
         networkCallback.assertNoCallback();
@@ -3228,13 +3301,12 @@
         NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
                 NetworkCapabilities.TRANSPORT_WIFI).build();
         final TestNetworkCallback networkCallback = new TestNetworkCallback();
-        final int requestTimeoutMs = 50;
-        mCm.requestNetwork(nr, networkCallback, requestTimeoutMs);
+        mCm.requestNetwork(nr, networkCallback, TEST_REQUEST_TIMEOUT_MS);
 
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(false);
-        final int assertTimeoutMs = 100;
-        networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, false, assertTimeoutMs);
+        networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, false, false,
+                TEST_CALLBACK_TIMEOUT_MS);
         mWiFiNetworkAgent.disconnect();
         networkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
 
@@ -3613,8 +3685,10 @@
 
     @Test
     public void testNetworkCallbackMaximum() {
-        final int MAX_REQUESTS = 100;
-        final int CALLBACKS = 90;
+        // We can only have 99 callbacks, because MultipathPolicyTracker is
+        // already one of them.
+        final int MAX_REQUESTS = 99;
+        final int CALLBACKS = 89;
         final int INTENTS = 10;
         assertEquals(MAX_REQUESTS, CALLBACKS + INTENTS);
 
@@ -3790,6 +3864,7 @@
         networkCallback.expectCallback(CallbackState.NETWORK_CAPABILITIES, networkAgent);
         CallbackInfo cbi = networkCallback.expectCallback(CallbackState.LINK_PROPERTIES,
                 networkAgent);
+        networkCallback.expectCallback(CallbackState.BLOCKED_STATUS, networkAgent);
         networkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, networkAgent);
         networkCallback.assertNoCallback();
         checkDirectlyConnectedRoutes(cbi.arg, Arrays.asList(myIpv4Address),
@@ -3998,6 +4073,7 @@
                 mCellNetworkAgent);
         CallbackInfo cbi = cellNetworkCallback.expectCallback(
                 CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
+        cellNetworkCallback.expectCallback(CallbackState.BLOCKED_STATUS, mCellNetworkAgent);
         cellNetworkCallback.assertNoCallback();
         assertFalse(((LinkProperties)cbi.arg).isPrivateDnsActive());
         assertNull(((LinkProperties)cbi.arg).getPrivateDnsServerName());
@@ -4056,6 +4132,7 @@
                 mCellNetworkAgent);
         CallbackInfo cbi = cellNetworkCallback.expectCallback(
                 CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
+        cellNetworkCallback.expectCallback(CallbackState.BLOCKED_STATUS, mCellNetworkAgent);
         cellNetworkCallback.assertNoCallback();
         assertFalse(((LinkProperties)cbi.arg).isPrivateDnsActive());
         assertNull(((LinkProperties)cbi.arg).getPrivateDnsServerName());
@@ -4432,6 +4509,101 @@
         mMockVpn.disconnect();
     }
 
+    @Test
+    public void testNetworkBlockedStatus() {
+        final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback();
+        final NetworkRequest cellRequest = new NetworkRequest.Builder()
+                .addTransportType(TRANSPORT_CELLULAR)
+                .build();
+        mCm.registerNetworkCallback(cellRequest, cellNetworkCallback);
+
+        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent.connect(true);
+        cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+
+        mService.setUidRulesChanged(RULE_REJECT_ALL);
+        cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
+
+        // ConnectivityService should cache it not to invoke the callback again.
+        mService.setUidRulesChanged(RULE_REJECT_METERED);
+        cellNetworkCallback.assertNoCallback();
+
+        mService.setUidRulesChanged(RULE_NONE);
+        cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
+
+        mService.setUidRulesChanged(RULE_REJECT_METERED);
+        cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
+
+        // Restrict the network based on UID rule and NOT_METERED capability change.
+        mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
+        cellNetworkCallback.expectCapabilitiesWith(NET_CAPABILITY_NOT_METERED, mCellNetworkAgent);
+        cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
+        mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_METERED);
+        cellNetworkCallback.expectCapabilitiesWithout(NET_CAPABILITY_NOT_METERED,
+                mCellNetworkAgent);
+        cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
+        mService.setUidRulesChanged(RULE_ALLOW_METERED);
+        cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
+
+        mService.setUidRulesChanged(RULE_NONE);
+        cellNetworkCallback.assertNoCallback();
+
+        // Restrict the network based on BackgroundRestricted.
+        mService.setRestrictBackgroundChanged(true);
+        cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
+        mService.setRestrictBackgroundChanged(true);
+        cellNetworkCallback.assertNoCallback();
+        mService.setRestrictBackgroundChanged(false);
+        cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
+        cellNetworkCallback.assertNoCallback();
+
+        mCm.unregisterNetworkCallback(cellNetworkCallback);
+    }
+
+    @Test
+    public void testNetworkBlockedStatusBeforeAndAfterConnect() {
+        final TestNetworkCallback defaultCallback = new TestNetworkCallback();
+        mCm.registerDefaultNetworkCallback(defaultCallback);
+
+        // No Networkcallbacks invoked before any network is active.
+        mService.setUidRulesChanged(RULE_REJECT_ALL);
+        mService.setUidRulesChanged(RULE_NONE);
+        mService.setUidRulesChanged(RULE_REJECT_METERED);
+        defaultCallback.assertNoCallback();
+
+        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent.connect(true);
+        defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent);
+        defaultCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mCellNetworkAgent);
+
+        // Allow to use the network after switching to NOT_METERED network.
+        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
+        mWiFiNetworkAgent.connect(true);
+        defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
+
+        // Switch to METERED network. Restrict the use of the network.
+        mWiFiNetworkAgent.disconnect();
+        defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+        defaultCallback.expectAvailableCallbacksValidatedAndBlocked(mCellNetworkAgent);
+
+        // Network becomes NOT_METERED.
+        mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
+        defaultCallback.expectCapabilitiesWith(NET_CAPABILITY_NOT_METERED, mCellNetworkAgent);
+        defaultCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
+
+        // Verify there's no Networkcallbacks invoked after data saver on/off.
+        mService.setRestrictBackgroundChanged(true);
+        mService.setRestrictBackgroundChanged(false);
+        defaultCallback.assertNoCallback();
+
+        mCellNetworkAgent.disconnect();
+        defaultCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
+        defaultCallback.assertNoCallback();
+
+        mCm.unregisterNetworkCallback(defaultCallback);
+    }
+
     /**
      * Make simulated InterfaceConfig for Nat464Xlat to query clat lower layer info.
      */
@@ -4524,4 +4696,104 @@
         mCellNetworkAgent.disconnect();
         mCm.unregisterNetworkCallback(networkCallback);
     }
+
+    @Test
+    public void testDataActivityTracking() throws RemoteException {
+        final TestNetworkCallback networkCallback = new TestNetworkCallback();
+        final NetworkRequest networkRequest = new NetworkRequest.Builder()
+                .addCapability(NET_CAPABILITY_INTERNET)
+                .build();
+        mCm.registerNetworkCallback(networkRequest, networkCallback);
+
+        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        final LinkProperties cellLp = new LinkProperties();
+        cellLp.setInterfaceName(MOBILE_IFNAME);
+        mCellNetworkAgent.sendLinkProperties(cellLp);
+        reset(mNetworkManagementService);
+        mCellNetworkAgent.connect(true);
+        networkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+        verify(mNetworkManagementService, times(1)).addIdleTimer(eq(MOBILE_IFNAME), anyInt(),
+                eq(ConnectivityManager.TYPE_MOBILE));
+
+        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        final LinkProperties wifiLp = new LinkProperties();
+        wifiLp.setInterfaceName(WIFI_IFNAME);
+        mWiFiNetworkAgent.sendLinkProperties(wifiLp);
+
+        // Network switch
+        reset(mNetworkManagementService);
+        mWiFiNetworkAgent.connect(true);
+        networkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+        networkCallback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+        networkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
+        verify(mNetworkManagementService, times(1)).addIdleTimer(eq(WIFI_IFNAME), anyInt(),
+                eq(ConnectivityManager.TYPE_WIFI));
+        verify(mNetworkManagementService, times(1)).removeIdleTimer(eq(MOBILE_IFNAME));
+
+        // Disconnect wifi and switch back to cell
+        reset(mNetworkManagementService);
+        mWiFiNetworkAgent.disconnect();
+        networkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+        assertNoCallbacks(networkCallback);
+        verify(mNetworkManagementService, times(1)).removeIdleTimer(eq(WIFI_IFNAME));
+        verify(mNetworkManagementService, times(1)).addIdleTimer(eq(MOBILE_IFNAME), anyInt(),
+                eq(ConnectivityManager.TYPE_MOBILE));
+
+        // reconnect wifi
+        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        wifiLp.setInterfaceName(WIFI_IFNAME);
+        mWiFiNetworkAgent.sendLinkProperties(wifiLp);
+        mWiFiNetworkAgent.connect(true);
+        networkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+        networkCallback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+        networkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
+
+        // Disconnect cell
+        reset(mNetworkManagementService);
+        mCellNetworkAgent.disconnect();
+        networkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
+        // LOST callback is triggered earlier than removing idle timer. Broadcast should also be
+        // sent as network being switched. Ensure rule removal for cell will not be triggered
+        // unexpectedly before network being removed.
+        waitForIdle();
+        verify(mNetworkManagementService, times(0)).removeIdleTimer(eq(MOBILE_IFNAME));
+        verify(mNetworkManagementService, times(1)).removeNetwork(
+                eq(mCellNetworkAgent.getNetwork().netId));
+
+        // Disconnect wifi
+        ConditionVariable cv = waitForConnectivityBroadcasts(1);
+        reset(mNetworkManagementService);
+        mWiFiNetworkAgent.disconnect();
+        waitFor(cv);
+        verify(mNetworkManagementService, times(1)).removeIdleTimer(eq(WIFI_IFNAME));
+
+        // Clean up
+        mCm.unregisterNetworkCallback(networkCallback);
+    }
+
+    private static final String TEST_TCP_BUFFER_SIZES = "1,2,3,4,5,6";
+
+    private void verifyTcpBufferSizeChange(String tcpBufferSizes) throws Exception {
+        String[] values = tcpBufferSizes.split(",");
+        String rmemValues = String.join(" ", values[0], values[1], values[2]);
+        String wmemValues = String.join(" ", values[3], values[4], values[5]);
+        waitForIdle();
+        verify(mMockNetd, atLeastOnce()).setTcpRWmemorySize(rmemValues, wmemValues);
+        reset(mMockNetd);
+    }
+
+    @Test
+    public void testTcpBufferReset() throws Exception {
+        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        reset(mMockNetd);
+        // Simple connection should have updated tcp buffer size.
+        mCellNetworkAgent.connect(false);
+        verifyTcpBufferSizeChange(ConnectivityService.DEFAULT_TCP_BUFFER_SIZES);
+
+        // Change link Properties should have updated tcp buffer size.
+        LinkProperties lp = new LinkProperties();
+        lp.setTcpBufferSizes(TEST_TCP_BUFFER_SIZES);
+        mCellNetworkAgent.sendLinkProperties(lp);
+        verifyTcpBufferSizeChange(TEST_TCP_BUFFER_SIZES);
+    }
 }
diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
index 102cb7c..d8f9618 100644
--- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -16,6 +16,8 @@
 
 package com.android.server;
 
+import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_INET6;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.fail;
@@ -34,16 +36,18 @@
 import android.net.IpSecConfig;
 import android.net.IpSecManager;
 import android.net.IpSecSpiResponse;
+import android.net.IpSecTransform;
 import android.net.IpSecTransformResponse;
 import android.net.IpSecTunnelInterfaceResponse;
+import android.net.IpSecUdpEncapResponse;
 import android.net.LinkAddress;
 import android.net.Network;
 import android.net.NetworkUtils;
 import android.os.Binder;
 import android.os.ParcelFileDescriptor;
-import android.test.mock.MockContext;
 import android.support.test.filters.SmallTest;
 import android.system.Os;
+import android.test.mock.MockContext;
 
 import java.net.Socket;
 import java.util.Arrays;
@@ -62,16 +66,20 @@
 
     private static final int TEST_SPI = 0xD1201D;
 
-    private final String mDestinationAddr;
     private final String mSourceAddr;
+    private final String mDestinationAddr;
     private final LinkAddress mLocalInnerAddress;
+    private final int mFamily;
+
+    private static final int[] ADDRESS_FAMILIES =
+            new int[] {AF_INET, AF_INET6};
 
     @Parameterized.Parameters
     public static Collection ipSecConfigs() {
         return Arrays.asList(
                 new Object[][] {
-                {"1.2.3.4", "8.8.4.4", "10.0.1.1/24"},
-                {"2601::2", "2601::10", "2001:db8::1/64"}
+                {"1.2.3.4", "8.8.4.4", "10.0.1.1/24", AF_INET},
+                {"2601::2", "2601::10", "2001:db8::1/64", AF_INET6}
         });
     }
 
@@ -121,6 +129,7 @@
     IpSecService.IpSecServiceConfiguration mMockIpSecSrvConfig;
     IpSecService mIpSecService;
     Network fakeNetwork = new Network(0xAB);
+    int mUid = Os.getuid();
 
     private static final IpSecAlgorithm AUTH_ALGO =
             new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA256, AUTH_KEY, AUTH_KEY.length * 4);
@@ -128,12 +137,14 @@
             new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
     private static final IpSecAlgorithm AEAD_ALGO =
             new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 128);
+    private static final int REMOTE_ENCAP_PORT = 4500;
 
     public IpSecServiceParameterizedTest(
-            String sourceAddr, String destAddr, String localInnerAddr) {
+            String sourceAddr, String destAddr, String localInnerAddr, int family) {
         mSourceAddr = sourceAddr;
         mDestinationAddr = destAddr;
         mLocalInnerAddress = new LinkAddress(localInnerAddr);
+        mFamily = family;
     }
 
     @Before
@@ -156,6 +167,8 @@
             .thenReturn(AppOpsManager.MODE_IGNORED);
     }
 
+    //TODO: Add a test to verify SPI.
+
     @Test
     public void testIpSecServiceReserveSpi() throws Exception {
         when(mMockNetd.ipSecAllocateSpi(anyInt(), anyString(), eq(mDestinationAddr), eq(TEST_SPI)))
@@ -181,16 +194,16 @@
 
         verify(mMockNetd)
                 .ipSecDeleteSecurityAssociation(
-                        eq(spiResp.resourceId),
+                        eq(mUid),
                         anyString(),
                         anyString(),
                         eq(TEST_SPI),
                         anyInt(),
+                        anyInt(),
                         anyInt());
 
         // Verify quota and RefcountedResource objects cleaned up
-        IpSecService.UserRecord userRecord =
-                mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
+        IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid);
         assertEquals(0, userRecord.mSpiQuotaTracker.mCurrent);
         try {
             userRecord.mSpiRecords.getRefcountedResourceOrThrow(spiResp.resourceId);
@@ -209,8 +222,7 @@
                 mIpSecService.allocateSecurityParameterIndex(
                         mDestinationAddr, TEST_SPI, new Binder());
 
-        IpSecService.UserRecord userRecord =
-                mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
+        IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid);
         IpSecService.RefcountedResource refcountedRecord =
                 userRecord.mSpiRecords.getRefcountedResourceOrThrow(spiResp.resourceId);
 
@@ -218,11 +230,12 @@
 
         verify(mMockNetd)
                 .ipSecDeleteSecurityAssociation(
-                        eq(spiResp.resourceId),
+                        eq(mUid),
                         anyString(),
                         anyString(),
                         eq(TEST_SPI),
                         anyInt(),
+                        anyInt(),
                         anyInt());
 
         // Verify quota and RefcountedResource objects cleaned up
@@ -258,6 +271,48 @@
         config.setAuthentication(AUTH_ALGO);
     }
 
+    private void addEncapSocketToIpSecConfig(int resourceId, IpSecConfig config) throws Exception {
+        config.setEncapType(IpSecTransform.ENCAP_ESPINUDP);
+        config.setEncapSocketResourceId(resourceId);
+        config.setEncapRemotePort(REMOTE_ENCAP_PORT);
+    }
+
+    private void verifyTransformNetdCalledForCreatingSA(
+            IpSecConfig config, IpSecTransformResponse resp) throws Exception {
+        verifyTransformNetdCalledForCreatingSA(config, resp, 0);
+    }
+
+    private void verifyTransformNetdCalledForCreatingSA(
+            IpSecConfig config, IpSecTransformResponse resp, int encapSocketPort) throws Exception {
+        IpSecAlgorithm auth = config.getAuthentication();
+        IpSecAlgorithm crypt = config.getEncryption();
+        IpSecAlgorithm authCrypt = config.getAuthenticatedEncryption();
+
+        verify(mMockNetd, times(1))
+                .ipSecAddSecurityAssociation(
+                        eq(mUid),
+                        eq(config.getMode()),
+                        eq(config.getSourceAddress()),
+                        eq(config.getDestinationAddress()),
+                        eq((config.getNetwork() != null) ? config.getNetwork().netId : 0),
+                        eq(TEST_SPI),
+                        eq(0),
+                        eq(0),
+                        eq((auth != null) ? auth.getName() : ""),
+                        eq((auth != null) ? auth.getKey() : new byte[] {}),
+                        eq((auth != null) ? auth.getTruncationLengthBits() : 0),
+                        eq((crypt != null) ? crypt.getName() : ""),
+                        eq((crypt != null) ? crypt.getKey() : new byte[] {}),
+                        eq((crypt != null) ? crypt.getTruncationLengthBits() : 0),
+                        eq((authCrypt != null) ? authCrypt.getName() : ""),
+                        eq((authCrypt != null) ? authCrypt.getKey() : new byte[] {}),
+                        eq((authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0),
+                        eq(config.getEncapType()),
+                        eq(encapSocketPort),
+                        eq(config.getEncapRemotePort()),
+                        eq(config.getXfrmInterfaceId()));
+    }
+
     @Test
     public void testCreateTransform() throws Exception {
         IpSecConfig ipSecConfig = new IpSecConfig();
@@ -268,28 +323,7 @@
                 mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
         assertEquals(IpSecManager.Status.OK, createTransformResp.status);
 
-        verify(mMockNetd)
-                .ipSecAddSecurityAssociation(
-                        eq(createTransformResp.resourceId),
-                        anyInt(),
-                        anyString(),
-                        anyString(),
-                        anyInt(),
-                        eq(TEST_SPI),
-                        anyInt(),
-                        anyInt(),
-                        eq(IpSecAlgorithm.AUTH_HMAC_SHA256),
-                        eq(AUTH_KEY),
-                        anyInt(),
-                        eq(IpSecAlgorithm.CRYPT_AES_CBC),
-                        eq(CRYPT_KEY),
-                        anyInt(),
-                        eq(""),
-                        eq(new byte[] {}),
-                        eq(0),
-                        anyInt(),
-                        anyInt(),
-                        anyInt());
+        verifyTransformNetdCalledForCreatingSA(ipSecConfig, createTransformResp);
     }
 
     @Test
@@ -303,28 +337,59 @@
                 mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
         assertEquals(IpSecManager.Status.OK, createTransformResp.status);
 
-        verify(mMockNetd)
-                .ipSecAddSecurityAssociation(
-                        eq(createTransformResp.resourceId),
-                        anyInt(),
-                        anyString(),
-                        anyString(),
-                        anyInt(),
-                        eq(TEST_SPI),
-                        anyInt(),
-                        anyInt(),
-                        eq(""),
-                        eq(new byte[] {}),
-                        eq(0),
-                        eq(""),
-                        eq(new byte[] {}),
-                        eq(0),
-                        eq(IpSecAlgorithm.AUTH_CRYPT_AES_GCM),
-                        eq(AEAD_KEY),
-                        anyInt(),
-                        anyInt(),
-                        anyInt(),
-                        anyInt());
+        verifyTransformNetdCalledForCreatingSA(ipSecConfig, createTransformResp);
+    }
+
+    @Test
+    public void testCreateTransportModeTransformWithEncap() throws Exception {
+        IpSecUdpEncapResponse udpSock = mIpSecService.openUdpEncapsulationSocket(0, new Binder());
+
+        IpSecConfig ipSecConfig = new IpSecConfig();
+        ipSecConfig.setMode(IpSecTransform.MODE_TRANSPORT);
+        addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
+        addAuthAndCryptToIpSecConfig(ipSecConfig);
+        addEncapSocketToIpSecConfig(udpSock.resourceId, ipSecConfig);
+
+        if (mFamily == AF_INET) {
+            IpSecTransformResponse createTransformResp =
+                    mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
+            assertEquals(IpSecManager.Status.OK, createTransformResp.status);
+
+            verifyTransformNetdCalledForCreatingSA(ipSecConfig, createTransformResp, udpSock.port);
+        } else {
+            try {
+                IpSecTransformResponse createTransformResp =
+                        mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
+                fail("Expected IllegalArgumentException on attempt to use UDP Encap in IPv6");
+            } catch (IllegalArgumentException expected) {
+            }
+        }
+    }
+
+    @Test
+    public void testCreateTunnelModeTransformWithEncap() throws Exception {
+        IpSecUdpEncapResponse udpSock = mIpSecService.openUdpEncapsulationSocket(0, new Binder());
+
+        IpSecConfig ipSecConfig = new IpSecConfig();
+        ipSecConfig.setMode(IpSecTransform.MODE_TUNNEL);
+        addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
+        addAuthAndCryptToIpSecConfig(ipSecConfig);
+        addEncapSocketToIpSecConfig(udpSock.resourceId, ipSecConfig);
+
+        if (mFamily == AF_INET) {
+            IpSecTransformResponse createTransformResp =
+                    mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
+            assertEquals(IpSecManager.Status.OK, createTransformResp.status);
+
+            verifyTransformNetdCalledForCreatingSA(ipSecConfig, createTransformResp, udpSock.port);
+        } else {
+            try {
+                IpSecTransformResponse createTransformResp =
+                        mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
+                fail("Expected IllegalArgumentException on attempt to use UDP Encap in IPv6");
+            } catch (IllegalArgumentException expected) {
+            }
+        }
     }
 
     @Test
@@ -361,17 +426,17 @@
 
         IpSecTransformResponse createTransformResp =
                 mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
-        IpSecService.UserRecord userRecord =
-                mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
+        IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid);
         assertEquals(1, userRecord.mSpiQuotaTracker.mCurrent);
         mIpSecService.releaseSecurityParameterIndex(ipSecConfig.getSpiResourceId());
         verify(mMockNetd, times(0))
                 .ipSecDeleteSecurityAssociation(
-                        eq(createTransformResp.resourceId),
+                        eq(mUid),
                         anyString(),
                         anyString(),
                         eq(TEST_SPI),
                         anyInt(),
+                        anyInt(),
                         anyInt());
         // quota is not released until the SPI is released by the Transform
         assertEquals(1, userRecord.mSpiQuotaTracker.mCurrent);
@@ -389,16 +454,16 @@
 
         verify(mMockNetd, times(1))
                 .ipSecDeleteSecurityAssociation(
-                        eq(createTransformResp.resourceId),
+                        eq(mUid),
                         anyString(),
                         anyString(),
                         eq(TEST_SPI),
                         anyInt(),
+                        anyInt(),
                         anyInt());
 
         // Verify quota and RefcountedResource objects cleaned up
-        IpSecService.UserRecord userRecord =
-                mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
+        IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid);
         assertEquals(0, userRecord.mTransformQuotaTracker.mCurrent);
         assertEquals(1, userRecord.mSpiQuotaTracker.mCurrent);
 
@@ -412,6 +477,7 @@
                         anyString(),
                         anyInt(),
                         anyInt(),
+                        anyInt(),
                         anyInt());
         assertEquals(0, userRecord.mSpiQuotaTracker.mCurrent);
 
@@ -433,8 +499,7 @@
         IpSecTransformResponse createTransformResp =
                 mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
 
-        IpSecService.UserRecord userRecord =
-                mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
+        IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid);
         IpSecService.RefcountedResource refcountedRecord =
                 userRecord.mTransformRecords.getRefcountedResourceOrThrow(
                         createTransformResp.resourceId);
@@ -443,11 +508,12 @@
 
         verify(mMockNetd)
                 .ipSecDeleteSecurityAssociation(
-                        eq(createTransformResp.resourceId),
+                        eq(mUid),
                         anyString(),
                         anyString(),
                         eq(TEST_SPI),
                         anyInt(),
+                        anyInt(),
                         anyInt());
 
         // Verify quota and RefcountedResource objects cleaned up
@@ -476,8 +542,8 @@
 
         verify(mMockNetd)
                 .ipSecApplyTransportModeTransform(
-                        eq(pfd.getFileDescriptor()),
-                        eq(resourceId),
+                        eq(pfd),
+                        eq(mUid),
                         eq(IpSecManager.DIRECTION_OUT),
                         anyString(),
                         anyString(),
@@ -489,7 +555,7 @@
         ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
         mIpSecService.removeTransportModeTransforms(pfd);
 
-        verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd.getFileDescriptor());
+        verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd);
     }
 
     private IpSecTunnelInterfaceResponse createAndValidateTunnel(
@@ -509,19 +575,19 @@
                 createAndValidateTunnel(mSourceAddr, mDestinationAddr, "blessedPackage");
 
         // Check that we have stored the tracking object, and retrieve it
-        IpSecService.UserRecord userRecord =
-                mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
+        IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid);
         IpSecService.RefcountedResource refcountedRecord =
                 userRecord.mTunnelInterfaceRecords.getRefcountedResourceOrThrow(
                         createTunnelResp.resourceId);
 
         assertEquals(1, userRecord.mTunnelQuotaTracker.mCurrent);
         verify(mMockNetd)
-                .addVirtualTunnelInterface(
+                .ipSecAddTunnelInterface(
                         eq(createTunnelResp.interfaceName),
                         eq(mSourceAddr),
                         eq(mDestinationAddr),
                         anyInt(),
+                        anyInt(),
                         anyInt());
     }
 
@@ -530,14 +596,13 @@
         IpSecTunnelInterfaceResponse createTunnelResp =
                 createAndValidateTunnel(mSourceAddr, mDestinationAddr, "blessedPackage");
 
-        IpSecService.UserRecord userRecord =
-                mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
+        IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid);
 
         mIpSecService.deleteTunnelInterface(createTunnelResp.resourceId, "blessedPackage");
 
         // Verify quota and RefcountedResource objects cleaned up
         assertEquals(0, userRecord.mTunnelQuotaTracker.mCurrent);
-        verify(mMockNetd).removeVirtualTunnelInterface(eq(createTunnelResp.interfaceName));
+        verify(mMockNetd).ipSecRemoveTunnelInterface(eq(createTunnelResp.interfaceName));
         try {
             userRecord.mTunnelInterfaceRecords.getRefcountedResourceOrThrow(
                     createTunnelResp.resourceId);
@@ -551,8 +616,7 @@
         IpSecTunnelInterfaceResponse createTunnelResp =
                 createAndValidateTunnel(mSourceAddr, mDestinationAddr, "blessedPackage");
 
-        IpSecService.UserRecord userRecord =
-                mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
+        IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid);
         IpSecService.RefcountedResource refcountedRecord =
                 userRecord.mTunnelInterfaceRecords.getRefcountedResourceOrThrow(
                         createTunnelResp.resourceId);
@@ -561,7 +625,7 @@
 
         // Verify quota and RefcountedResource objects cleaned up
         assertEquals(0, userRecord.mTunnelQuotaTracker.mCurrent);
-        verify(mMockNetd).removeVirtualTunnelInterface(eq(createTunnelResp.interfaceName));
+        verify(mMockNetd).ipSecRemoveTunnelInterface(eq(createTunnelResp.interfaceName));
         try {
             userRecord.mTunnelInterfaceRecords.getRefcountedResourceOrThrow(
                     createTunnelResp.resourceId);
@@ -571,6 +635,41 @@
     }
 
     @Test
+    public void testApplyTunnelModeTransform() throws Exception {
+        IpSecConfig ipSecConfig = new IpSecConfig();
+        ipSecConfig.setMode(IpSecTransform.MODE_TUNNEL);
+        addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
+        addAuthAndCryptToIpSecConfig(ipSecConfig);
+
+        IpSecTransformResponse createTransformResp =
+                mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
+        IpSecTunnelInterfaceResponse createTunnelResp =
+                createAndValidateTunnel(mSourceAddr, mDestinationAddr, "blessedPackage");
+
+        int transformResourceId = createTransformResp.resourceId;
+        int tunnelResourceId = createTunnelResp.resourceId;
+        mIpSecService.applyTunnelModeTransform(tunnelResourceId, IpSecManager.DIRECTION_OUT,
+                transformResourceId, "blessedPackage");
+
+        for (int selAddrFamily : ADDRESS_FAMILIES) {
+            verify(mMockNetd)
+                    .ipSecUpdateSecurityPolicy(
+                            eq(mUid),
+                            eq(selAddrFamily),
+                            eq(IpSecManager.DIRECTION_OUT),
+                            anyString(),
+                            anyString(),
+                            eq(TEST_SPI),
+                            anyInt(), // iKey/oKey
+                            anyInt(), // mask
+                            eq(tunnelResourceId));
+        }
+
+        ipSecConfig.setXfrmInterfaceId(tunnelResourceId);
+        verifyTransformNetdCalledForCreatingSA(ipSecConfig, createTransformResp);
+    }
+
+    @Test
     public void testAddRemoveAddressFromTunnelInterface() throws Exception {
         for (String pkgName : new String[]{"blessedPackage", "systemPackage"}) {
             IpSecTunnelInterfaceResponse createTunnelResp =
diff --git a/tests/net/java/com/android/server/IpSecServiceTest.java b/tests/net/java/com/android/server/IpSecServiceTest.java
index 2c94a60..724446e 100644
--- a/tests/net/java/com/android/server/IpSecServiceTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceTest.java
@@ -425,7 +425,7 @@
         ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
         mIpSecService.removeTransportModeTransforms(pfd);
 
-        verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd.getFileDescriptor());
+        verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd);
     }
 
     @Test
@@ -620,10 +620,10 @@
                 mIpSecService.openUdpEncapsulationSocket(0, new Binder());
 
         FileDescriptor sockFd = udpEncapResp.fileDescriptor.getFileDescriptor();
-        ArgumentMatcher<FileDescriptor> fdMatcher = (arg) -> {
+        ArgumentMatcher<ParcelFileDescriptor> fdMatcher = (arg) -> {
                     try {
                         StructStat sockStat = Os.fstat(sockFd);
-                        StructStat argStat = Os.fstat(arg);
+                        StructStat argStat = Os.fstat(arg.getFileDescriptor());
 
                         return sockStat.st_ino == argStat.st_ino
                                 && sockStat.st_dev == argStat.st_dev;
diff --git a/tests/net/java/com/android/server/connectivity/DnsManagerTest.java b/tests/net/java/com/android/server/connectivity/DnsManagerTest.java
index 1ec4eec..01b468a 100644
--- a/tests/net/java/com/android/server/connectivity/DnsManagerTest.java
+++ b/tests/net/java/com/android/server/connectivity/DnsManagerTest.java
@@ -19,6 +19,9 @@
 import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF;
 import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
 import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
+import static android.provider.Settings.Global.PRIVATE_DNS_DEFAULT_MODE;
+import static android.provider.Settings.Global.PRIVATE_DNS_MODE;
+import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -40,6 +43,7 @@
 import android.test.mock.MockContentResolver;
 
 import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.server.connectivity.DnsManager.PrivateDnsConfig;
 import com.android.server.connectivity.MockableSystemProperties;
 
 import java.net.InetAddress;
@@ -84,10 +88,9 @@
         mDnsManager = new DnsManager(mCtx, mNMService, mSystemProperties);
 
         // Clear the private DNS settings
-        Settings.Global.putString(mContentResolver,
-                Settings.Global.PRIVATE_DNS_MODE, "");
-        Settings.Global.putString(mContentResolver,
-                Settings.Global.PRIVATE_DNS_SPECIFIER, "");
+        Settings.Global.putString(mContentResolver, PRIVATE_DNS_DEFAULT_MODE, "");
+        Settings.Global.putString(mContentResolver, PRIVATE_DNS_MODE, "");
+        Settings.Global.putString(mContentResolver, PRIVATE_DNS_SPECIFIER, "");
     }
 
     @Test
@@ -127,9 +130,8 @@
                 TEST_IFACENAME));
 
         Settings.Global.putString(mContentResolver,
-                Settings.Global.PRIVATE_DNS_MODE, PRIVATE_DNS_MODE_PROVIDER_HOSTNAME);
-        Settings.Global.putString(mContentResolver,
-                Settings.Global.PRIVATE_DNS_SPECIFIER, "strictmode.com");
+                PRIVATE_DNS_MODE, PRIVATE_DNS_MODE_PROVIDER_HOSTNAME);
+        Settings.Global.putString(mContentResolver, PRIVATE_DNS_SPECIFIER, "strictmode.com");
         mDnsManager.updatePrivateDns(new Network(TEST_NETID),
                 new DnsManager.PrivateDnsConfig("strictmode.com", new InetAddress[] {
                     InetAddress.parseNumericAddress("6.6.6.6"),
@@ -222,8 +224,7 @@
         assertNull(lp.getPrivateDnsServerName());
 
         // Turn private DNS mode off
-        Settings.Global.putString(mContentResolver,
-                Settings.Global.PRIVATE_DNS_MODE, PRIVATE_DNS_MODE_OFF);
+        Settings.Global.putString(mContentResolver, PRIVATE_DNS_MODE, PRIVATE_DNS_MODE_OFF);
         mDnsManager.updatePrivateDns(new Network(TEST_NETID),
                 mDnsManager.getPrivateDnsConfig());
         mDnsManager.setDnsConfigurationForNetwork(TEST_NETID, lp, IS_DEFAULT);
@@ -234,4 +235,29 @@
         assertFalse(lp.isPrivateDnsActive());
         assertNull(lp.getPrivateDnsServerName());
     }
+
+    @Test
+    public void testOverrideDefaultMode() throws Exception {
+        // Hard-coded default is opportunistic mode.
+        final PrivateDnsConfig cfgAuto = DnsManager.getPrivateDnsConfig(mContentResolver);
+        assertTrue(cfgAuto.useTls);
+        assertEquals("", cfgAuto.hostname);
+        assertEquals(new InetAddress[0], cfgAuto.ips);
+
+        // Pretend a gservices push sets the default to "off".
+        Settings.Global.putString(mContentResolver, PRIVATE_DNS_DEFAULT_MODE, "off");
+        final PrivateDnsConfig cfgOff = DnsManager.getPrivateDnsConfig(mContentResolver);
+        assertFalse(cfgOff.useTls);
+        assertEquals("", cfgOff.hostname);
+        assertEquals(new InetAddress[0], cfgOff.ips);
+
+        // Strict mode still works.
+        Settings.Global.putString(
+                mContentResolver, PRIVATE_DNS_MODE, PRIVATE_DNS_MODE_PROVIDER_HOSTNAME);
+        Settings.Global.putString(mContentResolver, PRIVATE_DNS_SPECIFIER, "strictmode.com");
+        final PrivateDnsConfig cfgStrict = DnsManager.getPrivateDnsConfig(mContentResolver);
+        assertTrue(cfgStrict.useTls);
+        assertEquals("strictmode.com", cfgStrict.hostname);
+        assertEquals(new InetAddress[0], cfgStrict.ips);
+    }
 }
diff --git a/tests/net/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java b/tests/net/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java
new file mode 100644
index 0000000..e58811b
--- /dev/null
+++ b/tests/net/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2018 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 com.android.server.connectivity;
+
+import static android.content.Intent.ACTION_CONFIGURATION_CHANGED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkPolicy.LIMIT_DISABLED;
+import static android.net.NetworkPolicy.SNOOZE_NEVER;
+import static android.net.NetworkPolicy.WARNING_DISABLED;
+import static android.provider.Settings.Global.NETWORK_DEFAULT_DAILY_MULTIPATH_QUOTA_BYTES;
+
+import static com.android.server.net.NetworkPolicyManagerInternal.QUOTA_TYPE_MULTIPATH;
+import static com.android.server.net.NetworkPolicyManagerService.OPPORTUNISTIC_QUOTA_UNKNOWN;
+
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertNotNull;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.usage.NetworkStatsManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.res.Resources;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkPolicy;
+import android.net.NetworkPolicyManager;
+import android.net.NetworkTemplate;
+import android.net.StringNetworkSpecifier;
+import android.os.Handler;
+import android.provider.Settings;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.telephony.TelephonyManager;
+import android.test.mock.MockContentResolver;
+import android.util.DataUnit;
+import android.util.RecurrenceRule;
+
+import com.android.internal.R;
+import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.server.LocalServices;
+import com.android.server.net.NetworkPolicyManagerInternal;
+import com.android.server.net.NetworkStatsManagerInternal;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.time.Clock;
+import java.time.Instant;
+import java.time.Period;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.temporal.ChronoUnit;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class MultipathPolicyTrackerTest {
+    private static final Network TEST_NETWORK = new Network(123);
+    private static final int POLICY_SNOOZED = -100;
+
+    @Mock private Context mContext;
+    @Mock private Resources mResources;
+    @Mock private Handler mHandler;
+    @Mock private MultipathPolicyTracker.Dependencies mDeps;
+    @Mock private Clock mClock;
+    @Mock private ConnectivityManager mCM;
+    @Mock private NetworkPolicyManager mNPM;
+    @Mock private NetworkStatsManager mStatsManager;
+    @Mock private NetworkPolicyManagerInternal mNPMI;
+    @Mock private NetworkStatsManagerInternal mNetworkStatsManagerInternal;
+    @Mock private TelephonyManager mTelephonyManager;
+    private MockContentResolver mContentResolver;
+
+    private ArgumentCaptor<BroadcastReceiver> mConfigChangeReceiverCaptor;
+
+    private MultipathPolicyTracker mTracker;
+
+    private Clock mPreviousRecurrenceRuleClock;
+    private boolean mRecurrenceRuleClockMocked;
+
+    private <T> void mockService(String serviceName, Class<T> serviceClass, T service) {
+        when(mContext.getSystemServiceName(serviceClass)).thenReturn(serviceName);
+        when(mContext.getSystemService(serviceName)).thenReturn(service);
+    }
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mPreviousRecurrenceRuleClock = RecurrenceRule.sClock;
+        RecurrenceRule.sClock = mClock;
+        mRecurrenceRuleClockMocked = true;
+
+        mConfigChangeReceiverCaptor = ArgumentCaptor.forClass(BroadcastReceiver.class);
+
+        when(mContext.getResources()).thenReturn(mResources);
+        when(mContext.getApplicationInfo()).thenReturn(new ApplicationInfo());
+        when(mContext.registerReceiverAsUser(mConfigChangeReceiverCaptor.capture(),
+                any(), argThat(f -> f.hasAction(ACTION_CONFIGURATION_CHANGED)), any(), any()))
+                .thenReturn(null);
+
+        when(mDeps.getClock()).thenReturn(mClock);
+
+        when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mTelephonyManager);
+
+        mContentResolver = Mockito.spy(new MockContentResolver(mContext));
+        mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+        Settings.Global.clearProviderForTest();
+        when(mContext.getContentResolver()).thenReturn(mContentResolver);
+
+        mockService(Context.CONNECTIVITY_SERVICE, ConnectivityManager.class, mCM);
+        mockService(Context.NETWORK_POLICY_SERVICE, NetworkPolicyManager.class, mNPM);
+        mockService(Context.NETWORK_STATS_SERVICE, NetworkStatsManager.class, mStatsManager);
+        mockService(Context.TELEPHONY_SERVICE, TelephonyManager.class, mTelephonyManager);
+
+        LocalServices.removeServiceForTest(NetworkPolicyManagerInternal.class);
+        LocalServices.addService(NetworkPolicyManagerInternal.class, mNPMI);
+
+        LocalServices.removeServiceForTest(NetworkStatsManagerInternal.class);
+        LocalServices.addService(NetworkStatsManagerInternal.class, mNetworkStatsManagerInternal);
+
+        mTracker = new MultipathPolicyTracker(mContext, mHandler, mDeps);
+    }
+
+    @After
+    public void tearDown() {
+        // Avoid setting static clock to null (which should normally not be the case)
+        // if MockitoAnnotations.initMocks threw an exception
+        if (mRecurrenceRuleClockMocked) {
+            RecurrenceRule.sClock = mPreviousRecurrenceRuleClock;
+        }
+        mRecurrenceRuleClockMocked = false;
+    }
+
+    private void setDefaultQuotaGlobalSetting(long setting) {
+        Settings.Global.putInt(mContentResolver, NETWORK_DEFAULT_DAILY_MULTIPATH_QUOTA_BYTES,
+                (int) setting);
+    }
+
+    private void testGetMultipathPreference(
+            long usedBytesToday, long subscriptionQuota, long policyWarning, long policyLimit,
+            long defaultGlobalSetting, long defaultResSetting, boolean roaming) {
+
+        // TODO: tests should not use ZoneId.systemDefault() once code handles TZ correctly.
+        final ZonedDateTime now = ZonedDateTime.ofInstant(
+                Instant.parse("2017-04-02T10:11:12Z"), ZoneId.systemDefault());
+        final ZonedDateTime startOfDay = now.truncatedTo(ChronoUnit.DAYS);
+        when(mClock.millis()).thenReturn(now.toInstant().toEpochMilli());
+        when(mClock.instant()).thenReturn(now.toInstant());
+        when(mClock.getZone()).thenReturn(ZoneId.systemDefault());
+
+        // Setup plan quota
+        when(mNPMI.getSubscriptionOpportunisticQuota(TEST_NETWORK, QUOTA_TYPE_MULTIPATH))
+                .thenReturn(subscriptionQuota);
+
+        // Setup user policy warning / limit
+        if (policyWarning != WARNING_DISABLED || policyLimit != LIMIT_DISABLED) {
+            final Instant recurrenceStart = Instant.parse("2017-04-01T00:00:00Z");
+            final RecurrenceRule recurrenceRule = new RecurrenceRule(
+                    ZonedDateTime.ofInstant(
+                            recurrenceStart,
+                            ZoneId.systemDefault()),
+                    null /* end */,
+                    Period.ofMonths(1));
+            final boolean snoozeWarning = policyWarning == POLICY_SNOOZED;
+            final boolean snoozeLimit = policyLimit == POLICY_SNOOZED;
+            when(mNPM.getNetworkPolicies()).thenReturn(new NetworkPolicy[] {
+                    new NetworkPolicy(
+                            NetworkTemplate.buildTemplateMobileWildcard(),
+                            recurrenceRule,
+                            snoozeWarning ? 0 : policyWarning,
+                            snoozeLimit ? 0 : policyLimit,
+                            snoozeWarning ? recurrenceStart.toEpochMilli() + 1 : SNOOZE_NEVER,
+                            snoozeLimit ? recurrenceStart.toEpochMilli() + 1 : SNOOZE_NEVER,
+                            SNOOZE_NEVER,
+                            true /* metered */,
+                            false /* inferred */)
+            });
+        } else {
+            when(mNPM.getNetworkPolicies()).thenReturn(new NetworkPolicy[0]);
+        }
+
+        // Setup default quota in settings and resources
+        if (defaultGlobalSetting > 0) {
+            setDefaultQuotaGlobalSetting(defaultGlobalSetting);
+        }
+        when(mResources.getInteger(R.integer.config_networkDefaultDailyMultipathQuotaBytes))
+                .thenReturn((int) defaultResSetting);
+
+        when(mNetworkStatsManagerInternal.getNetworkTotalBytes(
+                any(),
+                eq(startOfDay.toInstant().toEpochMilli()),
+                eq(now.toInstant().toEpochMilli()))).thenReturn(usedBytesToday);
+
+        ArgumentCaptor<ConnectivityManager.NetworkCallback> networkCallback =
+                ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
+        mTracker.start();
+        verify(mCM).registerNetworkCallback(any(), networkCallback.capture(), any());
+
+        // Simulate callback after capability changes
+        final NetworkCapabilities capabilities = new NetworkCapabilities()
+                .addCapability(NET_CAPABILITY_INTERNET)
+                .addTransportType(TRANSPORT_CELLULAR)
+                .setNetworkSpecifier(new StringNetworkSpecifier("234"));
+        if (!roaming) {
+            capabilities.addCapability(NET_CAPABILITY_NOT_ROAMING);
+        }
+        networkCallback.getValue().onCapabilitiesChanged(
+                TEST_NETWORK,
+                capabilities);
+    }
+
+    @Test
+    public void testGetMultipathPreference_SubscriptionQuota() {
+        testGetMultipathPreference(
+                DataUnit.MEGABYTES.toBytes(2) /* usedBytesToday */,
+                DataUnit.MEGABYTES.toBytes(14) /* subscriptionQuota */,
+                DataUnit.MEGABYTES.toBytes(100) /* policyWarning */,
+                LIMIT_DISABLED,
+                DataUnit.MEGABYTES.toBytes(12) /* defaultGlobalSetting */,
+                2_500_000 /* defaultResSetting */,
+                false /* roaming */);
+
+        verify(mStatsManager, times(1)).registerUsageCallback(
+                any(), anyInt(), eq(DataUnit.MEGABYTES.toBytes(12)), any(), any());
+    }
+
+    @Test
+    public void testGetMultipathPreference_UserWarningQuota() {
+        testGetMultipathPreference(
+                DataUnit.MEGABYTES.toBytes(7) /* usedBytesToday */,
+                OPPORTUNISTIC_QUOTA_UNKNOWN,
+                // 29 days from Apr. 2nd to May 1st
+                DataUnit.MEGABYTES.toBytes(15 * 29 * 20) /* policyWarning */,
+                LIMIT_DISABLED,
+                DataUnit.MEGABYTES.toBytes(12) /* defaultGlobalSetting */,
+                2_500_000 /* defaultResSetting */,
+                false /* roaming */);
+
+        // Daily budget should be 15MB (5% of daily quota), 7MB used today: callback set for 8MB
+        verify(mStatsManager, times(1)).registerUsageCallback(
+                any(), anyInt(), eq(DataUnit.MEGABYTES.toBytes(8)), any(), any());
+    }
+
+    @Test
+    public void testGetMultipathPreference_SnoozedWarningQuota() {
+        testGetMultipathPreference(
+                DataUnit.MEGABYTES.toBytes(7) /* usedBytesToday */,
+                OPPORTUNISTIC_QUOTA_UNKNOWN,
+                // 29 days from Apr. 2nd to May 1st
+                POLICY_SNOOZED /* policyWarning */,
+                DataUnit.MEGABYTES.toBytes(15 * 29 * 20) /* policyLimit */,
+                DataUnit.MEGABYTES.toBytes(12) /* defaultGlobalSetting */,
+                2_500_000 /* defaultResSetting */,
+                false /* roaming */);
+
+        // Daily budget should be 15MB (5% of daily quota), 7MB used today: callback set for 8MB
+        verify(mStatsManager, times(1)).registerUsageCallback(
+                any(), anyInt(), eq(DataUnit.MEGABYTES.toBytes(8)), any(), any());
+    }
+
+    @Test
+    public void testGetMultipathPreference_SnoozedBothQuota() {
+        testGetMultipathPreference(
+                DataUnit.MEGABYTES.toBytes(7) /* usedBytesToday */,
+                OPPORTUNISTIC_QUOTA_UNKNOWN,
+                // 29 days from Apr. 2nd to May 1st
+                POLICY_SNOOZED /* policyWarning */,
+                POLICY_SNOOZED /* policyLimit */,
+                DataUnit.MEGABYTES.toBytes(12) /* defaultGlobalSetting */,
+                2_500_000 /* defaultResSetting */,
+                false /* roaming */);
+
+        // Default global setting should be used: 12 - 7 = 5
+        verify(mStatsManager, times(1)).registerUsageCallback(
+                any(), anyInt(), eq(DataUnit.MEGABYTES.toBytes(5)), any(), any());
+    }
+
+    @Test
+    public void testGetMultipathPreference_SettingChanged() {
+        testGetMultipathPreference(
+                DataUnit.MEGABYTES.toBytes(2) /* usedBytesToday */,
+                OPPORTUNISTIC_QUOTA_UNKNOWN,
+                WARNING_DISABLED,
+                LIMIT_DISABLED,
+                -1 /* defaultGlobalSetting */,
+                DataUnit.MEGABYTES.toBytes(10) /* defaultResSetting */,
+                false /* roaming */);
+
+        verify(mStatsManager, times(1)).registerUsageCallback(
+                any(), anyInt(), eq(DataUnit.MEGABYTES.toBytes(8)), any(), any());
+
+        // Update setting
+        setDefaultQuotaGlobalSetting(DataUnit.MEGABYTES.toBytes(14));
+        mTracker.mSettingsObserver.onChange(
+                false, Settings.Global.getUriFor(NETWORK_DEFAULT_DAILY_MULTIPATH_QUOTA_BYTES));
+
+        // Callback must have been re-registered with new setting
+        verify(mStatsManager, times(1)).unregisterUsageCallback(any());
+        verify(mStatsManager, times(1)).registerUsageCallback(
+                any(), anyInt(), eq(DataUnit.MEGABYTES.toBytes(12)), any(), any());
+    }
+
+    @Test
+    public void testGetMultipathPreference_ResourceChanged() {
+        testGetMultipathPreference(
+                DataUnit.MEGABYTES.toBytes(2) /* usedBytesToday */,
+                OPPORTUNISTIC_QUOTA_UNKNOWN,
+                WARNING_DISABLED,
+                LIMIT_DISABLED,
+                -1 /* defaultGlobalSetting */,
+                DataUnit.MEGABYTES.toBytes(14) /* defaultResSetting */,
+                false /* roaming */);
+
+        verify(mStatsManager, times(1)).registerUsageCallback(
+                any(), anyInt(), eq(DataUnit.MEGABYTES.toBytes(12)), any(), any());
+
+        when(mResources.getInteger(R.integer.config_networkDefaultDailyMultipathQuotaBytes))
+                .thenReturn((int) DataUnit.MEGABYTES.toBytes(16));
+
+        final BroadcastReceiver configChangeReceiver = mConfigChangeReceiverCaptor.getValue();
+        assertNotNull(configChangeReceiver);
+        configChangeReceiver.onReceive(mContext, new Intent());
+
+        // Uses the new setting (16 - 2 = 14MB)
+        verify(mStatsManager, times(1)).registerUsageCallback(
+                any(), anyInt(), eq(DataUnit.MEGABYTES.toBytes(14)), any(), any());
+    }
+}
diff --git a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
index dfe31bd..bf42412 100644
--- a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
+++ b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
@@ -31,6 +31,7 @@
 import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.NetworkInfo;
+import android.net.NetworkMisc;
 import android.os.Handler;
 import android.os.INetworkManagementService;
 import android.os.test.TestLooper;
@@ -55,6 +56,7 @@
     static final LinkAddress ADDR = new LinkAddress("192.0.2.5/29");
 
     @Mock ConnectivityService mConnectivity;
+    @Mock NetworkMisc mMisc;
     @Mock INetworkManagementService mNms;
     @Mock InterfaceConfiguration mConfig;
     @Mock NetworkAgentInfo mNai;
@@ -78,6 +80,7 @@
         mNai.networkInfo = new NetworkInfo(null);
         mNai.networkInfo.setType(ConnectivityManager.TYPE_WIFI);
         when(mNai.connService()).thenReturn(mConnectivity);
+        when(mNai.netMisc()).thenReturn(mMisc);
         when(mNai.handler()).thenReturn(mHandler);
 
         when(mNms.getInterfaceConfig(eq(STACKED_IFACE))).thenReturn(mConfig);
@@ -103,9 +106,16 @@
             mNai.networkInfo.setType(type);
             for (NetworkInfo.DetailedState state : supportedDetailedStates) {
                 mNai.networkInfo.setDetailedState(state, "reason", "extraInfo");
-                assertTrue(
-                        String.format("requiresClat expected for type=%d state=%s", type, state),
-                        Nat464Xlat.requiresClat(mNai));
+                String msg = String.format("requiresClat expected for type=%d state=%s",
+                        type, state);
+
+                mMisc.skip464xlat = true;
+                String errorMsg = msg + String.format(" skip464xlat=%b", mMisc.skip464xlat);
+                assertFalse(errorMsg, Nat464Xlat.requiresClat(mNai));
+
+                mMisc.skip464xlat = false;
+                errorMsg = msg + String.format(" skip464xlat=%b", mMisc.skip464xlat);
+                assertTrue(errorMsg, Nat464Xlat.requiresClat(mNai));
             }
         }
     }
diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
index 4a83d1b..f2bd770 100644
--- a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -21,17 +21,35 @@
 import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
 import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
 import static android.Manifest.permission.NETWORK_STACK;
-import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
+import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_OEM;
+import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_PRODUCT;
+import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_VENDOR;
 import static android.content.pm.PackageManager.GET_PERMISSIONS;
+import static android.os.Process.SYSTEM_UID;
+
+import static com.android.server.connectivity.PermissionMonitor.NETWORK;
+import static com.android.server.connectivity.PermissionMonitor.SYSTEM;
+
+import static junit.framework.Assert.fail;
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.os.Build;
+import android.os.INetworkManagementService;
+import android.os.UserHandle;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
@@ -40,15 +58,29 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+
+import java.util.HashMap;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class PermissionMonitorTest {
-    private static final int MOCK_UID = 10001;
-    private static final String[] MOCK_PACKAGE_NAMES = new String[] { "com.foo.bar" };
+    private static final int MOCK_USER1 = 0;
+    private static final int MOCK_USER2 = 1;
+    private static final int MOCK_UID1 = 10001;
+    private static final String MOCK_PACKAGE1 = "appName1";
+    private static final String SYSTEM_PACKAGE1 = "sysName1";
+    private static final String SYSTEM_PACKAGE2 = "sysName2";
+    private static final String PARTITION_SYSTEM = "system";
+    private static final String PARTITION_OEM = "oem";
+    private static final String PARTITION_PRODUCT = "product";
+    private static final String PARTITION_VENDOR = "vendor";
+    private static final int VERSION_P = Build.VERSION_CODES.P;
+    private static final int VERSION_Q = Build.VERSION_CODES.Q;
 
     @Mock private Context mContext;
     @Mock private PackageManager mPackageManager;
+    @Mock private INetworkManagementService mNMS;
 
     private PermissionMonitor mPermissionMonitor;
 
@@ -56,43 +88,59 @@
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
         when(mContext.getPackageManager()).thenReturn(mPackageManager);
-        when(mPackageManager.getPackagesForUid(MOCK_UID)).thenReturn(MOCK_PACKAGE_NAMES);
-        mPermissionMonitor = new PermissionMonitor(mContext, null);
+        mPermissionMonitor = spy(new PermissionMonitor(mContext, mNMS));
     }
 
-    private void expectPermission(String[] permissions, boolean preinstalled) throws Exception {
-        final PackageInfo packageInfo = packageInfoWithPermissions(permissions, preinstalled);
-        when(mPackageManager.getPackageInfo(MOCK_PACKAGE_NAMES[0], GET_PERMISSIONS))
-                .thenReturn(packageInfo);
+    private boolean hasBgPermission(String partition, int targetSdkVersion, int uid,
+            String... permission) throws Exception {
+        final PackageInfo packageInfo = packageInfoWithPermissions(permission, partition);
+        packageInfo.applicationInfo.targetSdkVersion = targetSdkVersion;
+        packageInfo.applicationInfo.uid = uid;
+        when(mPackageManager.getPackageInfoAsUser(
+                eq(MOCK_PACKAGE1), eq(GET_PERMISSIONS), anyInt())).thenReturn(packageInfo);
+        when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[] {MOCK_PACKAGE1});
+        return mPermissionMonitor.hasUseBackgroundNetworksPermission(uid);
     }
 
-    private PackageInfo packageInfoWithPermissions(String[] permissions, boolean preinstalled) {
+    private PackageInfo packageInfoWithPermissions(String[] permissions, String partition) {
         final PackageInfo packageInfo = new PackageInfo();
         packageInfo.requestedPermissions = permissions;
         packageInfo.applicationInfo = new ApplicationInfo();
-        packageInfo.applicationInfo.flags = preinstalled ? FLAG_SYSTEM : 0;
+        int privateFlags = 0;
+        switch (partition) {
+            case PARTITION_OEM:
+                privateFlags = PRIVATE_FLAG_OEM;
+                break;
+            case PARTITION_PRODUCT:
+                privateFlags = PRIVATE_FLAG_PRODUCT;
+                break;
+            case PARTITION_VENDOR:
+                privateFlags = PRIVATE_FLAG_VENDOR;
+                break;
+        }
+        packageInfo.applicationInfo.privateFlags = privateFlags;
         return packageInfo;
     }
 
     @Test
     public void testHasPermission() {
-        PackageInfo app = packageInfoWithPermissions(new String[] {}, false);
+        PackageInfo app = packageInfoWithPermissions(new String[] {}, PARTITION_SYSTEM);
         assertFalse(mPermissionMonitor.hasPermission(app, CHANGE_NETWORK_STATE));
         assertFalse(mPermissionMonitor.hasPermission(app, NETWORK_STACK));
         assertFalse(mPermissionMonitor.hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS));
         assertFalse(mPermissionMonitor.hasPermission(app, CONNECTIVITY_INTERNAL));
 
         app = packageInfoWithPermissions(new String[] {
-                CHANGE_NETWORK_STATE, NETWORK_STACK
-            }, false);
+            CHANGE_NETWORK_STATE, NETWORK_STACK
+        }, PARTITION_SYSTEM);
         assertTrue(mPermissionMonitor.hasPermission(app, CHANGE_NETWORK_STATE));
         assertTrue(mPermissionMonitor.hasPermission(app, NETWORK_STACK));
         assertFalse(mPermissionMonitor.hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS));
         assertFalse(mPermissionMonitor.hasPermission(app, CONNECTIVITY_INTERNAL));
 
         app = packageInfoWithPermissions(new String[] {
-                CONNECTIVITY_USE_RESTRICTED_NETWORKS, CONNECTIVITY_INTERNAL
-            }, false);
+            CONNECTIVITY_USE_RESTRICTED_NETWORKS, CONNECTIVITY_INTERNAL
+        }, PARTITION_SYSTEM);
         assertFalse(mPermissionMonitor.hasPermission(app, CHANGE_NETWORK_STATE));
         assertFalse(mPermissionMonitor.hasPermission(app, NETWORK_STACK));
         assertTrue(mPermissionMonitor.hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS));
@@ -100,35 +148,192 @@
     }
 
     @Test
-    public void testIsPreinstalledSystemApp() {
-        PackageInfo app = packageInfoWithPermissions(new String[] {}, false);
-        assertFalse(mPermissionMonitor.isPreinstalledSystemApp(app));
-
-        app = packageInfoWithPermissions(new String[] {}, true);
-        assertTrue(mPermissionMonitor.isPreinstalledSystemApp(app));
+    public void testIsVendorApp() {
+        PackageInfo app = packageInfoWithPermissions(new String[] {}, PARTITION_SYSTEM);
+        assertFalse(mPermissionMonitor.isVendorApp(app.applicationInfo));
+        app = packageInfoWithPermissions(new String[] {}, PARTITION_OEM);
+        assertTrue(mPermissionMonitor.isVendorApp(app.applicationInfo));
+        app = packageInfoWithPermissions(new String[] {}, PARTITION_PRODUCT);
+        assertTrue(mPermissionMonitor.isVendorApp(app.applicationInfo));
+        app = packageInfoWithPermissions(new String[] {}, PARTITION_VENDOR);
+        assertTrue(mPermissionMonitor.isVendorApp(app.applicationInfo));
     }
 
     @Test
     public void testHasUseBackgroundNetworksPermission() throws Exception {
-        expectPermission(new String[] { CHANGE_NETWORK_STATE }, false);
-        assertTrue(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
+        assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1));
+        assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CHANGE_NETWORK_STATE));
+        assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1, NETWORK_STACK));
+        assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CONNECTIVITY_INTERNAL));
+        assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1,
+                CONNECTIVITY_USE_RESTRICTED_NETWORKS));
+        assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CHANGE_WIFI_STATE));
 
-        expectPermission(new String[] { NETWORK_STACK, CONNECTIVITY_INTERNAL }, false);
-        assertTrue(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
+        assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_Q, MOCK_UID1));
+        assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_Q, MOCK_UID1, CHANGE_WIFI_STATE));
+    }
 
-        // TODO : make this false when b/31479477 is fixed
-        expectPermission(new String[] {}, true);
-        assertTrue(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
-        expectPermission(new String[] { CHANGE_WIFI_STATE }, true);
-        assertTrue(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
+    @Test
+    public void testHasUseBackgroundNetworksPermissionSystemUid() throws Exception {
+        doReturn(VERSION_P).when(mPermissionMonitor).getDeviceFirstSdkInt();
+        assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, SYSTEM_UID));
+        assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, SYSTEM_UID, CHANGE_WIFI_STATE));
+        assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, SYSTEM_UID,
+                CONNECTIVITY_USE_RESTRICTED_NETWORKS));
 
-        expectPermission(new String[] { NETWORK_STACK, CONNECTIVITY_INTERNAL }, true);
-        assertTrue(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
+        doReturn(VERSION_Q).when(mPermissionMonitor).getDeviceFirstSdkInt();
+        assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID));
+        assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID, CHANGE_WIFI_STATE));
+        assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID,
+                CONNECTIVITY_USE_RESTRICTED_NETWORKS));
+    }
 
-        expectPermission(new String[] {}, false);
-        assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
+    @Test
+    public void testHasUseBackgroundNetworksPermissionVendorApp() throws Exception {
+        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1));
+        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1, CHANGE_NETWORK_STATE));
+        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1, NETWORK_STACK));
+        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1, CONNECTIVITY_INTERNAL));
+        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1,
+                CONNECTIVITY_USE_RESTRICTED_NETWORKS));
+        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1, CHANGE_WIFI_STATE));
 
-        expectPermission(new String[] { CHANGE_WIFI_STATE }, false);
-        assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
+        assertFalse(hasBgPermission(PARTITION_VENDOR, VERSION_Q, MOCK_UID1));
+        assertFalse(hasBgPermission(PARTITION_VENDOR, VERSION_Q, MOCK_UID1, CHANGE_WIFI_STATE));
+    }
+
+    private class NMSMonitor {
+        private final HashMap<Integer, Boolean> mApps = new HashMap<>();
+
+        NMSMonitor(INetworkManagementService mockNMS) throws Exception {
+            // Add hook to verify and track result of setPermission.
+            doAnswer((InvocationOnMock invocation) -> {
+                final Object[] args = invocation.getArguments();
+                final Boolean isSystem = args[0].equals("SYSTEM");
+                for (final int uid : (int[]) args[1]) {
+                    // TODO: Currently, permission monitor will send duplicate commands for each uid
+                    // corresponding to each user. Need to fix that and uncomment below test.
+                    // if (mApps.containsKey(uid) && mApps.get(uid) == isSystem) {
+                    //     fail("uid " + uid + " is already set to " + isSystem);
+                    // }
+                    mApps.put(uid, isSystem);
+                }
+                return null;
+            }).when(mockNMS).setPermission(anyString(), any(int[].class));
+
+            // Add hook to verify and track result of clearPermission.
+            doAnswer((InvocationOnMock invocation) -> {
+                final Object[] args = invocation.getArguments();
+                for (final int uid : (int[]) args[0]) {
+                    // TODO: Currently, permission monitor will send duplicate commands for each uid
+                    // corresponding to each user. Need to fix that and uncomment below test.
+                    // if (!mApps.containsKey(uid)) {
+                    //     fail("uid " + uid + " does not exist.");
+                    // }
+                    mApps.remove(uid);
+                }
+                return null;
+            }).when(mockNMS).clearPermission(any(int[].class));
+        }
+
+        public void expectPermission(Boolean permission, int[] users, int[] apps) {
+            for (final int user : users) {
+                for (final int app : apps) {
+                    final int uid = UserHandle.getUid(user, app);
+                    if (!mApps.containsKey(uid)) {
+                        fail("uid " + uid + " does not exist.");
+                    }
+                    if (mApps.get(uid) != permission) {
+                        fail("uid " + uid + " has wrong permission: " +  permission);
+                    }
+                }
+            }
+        }
+
+        public void expectNoPermission(int[] users, int[] apps) {
+            for (final int user : users) {
+                for (final int app : apps) {
+                    final int uid = UserHandle.getUid(user, app);
+                    if (mApps.containsKey(uid)) {
+                        fail("uid " + uid + " has listed permissions, expected none.");
+                    }
+                }
+            }
+        }
+    }
+
+    @Test
+    public void testUserAndPackageAddRemove() throws Exception {
+        final NMSMonitor mNMSMonitor = new NMSMonitor(mNMS);
+
+        // MOCK_UID1: MOCK_PACKAGE1 only has network permission.
+        // SYSTEM_UID: SYSTEM_PACKAGE1 has system permission.
+        // SYSTEM_UID: SYSTEM_PACKAGE2 only has network permission.
+        doReturn(SYSTEM).when(mPermissionMonitor).highestPermissionForUid(eq(SYSTEM), anyString());
+        doReturn(SYSTEM).when(mPermissionMonitor).highestPermissionForUid(any(),
+                eq(SYSTEM_PACKAGE1));
+        doReturn(NETWORK).when(mPermissionMonitor).highestPermissionForUid(any(),
+                eq(SYSTEM_PACKAGE2));
+        doReturn(NETWORK).when(mPermissionMonitor).highestPermissionForUid(any(),
+                eq(MOCK_PACKAGE1));
+
+        // Add SYSTEM_PACKAGE2, expect only have network permission.
+        mPermissionMonitor.onUserAdded(MOCK_USER1);
+        addPackageForUsers(new int[]{MOCK_USER1}, SYSTEM_PACKAGE2, SYSTEM_UID);
+        mNMSMonitor.expectPermission(NETWORK, new int[]{MOCK_USER1}, new int[]{SYSTEM_UID});
+
+        // Add SYSTEM_PACKAGE1, expect permission escalate.
+        addPackageForUsers(new int[]{MOCK_USER1}, SYSTEM_PACKAGE1, SYSTEM_UID);
+        mNMSMonitor.expectPermission(SYSTEM, new int[]{MOCK_USER1}, new int[]{SYSTEM_UID});
+
+        mPermissionMonitor.onUserAdded(MOCK_USER2);
+        mNMSMonitor.expectPermission(SYSTEM, new int[]{MOCK_USER1, MOCK_USER2},
+                new int[]{SYSTEM_UID});
+
+        addPackageForUsers(new int[]{MOCK_USER1, MOCK_USER2}, MOCK_PACKAGE1, MOCK_UID1);
+        mNMSMonitor.expectPermission(SYSTEM, new int[]{MOCK_USER1, MOCK_USER2},
+                new int[]{SYSTEM_UID});
+        mNMSMonitor.expectPermission(NETWORK, new int[]{MOCK_USER1, MOCK_USER2},
+                new int[]{MOCK_UID1});
+
+        // Remove MOCK_UID1, expect no permission left for all user.
+        mPermissionMonitor.onPackageRemoved(MOCK_UID1);
+        removePackageForUsers(new int[]{MOCK_USER1, MOCK_USER2}, MOCK_UID1);
+        mNMSMonitor.expectNoPermission(new int[]{MOCK_USER1, MOCK_USER2}, new int[]{MOCK_UID1});
+
+        // Remove SYSTEM_PACKAGE1, expect permission downgrade.
+        when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[]{SYSTEM_PACKAGE2});
+        removePackageForUsers(new int[]{MOCK_USER1, MOCK_USER2}, SYSTEM_UID);
+        mNMSMonitor.expectPermission(NETWORK, new int[]{MOCK_USER1, MOCK_USER2},
+                new int[]{SYSTEM_UID});
+
+        mPermissionMonitor.onUserRemoved(MOCK_USER1);
+        mNMSMonitor.expectPermission(NETWORK, new int[]{MOCK_USER2}, new int[]{SYSTEM_UID});
+
+        // Remove all packages, expect no permission left.
+        when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[]{});
+        removePackageForUsers(new int[]{MOCK_USER2}, SYSTEM_UID);
+        mNMSMonitor.expectNoPermission(new int[]{MOCK_USER1, MOCK_USER2},
+                new int[]{SYSTEM_UID, MOCK_UID1});
+
+        // Remove last user, expect no redundant clearPermission is invoked.
+        mPermissionMonitor.onUserRemoved(MOCK_USER2);
+        mNMSMonitor.expectNoPermission(new int[]{MOCK_USER1, MOCK_USER2},
+                new int[]{SYSTEM_UID, MOCK_UID1});
+    }
+
+    // Normal package add/remove operations will trigger multiple intent for uids corresponding to
+    // each user. To simulate generic package operations, the onPackageAdded/Removed will need to be
+    // called multiple times with the uid corresponding to each user.
+    private void addPackageForUsers(int[] users, String packageName, int uid) {
+        for (final int user : users) {
+            mPermissionMonitor.onPackageAdded(packageName, UserHandle.getUid(user, uid));
+        }
+    }
+
+    private void removePackageForUsers(int[] users, int uid) {
+        for (final int user : users) {
+            mPermissionMonitor.onPackageRemoved(UserHandle.getUid(user, uid));
+        }
     }
 }
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index e377a47..9bf7587 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -507,13 +507,15 @@
 
     private static void assertBlocked(Vpn vpn, int... uids) {
         for (int uid : uids) {
-            assertTrue("Uid " + uid + " should be blocked", vpn.isBlockingUid(uid));
+            final boolean blocked = vpn.getLockdown() && vpn.isBlockingUid(uid);
+            assertTrue("Uid " + uid + " should be blocked", blocked);
         }
     }
 
     private static void assertUnblocked(Vpn vpn, int... uids) {
         for (int uid : uids) {
-            assertFalse("Uid " + uid + " should not be blocked", vpn.isBlockingUid(uid));
+            final boolean blocked = vpn.getLockdown() && vpn.isBlockingUid(uid);
+            assertFalse("Uid " + uid + " should not be blocked", blocked);
         }
     }
 
diff --git a/tests/net/java/com/android/server/net/NetworkStatsAccessTest.java b/tests/net/java/com/android/server/net/NetworkStatsAccessTest.java
index 23318c2..b870bbd 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsAccessTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsAccessTest.java
@@ -176,7 +176,7 @@
     }
 
     private void setHasAppOpsPermission(int appOpsMode, boolean hasPermission) {
-        when(mAppOps.checkOp(AppOpsManager.OP_GET_USAGE_STATS, TEST_UID, TEST_PKG))
+        when(mAppOps.noteOp(AppOpsManager.OP_GET_USAGE_STATS, TEST_UID, TEST_PKG))
                 .thenReturn(appOpsMode);
         when(mContext.checkCallingPermission(Manifest.permission.PACKAGE_USAGE_STATS)).thenReturn(
                 hasPermission ? PackageManager.PERMISSION_GRANTED
diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
index 6836626..f89f303 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -91,16 +91,16 @@
 import android.os.Message;
 import android.os.Messenger;
 import android.os.PowerManager;
+import android.os.SimpleClock;
 import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
 import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
 import android.telephony.TelephonyManager;
-import android.util.Log;
-import android.util.TrustedTime;
 
 import com.android.internal.net.VpnInfo;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.test.BroadcastInterceptingContext;
+import com.android.server.LocalServices;
 import com.android.server.net.NetworkStatsService.NetworkStatsSettings;
 import com.android.server.net.NetworkStatsService.NetworkStatsSettings.Config;
 
@@ -116,6 +116,8 @@
 import org.mockito.MockitoAnnotations;
 
 import java.io.File;
+import java.time.Clock;
+import java.time.ZoneOffset;
 import java.util.Objects;
 
 /**
@@ -160,7 +162,6 @@
     private File mStatsDir;
 
     private @Mock INetworkManagementService mNetManager;
-    private @Mock TrustedTime mTime;
     private @Mock NetworkStatsSettings mSettings;
     private @Mock IConnectivityManager mConnManager;
     private @Mock IBinder mBinder;
@@ -172,6 +173,13 @@
     private INetworkStatsSession mSession;
     private INetworkManagementEventObserver mNetworkObserver;
 
+    private final Clock mClock = new SimpleClock(ZoneOffset.UTC) {
+        @Override
+        public long millis() {
+            return currentTimeMillis();
+        }
+    };
+
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
@@ -189,7 +197,7 @@
                 powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
 
         mService = new NetworkStatsService(
-                mServiceContext, mNetManager, mAlarmManager, wakeLock, mTime,
+                mServiceContext, mNetManager, mAlarmManager, wakeLock, mClock,
                 TelephonyManager.getDefault(), mSettings, new NetworkStatsObservers(),
                 mStatsDir, getBaseDir(mStatsDir));
         mHandlerThread = new HandlerThread("HandlerThread");
@@ -201,7 +209,6 @@
 
         mElapsedRealtime = 0L;
 
-        expectCurrentTime();
         expectDefaultSettings();
         expectNetworkStatsUidDetail(buildEmptyStats());
         expectSystemReady();
@@ -226,7 +233,6 @@
         mStatsDir = null;
 
         mNetManager = null;
-        mTime = null;
         mSettings = null;
         mConnManager = null;
 
@@ -238,7 +244,6 @@
     public void testNetworkStatsWifi() throws Exception {
         // pretend that wifi network comes online; service should ask about full
         // network state, and poll any existing interfaces before updating.
-        expectCurrentTime();
         expectDefaultSettings();
         expectNetworkState(buildWifiState());
         expectNetworkStatsSummary(buildEmptyStats());
@@ -253,7 +258,6 @@
 
         // modify some number on wifi, and trigger poll event
         incrementCurrentTime(HOUR_IN_MILLIS);
-        expectCurrentTime();
         expectDefaultSettings();
         expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
                 .addIfaceValues(TEST_IFACE, 1024L, 1L, 2048L, 2L));
@@ -267,7 +271,6 @@
         // and bump forward again, with counters going higher. this is
         // important, since polling should correctly subtract last snapshot.
         incrementCurrentTime(DAY_IN_MILLIS);
-        expectCurrentTime();
         expectDefaultSettings();
         expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
                 .addIfaceValues(TEST_IFACE, 4096L, 4L, 8192L, 8L));
@@ -285,7 +288,6 @@
 
         // pretend that wifi network comes online; service should ask about full
         // network state, and poll any existing interfaces before updating.
-        expectCurrentTime();
         expectDefaultSettings();
         expectNetworkState(buildWifiState());
         expectNetworkStatsSummary(buildEmptyStats());
@@ -300,7 +302,6 @@
 
         // modify some number on wifi, and trigger poll event
         incrementCurrentTime(HOUR_IN_MILLIS);
-        expectCurrentTime();
         expectDefaultSettings();
         expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
                 .addIfaceValues(TEST_IFACE, 1024L, 8L, 2048L, 16L));
@@ -329,13 +330,11 @@
 
         // graceful shutdown system, which should trigger persist of stats, and
         // clear any values in memory.
-        expectCurrentTime();
         expectDefaultSettings();
         mServiceContext.sendBroadcast(new Intent(Intent.ACTION_SHUTDOWN));
         assertStatsFilesExist(true);
 
         // boot through serviceReady() again
-        expectCurrentTime();
         expectDefaultSettings();
         expectNetworkStatsUidDetail(buildEmptyStats());
         expectSystemReady();
@@ -363,7 +362,6 @@
 
         // pretend that wifi network comes online; service should ask about full
         // network state, and poll any existing interfaces before updating.
-        expectCurrentTime();
         expectSettings(0L, HOUR_IN_MILLIS, WEEK_IN_MILLIS);
         expectNetworkState(buildWifiState());
         expectNetworkStatsSummary(buildEmptyStats());
@@ -375,7 +373,6 @@
 
         // modify some number on wifi, and trigger poll event
         incrementCurrentTime(2 * HOUR_IN_MILLIS);
-        expectCurrentTime();
         expectSettings(0L, HOUR_IN_MILLIS, WEEK_IN_MILLIS);
         expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
                 .addIfaceValues(TEST_IFACE, 512L, 4L, 512L, 4L));
@@ -391,7 +388,6 @@
 
         // now change bucket duration setting and trigger another poll with
         // exact same values, which should resize existing buckets.
-        expectCurrentTime();
         expectSettings(0L, 30 * MINUTE_IN_MILLIS, WEEK_IN_MILLIS);
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(buildEmptyStats());
@@ -408,7 +404,6 @@
     @Test
     public void testUidStatsAcrossNetworks() throws Exception {
         // pretend first mobile network comes online
-        expectCurrentTime();
         expectDefaultSettings();
         expectNetworkState(buildMobile3gState(IMSI_1));
         expectNetworkStatsSummary(buildEmptyStats());
@@ -420,7 +415,6 @@
 
         // create some traffic on first network
         incrementCurrentTime(HOUR_IN_MILLIS);
-        expectCurrentTime();
         expectDefaultSettings();
         expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
                 .addIfaceValues(TEST_IFACE, 2048L, 16L, 512L, 4L));
@@ -442,7 +436,6 @@
         // now switch networks; this also tests that we're okay with interfaces
         // disappearing, to verify we don't count backwards.
         incrementCurrentTime(HOUR_IN_MILLIS);
-        expectCurrentTime();
         expectDefaultSettings();
         expectNetworkState(buildMobile3gState(IMSI_2));
         expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
@@ -459,7 +452,6 @@
 
         // create traffic on second network
         incrementCurrentTime(HOUR_IN_MILLIS);
-        expectCurrentTime();
         expectDefaultSettings();
         expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
                 .addIfaceValues(TEST_IFACE, 2176L, 17L, 1536L, 12L));
@@ -488,7 +480,6 @@
     @Test
     public void testUidRemovedIsMoved() throws Exception {
         // pretend that network comes online
-        expectCurrentTime();
         expectDefaultSettings();
         expectNetworkState(buildWifiState());
         expectNetworkStatsSummary(buildEmptyStats());
@@ -500,7 +491,6 @@
 
         // create some traffic
         incrementCurrentTime(HOUR_IN_MILLIS);
-        expectCurrentTime();
         expectDefaultSettings();
         expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
                 .addIfaceValues(TEST_IFACE, 4128L, 258L, 544L, 34L));
@@ -522,7 +512,6 @@
 
         // now pretend two UIDs are uninstalled, which should migrate stats to
         // special "removed" bucket.
-        expectCurrentTime();
         expectDefaultSettings();
         expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
                 .addIfaceValues(TEST_IFACE, 4128L, 258L, 544L, 34L));
@@ -550,7 +539,6 @@
     @Test
     public void testUid3g4gCombinedByTemplate() throws Exception {
         // pretend that network comes online
-        expectCurrentTime();
         expectDefaultSettings();
         expectNetworkState(buildMobile3gState(IMSI_1));
         expectNetworkStatsSummary(buildEmptyStats());
@@ -562,7 +550,6 @@
 
         // create some traffic
         incrementCurrentTime(HOUR_IN_MILLIS);
-        expectCurrentTime();
         expectDefaultSettings();
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
@@ -578,7 +565,6 @@
 
         // now switch over to 4g network
         incrementCurrentTime(HOUR_IN_MILLIS);
-        expectCurrentTime();
         expectDefaultSettings();
         expectNetworkState(buildMobile4gState(TEST_IFACE2));
         expectNetworkStatsSummary(buildEmptyStats());
@@ -593,7 +579,6 @@
 
         // create traffic on second network
         incrementCurrentTime(HOUR_IN_MILLIS);
-        expectCurrentTime();
         expectDefaultSettings();
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
@@ -612,7 +597,6 @@
     @Test
     public void testSummaryForAllUid() throws Exception {
         // pretend that network comes online
-        expectCurrentTime();
         expectDefaultSettings();
         expectNetworkState(buildWifiState());
         expectNetworkStatsSummary(buildEmptyStats());
@@ -624,7 +608,6 @@
 
         // create some traffic for two apps
         incrementCurrentTime(HOUR_IN_MILLIS);
-        expectCurrentTime();
         expectDefaultSettings();
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
@@ -642,7 +625,6 @@
 
         // now create more traffic in next hour, but only for one app
         incrementCurrentTime(HOUR_IN_MILLIS);
-        expectCurrentTime();
         expectDefaultSettings();
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
@@ -762,7 +744,6 @@
     @Test
     public void testForegroundBackground() throws Exception {
         // pretend that network comes online
-        expectCurrentTime();
         expectDefaultSettings();
         expectNetworkState(buildWifiState());
         expectNetworkStatsSummary(buildEmptyStats());
@@ -774,7 +755,6 @@
 
         // create some initial traffic
         incrementCurrentTime(HOUR_IN_MILLIS);
-        expectCurrentTime();
         expectDefaultSettings();
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
@@ -790,7 +770,6 @@
 
         // now switch to foreground
         incrementCurrentTime(HOUR_IN_MILLIS);
-        expectCurrentTime();
         expectDefaultSettings();
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
@@ -823,7 +802,6 @@
     @Test
     public void testMetered() throws Exception {
         // pretend that network comes online
-        expectCurrentTime();
         expectDefaultSettings();
         expectNetworkState(buildWifiState(true /* isMetered */));
         expectNetworkStatsSummary(buildEmptyStats());
@@ -835,7 +813,6 @@
 
         // create some initial traffic
         incrementCurrentTime(HOUR_IN_MILLIS);
-        expectCurrentTime();
         expectDefaultSettings();
         expectNetworkStatsSummary(buildEmptyStats());
         // Note that all traffic from NetworkManagementService is tagged as METERED_NO, ROAMING_NO
@@ -865,7 +842,6 @@
     @Test
     public void testRoaming() throws Exception {
         // pretend that network comes online
-        expectCurrentTime();
         expectDefaultSettings();
         expectNetworkState(buildMobile3gState(IMSI_1, true /* isRoaming */));
         expectNetworkStatsSummary(buildEmptyStats());
@@ -877,7 +853,6 @@
 
         // Create some traffic
         incrementCurrentTime(HOUR_IN_MILLIS);
-        expectCurrentTime();
         expectDefaultSettings();
         expectNetworkStatsSummary(buildEmptyStats());
         // Note that all traffic from NetworkManagementService is tagged as METERED_NO and
@@ -906,7 +881,6 @@
     @Test
     public void testTethering() throws Exception {
         // pretend first mobile network comes online
-        expectCurrentTime();
         expectDefaultSettings();
         expectNetworkState(buildMobile3gState(IMSI_1));
         expectNetworkStatsSummary(buildEmptyStats());
@@ -918,7 +892,6 @@
 
         // create some tethering traffic
         incrementCurrentTime(HOUR_IN_MILLIS);
-        expectCurrentTime();
         expectDefaultSettings();
 
         // Traffic seen by kernel counters (includes software tethering).
@@ -951,7 +924,6 @@
     public void testRegisterUsageCallback() throws Exception {
         // pretend that wifi network comes online; service should ask about full
         // network state, and poll any existing interfaces before updating.
-        expectCurrentTime();
         expectDefaultSettings();
         expectNetworkState(buildWifiState());
         expectNetworkStatsSummary(buildEmptyStats());
@@ -972,7 +944,6 @@
         Messenger messenger = new Messenger(latchedHandler);
 
         // Force poll
-        expectCurrentTime();
         expectDefaultSettings();
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(buildEmptyStats());
@@ -1001,7 +972,6 @@
         // modify some number on wifi, and trigger poll event
         // not enough traffic to call data usage callback
         incrementCurrentTime(HOUR_IN_MILLIS);
-        expectCurrentTime();
         expectDefaultSettings();
         expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
                 .addIfaceValues(TEST_IFACE, 1024L, 1L, 2048L, 2L));
@@ -1017,7 +987,6 @@
         // and bump forward again, with counters going higher. this is
         // important, since it will trigger the data usage callback
         incrementCurrentTime(DAY_IN_MILLIS);
-        expectCurrentTime();
         expectDefaultSettings();
         expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
                 .addIfaceValues(TEST_IFACE, 4096000L, 4L, 8192000L, 8L));
@@ -1160,7 +1129,6 @@
     private void expectSettings(long persistBytes, long bucketDuration, long deleteAge)
             throws Exception {
         when(mSettings.getPollInterval()).thenReturn(HOUR_IN_MILLIS);
-        when(mSettings.getTimeCacheMaxAge()).thenReturn(DAY_IN_MILLIS);
         when(mSettings.getSampleEnabled()).thenReturn(true);
 
         final Config config = new Config(bucketDuration, deleteAge, deleteAge);
@@ -1176,14 +1144,6 @@
         when(mSettings.getUidTagPersistBytes(anyLong())).thenReturn(MB_IN_BYTES);
     }
 
-    private void expectCurrentTime() throws Exception {
-        when(mTime.forceRefresh()).thenReturn(false);
-        when(mTime.hasCache()).thenReturn(true);
-        when(mTime.currentTimeMillis()).thenReturn(currentTimeMillis());
-        when(mTime.getCacheAge()).thenReturn(0L);
-        when(mTime.getCacheCertainty()).thenReturn(0L);
-    }
-
     private void expectBandwidthControlCheck() throws Exception {
         when(mNetManager.isBandwidthControlEnabled()).thenReturn(true);
     }
diff --git a/tests/net/res/raw/xt_qtaguid_with_clat b/tests/net/res/raw/xt_qtaguid_with_clat
index 77e5c7b..6cd7499 100644
--- a/tests/net/res/raw/xt_qtaguid_with_clat
+++ b/tests/net/res/raw/xt_qtaguid_with_clat
@@ -7,7 +7,7 @@
 7 v4-wlan0 0x0 10060 1 1448660 1041 31192 753 1448660 1041 0 0 0 0 31192 753 0 0 0 0
 8 v4-wlan0 0x0 10102 0 9702 16 2870 23 9702 16 0 0 0 0 2870 23 0 0 0 0
 9 v4-wlan0 0x0 10102 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-10 wlan0 0x0 0 0 11058671 7892 312046 5113 11043898 7811 13117 61 1656 20 306544 5046 3230 38 2272 29
+10 wlan0 0x0 0 0 11058671 7892 0 0 11043898 7811 13117 61 1656 20 0 0 0 0 0 0
 11 wlan0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 12 wlan0 0x0 1000 0 6126 13 2013 16 5934 11 192 2 0 0 1821 14 192 2 0 0
 13 wlan0 0x0 1000 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
@@ -41,3 +41,5 @@
 41 dummy0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 42 lo 0x0 0 0 1288 16 1288 16 0 0 532 8 756 8 0 0 532 8 756 8
 43 lo 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+44 wlan0 0x0 1029 0 0 0 312046 5113 0 0 0 0 0 0 306544 5046 3230 38 2272 29
+45 wlan0 0x0 1029 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
\ No newline at end of file
diff --git a/tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_after b/tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_after
index c78f84f..9f86153 100644
--- a/tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_after
+++ b/tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_after
@@ -9,7 +9,7 @@
 9 v4-wlan0 0x0 10057 1 728 7 392 7 0 0 728 7 0 0 0 0 392 7 0 0
 10 v4-wlan0 0x0 10106 0 2232 18 2232 18 0 0 2232 18 0 0 0 0 2232 18 0 0
 11 v4-wlan0 0x0 10106 1 432952718 314238 5442288 121260 432950238 314218 2480 20 0 0 5433900 121029 8388 231 0 0
-12 wlan0 0x0 0 0 440746376 329772 8524052 130894 439660007 315369 232001 1276 854368 13127 7871216 121284 108568 1325 544268 8285
+12 wlan0 0x0 0 0 440746376 329772 0 0 439660007 315369 232001 1276 854368 13127 0 0 0 0 0 0
 13 wlan0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 14 wlan0 0x0 1000 0 77113 272 56151 575 77113 272 0 0 0 0 19191 190 36960 385 0 0
 15 wlan0 0x0 1000 1 20227 80 8356 72 18539 74 1688 6 0 0 7562 66 794 6 0 0
@@ -185,3 +185,5 @@
 185 wlan0 0xffffff0900000000 1000 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 186 dummy0 0x0 0 0 0 0 168 3 0 0 0 0 0 0 0 0 0 0 168 3
 187 dummy0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+188 wlan0 0x0 1029 0 0 0 8524052 130894 0 0 0 0 0 0 7871216 121284 108568 1325 544268 8285
+189 wlan0 0x0 1029 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
diff --git a/tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_before b/tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_before
index d035387..ce4bcc3 100644
--- a/tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_before
+++ b/tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_before
@@ -9,7 +9,7 @@
 9 v4-wlan0 0x0 10057 1 728 7 392 7 0 0 728 7 0 0 0 0 392 7 0 0
 10 v4-wlan0 0x0 10106 0 1488 12 1488 12 0 0 1488 12 0 0 0 0 1488 12 0 0
 11 v4-wlan0 0x0 10106 1 323981189 235142 3509032 84542 323979453 235128 1736 14 0 0 3502676 84363 6356 179 0 0
-12 wlan0 0x0 0 0 330187296 250652 5855801 94173 329106990 236273 226202 1255 854104 13124 5208040 84634 103637 1256 544124 8283
+12 wlan0 0x0 0 0 330187296 250652 0 0 329106990 236273 226202 1255 854104 13124 0 0 0 0 0 0
 13 wlan0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 14 wlan0 0x0 1000 0 77113 272 56151 575 77113 272 0 0 0 0 19191 190 36960 385 0 0
 15 wlan0 0x0 1000 1 20227 80 8356 72 18539 74 1688 6 0 0 7562 66 794 6 0 0
@@ -183,3 +183,5 @@
 183 wlan0 0xffffff0900000000 1000 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 184 dummy0 0x0 0 0 0 0 168 3 0 0 0 0 0 0 0 0 0 0 168 3
 185 dummy0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+186 wlan0 0x0 1029 0 0 0 5855801 94173 0 0 0 0 0 0 5208040 84634 103637 1256 544124 8283
+187 wlan0 0x0 1029 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
diff --git a/tests/net/res/raw/xt_qtaguid_with_clat_simple b/tests/net/res/raw/xt_qtaguid_with_clat_simple
index 7f0e56f..8c132e7 100644
--- a/tests/net/res/raw/xt_qtaguid_with_clat_simple
+++ b/tests/net/res/raw/xt_qtaguid_with_clat_simple
@@ -1,5 +1,6 @@
 idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets
-2 v4-wlan0 0x0 10060 0 42600 213 4100 41 42600 213 4100 41 0 0 0 0 0 0 0 0
+2 v4-wlan0 0x0 10060 0 42600 213 4100 41 42600 213 0 0 0 0 4100 41 0 0 0 0
 3 v4-wlan0 0x0 10060 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-4 wlan0 0x0 0 0 46860 213 4920 41 46860 213 4920 41 0 0 0 0 0 0 0 0
+4 wlan0 0x0 0 0 46860 213 0 0 46860 213 0 0 0 0 0 0 0 0 0 0
 5 wlan0 0x0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+6 wlan0 0x0 1029 0 0 0 4920 41 0 0 0 0 0 0 4920 41 0 0 0 0