[automerger skipped] Merge changes from topic "sort stats" into pi-dev
am: 664145dcbc  -s ours

Change-Id: Iaf2d41bf8c4464e05f8733d453fcc1a168ef8323
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 c5cb1f5..4714587 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -22,10 +22,10 @@
 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.VERSION_CODES;
 import android.os.Bundle;
@@ -58,6 +58,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 +83,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
@@ -125,8 +127,8 @@
     /**
      * A temporary hack until SUPL system can get off the legacy APIS.
      * They do too many network requests and the long list of apps listening
-     * and waking due to the CONNECTIVITY_ACTION bcast makes it expensive.
-     * Use this bcast intent instead for SUPL requests.
+     * and waking due to the CONNECTIVITY_ACTION broadcast makes it expensive.
+     * Use this broadcast intent instead for SUPL requests.
      * @hide
      */
     public static final String CONNECTIVITY_ACTION_SUPL =
@@ -152,7 +154,7 @@
      * call {@link CaptivePortal#reportCaptivePortalDismissed} so the system can
      * reevaluate the network. If reevaluation finds the network no longer
      * subject to a captive portal, the network may become the default active
-     * data network. </li>
+     * data network.</li>
      * <li> When the app handling this action believes the user explicitly wants
      * to ignore the captive portal and the network, the app should call
      * {@link CaptivePortal#ignoreNetwork}. </li>
@@ -260,7 +262,8 @@
      * {@hide}
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_DATA_ACTIVITY_CHANGE = "android.net.conn.DATA_ACTIVITY_CHANGE";
+    public static final String ACTION_DATA_ACTIVITY_CHANGE =
+            "android.net.conn.DATA_ACTIVITY_CHANGE";
     /**
      * The lookup key for an enum that indicates the network device type on which this data activity
      * change happens.
@@ -310,6 +313,7 @@
      * @hide
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @UnsupportedAppUsage
     public static final String INET_CONDITION_ACTION =
             "android.net.conn.INET_CONDITION_ACTION";
 
@@ -324,6 +328,7 @@
      * @hide
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @UnsupportedAppUsage
     public static final String ACTION_TETHER_STATE_CHANGED =
             "android.net.conn.TETHER_STATE_CHANGED";
 
@@ -332,6 +337,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";
 
     /**
@@ -346,6 +352,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";
 
     /**
@@ -354,6 +361,7 @@
      * failed.  Use {@link #getLastTetherError} to find the error code
      * for any interfaces listed here.
      */
+    @UnsupportedAppUsage
     public static final String EXTRA_ERRORED_TETHER = "erroredArray";
 
     /**
@@ -391,14 +399,14 @@
 
     /**
      * Invalid tethering type.
-     * @see #startTethering(int, OnStartTetheringCallback, boolean)
+     * @see #startTethering(int, boolean, OnStartTetheringCallback)
      * @hide
      */
     public static final int TETHERING_INVALID   = -1;
 
     /**
      * Wifi tethering type.
-     * @see #startTethering(int, OnStartTetheringCallback, boolean)
+     * @see #startTethering(int, boolean, OnStartTetheringCallback)
      * @hide
      */
     @SystemApi
@@ -406,7 +414,7 @@
 
     /**
      * USB tethering type.
-     * @see #startTethering(int, OnStartTetheringCallback, boolean)
+     * @see #startTethering(int, boolean, OnStartTetheringCallback)
      * @hide
      */
     @SystemApi
@@ -414,7 +422,7 @@
 
     /**
      * Bluetooth tethering type.
-     * @see #startTethering(int, OnStartTetheringCallback, boolean)
+     * @see #startTethering(int, boolean, OnStartTetheringCallback)
      * @hide
      */
     @SystemApi
@@ -458,6 +466,7 @@
      * The absence of a connection type.
      * @hide
      */
+    @UnsupportedAppUsage
     public static final int TYPE_NONE        = -1;
 
     /**
@@ -574,6 +583,7 @@
      * {@hide}
      */
     @Deprecated
+    @UnsupportedAppUsage
     public static final int TYPE_MOBILE_FOTA = 10;
 
     /**
@@ -582,6 +592,7 @@
      * {@hide}
      */
     @Deprecated
+    @UnsupportedAppUsage
     public static final int TYPE_MOBILE_IMS  = 11;
 
     /**
@@ -590,6 +601,7 @@
      * {@hide}
      */
     @Deprecated
+    @UnsupportedAppUsage
     public static final int TYPE_MOBILE_CBS  = 12;
 
     /**
@@ -599,6 +611,7 @@
      * {@hide}
      */
     @Deprecated
+    @UnsupportedAppUsage
     public static final int TYPE_WIFI_P2P    = 13;
 
     /**
@@ -607,6 +620,7 @@
      * {@hide}
      */
     @Deprecated
+    @UnsupportedAppUsage
     public static final int TYPE_MOBILE_IA = 14;
 
     /**
@@ -616,6 +630,7 @@
      * {@hide}
      */
     @Deprecated
+    @UnsupportedAppUsage
     public static final int TYPE_MOBILE_EMERGENCY = 15;
 
     /**
@@ -624,6 +639,7 @@
      * {@hide}
      */
     @Deprecated
+    @UnsupportedAppUsage
     public static final int TYPE_PROXY = 16;
 
     /**
@@ -664,7 +680,7 @@
     /**
      * Static unique request used as a tombstone for NetworkCallbacks that have been unregistered.
      * This allows to distinguish when unregistering NetworkCallbacks those that were never
-     * registered and those that were already unregistered.
+     * registered from those that were already unregistered.
      * @hide
      */
     private static final NetworkRequest ALREADY_UNREGISTERED =
@@ -706,6 +722,7 @@
      */
     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
@@ -742,6 +759,7 @@
      * {@hide}
      */
     @Deprecated
+    @UnsupportedAppUsage
     public static String getNetworkTypeName(int type) {
         switch (type) {
           case TYPE_NONE:
@@ -796,6 +814,7 @@
      * {@hide}
      */
     @Deprecated
+    @UnsupportedAppUsage
     public static boolean isNetworkTypeMobile(int networkType) {
         switch (networkType) {
             case TYPE_MOBILE:
@@ -1009,6 +1028,7 @@
      * {@hide}
      */
     @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL)
+    @UnsupportedAppUsage
     public NetworkInfo getActiveNetworkInfoForUid(int uid) {
         return getActiveNetworkInfoForUid(uid, false);
     }
@@ -1106,6 +1126,7 @@
      */
     @Deprecated
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+    @UnsupportedAppUsage
     public Network getNetworkForType(int networkType) {
         try {
             return mService.getNetworkForType(networkType);
@@ -1134,6 +1155,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);
@@ -1152,6 +1174,7 @@
      * {@hide}
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+    @UnsupportedAppUsage
     public LinkProperties getActiveLinkProperties() {
         try {
             return mService.getActiveLinkProperties();
@@ -1176,6 +1199,7 @@
      */
     @Deprecated
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+    @UnsupportedAppUsage
     public LinkProperties getLinkProperties(int networkType) {
         try {
             return mService.getLinkPropertiesForType(networkType);
@@ -1331,6 +1355,7 @@
         return 1;
     }
 
+    @UnsupportedAppUsage
     private NetworkCapabilities networkCapabilitiesForFeature(int networkType, String feature) {
         if (networkType == TYPE_MOBILE) {
             switch (feature) {
@@ -1494,8 +1519,9 @@
         };
     }
 
-    private static HashMap<NetworkCapabilities, LegacyRequest> sLegacyRequests =
-            new HashMap<NetworkCapabilities, LegacyRequest>();
+    @UnsupportedAppUsage
+    private static final HashMap<NetworkCapabilities, LegacyRequest> sLegacyRequests =
+            new HashMap<>();
 
     private NetworkRequest findRequestForFeature(NetworkCapabilities netCap) {
         synchronized (sLegacyRequests) {
@@ -1522,6 +1548,7 @@
         Log.d(TAG, "expireRequest with " + ourSeqNum + ", " + sequenceNum);
     }
 
+    @UnsupportedAppUsage
     private NetworkRequest requestNetworkForFeatureLocked(NetworkCapabilities netCap) {
         int delay = -1;
         int type = legacyTypeForNetworkCapabilities(netCap);
@@ -1551,6 +1578,7 @@
         }
     }
 
+    @UnsupportedAppUsage
     private boolean removeRequestForFeature(NetworkCapabilities netCap) {
         final LegacyRequest l;
         synchronized (sLegacyRequests) {
@@ -1618,10 +1646,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) {}
     }
 
@@ -1635,8 +1666,9 @@
      * {@code onStarted} method will be called. If an error occurs, {@code onError} will be called,
      * specifying one of the {@code ERROR_*} constants in this class.
      *
-     * To stop an existing keepalive, call {@link stop}. The system will call {@code onStopped} if
-     * the operation was successfull or {@code onError} if an error occurred.
+     * To stop an existing keepalive, call {@link PacketKeepalive#stop}. The system will call
+     * {@link PacketKeepaliveCallback#onStopped} if the operation was successful or
+     * {@link PacketKeepaliveCallback#onError} if an error occurred.
      *
      * @hide
      */
@@ -1687,6 +1719,7 @@
             mLooper.quit();
         }
 
+        @UnsupportedAppUsage
         public void stop() {
             try {
                 mService.stopKeepalive(mNetwork, mSlot);
@@ -1742,6 +1775,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public PacketKeepalive startNattKeepalive(
             Network network, int intervalSeconds, PacketKeepaliveCallback callback,
             InetAddress srcAddr, int srcPort, InetAddress dstAddr) {
@@ -1803,6 +1837,7 @@
      *             {@link #bindProcessToNetwork} API.
      */
     @Deprecated
+    @UnsupportedAppUsage
     public boolean requestRouteToHostAddress(int networkType, InetAddress hostAddress) {
         checkLegacyRoutingApiAccess();
         try {
@@ -1846,12 +1881,14 @@
      * @hide
      */
     @Deprecated
+    @UnsupportedAppUsage
     public void setBackgroundDataSetting(boolean allowBackgroundData) {
         // ignored
     }
 
     /** {@hide} */
     @Deprecated
+    @UnsupportedAppUsage
     public NetworkQuotaInfo getActiveNetworkQuotaInfo() {
         try {
             return mService.getActiveNetworkQuotaInfo();
@@ -1865,6 +1902,7 @@
      * @deprecated Talk to TelephonyManager directly
      */
     @Deprecated
+    @UnsupportedAppUsage
     public boolean getMobileDataEnabled() {
         IBinder b = ServiceManager.getService(Context.TELEPHONY_SERVICE);
         if (b != null) {
@@ -1897,7 +1935,7 @@
          * to initiate network traffic), you can retrieve its instantaneous state with
          * {@link ConnectivityManager#isDefaultNetworkActive}.
          */
-        public void onNetworkActive();
+        void onNetworkActive();
     }
 
     private INetworkManagementService getNetworkManagementService() {
@@ -1912,8 +1950,7 @@
     }
 
     private final ArrayMap<OnNetworkActiveListener, INetworkActivityListener>
-            mNetworkActivityListeners
-                    = new ArrayMap<OnNetworkActiveListener, INetworkActivityListener>();
+            mNetworkActivityListeners = new ArrayMap<>();
 
     /**
      * Start listening to reports when the system's default data network is active, meaning it is
@@ -1985,6 +2022,7 @@
     }
 
     /** {@hide} */
+    @UnsupportedAppUsage
     public static ConnectivityManager from(Context context) {
         return (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
     }
@@ -2035,6 +2073,7 @@
      * @hide
      */
     @Deprecated
+    @UnsupportedAppUsage
     private static ConnectivityManager getInstance() {
         if (getInstanceOrNull() == null) {
             throw new IllegalStateException("No ConnectivityManager yet constructed");
@@ -2051,6 +2090,7 @@
      * {@hide}
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+    @UnsupportedAppUsage
     public String[] getTetherableIfaces() {
         try {
             return mService.getTetherableIfaces();
@@ -2067,6 +2107,7 @@
      * {@hide}
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+    @UnsupportedAppUsage
     public String[] getTetheredIfaces() {
         try {
             return mService.getTetheredIfaces();
@@ -2089,6 +2130,7 @@
      * {@hide}
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+    @UnsupportedAppUsage
     public String[] getTetheringErroredIfaces() {
         try {
             return mService.getTetheringErroredIfaces();
@@ -2135,6 +2177,7 @@
      *
      * {@hide}
      */
+    @UnsupportedAppUsage
     public int tether(String iface) {
         try {
             String pkgName = mContext.getOpPackageName();
@@ -2163,6 +2206,7 @@
      *
      * {@hide}
      */
+    @UnsupportedAppUsage
     public int untether(String iface) {
         try {
             String pkgName = mContext.getOpPackageName();
@@ -2216,12 +2260,12 @@
         /**
          * Called when tethering has been successfully started.
          */
-        public void onTetheringStarted() {};
+        public void onTetheringStarted() {}
 
         /**
          * Called when starting tethering failed.
          */
-        public void onTetheringFailed() {};
+        public void onTetheringFailed() {}
     }
 
     /**
@@ -2316,6 +2360,7 @@
      * {@hide}
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+    @UnsupportedAppUsage
     public String[] getTetherableUsbRegexs() {
         try {
             return mService.getTetherableUsbRegexs();
@@ -2335,6 +2380,7 @@
      * {@hide}
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+    @UnsupportedAppUsage
     public String[] getTetherableWifiRegexs() {
         try {
             return mService.getTetherableWifiRegexs();
@@ -2354,6 +2400,7 @@
      * {@hide}
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+    @UnsupportedAppUsage
     public String[] getTetherableBluetoothRegexs() {
         try {
             return mService.getTetherableBluetoothRegexs();
@@ -2379,6 +2426,7 @@
      *
      * {@hide}
      */
+    @UnsupportedAppUsage
     public int setUsbTethering(boolean enable) {
         try {
             String pkgName = mContext.getOpPackageName();
@@ -2425,6 +2473,7 @@
      * {@hide}
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+    @UnsupportedAppUsage
     public int getLastTetherError(String iface) {
         try {
             return mService.getLastTetherError(iface);
@@ -2445,6 +2494,7 @@
      * {@hide}
      */
     public void reportInetCondition(int networkType, int percentage) {
+        printStackTrace();
         try {
             mService.reportInetCondition(networkType, percentage);
         } catch (RemoteException e) {
@@ -2465,6 +2515,7 @@
      */
     @Deprecated
     public void reportBadNetwork(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,6 +2538,7 @@
      *                        Internet using {@code network} or {@code false} if not.
      */
     public void reportNetworkConnectivity(Network network, boolean hasConnectivity) {
+        printStackTrace();
         try {
             mService.reportNetworkConnectivity(network, hasConnectivity);
         } catch (RemoteException e) {
@@ -2578,6 +2630,7 @@
      */
     @Deprecated
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+    @UnsupportedAppUsage
     public boolean isNetworkSupported(int networkType) {
         try {
             return mService.isNetworkSupported(networkType);
@@ -2658,9 +2711,6 @@
     /**
      * Set sign in error notification to visible or in visible
      *
-     * @param visible
-     * @param networkType
-     *
      * {@hide}
      * @deprecated Doesn't properly deal with multiple connected networks of the same type.
      */
@@ -2681,7 +2731,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);
@@ -2691,6 +2745,7 @@
     }
 
     /** {@hide} */
+    @UnsupportedAppUsage
     public void registerNetworkFactory(Messenger messenger, String name) {
         try {
             mService.registerNetworkFactory(messenger, name);
@@ -2700,6 +2755,7 @@
     }
 
     /** {@hide} */
+    @UnsupportedAppUsage
     public void unregisterNetworkFactory(Messenger messenger) {
         try {
             mService.unregisterNetworkFactory(messenger);
@@ -2760,10 +2816,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.
@@ -2774,6 +2831,7 @@
             }
             onCapabilitiesChanged(network, networkCapabilities);
             onLinkPropertiesChanged(network, linkProperties);
+            onBlockedStatusChanged(network, blocked);
         }
 
         /**
@@ -2781,7 +2839,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.
          */
@@ -2860,6 +2919,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;
     }
 
@@ -2869,7 +2936,7 @@
      * @hide
      */
     public interface Errors {
-        static int TOO_MANY_REQUESTS = 1;
+        int TOO_MANY_REQUESTS = 1;
     }
 
     /** @hide */
@@ -2906,6 +2973,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) {
@@ -2920,6 +2989,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);
         }
@@ -2966,7 +3036,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: {
@@ -2999,6 +3069,10 @@
                     callback.onNetworkResumed(network);
                     break;
                 }
+                case CALLBACK_BLK_CHANGED: {
+                    boolean blocked = message.arg1 != 0;
+                    callback.onBlockedStatusChanged(network, blocked);
+                }
             }
         }
 
@@ -3024,6 +3098,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;
@@ -3126,7 +3201,7 @@
      * as these {@code NetworkCapabilities} represent states that a particular
      * network may never attain, and whether a network will attain these states
      * is unknown prior to bringing up the network so the framework does not
-     * know how to go about satisfing a request with these capabilities.
+     * know how to go about satisfying a request with these capabilities.
      *
      * <p>This method requires the caller to hold either the
      * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
@@ -3186,7 +3261,7 @@
      * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}, limited
      * by a timeout.
      *
-     * This function behaves identically to the non-timedout version, but if a suitable
+     * This function behaves identically to the version without timeout, but if a suitable
      * network is not found within the given time (in milliseconds) the
      * {@link NetworkCallback#onUnavailable} callback is called. The request can still be
      * released normally by calling {@link #unregisterNetworkCallback(NetworkCallback)} but does
@@ -3267,7 +3342,7 @@
      * as these {@code NetworkCapabilities} represent states that a particular
      * network may never attain, and whether a network will attain these states
      * is unknown prior to bringing up the network so the framework does not
-     * know how to go about satisfing a request with these capabilities.
+     * know how to go about satisfying a request with these capabilities.
      *
      * <p>This method requires the caller to hold either the
      * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
@@ -3283,6 +3358,7 @@
      *         {@link NetworkCapabilities#NET_CAPABILITY_CAPTIVE_PORTAL}.
      */
     public void requestNetwork(NetworkRequest request, PendingIntent operation) {
+        printStackTrace();
         checkPendingIntentNotNull(operation);
         try {
             mService.pendingRequestForNetwork(request.networkCapabilities, operation);
@@ -3306,6 +3382,7 @@
      *                  corresponding NetworkRequest you'd like to remove. Cannot be null.
      */
     public void releaseNetworkRequest(PendingIntent operation) {
+        printStackTrace();
         checkPendingIntentNotNull(operation);
         try {
             mService.releasePendingNetworkRequest(operation);
@@ -3390,6 +3467,7 @@
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
     public void registerNetworkCallback(NetworkRequest request, PendingIntent operation) {
+        printStackTrace();
         checkPendingIntentNotNull(operation);
         try {
             mService.pendingListenForNetwork(request.networkCapabilities, operation);
@@ -3432,9 +3510,9 @@
         // capabilities, this request is guaranteed, at all times, to be
         // satisfied by the same network, if any, that satisfies the default
         // request, i.e., the system default network.
-        NetworkCapabilities nullCapabilities = null;
         CallbackHandler cbHandler = new CallbackHandler(handler);
-        sendRequestForNetwork(nullCapabilities, networkCallback, 0, REQUEST, TYPE_NONE, cbHandler);
+        sendRequestForNetwork(null /* NetworkCapabilities need */, networkCallback, 0,
+                REQUEST, TYPE_NONE, cbHandler);
     }
 
     /**
@@ -3471,6 +3549,7 @@
      * @param networkCallback The {@link NetworkCallback} used when making the request.
      */
     public void unregisterNetworkCallback(NetworkCallback networkCallback) {
+        printStackTrace();
         checkCallbackNotNull(networkCallback);
         final List<NetworkRequest> reqs = new ArrayList<>();
         // Find all requests associated to this callback and stop callback triggers immediately.
@@ -3669,7 +3748,7 @@
      * @return {@code true} on success, {@code false} if the {@link Network} is no longer valid.
      */
     public boolean bindProcessToNetwork(Network network) {
-        // Forcing callers to call thru non-static function ensures ConnectivityManager
+        // Forcing callers to call through non-static function ensures ConnectivityManager
         // instantiated.
         return setProcessDefaultNetwork(network);
     }
@@ -3751,8 +3830,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;
         }
 
@@ -3769,11 +3849,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);
     }
 
@@ -3788,9 +3863,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());
     }
 
     /**
@@ -3880,4 +3956,41 @@
             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, InetSocketAddress local,
+                                     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 ce95b60..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;
@@ -182,4 +183,6 @@
     String getCaptivePortalServerUrl();
 
     byte[] getNetworkWatchlistConfigHash();
+
+    int getConnectionOwnerUid(in ConnectionInfo connectionInfo);
 }
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..1bc0d32 100644
--- a/core/java/android/net/LinkAddress.java
+++ b/core/java/android/net/LinkAddress.java
@@ -25,6 +25,7 @@
 import static android.system.OsConstants.RT_SCOPE_SITE;
 import static android.system.OsConstants.RT_SCOPE_UNIVERSE;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Pair;
@@ -54,11 +55,13 @@
     /**
      * IPv4 or IPv6 address.
      */
+    @UnsupportedAppUsage
     private InetAddress address;
 
     /**
      * Prefix length.
      */
+    @UnsupportedAppUsage
     private int prefixLength;
 
     /**
@@ -112,6 +115,7 @@
      * @return true if the address is IPv6.
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean isIPv6() {
         return address instanceof Inet6Address;
     }
@@ -163,6 +167,7 @@
      * @param prefixLength The prefix length.
      * @hide
      */
+    @UnsupportedAppUsage
     public LinkAddress(InetAddress address, int prefixLength) {
         this(address, prefixLength, 0, 0);
         this.scope = scopeForUnicastAddress(address);
@@ -185,6 +190,7 @@
      * @param string The string to parse.
      * @hide
      */
+    @UnsupportedAppUsage
     public LinkAddress(String address) {
         this(address, 0, 0);
         this.scope = scopeForUnicastAddress(this.address);
@@ -255,6 +261,7 @@
      * otherwise.
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean isSameAddressAs(LinkAddress other) {
         return address.equals(other.address) && prefixLength == other.prefixLength;
     }
@@ -278,6 +285,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 300a78b..1b9a66c 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
@@ -32,6 +33,7 @@
 import java.util.Hashtable;
 import java.util.List;
 import java.util.Objects;
+import java.util.StringJoiner;
 
 /**
  * Describes the properties of a network link.
@@ -47,14 +49,15 @@
  */
 public final class LinkProperties implements Parcelable {
     // The interface described by the network link.
+    @UnsupportedAppUsage
     private String mIfaceName;
-    private ArrayList<LinkAddress> mLinkAddresses = new ArrayList<LinkAddress>();
-    private ArrayList<InetAddress> mDnses = new ArrayList<InetAddress>();
-    private ArrayList<InetAddress> mValidatedPrivateDnses = new ArrayList<InetAddress>();
+    private ArrayList<LinkAddress> mLinkAddresses = new ArrayList<>();
+    private ArrayList<InetAddress> mDnses = new ArrayList<>();
+    private ArrayList<InetAddress> mValidatedPrivateDnses = new ArrayList<>();
     private boolean mUsePrivateDns;
     private String mPrivateDnsServerName;
     private String mDomains;
-    private ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
+    private ArrayList<RouteInfo> mRoutes = new ArrayList<>();
     private ProxyInfo mHttpProxy;
     private int mMtu;
     // in the format "rmem_min,rmem_def,rmem_max,wmem_min,wmem_def,wmem_max"
@@ -66,15 +69,14 @@
 
     // Stores the properties of links that are "stacked" above this link.
     // Indexed by interface name to allow modification and to prevent duplicates being added.
-    private Hashtable<String, LinkProperties> mStackedLinks =
-        new Hashtable<String, LinkProperties>();
+    private Hashtable<String, LinkProperties> mStackedLinks = new Hashtable<>();
 
     /**
      * @hide
      */
     public static class CompareResult<T> {
-        public final List<T> removed = new ArrayList<T>();
-        public final List<T> added = new ArrayList<T>();
+        public final List<T> removed = new ArrayList<>();
+        public final List<T> added = new ArrayList<>();
 
         public CompareResult() {}
 
@@ -93,12 +95,9 @@
 
         @Override
         public String toString() {
-            String retVal = "removed=[";
-            for (T addr : removed) retVal += addr.toString() + ",";
-            retVal += "] added=[";
-            for (T addr : added) retVal += addr.toString() + ",";
-            retVal += "]";
-            return retVal;
+            return "removed=[" + TextUtils.join(",", removed)
+                    + "] added=[" + TextUtils.join(",", added)
+                    + "]";
         }
     }
 
@@ -106,9 +105,13 @@
      * @hide
      */
     public enum ProvisioningChange {
+        @UnsupportedAppUsage
         STILL_NOT_PROVISIONED,
+        @UnsupportedAppUsage
         LOST_PROVISIONING,
+        @UnsupportedAppUsage
         GAINED_PROVISIONING,
+        @UnsupportedAppUsage
         STILL_PROVISIONED,
     }
 
@@ -117,10 +120,11 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public static ProvisioningChange compareProvisioning(
             LinkProperties before, LinkProperties after) {
         if (before.isProvisioned() && after.isProvisioned()) {
-            // On dualstack networks, DHCPv4 renewals can occasionally fail.
+            // On dual-stack networks, DHCPv4 renewals can occasionally fail.
             // When this happens, IPv6-reachable services continue to function
             // normally but IPv4-only services (naturally) fail.
             //
@@ -131,7 +135,7 @@
             //
             // For users, this is confusing and unexpected behaviour, and is
             // not necessarily easy to diagnose.  Therefore, we treat changing
-            // from a dualstack network to an IPv6-only network equivalent to
+            // from a dual-stack network to an IPv6-only network equivalent to
             // a total loss of provisioning.
             //
             // For one such example of this, see b/18867306.
@@ -139,7 +143,7 @@
             // Additionally, losing IPv6 provisioning can result in TCP
             // connections getting stuck until timeouts fire and other
             // baffling failures. Therefore, loss of either IPv4 or IPv6 on a
-            // previously dualstack network is deemed a lost of provisioning.
+            // previously dual-stack network is deemed a lost of provisioning.
             if ((before.isIPv4Provisioned() && !after.isIPv4Provisioned()) ||
                 (before.isIPv6Provisioned() && !after.isIPv6Provisioned())) {
                 return ProvisioningChange.LOST_PROVISIONING;
@@ -157,30 +161,29 @@
     /**
      * @hide
      */
+    @UnsupportedAppUsage
     public LinkProperties() {
     }
 
     /**
      * @hide
      */
+    @UnsupportedAppUsage
     public LinkProperties(LinkProperties source) {
         if (source != null) {
-            mIfaceName = source.getInterfaceName();
-            for (LinkAddress l : source.getLinkAddresses()) mLinkAddresses.add(l);
-            for (InetAddress i : source.getDnsServers()) mDnses.add(i);
-            for (InetAddress i : source.getValidatedPrivateDnsServers()) {
-                mValidatedPrivateDnses.add(i);
-            }
+            mIfaceName = source.mIfaceName;
+            mLinkAddresses.addAll(source.mLinkAddresses);
+            mDnses.addAll(source.mDnses);
+            mValidatedPrivateDnses.addAll(source.mValidatedPrivateDnses);
             mUsePrivateDns = source.mUsePrivateDns;
             mPrivateDnsServerName = source.mPrivateDnsServerName;
-            mDomains = source.getDomains();
-            for (RouteInfo r : source.getRoutes()) mRoutes.add(r);
-            mHttpProxy = (source.getHttpProxy() == null)  ?
-                    null : new ProxyInfo(source.getHttpProxy());
+            mDomains = source.mDomains;
+            mRoutes.addAll(source.mRoutes);
+            mHttpProxy = (source.mHttpProxy == null) ? null : new ProxyInfo(source.mHttpProxy);
             for (LinkProperties l: source.mStackedLinks.values()) {
                 addStackedLink(l);
             }
-            setMtu(source.getMtu());
+            setMtu(source.mMtu);
             mTcpBufferSizes = source.mTcpBufferSizes;
         }
     }
@@ -192,9 +195,10 @@
      * @param iface The name of the network interface used for this link.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setInterfaceName(String iface) {
         mIfaceName = iface;
-        ArrayList<RouteInfo> newRoutes = new ArrayList<RouteInfo>(mRoutes.size());
+        ArrayList<RouteInfo> newRoutes = new ArrayList<>(mRoutes.size());
         for (RouteInfo route : mRoutes) {
             newRoutes.add(routeWithInterface(route));
         }
@@ -213,9 +217,10 @@
     /**
      * @hide
      */
+    @UnsupportedAppUsage
     public List<String> getAllInterfaceNames() {
-        List<String> interfaceNames = new ArrayList<String>(mStackedLinks.size() + 1);
-        if (mIfaceName != null) interfaceNames.add(new String(mIfaceName));
+        List<String> interfaceNames = new ArrayList<>(mStackedLinks.size() + 1);
+        if (mIfaceName != null) interfaceNames.add(mIfaceName);
         for (LinkProperties stacked: mStackedLinks.values()) {
             interfaceNames.addAll(stacked.getAllInterfaceNames());
         }
@@ -229,11 +234,12 @@
      * prefix lengths for each address.  This is a simplified utility alternative to
      * {@link LinkProperties#getLinkAddresses}.
      *
-     * @return An umodifiable {@link List} of {@link InetAddress} for this link.
+     * @return An unmodifiable {@link List} of {@link InetAddress} for this link.
      * @hide
      */
+    @UnsupportedAppUsage
     public List<InetAddress> getAddresses() {
-        List<InetAddress> addresses = new ArrayList<InetAddress>();
+        List<InetAddress> addresses = new ArrayList<>();
         for (LinkAddress linkAddress : mLinkAddresses) {
             addresses.add(linkAddress.getAddress());
         }
@@ -244,8 +250,9 @@
      * 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<InetAddress>();
+        List<InetAddress> addresses = new ArrayList<>();
         for (LinkAddress linkAddress : mLinkAddresses) {
             addresses.add(linkAddress.getAddress());
         }
@@ -271,6 +278,7 @@
      * @return true if {@code address} was added or updated, false otherwise.
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean addLinkAddress(LinkAddress address) {
         if (address == null) {
             return false;
@@ -321,9 +329,9 @@
      * 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<LinkAddress>();
-        addresses.addAll(mLinkAddresses);
+        List<LinkAddress> addresses = new ArrayList<>(mLinkAddresses);
         for (LinkProperties stacked: mStackedLinks.values()) {
             addresses.addAll(stacked.getAllLinkAddresses());
         }
@@ -338,6 +346,7 @@
      *                  object.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setLinkAddresses(Collection<LinkAddress> addresses) {
         mLinkAddresses.clear();
         for (LinkAddress address: addresses) {
@@ -352,6 +361,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);
@@ -367,6 +377,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);
@@ -381,6 +392,7 @@
      * @param dnsServers The {@link Collection} of DNS servers to set in this object.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setDnsServers(Collection<InetAddress> dnsServers) {
         mDnses.clear();
         for (InetAddress dnsServer: dnsServers) {
@@ -391,7 +403,7 @@
     /**
      * Returns all the {@link InetAddress} for DNS servers on this link.
      *
-     * @return An umodifiable {@link List} of {@link InetAddress} for DNS servers on
+     * @return An unmodifiable {@link List} of {@link InetAddress} for DNS servers on
      *         this link.
      */
     public List<InetAddress> getDnsServers() {
@@ -517,6 +529,7 @@
      *                domains to search when resolving host names on this link.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setDomains(String domains) {
         mDomains = domains;
     }
@@ -539,6 +552,7 @@
      * @param mtu The MTU to use for this link.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setMtu(int mtu) {
         mMtu = mtu;
     }
@@ -550,6 +564,7 @@
      * @return The mtu value set for this link.
      * @hide
      */
+    @UnsupportedAppUsage
     public int getMtu() {
         return mMtu;
     }
@@ -562,6 +577,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public void setTcpBufferSizes(String tcpBufferSizes) {
         mTcpBufferSizes = tcpBufferSizes;
     }
@@ -573,6 +589,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public String getTcpBufferSizes() {
         return mTcpBufferSizes;
     }
@@ -596,6 +613,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean addRoute(RouteInfo route) {
         if (route != null) {
             String routeIface = route.getInterface();
@@ -622,6 +640,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean removeRoute(RouteInfo route) {
         return route != null &&
                 Objects.equals(mIfaceName, route.getInterface()) &&
@@ -643,7 +662,7 @@
      * @hide
      */
     public void ensureDirectlyConnectedRoutes() {
-        for (LinkAddress addr: mLinkAddresses) {
+        for (LinkAddress addr : mLinkAddresses) {
             addRoute(new RouteInfo(addr, null, mIfaceName));
         }
     }
@@ -652,9 +671,9 @@
      * 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<>();
-        routes.addAll(mRoutes);
+        List<RouteInfo> routes = new ArrayList<>(mRoutes);
         for (LinkProperties stacked: mStackedLinks.values()) {
             routes.addAll(stacked.getAllRoutes());
         }
@@ -669,6 +688,7 @@
      * @param proxy A {@link ProxyInfo} defining the HTTP Proxy to use on this link.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setHttpProxy(ProxyInfo proxy) {
         mHttpProxy = proxy;
     }
@@ -685,7 +705,7 @@
     /**
      * Adds a stacked link.
      *
-     * If there is already a stacked link with the same interfacename as link,
+     * If there is already a stacked link with the same interface name as link,
      * that link is replaced with link. Otherwise, link is added to the list
      * of stacked links. If link is null, nothing changes.
      *
@@ -693,6 +713,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);
@@ -723,11 +744,12 @@
      * Returns all the links stacked on top of this link.
      * @hide
      */
+    @UnsupportedAppUsage
     public @NonNull List<LinkProperties> getStackedLinks() {
         if (mStackedLinks.isEmpty()) {
-            return Collections.EMPTY_LIST;
+            return Collections.emptyList();
         }
-        List<LinkProperties> stacked = new ArrayList<LinkProperties>();
+        List<LinkProperties> stacked = new ArrayList<>();
         for (LinkProperties link : mStackedLinks.values()) {
             stacked.add(new LinkProperties(link));
         }
@@ -738,6 +760,7 @@
      * Clears this object to its initial state.
      * @hide
      */
+    @UnsupportedAppUsage
     public void clear() {
         mIfaceName = null;
         mLinkAddresses.clear();
@@ -761,57 +784,76 @@
 
     @Override
     public String toString() {
-        String ifaceName = (mIfaceName == null ? "" : "InterfaceName: " + mIfaceName + " ");
+        // Space as a separator, so no need for spaces at start/end of the individual fragments.
+        final StringJoiner resultJoiner = new StringJoiner(" ", "{", "}");
 
-        String linkAddresses = "LinkAddresses: [";
-        for (LinkAddress addr : mLinkAddresses) linkAddresses += addr.toString() + ",";
-        linkAddresses += "] ";
-
-        String dns = "DnsAddresses: [";
-        for (InetAddress addr : mDnses) dns += addr.getHostAddress() + ",";
-        dns += "] ";
-
-        String usePrivateDns = "UsePrivateDns: " + mUsePrivateDns + " ";
-
-        String privateDnsServerName = "";
-        if (privateDnsServerName != null) {
-            privateDnsServerName = "PrivateDnsServerName: " + mPrivateDnsServerName + " ";
+        if (mIfaceName != null) {
+            resultJoiner.add("InterfaceName:");
+            resultJoiner.add(mIfaceName);
         }
 
-        String validatedPrivateDns = "";
+        resultJoiner.add("LinkAddresses: [");
+        if (!mLinkAddresses.isEmpty()) {
+            resultJoiner.add(TextUtils.join(",", mLinkAddresses));
+        }
+        resultJoiner.add("]");
+
+        resultJoiner.add("DnsAddresses: [");
+        if (!mDnses.isEmpty()) {
+            resultJoiner.add(TextUtils.join(",", mDnses));
+        }
+        resultJoiner.add("]");
+
+        if (mUsePrivateDns) {
+            resultJoiner.add("UsePrivateDns: true");
+        }
+
+        if (mPrivateDnsServerName != null) {
+            resultJoiner.add("PrivateDnsServerName:");
+            resultJoiner.add(mPrivateDnsServerName);
+        }
+
         if (!mValidatedPrivateDnses.isEmpty()) {
-            validatedPrivateDns = "ValidatedPrivateDnsAddresses: [";
-            for (InetAddress addr : mValidatedPrivateDnses) {
-                validatedPrivateDns += addr.getHostAddress() + ",";
+            final StringJoiner validatedPrivateDnsesJoiner =
+                    new StringJoiner(",", "ValidatedPrivateDnsAddresses: [", "]");
+            for (final InetAddress addr : mValidatedPrivateDnses) {
+                validatedPrivateDnsesJoiner.add(addr.getHostAddress());
             }
-            validatedPrivateDns += "] ";
+            resultJoiner.add(validatedPrivateDnsesJoiner.toString());
         }
 
-        String domainName = "Domains: " + mDomains;
+        resultJoiner.add("Domains:");
+        resultJoiner.add(mDomains);
 
-        String mtu = " MTU: " + mMtu;
+        resultJoiner.add("MTU:");
+        resultJoiner.add(Integer.toString(mMtu));
 
-        String tcpBuffSizes = "";
         if (mTcpBufferSizes != null) {
-            tcpBuffSizes = " TcpBufferSizes: " + mTcpBufferSizes;
+            resultJoiner.add("TcpBufferSizes:");
+            resultJoiner.add(mTcpBufferSizes);
         }
 
-        String routes = " Routes: [";
-        for (RouteInfo route : mRoutes) routes += route.toString() + ",";
-        routes += "] ";
-        String proxy = (mHttpProxy == null ? "" : " HttpProxy: " + mHttpProxy.toString() + " ");
+        resultJoiner.add("Routes: [");
+        if (!mRoutes.isEmpty()) {
+            resultJoiner.add(TextUtils.join(",", mRoutes));
+        }
+        resultJoiner.add("]");
 
-        String stacked = "";
-        if (mStackedLinks.values().size() > 0) {
-            stacked += " Stacked: [";
-            for (LinkProperties link: mStackedLinks.values()) {
-                stacked += " [" + link.toString() + " ],";
+        if (mHttpProxy != null) {
+            resultJoiner.add("HttpProxy:");
+            resultJoiner.add(mHttpProxy.toString());
+        }
+
+        final Collection<LinkProperties> stackedLinksValues = mStackedLinks.values();
+        if (!stackedLinksValues.isEmpty()) {
+            final StringJoiner stackedLinksJoiner = new StringJoiner(",", "Stacked: [", "]");
+            for (final LinkProperties lp : stackedLinksValues) {
+                stackedLinksJoiner.add("[ " + lp + " ]");
             }
-            stacked += "] ";
+            resultJoiner.add(stackedLinksJoiner.toString());
         }
-        return "{" + ifaceName + linkAddresses + routes + dns + usePrivateDns
-            + privateDnsServerName + domainName + mtu + tcpBuffSizes + proxy
-            + stacked + "}";
+
+        return resultJoiner.toString();
     }
 
     /**
@@ -820,6 +862,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) {
@@ -847,6 +890,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()) {
@@ -862,6 +906,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()) {
@@ -877,6 +922,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()) {
@@ -892,6 +938,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) {
@@ -907,6 +954,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) {
@@ -936,6 +984,7 @@
      * @return {@code true} if the link is provisioned, {@code false} otherwise.
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean isIPv6Provisioned() {
         return (hasGlobalIPv6Address() &&
                 hasIPv6DefaultRoute() &&
@@ -949,6 +998,7 @@
      * @return {@code true} if the link is provisioned, {@code false} otherwise.
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean isProvisioned() {
         return (isIPv4Provisioned() || isIPv6Provisioned());
     }
@@ -960,6 +1010,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.
@@ -997,6 +1048,7 @@
      * @return {@code true} if both are identical, {@code false} otherwise.
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean isIdenticalInterfaceName(LinkProperties target) {
         return TextUtils.equals(getInterfaceName(), target.getInterfaceName());
     }
@@ -1008,6 +1060,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();
@@ -1022,13 +1075,14 @@
      * @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();
         if (mDomains == null) {
             if (targetDomains != null) return false;
         } else {
-            if (mDomains.equals(targetDomains) == false) return false;
+            if (!mDomains.equals(targetDomains)) return false;
         }
         return (mDnses.size() == targetDnses.size()) ?
                 mDnses.containsAll(targetDnses) : false;
@@ -1069,6 +1123,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()) ?
@@ -1082,6 +1137,7 @@
      * @return {@code true} if both are identical, {@code false} otherwise.
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean isIdenticalHttpProxy(LinkProperties target) {
         return getHttpProxy() == null ? target.getHttpProxy() == null :
                 getHttpProxy().equals(target.getHttpProxy());
@@ -1094,6 +1150,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;
@@ -1130,7 +1187,6 @@
         return Objects.equals(mTcpBufferSizes, target.mTcpBufferSizes);
     }
 
-    @Override
     /**
      * Compares this {@code LinkProperties} instance against the target
      * LinkProperties in {@code obj}. Two LinkPropertieses are equal if
@@ -1145,13 +1201,14 @@
      * @param obj the object to be tested for equality.
      * @return {@code true} if both objects are equal, {@code false} otherwise.
      */
+    @Override
     public boolean equals(Object obj) {
         if (this == obj) return true;
 
         if (!(obj instanceof LinkProperties)) return false;
 
         LinkProperties target = (LinkProperties) obj;
-        /**
+        /*
          * This method does not check that stacked interfaces are equal, because
          * stacked interfaces are not so much a property of the link as a
          * description of connections between links.
@@ -1258,12 +1315,13 @@
     }
 
 
-    @Override
     /**
-     * generate hashcode based on significant fields
+     * Generate hashcode based on significant fields
+     *
      * Equal objects must produce the same hash code, while unequal objects
      * may have the same hash codes.
      */
+    @Override
     public int hashCode() {
         return ((null == mIfaceName) ? 0 : mIfaceName.hashCode()
                 + mLinkAddresses.size() * 31
@@ -1313,7 +1371,7 @@
         } else {
             dest.writeByte((byte)0);
         }
-        ArrayList<LinkProperties> stackedLinks = new ArrayList(mStackedLinks.values());
+        ArrayList<LinkProperties> stackedLinks = new ArrayList<>(mStackedLinks.values());
         dest.writeList(stackedLinks);
     }
 
@@ -1331,7 +1389,7 @@
                 }
                 int addressCount = in.readInt();
                 for (int i = 0; i < addressCount; i++) {
-                    netProp.addLinkAddress((LinkAddress) in.readParcelable(null));
+                    netProp.addLinkAddress(in.readParcelable(null));
                 }
                 addressCount = in.readInt();
                 for (int i = 0; i < addressCount; i++) {
@@ -1353,10 +1411,10 @@
                 netProp.setTcpBufferSizes(in.readString());
                 addressCount = in.readInt();
                 for (int i = 0; i < addressCount; i++) {
-                    netProp.addRoute((RouteInfo) in.readParcelable(null));
+                    netProp.addRoute(in.readParcelable(null));
                 }
                 if (in.readByte() == 1) {
-                    netProp.setHttpProxy((ProxyInfo) in.readParcelable(null));
+                    netProp.setHttpProxy(in.readParcelable(null));
                 }
                 ArrayList<LinkProperties> stackedLinks = new ArrayList<LinkProperties>();
                 in.readList(stackedLinks, LinkProperties.class.getClassLoader());
@@ -1377,10 +1435,9 @@
      */
     public static boolean isValidMtu(int mtu, boolean ipv6) {
         if (ipv6) {
-            if (mtu >= MIN_MTU_V6 && mtu <= MAX_MTU) return true;
+            return mtu >= MIN_MTU_V6 && mtu <= MAX_MTU;
         } else {
-            if (mtu >= MIN_MTU && mtu <= MAX_MTU) return true;
+            return mtu >= MIN_MTU && mtu <= MAX_MTU;
         }
-        return false;
     }
 }
diff --git a/core/java/android/net/MacAddress.java b/core/java/android/net/MacAddress.java
index 74d6470..98f3567 100644
--- a/core/java/android/net/MacAddress.java
+++ b/core/java/android/net/MacAddress.java
@@ -18,6 +18,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -50,6 +51,7 @@
      * The MacAddress zero MAC address.
      * @hide
      */
+    @UnsupportedAppUsage
     public static final MacAddress ALL_ZEROS_ADDRESS = new MacAddress(0);
 
     /** @hide */
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
index d75d439..bf2344d 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -16,6 +16,7 @@
 
 package android.net;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.system.ErrnoException;
@@ -59,6 +60,7 @@
     /**
      * @hide
      */
+    @UnsupportedAppUsage
     public final int netId;
 
     // Objects used to perform per-network operations such as getSocketFactory
@@ -98,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);
     }
 
     /**
@@ -130,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.
@@ -141,14 +151,14 @@
     }
 
     /**
-     * Specify whether or not Private DNS should be bypassed when attempting
-     * to use {@link getAllByName()}/{@link getByName()} methods on the given
+     * 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);
     }
 
     /**
@@ -169,13 +179,6 @@
      * A {@code SocketFactory} that produces {@code Socket}'s bound to this network.
      */
     private class NetworkBoundSocketFactory extends SocketFactory {
-        private final int mNetId;
-
-        public NetworkBoundSocketFactory(int netId) {
-            super();
-            mNetId = netId;
-        }
-
         private Socket connectToHost(String host, int port, SocketAddress localAddress)
                 throws IOException {
             // Lookup addresses only on this Network.
@@ -201,7 +204,8 @@
         }
 
         @Override
-        public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
+        public Socket createSocket(String host, int port, InetAddress localHost, int localPort)
+                throws IOException {
             return connectToHost(host, port, new InetSocketAddress(localHost, localPort));
         }
 
@@ -265,7 +269,7 @@
         if (mNetworkBoundSocketFactory == null) {
             synchronized (mLock) {
                 if (mNetworkBoundSocketFactory == null) {
-                    mNetworkBoundSocketFactory = new NetworkBoundSocketFactory(netId);
+                    mNetworkBoundSocketFactory = new NetworkBoundSocketFactory();
                 }
             }
         }
@@ -475,7 +479,7 @@
 
     @Override
     public boolean equals(Object obj) {
-        if (obj instanceof Network == false) return false;
+        if (!(obj instanceof Network)) return false;
         Network other = (Network)obj;
         return this.netId == other.netId;
     }
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index 52a2354..114b423 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -16,6 +16,7 @@
 
 package android.net;
 
+import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.net.ConnectivityManager.PacketKeepalive;
 import android.os.Bundle;
@@ -351,6 +352,7 @@
     /**
      * Called by the bearer code when it has new NetworkInfo data.
      */
+    @UnsupportedAppUsage
     public void sendNetworkInfo(NetworkInfo networkInfo) {
         queueOrSendMessage(EVENT_NETWORK_INFO_CHANGED, new NetworkInfo(networkInfo));
     }
@@ -372,7 +374,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 +389,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 e3a1107..0bdfca7 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -19,7 +19,9 @@
 import android.annotation.IntDef;
 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;
@@ -56,6 +58,7 @@
     /**
      * @hide
      */
+    @UnsupportedAppUsage
     public NetworkCapabilities() {
         clearAll();
         mNetworkCapabilities = DEFAULT_CAPABILITIES;
@@ -103,6 +106,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;
 
     /**
@@ -371,6 +375,7 @@
      * @return This NetworkCapabilities instance, to facilitate chaining.
      * @hide
      */
+    @UnsupportedAppUsage
     public NetworkCapabilities addCapability(@NetCapability int capability) {
         checkValidCapability(capability);
         mNetworkCapabilities |= 1 << capability;
@@ -400,13 +405,14 @@
     /**
      * Removes (if found) the given capability from this {@code NetworkCapability} instance.
      * <p>
-     * Note that this method removes capabilities that was added via {@link #addCapability(int)},
+     * Note that this method removes capabilities that were added via {@link #addCapability(int)},
      * {@link #addUnwantedCapability(int)} or {@link #setCapabilities(int[], int[])} .
      *
      * @param capability the capability to be removed.
      * @return This NetworkCapabilities instance, to facilitate chaining.
      * @hide
      */
+    @UnsupportedAppUsage
     public NetworkCapabilities removeCapability(@NetCapability int capability) {
         checkValidCapability(capability);
         final long mask = ~(1 << capability);
@@ -490,6 +496,7 @@
                 && ((mUnwantedNetworkCapabilities & (1 << capability)) != 0);
     }
 
+    /** Note this method may result in having the same capability in wanted and unwanted lists. */
     private void combineNetCapabilities(NetworkCapabilities nc) {
         this.mNetworkCapabilities |= nc.mNetworkCapabilities;
         this.mUnwantedNetworkCapabilities |= nc.mUnwantedNetworkCapabilities;
@@ -658,6 +665,7 @@
      * @return This NetworkCapabilities instance, to facilitate chaining.
      * @hide
      */
+    @UnsupportedAppUsage
     public NetworkCapabilities addTransportType(@Transport int transportType) {
         checkValidTransportType(transportType);
         mTransportTypes |= 1 << transportType;
@@ -897,6 +905,7 @@
      *         specifier. See {@link #setNetworkSpecifier}.
      * @hide
      */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public NetworkSpecifier getNetworkSpecifier() {
         return mNetworkSpecifier;
     }
@@ -929,6 +938,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;
 
     /**
@@ -944,6 +954,7 @@
      * @param signalStrength the bearer-specific signal strength.
      * @hide
      */
+    @UnsupportedAppUsage
     public NetworkCapabilities setSignalStrength(int signalStrength) {
         mSignalStrength = signalStrength;
         return this;
@@ -954,6 +965,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean hasSignalStrength() {
         return mSignalStrength > SIGNAL_STRENGTH_UNSPECIFIED;
     }
@@ -964,6 +976,7 @@
      * @return The bearer-specific signal strength.
      * @hide
      */
+    @UnsupportedAppUsage
     public int getSignalStrength() {
         return mSignalStrength;
     }
@@ -1213,7 +1226,11 @@
     }
 
     /**
-     * Combine a set of Capabilities to this one.  Useful for coming up with the complete set
+     * Combine a set of Capabilities to this one.  Useful for coming up with the complete set.
+     * <p>
+     * Note that this method may break an invariant of having a particular capability in either
+     * wanted or unwanted lists but never in both.  Requests that have the same capability in
+     * both lists will never be satisfied.
      * @hide
      */
     public void combineCapabilities(NetworkCapabilities nc) {
@@ -1539,6 +1556,7 @@
     /**
      * @hide
      */
+    @UnsupportedAppUsage
     public static String transportNamesOf(@Transport int[] types) {
         StringJoiner joiner = new StringJoiner("|");
         if (types != null) {
@@ -1572,4 +1590,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..1a1d2d3 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;
 
@@ -129,6 +130,7 @@
     /**
      * @hide
      */
+    @UnsupportedAppUsage
     public NetworkInfo(int type, int subtype, String typeName, String subtypeName) {
         if (!ConnectivityManager.isNetworkTypeValid(type)
                 && type != ConnectivityManager.TYPE_NONE) {
@@ -143,6 +145,7 @@
     }
 
     /** {@hide} */
+    @UnsupportedAppUsage
     public NetworkInfo(NetworkInfo source) {
         if (source != null) {
             synchronized (source) {
@@ -199,7 +202,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 +214,7 @@
     /**
      * @hide
      */
+    @UnsupportedAppUsage
     public void setSubtype(int subtype, String subtypeName) {
         synchronized (this) {
             mSubtype = subtype;
@@ -239,7 +245,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 +282,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 +333,7 @@
      * @hide
      */
     @Deprecated
+    @UnsupportedAppUsage
     public void setIsAvailable(boolean isAvailable) {
         synchronized (this) {
             mIsAvailable = isAvailable;
@@ -347,6 +364,7 @@
      * @hide
      */
     @Deprecated
+    @UnsupportedAppUsage
     public void setFailover(boolean isFailover) {
         synchronized (this) {
             mIsFailover = isFailover;
@@ -377,6 +395,7 @@
      */
     @VisibleForTesting
     @Deprecated
+    @UnsupportedAppUsage
     public void setRoaming(boolean isRoaming) {
         synchronized (this) {
             mIsRoaming = isRoaming;
@@ -404,7 +423,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 +449,7 @@
      * @hide
      */
     @Deprecated
+    @UnsupportedAppUsage
     public void setDetailedState(DetailedState detailedState, String reason, String extraInfo) {
         synchronized (this) {
             this.mDetailedState = detailedState;
diff --git a/core/java/android/net/NetworkMisc.java b/core/java/android/net/NetworkMisc.java
index a2da6ea..c0487b5 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 16c2342..04b6b44 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -17,6 +17,7 @@
 package android.net;
 
 import android.annotation.NonNull;
+import android.annotation.UnsupportedAppUsage;
 import android.net.NetworkCapabilities.NetCapability;
 import android.net.NetworkCapabilities.Transport;
 import android.os.Parcel;
@@ -38,6 +39,7 @@
      * The {@link NetworkCapabilities} that define this request.
      * @hide
      */
+    @UnsupportedAppUsage
     public final @NonNull NetworkCapabilities networkCapabilities;
 
     /**
@@ -46,6 +48,7 @@
      * the request.
      * @hide
      */
+    @UnsupportedAppUsage
     public final int requestId;
 
     /**
@@ -53,6 +56,7 @@
      * Causes CONNECTIVITY_ACTION broadcasts to be sent.
      * @hide
      */
+    @UnsupportedAppUsage
     public final int legacyType;
 
     /**
@@ -241,6 +245,7 @@
          * @return The builder to facilitate chaining.
          * @hide
          */
+        @UnsupportedAppUsage
         public Builder clearCapabilities() {
             mNetworkCapabilities.clearAll();
             return this;
@@ -339,6 +344,7 @@
          * @param signalStrength the bearer-specific signal strength.
          * @hide
          */
+        @UnsupportedAppUsage
         public Builder setSignalStrength(int signalStrength) {
             mNetworkCapabilities.setSignalStrength(signalStrength);
             return this;
diff --git a/core/java/android/net/NetworkState.java b/core/java/android/net/NetworkState.java
index 321f971..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;
@@ -33,6 +35,7 @@
     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;
@@ -58,6 +61,7 @@
         }
     }
 
+    @UnsupportedAppUsage
     public NetworkState(Parcel in) {
         networkInfo = in.readParcelable(null);
         linkProperties = in.readParcelable(null);
@@ -82,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..34e9476 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -16,11 +16,13 @@
 
 package android.net;
 
+import android.annotation.UnsupportedAppUsage;
 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 +45,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 +53,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 +64,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 +113,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 +132,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 +274,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));
@@ -204,6 +295,7 @@
      * @return the InetAddress
      * @hide
      */
+    @UnsupportedAppUsage
     public static InetAddress numericToInetAddress(String addrString)
             throws IllegalArgumentException {
         return InetAddress.parseNumericAddress(addrString);
@@ -278,6 +370,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 +409,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 +483,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..3e73d3d 100644
--- a/core/java/android/net/RouteInfo.java
+++ b/core/java/android/net/RouteInfo.java
@@ -16,6 +16,7 @@
 
 package android.net;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -55,6 +56,7 @@
     /**
      * The gateway address for this route.
      */
+    @UnsupportedAppUsage
     private final InetAddress mGateway;
 
     /**
@@ -79,6 +81,7 @@
 
     // Derived data members.
     // TODO: remove these.
+    @UnsupportedAppUsage
     private final boolean mIsHost;
     private final boolean mHasGateway;
 
@@ -160,6 +163,7 @@
     /**
      *  @hide
      */
+    @UnsupportedAppUsage
     public RouteInfo(IpPrefix destination, InetAddress gateway, String iface) {
         this(destination, gateway, iface, RTN_UNICAST);
     }
@@ -167,6 +171,7 @@
     /**
      * @hide
      */
+    @UnsupportedAppUsage
     public RouteInfo(LinkAddress destination, InetAddress gateway, String iface) {
         this(destination == null ? null :
                 new IpPrefix(destination.getAddress(), destination.getPrefixLength()),
@@ -197,6 +202,7 @@
      *
      * TODO: Remove this.
      */
+    @UnsupportedAppUsage
     public RouteInfo(LinkAddress destination, InetAddress gateway) {
         this(destination, gateway, null);
     }
@@ -208,6 +214,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public RouteInfo(InetAddress gateway) {
         this((IpPrefix) null, gateway, null);
     }
@@ -258,6 +265,7 @@
         }
     }
 
+    @UnsupportedAppUsage
     private boolean isHost() {
         return (mDestination.getAddress() instanceof Inet4Address &&
                 mDestination.getPrefixLength() == 32) ||
@@ -355,6 +363,7 @@
      * @return {@code true} if a gateway is specified
      * @hide
      */
+    @UnsupportedAppUsage
     public boolean hasGateway() {
         return mHasGateway;
     }
@@ -379,6 +388,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/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 6ef1c14..74c8023 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;
@@ -75,7 +82,6 @@
 import android.net.NetworkState;
 import android.net.NetworkUtils;
 import android.net.NetworkWatchlistManager;
-import android.net.Proxy;
 import android.net.ProxyInfo;
 import android.net.RouteInfo;
 import android.net.UidRange;
@@ -83,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;
@@ -128,7 +136,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.net.LegacyVpnInfo;
-import com.android.internal.net.NetworkStatsFactory;
 import com.android.internal.net.VpnConfig;
 import com.android.internal.net.VpnInfo;
 import com.android.internal.net.VpnProfile;
@@ -154,8 +161,8 @@
 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;
 import com.android.server.connectivity.Vpn;
 import com.android.server.connectivity.tethering.TetheringDependencies;
@@ -183,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,14 +208,16 @@
         implements PendingIntent.OnFinished {
     private static final String TAG = ConnectivityService.class.getSimpleName();
 
-    public static final String DIAG_ARG = "--diag";
+    private static final String DIAG_ARG = "--diag";
     public static final String SHORT_ARG = "--short";
-    public static final String TETHERING_ARG = "tethering";
+    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_RULES = false;
     private static final boolean LOGD_BLOCKED_NETWORKINFO = true;
 
     // TODO: create better separation between radio types and network types
@@ -241,7 +252,7 @@
 
     @VisibleForTesting
     @GuardedBy("mVpns")
-    protected final SparseArray<Vpn> mVpns = new SparseArray<Vpn>();
+    protected final SparseArray<Vpn> mVpns = new SparseArray<>();
 
     // TODO: investigate if mLockdownEnabled can be removed and replaced everywhere by
     // a direct call to LockdownVpnTracker.isEnabled().
@@ -250,25 +261,26 @@
     @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;
-    private int mNetworkPreference;
     // 0 is full bad, 100 is full good
     private int mDefaultInetConditionPublished = 0;
 
-    private boolean mTestMode;
-    private static ConnectivityService sServiceInstance;
-
-    private INetworkManagementService mNetd;
+    private INetworkManagementService mNMS;
+    private INetd mNetd;
     private INetworkStatsService mStatsService;
     private INetworkPolicyManager mPolicyManager;
     private NetworkPolicyManagerInternal mPolicyManagerInternal;
-    private IIpConnectivityMetrics mIpConnectivityMetrics;
 
     private String mCurrentTcpBufferSizes;
 
-    private static final int ENABLED  = 1;
-    private static final int DISABLED = 0;
-
     private static final SparseArray<String> sMagicDecoderRing = MessageUtils.findMessageNames(
             new Class[] { AsyncChannel.class, ConnectivityService.class, NetworkAgent.class,
                     NetworkAgentInfo.class });
@@ -281,7 +293,7 @@
         // Don't reap networks.  This should be passed when some networks have not yet been
         // rematched against all NetworkRequests.
         DONT_REAP
-    };
+    }
 
     private enum UnneededFor {
         LINGER,    // Determine whether this network is unneeded and should be lingered.
@@ -289,11 +301,6 @@
     }
 
     /**
-     * used internally to change our mobile data enabled flag
-     */
-    private static final int EVENT_CHANGE_MOBILE_DATA_ENABLED = 2;
-
-    /**
      * used internally to clear a wakelock when transitioning
      * from one net to another.  Clear happens when we get a new
      * network - EVENT_EXPIRE_NET_TRANSITION_WAKELOCK happens
@@ -398,9 +405,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
@@ -424,6 +431,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));
     }
@@ -444,29 +461,21 @@
     private int mNetTransitionWakeLockTimeout;
     private final PowerManager.WakeLock mPendingIntentWakeLock;
 
-    // track the current default http proxy - tell the world if we get a new one (real change)
-    private volatile ProxyInfo mDefaultProxy = null;
-    private Object mProxyLock = new Object();
-    private boolean mDefaultProxyDisabled = false;
-
-    // track the global proxy.
-    private ProxyInfo mGlobalProxy = null;
-
-    private PacManager mPacManager = null;
+    // A helper object to track the current default HTTP proxy. ConnectivityService needs to tell
+    // the world when it changes.
+    private final ProxyTracker mProxyTracker;
 
     final private SettingsObserver mSettingsObserver;
 
     private UserManager mUserManager;
 
-    NetworkConfig[] mNetConfigs;
-    int mNetworksDefined;
+    private NetworkConfig[] mNetConfigs;
+    private int mNetworksDefined;
 
     // the set of network types that can only be enabled by system/sig apps
-    List mProtectedNetworks;
+    private List mProtectedNetworks;
 
-    private DataConnectionStats mDataConnectionStats;
-
-    TelephonyManager mTelephonyManager;
+    private TelephonyManager mTelephonyManager;
 
     private KeepaliveTracker mKeepaliveTracker;
     private NetworkNotificationManager mNotifier;
@@ -509,8 +518,7 @@
             mLog = log;
         }
     }
-    private final ArrayDeque<ValidationLog> mValidationLogs =
-            new ArrayDeque<ValidationLog>(MAX_VALIDATION_LOGS);
+    private final ArrayDeque<ValidationLog> mValidationLogs = new ArrayDeque<>(MAX_VALIDATION_LOGS);
 
     private void addValidationLogs(ReadOnlyLocalLog log, Network network, String name) {
         synchronized (mValidationLogs) {
@@ -523,6 +531,9 @@
 
     private final IpConnectivityLog mMetricsLog;
 
+    @GuardedBy("mBandwidthRequests")
+    private final SparseArray<Integer> mBandwidthRequests = new SparseArray(10);
+
     @VisibleForTesting
     final MultinetworkPolicyTracker mMultinetworkPolicyTracker;
 
@@ -583,7 +594,7 @@
                 throw new IllegalStateException(
                         "legacy list for type " + type + "already initialized");
             }
-            mTypeLists[type] = new ArrayList<NetworkAgentInfo>();
+            mTypeLists[type] = new ArrayList<>();
         }
 
         public boolean isTypeSupported(int type) {
@@ -686,7 +697,7 @@
         }
 
         private String naiToString(NetworkAgentInfo nai) {
-            String name = (nai != null) ? nai.name() : "null";
+            String name = nai.name();
             String state = (nai.networkInfo != null) ?
                     nai.networkInfo.getState() + "/" + nai.networkInfo.getDetailedState() :
                     "???/???";
@@ -762,6 +773,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());
@@ -773,16 +790,21 @@
         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(
                 LocalServices.getService(NetworkPolicyManagerInternal.class),
                 "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) {
@@ -860,12 +882,9 @@
             }
         }
 
-        mTestMode = mSystemProperties.get("cm.test.mode").equals("true")
-                && mSystemProperties.get("ro.build.type").equals("eng");
-
         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();
@@ -880,8 +899,8 @@
                 new IntentFilter(Intent.ACTION_USER_PRESENT), null, null);
 
         try {
-            mNetd.registerObserver(mTethering);
-            mNetd.registerObserver(mDataActivityObserver);
+            mNMS.registerObserver(mTethering);
+            mNMS.registerObserver(mDataActivityObserver);
         } catch (RemoteException e) {
             loge("Error registering observer :" + e);
         }
@@ -889,10 +908,8 @@
         mSettingsObserver = new SettingsObserver(mContext, mHandler);
         registerSettingsCallbacks();
 
-        mDataConnectionStats = new DataConnectionStats(mContext);
-        mDataConnectionStats.startMonitoring();
-
-        mPacManager = new PacManager(mContext, mHandler, EVENT_PROXY_HAS_CHANGED);
+        final DataConnectionStats dataConnectionStats = new DataConnectionStats(mContext);
+        dataConnectionStats.startMonitoring();
 
         mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
 
@@ -914,11 +931,12 @@
 
         mMultipathPolicyTracker = new MultipathPolicyTracker(mContext, mHandler);
 
-        mDnsManager = new DnsManager(mContext, mNetd, mSystemProperties);
+        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
@@ -930,7 +948,7 @@
                 return mDefaultRequest;
             }
         };
-        return new Tethering(mContext, mNetd, mStatsService, mPolicyManager,
+        return new Tethering(mContext, mNMS, mStatsService, mPolicyManager,
                 IoThread.get().getLooper(), new MockableSystemProperties(),
                 deps);
     }
@@ -962,8 +980,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.
@@ -972,22 +990,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(
@@ -997,7 +1023,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() {
@@ -1026,7 +1057,7 @@
         throw new IllegalStateException("No free netIds");
     }
 
-    private NetworkState getFilteredNetworkState(int networkType, int uid, boolean ignoreBlocked) {
+    private NetworkState getFilteredNetworkState(int networkType, int uid) {
         if (mLegacyTypeTracker.isTypeSupported(networkType)) {
             final NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType);
             final NetworkState state;
@@ -1044,14 +1075,15 @@
                 state = new NetworkState(info, new LinkProperties(), capabilities,
                         null, null, null);
             }
-            filterNetworkStateForUid(state, uid, ignoreBlocked);
+            filterNetworkStateForUid(state, uid, false);
             return state;
         } else {
             return NetworkState.EMPTY;
         }
     }
 
-    private NetworkAgentInfo getNetworkAgentInfoForNetwork(Network network) {
+    @VisibleForTesting
+    protected NetworkAgentInfo getNetworkAgentInfoForNetwork(Network network) {
         if (network == null) {
             return null;
         }
@@ -1110,14 +1142,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;
             }
         }
@@ -1144,6 +1171,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
@@ -1249,7 +1287,7 @@
                 return state.networkInfo;
             }
         }
-        final NetworkState state = getFilteredNetworkState(networkType, uid, false);
+        final NetworkState state = getFilteredNetworkState(networkType, uid);
         return state.networkInfo;
     }
 
@@ -1284,7 +1322,7 @@
     public Network getNetworkForType(int networkType) {
         enforceAccessPermission();
         final int uid = Binder.getCallingUid();
-        NetworkState state = getFilteredNetworkState(networkType, uid, false);
+        NetworkState state = getFilteredNetworkState(networkType, uid);
         if (!isNetworkWithLinkPropertiesBlocked(state.linkProperties, uid, false)) {
             return state.network;
         }
@@ -1321,7 +1359,7 @@
         // default.
         enforceAccessPermission();
 
-        HashMap<Network, NetworkCapabilities> result = new HashMap<Network, NetworkCapabilities>();
+        HashMap<Network, NetworkCapabilities> result = new HashMap<>();
 
         NetworkAgentInfo nai = getDefaultNetwork();
         NetworkCapabilities nc = getNetworkCapabilitiesInternal(nai);
@@ -1493,6 +1531,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
@@ -1503,6 +1555,9 @@
      */
     @Override
     public boolean requestRouteToHostAddress(int networkType, byte[] hostAddress) {
+        if (disallowedBecauseSystemCaller()) {
+            return false;
+        }
         enforceChangePermission();
         if (mProtectedNetworks.contains(networkType)) {
             enforceConnectivityInternalPermission();
@@ -1580,7 +1635,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);
@@ -1608,15 +1663,16 @@
 
     @VisibleForTesting
     protected void registerNetdEventCallback() {
-        mIpConnectivityMetrics =
-                (IIpConnectivityMetrics) IIpConnectivityMetrics.Stub.asInterface(
-                ServiceManager.getService(IpConnectivityLog.SERVICE_NAME));
-        if (mIpConnectivityMetrics == null) {
+        final IIpConnectivityMetrics ipConnectivityMetrics =
+                IIpConnectivityMetrics.Stub.asInterface(
+                        ServiceManager.getService(IpConnectivityLog.SERVICE_NAME));
+        if (ipConnectivityMetrics == null) {
             Slog.wtf(TAG, "Missing IIpConnectivityMetrics");
+            return;
         }
 
         try {
-            mIpConnectivityMetrics.addNetdEventCallback(
+            ipConnectivityMetrics.addNetdEventCallback(
                     INetdEventCallback.CALLBACK_CALLER_CONNECTIVITY_SERVICE,
                     mNetdEventCallback);
         } catch (Exception e) {
@@ -1627,10 +1683,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");
@@ -1639,6 +1702,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.
@@ -1655,6 +1762,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,
@@ -1699,6 +1816,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(
@@ -1769,7 +1898,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);
@@ -1794,7 +1924,7 @@
                 try {
                     bs.noteConnectivityChanged(intent.getIntExtra(
                             ConnectivityManager.EXTRA_NETWORK_TYPE, ConnectivityManager.TYPE_NONE),
-                            ni != null ? ni.getState().toString() : "?");
+                            ni.getState().toString());
                 } catch (RemoteException e) {
                 }
                 intent.addFlags(Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
@@ -1808,7 +1938,7 @@
     }
 
     void systemReady() {
-        loadGlobalProxy();
+        mProxyTracker.loadGlobalProxy();
         registerNetdEventCallback();
 
         synchronized (this) {
@@ -1818,15 +1948,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));
 
@@ -1864,7 +1992,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);
@@ -1882,8 +2010,8 @@
         if (iface != null && (caps.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) ||
                               caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))) {
             try {
-                // the call fails silently if no idletimer setup for this interface
-                mNetd.removeIdleTimer(iface);
+                // the call fails silently if no idle timer setup for this interface
+                mNMS.removeIdleTimer(iface);
             } catch (Exception e) {
                 loge("Exception in removeDataActivityTracking " + e);
             }
@@ -1891,7 +2019,19 @@
     }
 
     /**
-     * Reads the network specific MTU size from reources.
+     * 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.
      */
     private void updateMtu(LinkProperties newLp, LinkProperties oldLp) {
@@ -1917,8 +2057,8 @@
         }
 
         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);
         }
@@ -1953,7 +2093,7 @@
         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]);
@@ -2000,7 +2140,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,
@@ -2031,6 +2171,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:");
@@ -2051,37 +2197,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();
@@ -2132,10 +2279,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);
@@ -2204,8 +2412,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: {
@@ -2213,7 +2420,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: {
@@ -2527,7 +2734,7 @@
         }
         nai.clearLingerState();
         if (nai.isSatisfyingRequest(mDefaultRequest.requestId)) {
-            removeDataActivityTracking(nai);
+            updateDataActivityTracking(null /* newNetwork */, nai);
             notifyLockdownVpn(nai);
             ensureNetworkTransitionWakelock(nai.name());
         }
@@ -2547,7 +2754,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);
             }
@@ -2725,7 +2932,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.");
                 }
@@ -2745,20 +2952,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
@@ -2848,7 +3041,7 @@
         if (!accept) {
             // Tell the NetworkAgent to not automatically reconnect to the network.
             nai.asyncChannel.sendMessage(NetworkAgent.CMD_PREVENT_AUTOMATIC_RECONNECT);
-            // Teardown the nework.
+            // Teardown the network.
             teardownUnneededNetwork(nai);
         }
 
@@ -2927,7 +3120,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());
             }
@@ -2962,7 +3155,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
@@ -3017,7 +3210,7 @@
                     break;
                 }
                 case EVENT_APPLY_GLOBAL_HTTP_PROXY: {
-                    handleDeprecatedGlobalHttpProxy();
+                    mProxyTracker.loadDeprecatedGlobalHttpProxy();
                     break;
                 }
                 case EVENT_PROXY_HAS_CHANGED: {
@@ -3072,8 +3265,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.
@@ -3107,6 +3300,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;
             }
         }
     }
@@ -3116,8 +3315,7 @@
     public int tether(String iface, String callerPkg) {
         ConnectivityManager.enforceTetherChangePermission(mContext, callerPkg);
         if (isTetheringSupported()) {
-            final int status = mTethering.tether(iface);
-            return status;
+            return mTethering.tether(iface);
         } else {
             return ConnectivityManager.TETHER_ERROR_UNSUPPORTED;
         }
@@ -3129,8 +3327,7 @@
         ConnectivityManager.enforceTetherChangePermission(mContext, callerPkg);
 
         if (isTetheringSupported()) {
-            final int status = mTethering.untether(iface);
-            return status;
+            return mTethering.untether(iface);
         } else {
             return ConnectivityManager.TETHER_ERROR_UNSUPPORTED;
         }
@@ -3362,22 +3559,10 @@
         nai.networkMonitor.forceReevaluation(uid);
     }
 
-    private ProxyInfo getDefaultProxy() {
-        // this information is already available as a world read/writable jvm property
-        // so this API change wouldn't have a benifit.  It also breaks the passing
-        // of proxy info to all the JVMs.
-        // enforceAccessPermission();
-        synchronized (mProxyLock) {
-            ProxyInfo ret = mGlobalProxy;
-            if ((ret == null) && !mDefaultProxyDisabled) ret = mDefaultProxy;
-            return ret;
-        }
-    }
-
     @Override
     public ProxyInfo getProxyForNetwork(Network network) {
-        if (network == null) return getDefaultProxy();
-        final ProxyInfo globalProxy = getGlobalProxy();
+        if (network == null) return mProxyTracker.getDefaultProxy();
+        final ProxyInfo globalProxy = mProxyTracker.getGlobalProxy();
         if (globalProxy != null) return globalProxy;
         if (!NetworkUtils.queryUserAccess(Binder.getCallingUid(), network.netId)) return null;
         // Don't call getLinkProperties() as it requires ACCESS_NETWORK_STATE permission, which
@@ -3391,115 +3576,16 @@
         }
     }
 
-    // Convert empty ProxyInfo's to null as null-checks are used to determine if proxies are present
-    // (e.g. if mGlobalProxy==null fall back to network-specific proxy, if network-specific
-    // proxy is null then there is no proxy in place).
-    private ProxyInfo canonicalizeProxyInfo(ProxyInfo proxy) {
-        if (proxy != null && TextUtils.isEmpty(proxy.getHost())
-                && (proxy.getPacFileUrl() == null || Uri.EMPTY.equals(proxy.getPacFileUrl()))) {
-            proxy = null;
-        }
-        return proxy;
-    }
-
-    // ProxyInfo equality function with a couple modifications over ProxyInfo.equals() to make it
-    // better for determining if a new proxy broadcast is necessary:
-    // 1. Canonicalize empty ProxyInfos to null so an empty proxy compares equal to null so as to
-    //    avoid unnecessary broadcasts.
-    // 2. Make sure all parts of the ProxyInfo's compare true, including the host when a PAC URL
-    //    is in place.  This is important so legacy PAC resolver (see com.android.proxyhandler)
-    //    changes aren't missed.  The legacy PAC resolver pretends to be a simple HTTP proxy but
-    //    actually uses the PAC to resolve; this results in ProxyInfo's with PAC URL, host and port
-    //    all set.
-    private boolean proxyInfoEqual(ProxyInfo a, ProxyInfo b) {
-        a = canonicalizeProxyInfo(a);
-        b = canonicalizeProxyInfo(b);
-        // ProxyInfo.equals() doesn't check hosts when PAC URLs are present, but we need to check
-        // hosts even when PAC URLs are present to account for the legacy PAC resolver.
-        return Objects.equals(a, b) && (a == null || Objects.equals(a.getHost(), b.getHost()));
-    }
-
-    public void setGlobalProxy(ProxyInfo proxyProperties) {
+    @Override
+    public void setGlobalProxy(final ProxyInfo proxyProperties) {
         enforceConnectivityInternalPermission();
-
-        synchronized (mProxyLock) {
-            if (proxyProperties == mGlobalProxy) return;
-            if (proxyProperties != null && proxyProperties.equals(mGlobalProxy)) return;
-            if (mGlobalProxy != null && mGlobalProxy.equals(proxyProperties)) return;
-
-            String host = "";
-            int port = 0;
-            String exclList = "";
-            String pacFileUrl = "";
-            if (proxyProperties != null && (!TextUtils.isEmpty(proxyProperties.getHost()) ||
-                    !Uri.EMPTY.equals(proxyProperties.getPacFileUrl()))) {
-                if (!proxyProperties.isValid()) {
-                    if (DBG)
-                        log("Invalid proxy properties, ignoring: " + proxyProperties.toString());
-                    return;
-                }
-                mGlobalProxy = new ProxyInfo(proxyProperties);
-                host = mGlobalProxy.getHost();
-                port = mGlobalProxy.getPort();
-                exclList = mGlobalProxy.getExclusionListAsString();
-                if (!Uri.EMPTY.equals(proxyProperties.getPacFileUrl())) {
-                    pacFileUrl = proxyProperties.getPacFileUrl().toString();
-                }
-            } else {
-                mGlobalProxy = null;
-            }
-            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);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-
-            if (mGlobalProxy == null) {
-                proxyProperties = mDefaultProxy;
-            }
-            sendProxyBroadcast(proxyProperties);
-        }
+        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 (mProxyLock) {
-                mGlobalProxy = proxyProperties;
-            }
-        }
-    }
-
+    @Override
+    @Nullable
     public ProxyInfo getGlobalProxy() {
-        // this information is already available as a world read/writable jvm property
-        // so this API change wouldn't have a benifit.  It also breaks the passing
-        // of proxy info to all the JVMs.
-        // enforceAccessPermission();
-        synchronized (mProxyLock) {
-            return mGlobalProxy;
-        }
+        return mProxyTracker.getGlobalProxy();
     }
 
     private void handleApplyDefaultProxy(ProxyInfo proxy) {
@@ -3507,33 +3593,7 @@
                 && Uri.EMPTY.equals(proxy.getPacFileUrl())) {
             proxy = null;
         }
-        synchronized (mProxyLock) {
-            if (mDefaultProxy != null && mDefaultProxy.equals(proxy)) return;
-            if (mDefaultProxy == proxy) return; // catches repeated nulls
-            if (proxy != null &&  !proxy.isValid()) {
-                if (DBG) log("Invalid proxy properties, ignoring: " + proxy.toString());
-                return;
-            }
-
-            // This call could be coming from the PacManager, containing the port of the local
-            // proxy.  If this new proxy matches the global proxy then copy this proxy to the
-            // global (to get the correct local port), and send a broadcast.
-            // TODO: Switch PacManager to have its own message to send back rather than
-            // reusing EVENT_HAS_CHANGED_PROXY and this call to handleApplyDefaultProxy.
-            if ((mGlobalProxy != null) && (proxy != null)
-                    && (!Uri.EMPTY.equals(proxy.getPacFileUrl()))
-                    && proxy.getPacFileUrl().equals(mGlobalProxy.getPacFileUrl())) {
-                mGlobalProxy = proxy;
-                sendProxyBroadcast(mGlobalProxy);
-                return;
-            }
-            mDefaultProxy = proxy;
-
-            if (mGlobalProxy != null) return;
-            if (!mDefaultProxyDisabled) {
-                sendProxyBroadcast(proxy);
-            }
-        }
+        mProxyTracker.setDefaultProxy(proxy);
     }
 
     // If the proxy has changed from oldLp to newLp, resend proxy broadcast with default proxy.
@@ -3541,51 +3601,12 @@
     // the default proxy (even if it hasn't changed).
     // TODO: Deprecate the broadcast extras as they aren't necessarily applicable in a multi-network
     // world where an app might be bound to a non-default network.
-    private void updateProxy(LinkProperties newLp, LinkProperties oldLp, NetworkAgentInfo nai) {
+    private void updateProxy(LinkProperties newLp, LinkProperties oldLp) {
         ProxyInfo newProxyInfo = newLp == null ? null : newLp.getHttpProxy();
         ProxyInfo oldProxyInfo = oldLp == null ? null : oldLp.getHttpProxy();
 
-        if (!proxyInfoEqual(newProxyInfo, oldProxyInfo)) {
-            sendProxyBroadcast(getDefaultProxy());
-        }
-    }
-
-    private void handleDeprecatedGlobalHttpProxy() {
-        String proxy = Settings.Global.getString(mContext.getContentResolver(),
-                Settings.Global.HTTP_PROXY);
-        if (!TextUtils.isEmpty(proxy)) {
-            String data[] = proxy.split(":");
-            if (data.length == 0) {
-                return;
-            }
-
-            String proxyHost =  data[0];
-            int proxyPort = 8080;
-            if (data.length > 1) {
-                try {
-                    proxyPort = Integer.parseInt(data[1]);
-                } catch (NumberFormatException e) {
-                    return;
-                }
-            }
-            ProxyInfo p = new ProxyInfo(data[0], proxyPort, "");
-            setGlobalProxy(p);
-        }
-    }
-
-    private void sendProxyBroadcast(ProxyInfo proxy) {
-        if (proxy == null) proxy = new ProxyInfo("", 0, "");
-        if (mPacManager.setCurrentProxyScriptUrl(proxy)) return;
-        if (DBG) log("sending Proxy Broadcast for " + proxy);
-        Intent intent = new Intent(Proxy.PROXY_CHANGE_ACTION);
-        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING |
-            Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-        intent.putExtra(Proxy.EXTRA_PROXY_INFO, proxy);
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
-        } finally {
-            Binder.restoreCallingIdentity(ident);
+        if (!ProxyTracker.proxyInfoEqual(newProxyInfo, oldProxyInfo)) {
+            mProxyTracker.sendProxyBroadcast();
         }
     }
 
@@ -3596,7 +3617,7 @@
 
         SettingsObserver(Context context, Handler handler) {
             super(null);
-            mUriEventMap = new HashMap<Uri, Integer>();
+            mUriEventMap = new HashMap<>();
             mContext = context;
             mHandler = handler;
         }
@@ -3856,7 +3877,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);
             }
@@ -3873,6 +3894,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();
@@ -3902,7 +3925,7 @@
         synchronized (mVpns) {
             Vpn vpn = mVpns.get(userId);
             if (vpn == null) {
-                // Shouldn't happen as all codepaths that point here should have checked the Vpn
+                // Shouldn't happen as all code paths that point here should have checked the Vpn
                 // exists already.
                 Slog.wtf(TAG, "User " + userId + " has no Vpn configuration");
                 return false;
@@ -4066,7 +4089,7 @@
             url = String.format(url,
                     mTelephonyManager.getSimSerialNumber() /* ICCID */,
                     mTelephonyManager.getDeviceId() /* IMEI */,
-                    phoneNumber /* Phone numer */);
+                    phoneNumber /* Phone number */);
         }
 
         return url;
@@ -4091,7 +4114,7 @@
 
     @Override
     public void setAirplaneMode(boolean enable) {
-        enforceConnectivityInternalPermission();
+        enforceNetworkStackSettingsOrSetup();
         final long ident = Binder.clearCallingIdentity();
         try {
             final ContentResolver cr = mContext.getContentResolver();
@@ -4111,7 +4134,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();
@@ -4193,10 +4216,8 @@
         }
     };
 
-    private final HashMap<Messenger, NetworkFactoryInfo> mNetworkFactoryInfos =
-            new HashMap<Messenger, NetworkFactoryInfo>();
-    private final HashMap<NetworkRequest, NetworkRequestInfo> mNetworkRequests =
-            new HashMap<NetworkRequest, NetworkRequestInfo>();
+    private final HashMap<Messenger, NetworkFactoryInfo> mNetworkFactoryInfos = new HashMap<>();
+    private final HashMap<NetworkRequest, NetworkRequestInfo> mNetworkRequests = new HashMap<>();
 
     private static final int MAX_NETWORK_REQUESTS_PER_UID = 100;
     // Map from UID to number of NetworkRequests that UID has filed.
@@ -4310,7 +4331,7 @@
     }
 
     private ArrayList<Integer> getSignalStrengthThresholds(NetworkAgentInfo nai) {
-        final SortedSet<Integer> thresholds = new TreeSet();
+        final SortedSet<Integer> thresholds = new TreeSet<>();
         synchronized (nai) {
             for (NetworkRequestInfo nri : mNetworkRequests.values()) {
                 if (nri.request.networkCapabilities.hasSignalStrength() &&
@@ -4319,7 +4340,7 @@
                 }
             }
         }
-        return new ArrayList<Integer>(thresholds);
+        return new ArrayList<>(thresholds);
     }
 
     private void updateSignalStrengthThresholds(
@@ -4424,6 +4445,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;
@@ -4599,13 +4628,11 @@
      */
     // NOTE: Accessed on multiple threads, must be synchronized on itself.
     @GuardedBy("mNetworkForRequestId")
-    private final SparseArray<NetworkAgentInfo> mNetworkForRequestId =
-            new SparseArray<NetworkAgentInfo>();
+    private final SparseArray<NetworkAgentInfo> mNetworkForRequestId = new SparseArray<>();
 
     // NOTE: Accessed on multiple threads, must be synchronized on itself.
     @GuardedBy("mNetworkForNetId")
-    private final SparseArray<NetworkAgentInfo> mNetworkForNetId =
-            new SparseArray<NetworkAgentInfo>();
+    private final SparseArray<NetworkAgentInfo> mNetworkForNetId = new SparseArray<>();
     // NOTE: Accessed on multiple threads, synchronized with mNetworkForNetId.
     // An entry is first added to mNetIdInUse, prior to mNetworkForNetId, so
     // there may not be a strict 1:1 correlation between the two.
@@ -4615,11 +4642,10 @@
     // NetworkAgentInfo keyed off its connecting messenger
     // TODO - eval if we can reduce the number of lists/hashmaps/sparsearrays
     // NOTE: Only should be accessed on ConnectivityServiceThread, except dump().
-    private final HashMap<Messenger, NetworkAgentInfo> mNetworkAgentInfos =
-            new HashMap<Messenger, NetworkAgentInfo>();
+    private final HashMap<Messenger, NetworkAgentInfo> mNetworkAgentInfos = new HashMap<>();
 
     @GuardedBy("mBlockedAppUids")
-    private final HashSet<Integer> mBlockedAppUids = new HashSet();
+    private final HashSet<Integer> mBlockedAppUids = new HashSet<>();
 
     // Note: if mDefaultRequest is changed, NetworkMonitor needs to be updated.
     private final NetworkRequest mDefaultRequest;
@@ -4628,6 +4654,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);
@@ -4725,11 +4755,11 @@
         mDnsManager.updatePrivateDnsStatus(netId, newLp);
 
         // Start or stop clat accordingly to network state.
-        networkAgent.updateClat(mNetd);
+        networkAgent.updateClat(mNMS);
         if (isDefaultNetwork(networkAgent)) {
             handleApplyDefaultProxy(newLp.getHttpProxy());
         } else {
-            updateProxy(newLp, oldLp, networkAgent);
+            updateProxy(newLp, oldLp);
         }
         // TODO - move this check to cover the whole function
         if (!Objects.equals(newLp, oldLp)) {
@@ -4744,7 +4774,7 @@
     }
 
     private void wakeupModifyInterface(String iface, NetworkCapabilities caps, boolean add) {
-        // Marks are only available on WiFi interaces. Checking for
+        // Marks are only available on WiFi interfaces. Checking for
         // marks on unsupported interfaces is harmless.
         if (!caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
             return;
@@ -4764,9 +4794,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);
@@ -4776,13 +4806,13 @@
 
     private void updateInterfaces(LinkProperties newLp, LinkProperties oldLp, int netId,
                                   NetworkCapabilities caps) {
-        CompareResult<String> interfaceDiff = new CompareResult<String>(
+        CompareResult<String> interfaceDiff = new CompareResult<>(
                 oldLp != null ? oldLp.getAllInterfaceNames() : null,
                 newLp != null ? newLp.getAllInterfaceNames() : null);
         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);
@@ -4792,7 +4822,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);
             }
@@ -4811,12 +4841,12 @@
 
         // add routes before removing old in case it helps with continuous connectivity
 
-        // do this twice, adding non-nexthop routes first, then routes they are dependent on
+        // 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);
@@ -4825,9 +4855,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);
@@ -4836,9 +4866,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);
             }
@@ -4865,15 +4895,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;
     }
 
     /**
@@ -4946,11 +4975,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);
             }
@@ -4977,12 +5006,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();
             }
@@ -5010,12 +5047,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!
@@ -5030,7 +5067,7 @@
         }
         // 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);
@@ -5054,7 +5091,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);
@@ -5112,6 +5151,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: {
@@ -5129,6 +5170,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);
@@ -5184,9 +5230,9 @@
 
     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);
         }
@@ -5260,12 +5306,12 @@
         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.
-        ArrayList<NetworkAgentInfo> affectedNetworks = new ArrayList<NetworkAgentInfo>();
-        ArrayList<NetworkRequestInfo> addedRequests = new ArrayList<NetworkRequestInfo>();
+        ArrayList<NetworkAgentInfo> affectedNetworks = new ArrayList<>();
+        ArrayList<NetworkRequestInfo> addedRequests = new ArrayList<>();
         NetworkCapabilities nc = newNetwork.networkCapabilities;
         if (VDBG) log(" network has: " + nc);
         for (NetworkRequestInfo nri : mNetworkRequests.values()) {
@@ -5291,7 +5337,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);
@@ -5299,12 +5345,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);
@@ -5315,7 +5363,7 @@
                     keep = true;
                     // Tell NetworkFactories about the new score, so they can stop
                     // trying to connect if they know they cannot match it.
-                    // TODO - this could get expensive if we have alot of requests for this
+                    // TODO - this could get expensive if we have a lot of requests for this
                     // network.  Think about if there is a way to reduce this.  Push
                     // netid->request mapping to each factory?
                     sendUpdatedScoreToFactories(nri.request, score);
@@ -5359,6 +5407,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.
@@ -5436,7 +5485,7 @@
 
             // This has to happen after the notifyNetworkCallbacks as that tickles each
             // ConnectivityManager instance so that legacy requests correctly bind dns
-            // requests to this network.  The legacy users are listening for this bcast
+            // requests to this network.  The legacy users are listening for this broadcast
             // and will generally do a dns request so they can ensureRouteToHost and if
             // they do that before the callbacks happen they'll use the default network.
             //
@@ -5501,7 +5550,7 @@
         // TODO: This may get slow.  The "changed" parameter is provided for future optimization
         // to avoid the slowness.  It is not simply enough to process just "changed", for
         // example in the case where "changed"'s score decreases and another network should begin
-        // satifying a NetworkRequest that "changed" currently satisfies.
+        // satisfying a NetworkRequest that "changed" currently satisfies.
 
         // Optimization: Only reprocess "changed" if its score improved.  This is safe because it
         // can only add more NetworkRequests satisfied by "changed", and this is exactly what
@@ -5581,12 +5630,12 @@
             try {
                 // This should never fail.  Specifying an already in use NetID will cause failure.
                 if (networkAgent.isVPN()) {
-                    mNetd.createVirtualNetwork(networkAgent.network.netId,
+                    mNMS.createVirtualNetwork(networkAgent.network.netId,
                             !networkAgent.linkProperties.getDnsServers().isEmpty(),
                             (networkAgent.networkMisc == null ||
                                 !networkAgent.networkMisc.allowBypass));
                 } else {
-                    mNetd.createPhysicalNetwork(networkAgent.network.netId,
+                    mNMS.createPhysicalNetwork(networkAgent.network.netId,
                             getNetworkPermission(networkAgent.networkCapabilities));
                 }
             } catch (Exception e) {
@@ -5600,23 +5649,19 @@
         if (!networkAgent.everConnected && state == NetworkInfo.State.CONNECTED) {
             networkAgent.everConnected = true;
 
+            if (networkAgent.linkProperties == null) {
+                Slog.wtf(TAG, networkAgent.name() + " connected with null LinkProperties");
+            }
+
             handlePerNetworkPrivateDnsConfig(networkAgent, mDnsManager.getPrivateDnsConfig());
             updateLinkProperties(networkAgent, null);
-            notifyIfacesChangedForNetworkStats();
 
             networkAgent.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_CONNECTED);
             scheduleUnvalidatedPrompt(networkAgent);
 
             if (networkAgent.isVPN()) {
                 // Temporarily disable the default proxy (not global).
-                synchronized (mProxyLock) {
-                    if (!mDefaultProxyDisabled) {
-                        mDefaultProxyDisabled = true;
-                        if (mGlobalProxy == null && mDefaultProxy != null) {
-                            sendProxyBroadcast(null);
-                        }
-                    }
-                }
+                mProxyTracker.setDefaultProxyEnabled(false);
                 // TODO: support proxy per network.
             }
 
@@ -5638,20 +5683,13 @@
         } else if (state == NetworkInfo.State.DISCONNECTED) {
             networkAgent.asyncChannel.disconnect();
             if (networkAgent.isVPN()) {
-                synchronized (mProxyLock) {
-                    if (mDefaultProxyDisabled) {
-                        mDefaultProxyDisabled = false;
-                        if (mGlobalProxy == null && mDefaultProxy != null) {
-                            sendProxyBroadcast(mDefaultProxy);
-                        }
-                    }
-                }
+                mProxyTracker.setDefaultProxyEnabled(true);
                 updateUids(networkAgent, networkAgent.networkCapabilities, null);
             }
             disconnectAndDestroyNetwork(networkAgent);
         } else if ((oldInfo != null && oldInfo.getState() == NetworkInfo.State.SUSPENDED) ||
                 state == NetworkInfo.State.SUSPENDED) {
-            // going into or coming out of SUSPEND: rescore and notify
+            // going into or coming out of SUSPEND: re-score and notify
             if (networkAgent.getCurrentScore() != oldScore) {
                 rematchAllNetworksAndRequests(networkAgent, oldScore);
             }
@@ -5667,7 +5705,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");
@@ -5694,7 +5732,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) {
@@ -5744,7 +5851,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());
         }
@@ -6018,4 +6125,49 @@
             pw.println("    Get airplane mode.");
         }
     }
+
+    /**
+     * 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 c0beb37..b8f057d 100644
--- a/services/core/java/com/android/server/connectivity/DnsManager.java
+++ b/services/core/java/com/android/server/connectivity/DnsManager.java
@@ -35,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;
@@ -174,15 +173,6 @@
         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),
diff --git a/services/core/java/com/android/server/connectivity/LingerMonitor.java b/services/core/java/com/android/server/connectivity/LingerMonitor.java
index 635db19..0e727c5 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 - mFirstNotificationMillis) >= rateLimitMillis at first
+        mFirstNotificationMillis = -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 e471c7d..94c94a5 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
@@ -23,7 +23,10 @@
 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.annotation.NonNull;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -34,6 +37,7 @@
 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;
@@ -62,6 +66,7 @@
     private static final boolean DBG = true;
     private static final Boolean SYSTEM = Boolean.TRUE;
     private static final Boolean NETWORK = Boolean.FALSE;
+    private static final int VERSION_Q = Build.VERSION_CODES.Q;
 
     private final Context mContext;
     private final PackageManager mPackageManager;
@@ -85,7 +90,7 @@
             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);
+                int appUid = intent.getIntExtra(Intent.EXTRA_UID, INVALID_UID);
                 Uri appData = intent.getData();
                 String appName = appData != null ? appData.getSchemeSpecificPart() : null;
 
@@ -125,7 +130,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 +160,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 +186,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 +208,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) {
diff --git a/services/core/java/com/android/server/connectivity/ProxyTracker.java b/services/core/java/com/android/server/connectivity/ProxyTracker.java
new file mode 100644
index 0000000..15468ff
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/ProxyTracker.java
@@ -0,0 +1,327 @@
+/**
+ * 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.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;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Proxy;
+import android.net.ProxyInfo;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.Objects;
+
+/**
+ * A class to handle proxy for ConnectivityService.
+ *
+ * @hide
+ */
+public class ProxyTracker {
+    private static final String TAG = ProxyTracker.class.getSimpleName();
+    private static final boolean DBG = true;
+
+    @NonNull
+    private final Context mContext;
+
+    @NonNull
+    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")
+    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
+    // in its LinkProperties, or when ConnectivityService switches to a new default network, or
+    // when PacManager resolves the proxy.
+    @Nullable
+    @GuardedBy("mProxyLock")
+    private volatile ProxyInfo mDefaultProxy = null;
+    // Whether the default proxy is enabled.
+    @GuardedBy("mProxyLock")
+    private boolean mDefaultProxyEnabled = true;
+
+    // The object responsible for Proxy Auto Configuration (PAC).
+    @NonNull
+    private final PacManager mPacManager;
+
+    public ProxyTracker(@NonNull final Context context,
+            @NonNull final Handler connectivityServiceInternalHandler, final int pacChangedEvent) {
+        mContext = context;
+        mPacManager = new PacManager(context, connectivityServiceInternalHandler, pacChangedEvent);
+    }
+
+    // Convert empty ProxyInfo's to null as null-checks are used to determine if proxies are present
+    // (e.g. if mGlobalProxy==null fall back to network-specific proxy, if network-specific
+    // proxy is null then there is no proxy in place).
+    @Nullable
+    private static ProxyInfo canonicalizeProxyInfo(@Nullable final ProxyInfo proxy) {
+        if (proxy != null && TextUtils.isEmpty(proxy.getHost())
+                && Uri.EMPTY.equals(proxy.getPacFileUrl())) {
+            return null;
+        }
+        return proxy;
+    }
+
+    // ProxyInfo equality functions with a couple modifications over ProxyInfo.equals() to make it
+    // better for determining if a new proxy broadcast is necessary:
+    // 1. Canonicalize empty ProxyInfos to null so an empty proxy compares equal to null so as to
+    //    avoid unnecessary broadcasts.
+    // 2. Make sure all parts of the ProxyInfo's compare true, including the host when a PAC URL
+    //    is in place.  This is important so legacy PAC resolver (see com.android.proxyhandler)
+    //    changes aren't missed.  The legacy PAC resolver pretends to be a simple HTTP proxy but
+    //    actually uses the PAC to resolve; this results in ProxyInfo's with PAC URL, host and port
+    //    all set.
+    public static boolean proxyInfoEqual(@Nullable final ProxyInfo a, @Nullable final ProxyInfo b) {
+        final ProxyInfo pa = canonicalizeProxyInfo(a);
+        final ProxyInfo pb = canonicalizeProxyInfo(b);
+        // ProxyInfo.equals() doesn't check hosts when PAC URLs are present, but we need to check
+        // hosts even when PAC URLs are present to account for the legacy PAC resolver.
+        return Objects.equals(pa, pb) && (pa == null || Objects.equals(pa.getHost(), pb.getHost()));
+    }
+
+    /**
+     * Gets the default system-wide proxy.
+     *
+     * This will return the global proxy if set, otherwise the default proxy if in use. Note
+     * that this is not necessarily the proxy that any given process should use, as the right
+     * proxy for a process is the proxy for the network this process will use, which may be
+     * different from this value. This value is simply the default in case there is no proxy set
+     * in the network that will be used by a specific process.
+     * @return The default system-wide proxy or null if none.
+     */
+    @Nullable
+    public ProxyInfo getDefaultProxy() {
+        // This information is already available as a world read/writable jvm property.
+        synchronized (mProxyLock) {
+            if (mGlobalProxy != null) return mGlobalProxy;
+            if (mDefaultProxyEnabled) return mDefaultProxy;
+            return null;
+        }
+    }
+
+    /**
+     * Gets the global proxy.
+     *
+     * @return The global proxy or null if none.
+     */
+    @Nullable
+    public ProxyInfo getGlobalProxy() {
+        // This information is already available as a world read/writable jvm property.
+        synchronized (mProxyLock) {
+            return mGlobalProxy;
+        }
+    }
+
+    /**
+     * 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.
+     */
+    public void sendProxyBroadcast() {
+        final ProxyInfo defaultProxy = getDefaultProxy();
+        final ProxyInfo proxyInfo = null != defaultProxy ? defaultProxy : new ProxyInfo("", 0, "");
+        if (mPacManager.setCurrentProxyScriptUrl(proxyInfo)) 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 |
+                Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+        intent.putExtra(Proxy.EXTRA_PROXY_INFO, proxyInfo);
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    /**
+     * Sets the global proxy in memory. Also writes the values to the global settings of the device.
+     *
+     * @param proxyInfo the proxy spec, or null for no proxy.
+     */
+    public void setGlobalProxy(@Nullable ProxyInfo proxyInfo) {
+        synchronized (mProxyLock) {
+            // ProxyInfo#equals is not commutative :( and is public API, so it can't be fixed.
+            if (proxyInfo == mGlobalProxy) return;
+            if (proxyInfo != null && proxyInfo.equals(mGlobalProxy)) return;
+            if (mGlobalProxy != null && mGlobalProxy.equals(proxyInfo)) return;
+
+            final String host;
+            final int port;
+            final String exclList;
+            final String pacFileUrl;
+            if (proxyInfo != null && (!TextUtils.isEmpty(proxyInfo.getHost()) ||
+                    !Uri.EMPTY.equals(proxyInfo.getPacFileUrl()))) {
+                if (!proxyInfo.isValid()) {
+                    if (DBG) Slog.d(TAG, "Invalid proxy properties, ignoring: " + proxyInfo);
+                    return;
+                }
+                mGlobalProxy = new ProxyInfo(proxyInfo);
+                host = mGlobalProxy.getHost();
+                port = mGlobalProxy.getPort();
+                exclList = mGlobalProxy.getExclusionListAsString();
+                pacFileUrl = Uri.EMPTY.equals(proxyInfo.getPacFileUrl())
+                        ? "" : proxyInfo.getPacFileUrl().toString();
+            } else {
+                host = "";
+                port = 0;
+                exclList = "";
+                pacFileUrl = "";
+                mGlobalProxy = null;
+            }
+            final ContentResolver res = mContext.getContentResolver();
+            final long token = Binder.clearCallingIdentity();
+            try {
+                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();
+        }
+    }
+
+    /**
+     * Sets the default proxy for the device.
+     *
+     * The default proxy is the proxy used for networks that do not have a specific proxy.
+     * @param proxyInfo the proxy spec, or null for no proxy.
+     */
+    public void setDefaultProxy(@Nullable ProxyInfo proxyInfo) {
+        synchronized (mProxyLock) {
+            if (Objects.equals(mDefaultProxy, proxyInfo)) return;
+            if (proxyInfo != null &&  !proxyInfo.isValid()) {
+                if (DBG) Slog.d(TAG, "Invalid proxy properties, ignoring: " + proxyInfo);
+                return;
+            }
+
+            // This call could be coming from the PacManager, containing the port of the local
+            // proxy. If this new proxy matches the global proxy then copy this proxy to the
+            // global (to get the correct local port), and send a broadcast.
+            // TODO: Switch PacManager to have its own message to send back rather than
+            // reusing EVENT_HAS_CHANGED_PROXY and this call to handleApplyDefaultProxy.
+            if ((mGlobalProxy != null) && (proxyInfo != null)
+                    && (!Uri.EMPTY.equals(proxyInfo.getPacFileUrl()))
+                    && proxyInfo.getPacFileUrl().equals(mGlobalProxy.getPacFileUrl())) {
+                mGlobalProxy = proxyInfo;
+                sendProxyBroadcast();
+                return;
+            }
+            mDefaultProxy = proxyInfo;
+
+            if (mGlobalProxy != null) return;
+            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/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/NetworkCapabilitiesTest.java b/tests/net/java/android/net/NetworkCapabilitiesTest.java
index a112fa6..b40921f 100644
--- a/tests/net/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/java/android/net/NetworkCapabilitiesTest.java
@@ -303,7 +303,7 @@
         assertTrue("Request: " + request + ", Network:" + network,
                 request.satisfiedByNetworkCapabilities(network));
 
-        // Adding capabilities that doesn't exist in the network anyway
+        // Requesting absence of capabilities that network doesn't have. Request should satisfy.
         request.addUnwantedCapability(NET_CAPABILITY_WIFI_P2P);
         request.addUnwantedCapability(NET_CAPABILITY_NOT_METERED);
         assertTrue(request.satisfiedByNetworkCapabilities(network));
@@ -319,7 +319,6 @@
         assertTrue(request.hasUnwantedCapability(NET_CAPABILITY_NOT_RESTRICTED));
         assertFalse(request.hasCapability(NET_CAPABILITY_NOT_RESTRICTED));
 
-
         // Now this request won't be satisfied because network contains NOT_RESTRICTED.
         assertFalse(request.satisfiedByNetworkCapabilities(network));
         network.removeCapability(NET_CAPABILITY_NOT_RESTRICTED);
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/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 2208580..17bcea0 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -51,19 +51,22 @@
 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;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
-import static 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;
@@ -80,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;
@@ -93,8 +96,10 @@
 import android.net.ConnectivityManager.PacketKeepaliveCallback;
 import android.net.ConnectivityManager.TooManyRequestsException;
 import android.net.ConnectivityThread;
+import android.net.INetworkPolicyListener;
 import android.net.INetworkPolicyManager;
 import android.net.INetworkStatsService;
+import android.net.InterfaceConfiguration;
 import android.net.IpPrefix;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
@@ -112,7 +117,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;
@@ -125,6 +129,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.Process;
+import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.provider.Settings;
@@ -145,8 +150,10 @@
 import com.android.server.connectivity.DnsManager;
 import com.android.server.connectivity.IpConnectivityMetrics;
 import com.android.server.connectivity.MockableSystemProperties;
+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;
@@ -161,10 +168,13 @@
 import org.mockito.MockitoAnnotations;
 import org.mockito.Spy;
 
+import java.net.Inet4Address;
 import java.net.InetAddress;
+import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Objects;
@@ -188,8 +198,18 @@
     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";
     private static final String WIFI_IFNAME = "test_wlan0";
 
@@ -201,11 +221,13 @@
     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;
 
     private ArgumentCaptor<String[]> mStringArrayCaptor = ArgumentCaptor.forClass(String[].class);
 
@@ -235,7 +257,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());
@@ -247,7 +269,7 @@
                     });
 
             mContentResolver = new MockContentResolver();
-            mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+            mContentResolver.addProvider(Settings.AUTHORITY, settingsProvider);
         }
 
         @Override
@@ -867,7 +889,7 @@
                 NetworkAgentInfo networkAgentInfo, NetworkRequest defaultRequest,
                 IpConnectivityLog log) {
             super(context, handler, networkAgentInfo, defaultRequest, log,
-                    NetworkMonitor.NetworkMonitorSettings.DEFAULT);
+                    NetworkMonitor.Dependencies.DEFAULT);
             connectivityHandler = handler;
         }
 
@@ -920,6 +942,11 @@
         }
 
         @Override
+        protected Tethering makeTethering() {
+            return mock(Tethering.class);
+        }
+
+        @Override
         protected int reserveNetId() {
             while (true) {
                 final int netId = super.reserveNetId();
@@ -950,6 +977,10 @@
             return monitor;
         }
 
+        public Nat464Xlat getNat464Xlat(MockNetworkAgent mna) {
+            return getNetworkAgentInfoForNetwork(mna.getNetwork()).clatd;
+        }
+
         @Override
         public MultinetworkPolicyTracker createMultinetworkPolicyTracker(
                 Context c, Handler h, Runnable r) {
@@ -1005,6 +1036,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) {
+            }
+        }
     }
 
     /**
@@ -1031,16 +1076,24 @@
             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),
+                mNpm,
                 mock(IpConnectivityLog.class));
 
+        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);
@@ -1050,13 +1103,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;
@@ -1069,6 +1122,7 @@
             mEthernetNetworkAgent.disconnect();
             mEthernetNetworkAgent = null;
         }
+        FakeSettingsProvider.clearSettingsProvider();
     }
 
     private static int transportToLegacyType(int transport) {
@@ -1420,7 +1474,8 @@
         RESUMED,
         LOSING,
         LOST,
-        UNAVAILABLE
+        UNAVAILABLE,
+        BLOCKED_STATUS
     }
 
     private static class CallbackInfo {
@@ -1452,11 +1507,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;
 
@@ -1506,6 +1556,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;
         }
@@ -1532,20 +1587,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) {
@@ -1566,6 +1621,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.
@@ -1573,7 +1629,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);
@@ -1584,19 +1640,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
@@ -1604,9 +1669,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);
         }
@@ -1620,7 +1688,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,
@@ -1632,7 +1700,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,
@@ -1649,6 +1717,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();
@@ -1756,6 +1830,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();
@@ -1973,7 +2053,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.
@@ -2005,7 +2085,7 @@
 
     @Test
     public void testNetworkGoesIntoBackgroundAfterLinger() {
-        setMobileDataAlwaysOn(true);
+        setAlwaysOnNetworks(true);
         NetworkRequest request = new NetworkRequest.Builder()
                 .clearCapabilities()
                 .build();
@@ -2750,10 +2830,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();
     }
 
@@ -2775,7 +2855,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();
@@ -2973,7 +3053,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());
 
@@ -2993,7 +3073,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.
@@ -3197,12 +3277,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();
@@ -3217,13 +3297,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);
 
@@ -3781,6 +3860,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),
@@ -3989,6 +4069,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());
@@ -4047,6 +4128,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());
@@ -4422,4 +4504,266 @@
 
         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.
+     */
+    private InterfaceConfiguration getClatInterfaceConfig(LinkAddress la) {
+        InterfaceConfiguration cfg = new InterfaceConfiguration();
+        cfg.setHardwareAddress("11:22:33:44:55:66");
+        cfg.setLinkAddress(la);
+        return cfg;
+    }
+
+    /**
+     * Make expected stack link properties, copied from Nat464Xlat.
+     */
+    private LinkProperties makeClatLinkProperties(LinkAddress la) {
+        LinkAddress clatAddress = la;
+        LinkProperties stacked = new LinkProperties();
+        stacked.setInterfaceName(CLAT_PREFIX + MOBILE_IFNAME);
+        RouteInfo ipv4Default = new RouteInfo(
+                new LinkAddress(Inet4Address.ANY, 0),
+                clatAddress.getAddress(), CLAT_PREFIX + MOBILE_IFNAME);
+        stacked.addRoute(ipv4Default);
+        stacked.addLinkAddress(clatAddress);
+        return stacked;
+    }
+
+    @Test
+    public void testStackedLinkProperties() throws UnknownHostException, RemoteException {
+        final LinkAddress myIpv4 = new LinkAddress("1.2.3.4/24");
+        final LinkAddress myIpv6 = new LinkAddress("2001:db8:1::1/64");
+        final NetworkRequest networkRequest = new NetworkRequest.Builder()
+                .addTransportType(TRANSPORT_CELLULAR)
+                .addCapability(NET_CAPABILITY_INTERNET)
+                .build();
+        final TestNetworkCallback networkCallback = new TestNetworkCallback();
+        mCm.registerNetworkCallback(networkRequest, networkCallback);
+
+        // Prepare ipv6 only link properties and connect.
+        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        final LinkProperties cellLp = new LinkProperties();
+        cellLp.setInterfaceName(MOBILE_IFNAME);
+        cellLp.addLinkAddress(myIpv6);
+        cellLp.addRoute(new RouteInfo((IpPrefix) null, myIpv6.getAddress(), MOBILE_IFNAME));
+        cellLp.addRoute(new RouteInfo(myIpv6, null, MOBILE_IFNAME));
+        reset(mNetworkManagementService);
+        when(mNetworkManagementService.getInterfaceConfig(CLAT_PREFIX + MOBILE_IFNAME))
+                .thenReturn(getClatInterfaceConfig(myIpv4));
+
+        // Connect with ipv6 link properties, then expect clat setup ipv4 and update link
+        // properties properly.
+        mCellNetworkAgent.sendLinkProperties(cellLp);
+        mCellNetworkAgent.connect(true);
+        networkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+        verify(mNetworkManagementService, times(1)).startClatd(MOBILE_IFNAME);
+        Nat464Xlat clat = mService.getNat464Xlat(mCellNetworkAgent);
+
+        // Clat iface up, expect stack link updated.
+        clat.interfaceLinkStateChanged(CLAT_PREFIX + MOBILE_IFNAME, true);
+        waitForIdle();
+        List<LinkProperties> stackedLps = mCm.getLinkProperties(mCellNetworkAgent.getNetwork())
+                .getStackedLinks();
+        assertEquals(makeClatLinkProperties(myIpv4), stackedLps.get(0));
+
+        // Change trivial linkproperties and see if stacked link is preserved.
+        cellLp.addDnsServer(InetAddress.getByName("8.8.8.8"));
+        mCellNetworkAgent.sendLinkProperties(cellLp);
+        waitForIdle();
+        networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
+
+        List<LinkProperties> stackedLpsAfterChange =
+                mCm.getLinkProperties(mCellNetworkAgent.getNetwork()).getStackedLinks();
+        assertNotEquals(stackedLpsAfterChange, Collections.EMPTY_LIST);
+        assertEquals(makeClatLinkProperties(myIpv4), stackedLpsAfterChange.get(0));
+
+        // Add ipv4 address, expect stacked linkproperties be cleaned up
+        cellLp.addLinkAddress(myIpv4);
+        cellLp.addRoute(new RouteInfo(myIpv4, null, MOBILE_IFNAME));
+        mCellNetworkAgent.sendLinkProperties(cellLp);
+        waitForIdle();
+        networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
+        verify(mNetworkManagementService, times(1)).stopClatd(MOBILE_IFNAME);
+
+        // Clat iface removed, expect linkproperties revert to original one
+        clat.interfaceRemoved(CLAT_PREFIX + MOBILE_IFNAME);
+        waitForIdle();
+        networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
+        LinkProperties actualLpAfterIpv4 = mCm.getLinkProperties(mCellNetworkAgent.getNetwork());
+        assertEquals(cellLp, actualLpAfterIpv4);
+
+        // Clean up
+        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);
+    }
 }
diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
index e573d35..9b919ab 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,22 +36,25 @@
 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;
 import java.util.Collection;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -61,16 +66,17 @@
 
     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;
 
     @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}
         });
     }
 
@@ -120,6 +126,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);
@@ -127,12 +134,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
@@ -155,6 +164,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)))
@@ -180,7 +191,7 @@
 
         verify(mMockNetd)
                 .ipSecDeleteSecurityAssociation(
-                        eq(spiResp.resourceId),
+                        eq(mUid),
                         anyString(),
                         anyString(),
                         eq(TEST_SPI),
@@ -188,8 +199,7 @@
                         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);
@@ -208,8 +218,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);
 
@@ -217,7 +226,7 @@
 
         verify(mMockNetd)
                 .ipSecDeleteSecurityAssociation(
-                        eq(spiResp.resourceId),
+                        eq(mUid),
                         anyString(),
                         anyString(),
                         eq(TEST_SPI),
@@ -257,6 +266,47 @@
         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()));
+    }
+
     @Test
     public void testCreateTransform() throws Exception {
         IpSecConfig ipSecConfig = new IpSecConfig();
@@ -267,28 +317,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
@@ -302,28 +331,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
@@ -360,13 +420,12 @@
 
         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),
@@ -388,7 +447,7 @@
 
         verify(mMockNetd, times(1))
                 .ipSecDeleteSecurityAssociation(
-                        eq(createTransformResp.resourceId),
+                        eq(mUid),
                         anyString(),
                         anyString(),
                         eq(TEST_SPI),
@@ -396,8 +455,7 @@
                         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);
 
@@ -432,8 +490,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);
@@ -442,7 +499,7 @@
 
         verify(mMockNetd)
                 .ipSecDeleteSecurityAssociation(
-                        eq(createTransformResp.resourceId),
+                        eq(mUid),
                         anyString(),
                         anyString(),
                         eq(TEST_SPI),
@@ -476,7 +533,7 @@
         verify(mMockNetd)
                 .ipSecApplyTransportModeTransform(
                         eq(pfd.getFileDescriptor()),
-                        eq(resourceId),
+                        eq(mUid),
                         eq(IpSecManager.DIRECTION_OUT),
                         anyString(),
                         anyString(),
@@ -508,8 +565,7 @@
                 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);
@@ -529,8 +585,7 @@
         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");
 
@@ -550,8 +605,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);
@@ -592,6 +646,7 @@
         }
     }
 
+    @Ignore
     @Test
     public void testAddTunnelFailsForBadPackageName() throws Exception {
         try {
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 f025f41..af7123b 100644
--- a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -21,19 +21,25 @@
 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 org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.anyInt;
+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.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
@@ -48,6 +54,12 @@
 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 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;
@@ -58,43 +70,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);
+        when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(MOCK_PACKAGE_NAMES);
+        mPermissionMonitor = spy(new PermissionMonitor(mContext, null));
     }
 
-    private void expectPermission(String[] permissions, boolean preinstalled) throws Exception {
-        final PackageInfo packageInfo = packageInfoWithPermissions(permissions, preinstalled);
+    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_PACKAGE_NAMES[0]), eq(GET_PERMISSIONS), anyInt())).thenReturn(packageInfo);
+        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));
@@ -102,35 +130,57 @@
     }
 
     @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_UID));
+        assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID, CHANGE_NETWORK_STATE));
+        assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID, NETWORK_STACK));
+        assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID, CONNECTIVITY_INTERNAL));
+        assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID,
+                CONNECTIVITY_USE_RESTRICTED_NETWORKS));
+        assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID, CHANGE_WIFI_STATE));
 
-        expectPermission(new String[] { NETWORK_STACK, CONNECTIVITY_INTERNAL }, false);
-        assertTrue(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
+        assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_Q, MOCK_UID));
+        assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_Q, MOCK_UID, 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_UID));
+        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID, CHANGE_NETWORK_STATE));
+        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID, NETWORK_STACK));
+        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID, CONNECTIVITY_INTERNAL));
+        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID,
+                CONNECTIVITY_USE_RESTRICTED_NETWORKS));
+        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID, CHANGE_WIFI_STATE));
 
-        expectPermission(new String[] { CHANGE_WIFI_STATE }, false);
-        assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
+        assertFalse(hasBgPermission(PARTITION_VENDOR, VERSION_Q, MOCK_UID));
+        assertFalse(hasBgPermission(PARTITION_VENDOR, VERSION_Q, MOCK_UID, CHANGE_WIFI_STATE));
     }
 }
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/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
index e371abc..f89f303 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -227,9 +227,6 @@
 
     @After
     public void tearDown() throws Exception {
-        // Registered by NetworkStatsService's constructor.
-        LocalServices.removeServiceForTest(NetworkStatsManagerInternal.class);
-
         IoUtils.deleteContents(mStatsDir);
 
         mServiceContext = null;
@@ -937,7 +934,6 @@
 
         // verify service has empty history for wifi
         assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
-        String callingPackage = "the.calling.package";
         long thresholdInBytes = 1L;  // very small; should be overriden by framework
         DataUsageRequest inputRequest = new DataUsageRequest(
                 DataUsageRequest.REQUEST_ID_UNSET, sTemplateWifi, thresholdInBytes);
@@ -956,7 +952,7 @@
 
         // Register and verify request and that binder was called
         DataUsageRequest request =
-                mService.registerUsageCallback(callingPackage, inputRequest,
+                mService.registerUsageCallback(mServiceContext.getOpPackageName(), inputRequest,
                         messenger, mBinder);
         assertTrue(request.requestId > 0);
         assertTrue(Objects.equals(sTemplateWifi, request.template));