Merge "API review: access field by method" into rvc-dev
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 81735ac..2a323e5 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -145,16 +145,6 @@
     public static final String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";
 
     /**
-     * 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 broadcast makes it expensive.
-     * Use this broadcast intent instead for SUPL requests.
-     * @hide
-     */
-    public static final String CONNECTIVITY_ACTION_SUPL =
-            "android.net.conn.CONNECTIVITY_CHANGE_SUPL";
-
-    /**
      * The device has connected to a network that has presented a captive
      * portal, which is blocking Internet connectivity. The user was presented
      * with a notification that network sign in is required,
@@ -1517,84 +1507,6 @@
         return null;
     }
 
-    /**
-     * Guess what the network request was trying to say so that the resulting
-     * network is accessible via the legacy (deprecated) API such as
-     * requestRouteToHost.
-     *
-     * This means we should try to be fairly precise about transport and
-     * capability but ignore things such as networkSpecifier.
-     * If the request has more than one transport or capability it doesn't
-     * match the old legacy requests (they selected only single transport/capability)
-     * so this function cannot map the request to a single legacy type and
-     * the resulting network will not be available to the legacy APIs.
-     *
-     * This code is only called from the requestNetwork API (L and above).
-     *
-     * Setting a legacy type causes CONNECTIVITY_ACTION broadcasts, which are expensive
-     * because they wake up lots of apps - see http://b/23350688 . So we currently only
-     * do this for SUPL requests, which are the only ones that we know need it. If
-     * omitting these broadcasts causes unacceptable app breakage, then for backwards
-     * compatibility we can send them:
-     *
-     * if (targetSdkVersion < Build.VERSION_CODES.M) &&        // legacy API unsupported >= M
-     *     targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP))  // requestNetwork not present < L
-     *
-     * TODO - This should be removed when the legacy APIs are removed.
-     */
-    private int inferLegacyTypeForNetworkCapabilities(NetworkCapabilities netCap) {
-        if (netCap == null) {
-            return TYPE_NONE;
-        }
-
-        if (!netCap.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
-            return TYPE_NONE;
-        }
-
-        // Do this only for SUPL, until GnssLocationProvider is fixed. http://b/25876485 .
-        if (!netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_SUPL)) {
-            // NOTE: if this causes app breakage, we should not just comment out this early return;
-            // instead, we should make this early return conditional on the requesting app's target
-            // SDK version, as described in the comment above.
-            return TYPE_NONE;
-        }
-
-        String type = null;
-        int result = TYPE_NONE;
-
-        if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_CBS)) {
-            type = "enableCBS";
-            result = TYPE_MOBILE_CBS;
-        } else if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_IMS)) {
-            type = "enableIMS";
-            result = TYPE_MOBILE_IMS;
-        } else if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_FOTA)) {
-            type = "enableFOTA";
-            result = TYPE_MOBILE_FOTA;
-        } else if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN)) {
-            type = "enableDUN";
-            result = TYPE_MOBILE_DUN;
-        } else if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_SUPL)) {
-            type = "enableSUPL";
-            result = TYPE_MOBILE_SUPL;
-        // back out this hack for mms as they no longer need this and it's causing
-        // device slowdowns - b/23350688 (note, supl still needs this)
-        //} else if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS)) {
-        //    type = "enableMMS";
-        //    result = TYPE_MOBILE_MMS;
-        } else if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
-            type = "enableHIPRI";
-            result = TYPE_MOBILE_HIPRI;
-        }
-        if (type != null) {
-            NetworkCapabilities testCap = networkCapabilitiesForFeature(TYPE_MOBILE, type);
-            if (testCap.equalsNetCapabilities(netCap) && testCap.equalsTransportTypes(netCap)) {
-                return result;
-            }
-        }
-        return TYPE_NONE;
-    }
-
     private int legacyTypeForNetworkCapabilities(NetworkCapabilities netCap) {
         if (netCap == null) return TYPE_NONE;
         if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_CBS)) {
@@ -2575,13 +2487,13 @@
             }
 
             @Override
-            public void onTetheringFailed(final int resultCode) {
+            public void onTetheringFailed(final int error) {
                 callback.onTetheringFailed();
             }
         };
 
         final TetheringRequest request = new TetheringRequest.Builder(type)
-                .setSilentProvisioning(!showProvisioningUi).build();
+                .setShouldShowEntitlementUi(showProvisioningUi).build();
 
         mTetheringManager.startTethering(request, executor, tetheringCallback);
     }
@@ -2801,11 +2713,12 @@
     public static final int TETHER_ERROR_UNAVAIL_IFACE =
             TetheringManager.TETHER_ERROR_UNAVAIL_IFACE;
     /**
-     * @deprecated Use {@link TetheringManager#TETHER_ERROR_MASTER_ERROR}.
+     * @deprecated Use {@link TetheringManager#TETHER_ERROR_INTERNAL_ERROR}.
      * {@hide}
      */
     @Deprecated
-    public static final int TETHER_ERROR_MASTER_ERROR = TetheringManager.TETHER_ERROR_MASTER_ERROR;
+    public static final int TETHER_ERROR_MASTER_ERROR =
+            TetheringManager.TETHER_ERROR_INTERNAL_ERROR;
     /**
      * @deprecated Use {@link TetheringManager#TETHER_ERROR_TETHER_IFACE_ERROR}.
      * {@hide}
@@ -2821,19 +2734,19 @@
     public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR =
             TetheringManager.TETHER_ERROR_UNTETHER_IFACE_ERROR;
     /**
-     * @deprecated Use {@link TetheringManager#TETHER_ERROR_ENABLE_NAT_ERROR}.
+     * @deprecated Use {@link TetheringManager#TETHER_ERROR_ENABLE_FORWARDING_ERROR}.
      * {@hide}
      */
     @Deprecated
     public static final int TETHER_ERROR_ENABLE_NAT_ERROR =
-            TetheringManager.TETHER_ERROR_ENABLE_NAT_ERROR;
+            TetheringManager.TETHER_ERROR_ENABLE_FORWARDING_ERROR;
     /**
-     * @deprecated Use {@link TetheringManager#TETHER_ERROR_DISABLE_NAT_ERROR}.
+     * @deprecated Use {@link TetheringManager#TETHER_ERROR_DISABLE_FORWARDING_ERROR}.
      * {@hide}
      */
     @Deprecated
     public static final int TETHER_ERROR_DISABLE_NAT_ERROR =
-            TetheringManager.TETHER_ERROR_DISABLE_NAT_ERROR;
+            TetheringManager.TETHER_ERROR_DISABLE_FORWARDING_ERROR;
     /**
      * @deprecated Use {@link TetheringManager#TETHER_ERROR_IFACE_CFG_ERROR}.
      * {@hide}
@@ -2842,13 +2755,13 @@
     public static final int TETHER_ERROR_IFACE_CFG_ERROR =
             TetheringManager.TETHER_ERROR_IFACE_CFG_ERROR;
     /**
-     * @deprecated Use {@link TetheringManager#TETHER_ERROR_PROVISION_FAILED}.
+     * @deprecated Use {@link TetheringManager#TETHER_ERROR_PROVISIONING_FAILED}.
      * {@hide}
      */
     @SystemApi
     @Deprecated
     public static final int TETHER_ERROR_PROVISION_FAILED =
-            TetheringManager.TETHER_ERROR_PROVISION_FAILED;
+            TetheringManager.TETHER_ERROR_PROVISIONING_FAILED;
     /**
      * @deprecated Use {@link TetheringManager#TETHER_ERROR_DHCPSERVER_ERROR}.
      * {@hide}
@@ -2880,7 +2793,14 @@
     @UnsupportedAppUsage
     @Deprecated
     public int getLastTetherError(String iface) {
-        return mTetheringManager.getLastTetherError(iface);
+        int error = mTetheringManager.getLastTetherError(iface);
+        if (error == TetheringManager.TETHER_ERROR_UNKNOWN_TYPE) {
+            // TETHER_ERROR_UNKNOWN_TYPE was introduced with TetheringManager and has never been
+            // returned by ConnectivityManager. Convert it to the legacy TETHER_ERROR_UNKNOWN_IFACE
+            // instead.
+            error = TetheringManager.TETHER_ERROR_UNKNOWN_IFACE;
+        }
+        return error;
     }
 
     /** @hide */
@@ -3773,29 +3693,29 @@
     /**
      * Helper function to request a network with a particular legacy type.
      *
-     * @deprecated This is temporarily public for tethering to backwards compatibility that uses
-     * the NetworkRequest API to request networks with legacy type and relies on
-     * CONNECTIVITY_ACTION broadcasts instead of NetworkCallbacks. New caller should use
+     * This API is only for use in internal system code that requests networks with legacy type and
+     * relies on CONNECTIVITY_ACTION broadcasts instead of NetworkCallbacks. New caller should use
      * {@link #requestNetwork(NetworkRequest, NetworkCallback, Handler)} instead.
      *
-     * TODO: update said system code to rely on NetworkCallbacks and make this method private.
-
      * @param request {@link NetworkRequest} describing this request.
-     * @param networkCallback The {@link NetworkCallback} to be utilized for this request. Note
-     *                        the callback must not be shared - it uniquely specifies this request.
      * @param timeoutMs The time in milliseconds to attempt looking for a suitable network
      *                  before {@link NetworkCallback#onUnavailable()} is called. The timeout must
      *                  be a positive value (i.e. >0).
      * @param legacyType to specify the network type(#TYPE_*).
      * @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
+     * @param networkCallback The {@link NetworkCallback} to be utilized for this request. Note
+     *                        the callback must not be shared - it uniquely specifies this request.
      *
      * @hide
      */
     @SystemApi
-    @Deprecated
+    @RequiresPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK)
     public void requestNetwork(@NonNull NetworkRequest request,
-            @NonNull NetworkCallback networkCallback, int timeoutMs, int legacyType,
-            @NonNull Handler handler) {
+            int timeoutMs, int legacyType, @NonNull Handler handler,
+            @NonNull NetworkCallback networkCallback) {
+        if (legacyType == TYPE_NONE) {
+            throw new IllegalArgumentException("TYPE_NONE is meaningless legacy type");
+        }
         CallbackHandler cbHandler = new CallbackHandler(handler);
         NetworkCapabilities nc = request.networkCapabilities;
         sendRequestForNetwork(nc, networkCallback, timeoutMs, REQUEST, legacyType, cbHandler);
@@ -3893,9 +3813,9 @@
      */
     public void requestNetwork(@NonNull NetworkRequest request,
             @NonNull NetworkCallback networkCallback, @NonNull Handler handler) {
-        int legacyType = inferLegacyTypeForNetworkCapabilities(request.networkCapabilities);
         CallbackHandler cbHandler = new CallbackHandler(handler);
-        requestNetwork(request, networkCallback, 0, legacyType, cbHandler);
+        NetworkCapabilities nc = request.networkCapabilities;
+        sendRequestForNetwork(nc, networkCallback, 0, REQUEST, TYPE_NONE, cbHandler);
     }
 
     /**
@@ -3928,8 +3848,9 @@
     public void requestNetwork(@NonNull NetworkRequest request,
             @NonNull NetworkCallback networkCallback, int timeoutMs) {
         checkTimeout(timeoutMs);
-        int legacyType = inferLegacyTypeForNetworkCapabilities(request.networkCapabilities);
-        requestNetwork(request, networkCallback, timeoutMs, legacyType, getDefaultHandler());
+        NetworkCapabilities nc = request.networkCapabilities;
+        sendRequestForNetwork(nc, networkCallback, timeoutMs, REQUEST, TYPE_NONE,
+                getDefaultHandler());
     }
 
     /**
@@ -3954,9 +3875,9 @@
     public void requestNetwork(@NonNull NetworkRequest request,
             @NonNull NetworkCallback networkCallback, @NonNull Handler handler, int timeoutMs) {
         checkTimeout(timeoutMs);
-        int legacyType = inferLegacyTypeForNetworkCapabilities(request.networkCapabilities);
         CallbackHandler cbHandler = new CallbackHandler(handler);
-        requestNetwork(request, networkCallback, timeoutMs, legacyType, cbHandler);
+        NetworkCapabilities nc = request.networkCapabilities;
+        sendRequestForNetwork(nc, networkCallback, timeoutMs, REQUEST, TYPE_NONE, cbHandler);
     }
 
     /**
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 732ceb5..2c356e4 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -167,7 +167,19 @@
         this(source, false /* parcelSensitiveFields */);
     }
 
-    private LinkProperties(@Nullable LinkProperties source, boolean parcelSensitiveFields) {
+    /**
+     * Create a copy of a {@link LinkProperties} that may preserve fields that were set
+     * based on the permissions of the process that originally received it.
+     *
+     * <p>By default {@link LinkProperties} does not preserve such fields during parceling, as
+     * they should not be shared outside of the process that receives them without appropriate
+     * checks.
+     * @param parcelSensitiveFields Whether the sensitive fields should be kept when parceling
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public LinkProperties(@Nullable LinkProperties source, boolean parcelSensitiveFields) {
         mParcelSensitiveFields = parcelSensitiveFields;
         if (source == null) return;
         mIfaceName = source.mIfaceName;
@@ -674,17 +686,29 @@
             route.getDestination(),
             route.getGateway(),
             mIfaceName,
-            route.getType());
+            route.getType(),
+            route.getMtu());
+    }
+
+    private int findRouteIndexByDestination(RouteInfo route) {
+        for (int i = 0; i < mRoutes.size(); i++) {
+            if (mRoutes.get(i).isSameDestinationAs(route)) {
+                return i;
+            }
+        }
+        return -1;
     }
 
     /**
-     * Adds a {@link RouteInfo} to this {@code LinkProperties}, if not present. If the
-     * {@link RouteInfo} had an interface name set and that differs from the interface set for this
-     * {@code LinkProperties} an {@link IllegalArgumentException} will be thrown.  The proper
+     * Adds a {@link RouteInfo} to this {@code LinkProperties}, if a {@link RouteInfo}
+     * with the same destination exists with different properties (e.g., different MTU),
+     * it will be updated. If the {@link RouteInfo} had an interface name set and
+     * that differs from the interface set for this {@code LinkProperties} an
+     * {@link IllegalArgumentException} will be thrown.  The proper
      * course is to add either un-named or properly named {@link RouteInfo}.
      *
      * @param route A {@link RouteInfo} to add to this object.
-     * @return {@code false} if the route was already present, {@code true} if it was added.
+     * @return {@code true} was added or updated, false otherwise.
      */
     public boolean addRoute(@NonNull RouteInfo route) {
         String routeIface = route.getInterface();
@@ -694,11 +718,20 @@
                             + " vs. " + mIfaceName);
         }
         route = routeWithInterface(route);
-        if (!mRoutes.contains(route)) {
+
+        int i = findRouteIndexByDestination(route);
+        if (i == -1) {
+            // Route was not present. Add it.
             mRoutes.add(route);
             return true;
+        } else if (mRoutes.get(i).equals(route)) {
+            // Route was present and has same properties. Do nothing.
+            return false;
+        } else {
+            // Route was present and has different properties. Update it.
+            mRoutes.set(i, route);
+            return true;
         }
-        return false;
     }
 
     /**
@@ -706,6 +739,7 @@
      * specify an interface and the interface must match the interface of this
      * {@code LinkProperties}, or it will not be removed.
      *
+     * @param route A {@link RouteInfo} specifying the route to remove.
      * @return {@code true} if the route was removed, {@code false} if it was not present.
      *
      * @hide
@@ -1561,22 +1595,6 @@
     }
 
     /**
-     * Create a copy of this {@link LinkProperties} that will preserve fields that were set
-     * based on the permissions of the process that received this {@link LinkProperties}.
-     *
-     * <p>By default {@link LinkProperties} does not preserve such fields during parceling, as
-     * they should not be shared outside of the process that receives them without appropriate
-     * checks.
-     * @hide
-     */
-    @SystemApi
-    @TestApi
-    @NonNull
-    public LinkProperties makeSensitiveFieldsParcelingCopy() {
-        return new LinkProperties(this, true /* parcelSensitiveFields */);
-    }
-
-    /**
      * Compares this {@code LinkProperties} instance against the target
      * LinkProperties in {@code obj}. Two LinkPropertieses are equal if
      * all their fields are equal in values.
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
index 6f5471b..f807a49 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -64,7 +64,7 @@
      * The unique id of the network.
      * @hide
      */
-    @SystemApi
+    @UnsupportedAppUsage
     public final int netId;
 
     // Objects used to perform per-network operations such as getSocketFactory
@@ -170,6 +170,17 @@
     }
 
     /**
+     * Get the unique id of the network.
+     *
+     * @hide
+     */
+    @TestApi
+    @SystemApi
+    public int getNetId() {
+        return netId;
+    }
+
+    /**
      * Returns a netid marked with the Private DNS bypass flag.
      *
      * This flag must be kept in sync with the NETID_USE_LOCAL_NAMESERVERS flag
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 116e343..fcfcebd 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -19,6 +19,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -32,14 +33,13 @@
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.BitUtils;
 import com.android.internal.util.Preconditions;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
+import java.util.Arrays;
 import java.util.Objects;
 import java.util.Set;
 import java.util.StringJoiner;
@@ -88,6 +88,7 @@
     /**
      * Completely clears the contents of this object, removing even the capabilities that are set
      * by default when the object is constructed.
+     * @hide
      */
     public void clearAll() {
         mNetworkCapabilities = mTransportTypes = mUnwantedNetworkCapabilities = 0;
@@ -96,7 +97,7 @@
         mTransportInfo = null;
         mSignalStrength = SIGNAL_STRENGTH_UNSPECIFIED;
         mUids = null;
-        mAdministratorUids.clear();
+        mAdministratorUids = new int[0];
         mOwnerUid = Process.INVALID_UID;
         mSSID = null;
         mPrivateDnsBroken = false;
@@ -415,11 +416,12 @@
 
     /**
      * Adds the given capability to this {@code NetworkCapability} instance.
-     * Multiple capabilities may be applied sequentially.  Note that when searching
-     * for a network to satisfy a request, all capabilities requested must be satisfied.
+     * Note that when searching for a network to satisfy a request, all capabilities
+     * requested must be satisfied.
      *
      * @param capability the capability to be added.
      * @return This NetworkCapabilities instance, to facilitate chaining.
+     * @hide
      */
     public @NonNull NetworkCapabilities addCapability(@NetCapability int capability) {
         // If the given capability was previously added to the list of unwanted capabilities
@@ -434,9 +436,9 @@
 
     /**
      * Adds the given capability to the list of unwanted capabilities of this
-     * {@code NetworkCapability} instance.  Multiple unwanted capabilities may be applied
-     * sequentially.  Note that when searching for a network to satisfy a request, the network
-     * must not contain any capability from unwanted capability list.
+     * {@code NetworkCapability} instance. Note that when searching for a network to
+     * satisfy a request, the network must not contain any capability from unwanted capability
+     * list.
      * <p>
      * If the capability was previously added to the list of required capabilities (for
      * example, it was there by default or added using {@link #addCapability(int)} method), then
@@ -456,6 +458,7 @@
      *
      * @param capability the capability to be removed.
      * @return This NetworkCapabilities instance, to facilitate chaining.
+     * @hide
      */
     public @NonNull NetworkCapabilities removeCapability(@NetCapability int capability) {
         // Note that this method removes capabilities that were added via addCapability(int),
@@ -470,7 +473,7 @@
     /**
      * Sets (or clears) the given capability on this {@link NetworkCapabilities}
      * instance.
-     *
+     * @hide
      */
     public @NonNull NetworkCapabilities setCapability(@NetCapability int capability,
             boolean value) {
@@ -730,7 +733,7 @@
 
     /**
      * Adds the given transport type to this {@code NetworkCapability} instance.
-     * Multiple transports may be applied sequentially.  Note that when searching
+     * Multiple transports may be applied.  Note that when searching
      * for a network to satisfy a request, any listed in the request will satisfy the request.
      * For example {@code TRANSPORT_WIFI} and {@code TRANSPORT_ETHERNET} added to a
      * {@code NetworkCapabilities} would cause either a Wi-Fi network or an Ethernet network
@@ -739,6 +742,7 @@
      *
      * @param transportType the transport type to be added.
      * @return This NetworkCapabilities instance, to facilitate chaining.
+     * @hide
      */
     public @NonNull NetworkCapabilities addTransportType(@Transport int transportType) {
         checkValidTransportType(transportType);
@@ -851,6 +855,7 @@
 
     /**
      * Set the UID of the owner app.
+     * @hide
      */
     public @NonNull NetworkCapabilities setOwnerUid(final int uid) {
         mOwnerUid = uid;
@@ -868,6 +873,8 @@
      *   <li>The user's location toggle is on
      * </ol>
      *
+     * Instances of NetworkCapabilities sent to apps without the appropriate permissions will
+     * have this field cleared out.
      */
     public int getOwnerUid() {
         return mOwnerUid;
@@ -884,10 +891,10 @@
      * empty unless the destination is 1) the System Server, or 2) Telephony. In either case, the
      * receiving entity must have the ACCESS_FINE_LOCATION permission and target R+.
      */
-    private final List<Integer> mAdministratorUids = new ArrayList<>();
+    private int[] mAdministratorUids = new int[0];
 
     /**
-     * Sets the list of UIDs that are administrators of this network.
+     * Sets the int[] of UIDs that are administrators of this network.
      *
      * <p>UIDs included in administratorUids gain administrator privileges over this Network.
      * Examples of UIDs that should be included in administratorUids are:
@@ -906,24 +913,22 @@
      * @hide
      */
     @NonNull
-    @SystemApi
-    public NetworkCapabilities setAdministratorUids(
-            @NonNull final List<Integer> administratorUids) {
-        mAdministratorUids.clear();
-        mAdministratorUids.addAll(administratorUids);
+    public NetworkCapabilities setAdministratorUids(@NonNull final int[] administratorUids) {
+        mAdministratorUids = Arrays.copyOf(administratorUids, administratorUids.length);
         return this;
     }
 
     /**
-     * Retrieves the list of UIDs that are administrators of this Network.
+     * Retrieves the UIDs that are administrators of this Network.
      *
-     * @return the List of UIDs that are administrators of this Network
+     * @return the int[] of UIDs that are administrators of this Network
      * @hide
      */
     @NonNull
     @SystemApi
-    public List<Integer> getAdministratorUids() {
-        return Collections.unmodifiableList(mAdministratorUids);
+    @TestApi
+    public int[] getAdministratorUids() {
+        return Arrays.copyOf(mAdministratorUids, mAdministratorUids.length);
     }
 
     /**
@@ -944,15 +949,10 @@
      * Sets the upstream bandwidth for this network in Kbps.  This always only refers to
      * the estimated first hop transport bandwidth.
      * <p>
-     * Note that when used to request a network, this specifies the minimum acceptable.
-     * When received as the state of an existing network this specifies the typical
-     * first hop bandwidth expected.  This is never measured, but rather is inferred
-     * from technology type and other link parameters.  It could be used to differentiate
-     * between very slow 1xRTT cellular links and other faster networks or even between
-     * 802.11b vs 802.11AC wifi technologies.  It should not be used to differentiate between
-     * fast backhauls and slow backhauls.
+     * {@see Builder#setLinkUpstreamBandwidthKbps}
      *
      * @param upKbps the estimated first hop upstream (device to network) bandwidth.
+     * @hide
      */
     public @NonNull NetworkCapabilities setLinkUpstreamBandwidthKbps(int upKbps) {
         mLinkUpBandwidthKbps = upKbps;
@@ -973,15 +973,10 @@
      * Sets the downstream bandwidth for this network in Kbps.  This always only refers to
      * the estimated first hop transport bandwidth.
      * <p>
-     * Note that when used to request a network, this specifies the minimum acceptable.
-     * When received as the state of an existing network this specifies the typical
-     * first hop bandwidth expected.  This is never measured, but rather is inferred
-     * from technology type and other link parameters.  It could be used to differentiate
-     * between very slow 1xRTT cellular links and other faster networks or even between
-     * 802.11b vs 802.11AC wifi technologies.  It should not be used to differentiate between
-     * fast backhauls and slow backhauls.
+     * {@see Builder#setLinkUpstreamBandwidthKbps}
      *
      * @param downKbps the estimated first hop downstream (network to device) bandwidth.
+     * @hide
      */
     public @NonNull NetworkCapabilities setLinkDownstreamBandwidthKbps(int downKbps) {
         mLinkDownBandwidthKbps = downKbps;
@@ -1040,6 +1035,7 @@
      * @param networkSpecifier A concrete, parcelable framework class that extends
      *                         NetworkSpecifier.
      * @return This NetworkCapabilities instance, to facilitate chaining.
+     * @hide
      */
     public @NonNull NetworkCapabilities setNetworkSpecifier(
             @NonNull NetworkSpecifier networkSpecifier) {
@@ -1061,7 +1057,6 @@
      * @return This NetworkCapabilities instance, to facilitate chaining.
      * @hide
      */
-    @SystemApi
     public @NonNull NetworkCapabilities setTransportInfo(@NonNull TransportInfo transportInfo) {
         mTransportInfo = transportInfo;
         return this;
@@ -1071,7 +1066,7 @@
      * Gets the optional bearer specific network specifier. May be {@code null} if not set.
      *
      * @return The optional {@link NetworkSpecifier} specifying the bearer specific network
-     *         specifier or {@code null}. See {@link #setNetworkSpecifier}.
+     *         specifier or {@code null}.
      */
     public @Nullable NetworkSpecifier getNetworkSpecifier() {
         return mNetworkSpecifier;
@@ -1141,6 +1136,7 @@
      * effect when requesting a callback.
      *
      * @param signalStrength the bearer-specific signal strength.
+     * @hide
      */
     public @NonNull NetworkCapabilities setSignalStrength(int signalStrength) {
         mSignalStrength = signalStrength;
@@ -1367,7 +1363,6 @@
      * Sets the SSID of this network.
      * @hide
      */
-    @SystemApi
     public @NonNull NetworkCapabilities setSSID(@Nullable String ssid) {
         mSSID = ssid;
         return this;
@@ -1378,7 +1373,8 @@
      * @hide
      */
     @SystemApi
-    public @Nullable String getSSID() {
+    @TestApi
+    public @Nullable String getSsid() {
         return mSSID;
     }
 
@@ -1584,7 +1580,7 @@
         dest.writeArraySet(mUids);
         dest.writeString(mSSID);
         dest.writeBoolean(mPrivateDnsBroken);
-        dest.writeList(mAdministratorUids);
+        dest.writeIntArray(mAdministratorUids);
         dest.writeInt(mOwnerUid);
         dest.writeInt(mRequestorUid);
         dest.writeString(mRequestorPackageName);
@@ -1608,7 +1604,7 @@
                         null /* ClassLoader, null for default */);
                 netCap.mSSID = in.readString();
                 netCap.mPrivateDnsBroken = in.readBoolean();
-                netCap.setAdministratorUids(in.readArrayList(null));
+                netCap.setAdministratorUids(in.createIntArray());
                 netCap.mOwnerUid = in.readInt();
                 netCap.mRequestorUid = in.readInt();
                 netCap.mRequestorPackageName = in.readString();
@@ -1665,8 +1661,8 @@
             sb.append(" OwnerUid: ").append(mOwnerUid);
         }
 
-        if (!mAdministratorUids.isEmpty()) {
-            sb.append(" AdministratorUids: ").append(mAdministratorUids);
+        if (mAdministratorUids.length == 0) {
+            sb.append(" AdministratorUids: ").append(Arrays.toString(mAdministratorUids));
         }
 
         if (null != mSSID) {
@@ -1858,25 +1854,32 @@
     }
 
     /**
-     * Set the uid of the app making the request.
+     * Set the UID of the app making the request.
      *
-     * Note: This works only for {@link NetworkAgent} instances. Any capabilities passed in
-     * via the public {@link ConnectivityManager} API's will have this field overwritten.
+     * For instances of NetworkCapabilities representing a request, sets the
+     * UID of the app making the request. For a network created by the system,
+     * sets the UID of the only app whose requests can match this network.
+     * This can be set to {@link Process#INVALID_UID} if there is no such app,
+     * or if this instance of NetworkCapabilities is about to be sent to a
+     * party that should not learn about this.
      *
      * @param uid UID of the app.
      * @hide
      */
-    @SystemApi
     public @NonNull NetworkCapabilities setRequestorUid(int uid) {
         mRequestorUid = uid;
         return this;
     }
 
     /**
-     * @return the uid of the app making the request.
+     * Returns the UID of the app making the request.
      *
-     * Note: This could return {@link Process#INVALID_UID} if the {@link NetworkRequest}
-     * object was not obtained from {@link ConnectivityManager}.
+     * For a NetworkRequest being made by an app, contains the app's UID. For a network
+     * created by the system, contains the UID of the only app whose requests can match
+     * this network, or {@link Process#INVALID_UID} if none or if the
+     * caller does not have permission to learn about this.
+     *
+     * @return the uid of the app making the request.
      * @hide
      */
     public int getRequestorUid() {
@@ -1886,23 +1889,29 @@
     /**
      * Set the package name of the app making the request.
      *
-     * Note: This works only for {@link NetworkAgent} instances. Any capabilities passed in
-     * via the public {@link ConnectivityManager} API's will have this field overwritten.
+     * For instances of NetworkCapabilities representing a request, sets the
+     * package name of the app making the request. For a network created by the system,
+     * sets the package name of the only app whose requests can match this network.
+     * This can be set to null if there is no such app, or if this instance of
+     * NetworkCapabilities is about to be sent to a party that should not learn about this.
      *
      * @param packageName package name of the app.
      * @hide
      */
-    @SystemApi
     public @NonNull NetworkCapabilities setRequestorPackageName(@NonNull String packageName) {
         mRequestorPackageName = packageName;
         return this;
     }
 
     /**
-     * @return the package name of the app making the request.
+     * Returns the package name of the app making the request.
      *
-     * Note: This could return {@code null} if the {@link NetworkRequest} object was not obtained
-     * from {@link ConnectivityManager}.
+     * For a NetworkRequest being made by an app, contains the app's package name. For a
+     * network created by the system, contains the package name of the only app whose
+     * requests can match this network, or null if none or if the caller does not have
+     * permission to learn about this.
+     *
+     * @return the package name of the app making the request.
      * @hide
      */
     @Nullable
@@ -1911,9 +1920,9 @@
     }
 
     /**
-     * Set the uid and package name of the app making the request.
+     * Set the uid and package name of the app causing this network to exist.
      *
-     * Note: This is intended to be only invoked from within connectivitiy service.
+     * {@see #setRequestorUid} and {@link #setRequestorPackageName}
      *
      * @param uid UID of the app.
      * @param packageName package name of the app.
@@ -1972,4 +1981,316 @@
         return mRequestorUid == nc.mRequestorUid
                 && TextUtils.equals(mRequestorPackageName, nc.mRequestorPackageName);
     }
+
+    /**
+     * Builder class for NetworkCapabilities.
+     *
+     * This class is mainly for for {@link NetworkAgent} instances to use. Many fields in
+     * the built class require holding a signature permission to use - mostly
+     * {@link android.Manifest.permission.NETWORK_FACTORY}, but refer to the specific
+     * description of each setter. As this class lives entirely in app space it does not
+     * enforce these restrictions itself but the system server clears out the relevant
+     * fields when receiving a NetworkCapabilities object from a caller without the
+     * appropriate permission.
+     *
+     * Apps don't use this builder directly. Instead, they use {@link NetworkRequest} via
+     * its builder object.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public static class Builder {
+        private final NetworkCapabilities mCaps;
+
+        /**
+         * Creates a new Builder to construct NetworkCapabilities objects.
+         */
+        public Builder() {
+            mCaps = new NetworkCapabilities();
+        }
+
+        /**
+         * Creates a new Builder of NetworkCapabilities from an existing instance.
+         */
+        public Builder(@NonNull final NetworkCapabilities nc) {
+            Objects.requireNonNull(nc);
+            mCaps = new NetworkCapabilities(nc);
+        }
+
+        /**
+         * Adds the given transport type.
+         *
+         * Multiple transports may be added. Note that when searching for a network to satisfy a
+         * request, satisfying any of the transports listed in the request will satisfy the request.
+         * For example {@code TRANSPORT_WIFI} and {@code TRANSPORT_ETHERNET} added to a
+         * {@code NetworkCapabilities} would cause either a Wi-Fi network or an Ethernet network
+         * to be selected. This is logically different than
+         * {@code NetworkCapabilities.NET_CAPABILITY_*}.
+         *
+         * @param transportType the transport type to be added or removed.
+         * @return this builder
+         */
+        @NonNull
+        public Builder addTransportType(@Transport int transportType) {
+            checkValidTransportType(transportType);
+            mCaps.addTransportType(transportType);
+            return this;
+        }
+
+        /**
+         * Removes the given transport type.
+         *
+         * {@see #addTransportType}.
+         *
+         * @param transportType the transport type to be added or removed.
+         * @return this builder
+         */
+        @NonNull
+        public Builder removeTransportType(@Transport int transportType) {
+            checkValidTransportType(transportType);
+            mCaps.removeTransportType(transportType);
+            return this;
+        }
+
+        /**
+         * Adds the given capability.
+         *
+         * @param capability the capability
+         * @return this builder
+         */
+        @NonNull
+        public Builder addCapability(@NetCapability final int capability) {
+            mCaps.setCapability(capability, true);
+            return this;
+        }
+
+        /**
+         * Removes the given capability.
+         *
+         * @param capability the capability
+         * @return this builder
+         */
+        @NonNull
+        public Builder removeCapability(@NetCapability final int capability) {
+            mCaps.setCapability(capability, false);
+            return this;
+        }
+
+        /**
+         * Sets the owner UID.
+         *
+         * The default value is {@link Process#INVALID_UID}. Pass this value to reset.
+         *
+         * Note: for security the system will clear out this field when received from a
+         * non-privileged source.
+         *
+         * @param ownerUid the owner UID
+         * @return this builder
+         */
+        @NonNull
+        @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+        public Builder setOwnerUid(final int ownerUid) {
+            mCaps.setOwnerUid(ownerUid);
+            return this;
+        }
+
+        /**
+         * Sets the list of UIDs that are administrators of this network.
+         *
+         * <p>UIDs included in administratorUids gain administrator privileges over this
+         * Network. Examples of UIDs that should be included in administratorUids are:
+         * <ul>
+         *     <li>Carrier apps with privileges for the relevant subscription
+         *     <li>Active VPN apps
+         *     <li>Other application groups with a particular Network-related role
+         * </ul>
+         *
+         * <p>In general, user-supplied networks (such as WiFi networks) do not have
+         * administrators.
+         *
+         * <p>An app is granted owner privileges over Networks that it supplies. The owner
+         * UID MUST always be included in administratorUids.
+         *
+         * The default value is the empty array. Pass an empty array to reset.
+         *
+         * Note: for security the system will clear out this field when received from a
+         * non-privileged source, such as an app using reflection to call this or
+         * mutate the member in the built object.
+         *
+         * @param administratorUids the UIDs to be set as administrators of this Network.
+         * @return this builder
+         */
+        @NonNull
+        @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+        public Builder setAdministratorUids(@NonNull final int[] administratorUids) {
+            Objects.requireNonNull(administratorUids);
+            mCaps.setAdministratorUids(administratorUids);
+            return this;
+        }
+
+        /**
+         * Sets the upstream bandwidth of the link.
+         *
+         * Sets the upstream bandwidth for this network in Kbps. This always only refers to
+         * the estimated first hop transport bandwidth.
+         * <p>
+         * Note that when used to request a network, this specifies the minimum acceptable.
+         * When received as the state of an existing network this specifies the typical
+         * first hop bandwidth expected. This is never measured, but rather is inferred
+         * from technology type and other link parameters. It could be used to differentiate
+         * between very slow 1xRTT cellular links and other faster networks or even between
+         * 802.11b vs 802.11AC wifi technologies. It should not be used to differentiate between
+         * fast backhauls and slow backhauls.
+         *
+         * @param upKbps the estimated first hop upstream (device to network) bandwidth.
+         * @return this builder
+         */
+        @NonNull
+        public Builder setLinkUpstreamBandwidthKbps(final int upKbps) {
+            mCaps.setLinkUpstreamBandwidthKbps(upKbps);
+            return this;
+        }
+
+        /**
+         * Sets the downstream bandwidth for this network in Kbps. This always only refers to
+         * the estimated first hop transport bandwidth.
+         * <p>
+         * Note that when used to request a network, this specifies the minimum acceptable.
+         * When received as the state of an existing network this specifies the typical
+         * first hop bandwidth expected. This is never measured, but rather is inferred
+         * from technology type and other link parameters. It could be used to differentiate
+         * between very slow 1xRTT cellular links and other faster networks or even between
+         * 802.11b vs 802.11AC wifi technologies. It should not be used to differentiate between
+         * fast backhauls and slow backhauls.
+         *
+         * @param downKbps the estimated first hop downstream (network to device) bandwidth.
+         * @return this builder
+         */
+        @NonNull
+        public Builder setLinkDownstreamBandwidthKbps(final int downKbps) {
+            mCaps.setLinkDownstreamBandwidthKbps(downKbps);
+            return this;
+        }
+
+        /**
+         * Sets the optional bearer specific network specifier.
+         * This has no meaning if a single transport is also not specified, so calling
+         * this without a single transport set will generate an exception, as will
+         * subsequently adding or removing transports after this is set.
+         * </p>
+         *
+         * @param specifier a concrete, parcelable framework class that extends NetworkSpecifier,
+         *        or null to clear it.
+         * @return this builder
+         */
+        @NonNull
+        public Builder setNetworkSpecifier(@Nullable final NetworkSpecifier specifier) {
+            mCaps.setNetworkSpecifier(specifier);
+            return this;
+        }
+
+        /**
+         * Sets the optional transport specific information.
+         *
+         * @param info A concrete, parcelable framework class that extends {@link TransportInfo},
+         *             or null to clear it.
+         * @return this builder
+         */
+        @NonNull
+        public Builder setTransportInfo(@Nullable final TransportInfo info) {
+            mCaps.setTransportInfo(info);
+            return this;
+        }
+
+        /**
+         * Sets the signal strength. This is a signed integer, with higher values indicating a
+         * stronger signal. The exact units are bearer-dependent. For example, Wi-Fi uses the
+         * same RSSI units reported by wifi code.
+         * <p>
+         * Note that when used to register a network callback, this specifies the minimum
+         * acceptable signal strength. When received as the state of an existing network it
+         * specifies the current value. A value of code SIGNAL_STRENGTH_UNSPECIFIED} means
+         * no value when received and has no effect when requesting a callback.
+         *
+         * Note: for security the system will throw if it receives a NetworkRequest where
+         * the underlying NetworkCapabilities has this member set from a source that does
+         * not hold the {@link android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP}
+         * permission. Apps with this permission can use this indirectly through
+         * {@link android.net.NetworkRequest}.
+         *
+         * @param signalStrength the bearer-specific signal strength.
+         * @return this builder
+         */
+        @NonNull
+        @RequiresPermission(android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP)
+        public Builder setSignalStrength(final int signalStrength) {
+            mCaps.setSignalStrength(signalStrength);
+            return this;
+        }
+
+        /**
+         * Sets the SSID of this network.
+         *
+         * Note: for security the system will clear out this field when received from a
+         * non-privileged source, like an app using reflection to set this.
+         *
+         * @param ssid the SSID, or null to clear it.
+         * @return this builder
+         */
+        @NonNull
+        @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+        public Builder setSsid(@Nullable final String ssid) {
+            mCaps.setSSID(ssid);
+            return this;
+        }
+
+        /**
+         * Set the uid of the app causing this network to exist.
+         *
+         * Note: for security the system will clear out this field when received from a
+         * non-privileged source.
+         *
+         * @param uid UID of the app.
+         * @return this builder
+         */
+        @NonNull
+        @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+        public Builder setRequestorUid(final int uid) {
+            mCaps.setRequestorUid(uid);
+            return this;
+        }
+
+        /**
+         * Set the package name of the app causing this network to exist.
+         *
+         * Note: for security the system will clear out this field when received from a
+         * non-privileged source.
+         *
+         * @param packageName package name of the app, or null to clear it.
+         * @return this builder
+         */
+        @NonNull
+        @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+        public Builder setRequestorPackageName(@Nullable final String packageName) {
+            mCaps.setRequestorPackageName(packageName);
+            return this;
+        }
+
+        /**
+         * Builds the instance of the capabilities.
+         *
+         * @return the built instance of NetworkCapabilities.
+         */
+        @NonNull
+        public NetworkCapabilities build() {
+            if (mCaps.getOwnerUid() != Process.INVALID_UID) {
+                if (!ArrayUtils.contains(mCaps.getAdministratorUids(), mCaps.getOwnerUid())) {
+                    throw new IllegalStateException("The owner UID must be included in "
+                            + " administrator UIDs.");
+                }
+            }
+            return new NetworkCapabilities(mCaps);
+        }
+    }
 }
diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java
index 2b9e9fe..fec2df4 100644
--- a/core/java/android/net/RouteInfo.java
+++ b/core/java/android/net/RouteInfo.java
@@ -527,6 +527,26 @@
     }
 
     /**
+     * Compares this RouteInfo object against the specified object and indicates if the
+     * destinations of both routes are equal.
+     * @return {@code true} if the route destinations are equal, {@code false} otherwise.
+     *
+     * @hide
+     */
+    public boolean isSameDestinationAs(@Nullable Object obj) {
+        if (this == obj) return true;
+
+        if (!(obj instanceof RouteInfo)) return false;
+
+        RouteInfo target = (RouteInfo) obj;
+
+        if (Objects.equals(mDestination, target.getDestination())) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
      *  Returns a hashcode for this <code>RouteInfo</code> object.
      */
     public int hashCode() {
diff --git a/core/java/android/net/TestNetworkManager.java b/core/java/android/net/TestNetworkManager.java
index c3284df..a0a563b 100644
--- a/core/java/android/net/TestNetworkManager.java
+++ b/core/java/android/net/TestNetworkManager.java
@@ -30,6 +30,18 @@
  */
 @TestApi
 public class TestNetworkManager {
+    /**
+     * Prefix for tun interfaces created by this class.
+     * @hide
+     */
+    public static final String TEST_TUN_PREFIX = "testtun";
+
+    /**
+     * Prefix for tap interfaces created by this class.
+     * @hide
+     */
+    public static final String TEST_TAP_PREFIX = "testtap";
+
     @NonNull private static final String TAG = TestNetworkManager.class.getSimpleName();
 
     @NonNull private final ITestNetworkManager mService;
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index deae459..4d504e7 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -110,6 +110,7 @@
 import android.net.PrivateDnsConfigParcel;
 import android.net.ProxyInfo;
 import android.net.RouteInfo;
+import android.net.RouteInfoParcel;
 import android.net.SocketKeepalive;
 import android.net.TetheringManager;
 import android.net.UidRange;
@@ -120,6 +121,7 @@
 import android.net.metrics.NetworkEvent;
 import android.net.netlink.InetDiagMessage;
 import android.net.shared.PrivateDnsConfig;
+import android.net.util.LinkPropertiesUtils.CompareOrUpdateResult;
 import android.net.util.LinkPropertiesUtils.CompareResult;
 import android.net.util.MultinetworkPolicyTracker;
 import android.net.util.NetdService;
@@ -232,6 +234,7 @@
 import java.util.StringJoiner;
 import java.util.TreeSet;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Function;
 
 /**
  * @hide
@@ -1666,7 +1669,7 @@
         if (newNc.getNetworkSpecifier() != null) {
             newNc.setNetworkSpecifier(newNc.getNetworkSpecifier().redact());
         }
-        newNc.setAdministratorUids(Collections.EMPTY_LIST);
+        newNc.setAdministratorUids(new int[0]);
 
         return newNc;
     }
@@ -1710,7 +1713,7 @@
         }
 
         if (checkSettingsPermission(callerPid, callerUid)) {
-            return lp.makeSensitiveFieldsParcelingCopy();
+            return new LinkProperties(lp, true /* parcelSensitiveFields */);
         }
 
         final LinkProperties newLp = new LinkProperties(lp);
@@ -1727,7 +1730,7 @@
             nc.setSingleUid(callerUid);
         }
         nc.setRequestorUidAndPackageName(callerUid, callerPackageName);
-        nc.setAdministratorUids(Collections.EMPTY_LIST);
+        nc.setAdministratorUids(new int[0]);
 
         // Clear owner UID; this can never come from an app.
         nc.setOwnerUid(INVALID_UID);
@@ -2237,14 +2240,9 @@
             if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) {
                 final NetworkInfo ni = intent.getParcelableExtra(
                         ConnectivityManager.EXTRA_NETWORK_INFO);
-                if (ni.getType() == ConnectivityManager.TYPE_MOBILE_SUPL) {
-                    intent.setAction(ConnectivityManager.CONNECTIVITY_ACTION_SUPL);
-                    intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
-                } else {
-                    BroadcastOptions opts = BroadcastOptions.makeBasic();
-                    opts.setMaxManifestReceiverApiLevel(Build.VERSION_CODES.M);
-                    options = opts.toBundle();
-                }
+                final BroadcastOptions opts = BroadcastOptions.makeBasic();
+                opts.setMaxManifestReceiverApiLevel(Build.VERSION_CODES.M);
+                options = opts.toBundle();
                 final IBatteryStats bs = mDeps.getBatteryStatsService();
                 try {
                     bs.noteConnectivityChanged(intent.getIntExtra(
@@ -5333,7 +5331,7 @@
     // specific SSID/SignalStrength, or the calling app has permission to do so.
     private void ensureSufficientPermissionsForRequest(NetworkCapabilities nc,
             int callerPid, int callerUid, String callerPackageName) {
-        if (null != nc.getSSID() && !checkSettingsPermission(callerPid, callerUid)) {
+        if (null != nc.getSsid() && !checkSettingsPermission(callerPid, callerUid)) {
             throw new SecurityException("Insufficient permissions to request a specific SSID");
         }
 
@@ -5402,6 +5400,9 @@
     public NetworkRequest requestNetwork(NetworkCapabilities networkCapabilities,
             Messenger messenger, int timeoutMs, IBinder binder, int legacyType,
             @NonNull String callingPackageName) {
+        if (legacyType != TYPE_NONE && !checkNetworkStackPermission()) {
+            throw new SecurityException("Insufficient permissions to specify legacy type");
+        }
         final int callingUid = Binder.getCallingUid();
         final NetworkRequest.Type type = (networkCapabilities == null)
                 ? NetworkRequest.Type.TRACK_DEFAULT
@@ -5793,7 +5794,7 @@
         nai.getAndSetNetworkCapabilities(mixInCapabilities(nai, nc));
         final String extraInfo = networkInfo.getExtraInfo();
         final String name = TextUtils.isEmpty(extraInfo)
-                ? nai.networkCapabilities.getSSID() : extraInfo;
+                ? nai.networkCapabilities.getSsid() : extraInfo;
         if (DBG) log("registerNetworkAgent " + nai);
         final long token = Binder.clearCallingIdentity();
         try {
@@ -5951,15 +5952,49 @@
         }
     }
 
+    // TODO: move to frameworks/libs/net.
+    private RouteInfoParcel convertRouteInfo(RouteInfo route) {
+        final String nextHop;
+
+        switch (route.getType()) {
+            case RouteInfo.RTN_UNICAST:
+                if (route.hasGateway()) {
+                    nextHop = route.getGateway().getHostAddress();
+                } else {
+                    nextHop = INetd.NEXTHOP_NONE;
+                }
+                break;
+            case RouteInfo.RTN_UNREACHABLE:
+                nextHop = INetd.NEXTHOP_UNREACHABLE;
+                break;
+            case RouteInfo.RTN_THROW:
+                nextHop = INetd.NEXTHOP_THROW;
+                break;
+            default:
+                nextHop = INetd.NEXTHOP_NONE;
+                break;
+        }
+
+        final RouteInfoParcel rip = new RouteInfoParcel();
+        rip.ifName = route.getInterface();
+        rip.destination = route.getDestination().toString();
+        rip.nextHop = nextHop;
+        rip.mtu = route.getMtu();
+
+        return rip;
+    }
+
     /**
      * Have netd update routes from oldLp to newLp.
      * @return true if routes changed between oldLp and newLp
      */
     private boolean updateRoutes(LinkProperties newLp, LinkProperties oldLp, int netId) {
-        // Compare the route diff to determine which routes should be added and removed.
-        CompareResult<RouteInfo> routeDiff = new CompareResult<>(
+        Function<RouteInfo, IpPrefix> getDestination = (r) -> r.getDestination();
+        // compare the route diff to determine which routes have been updated
+        CompareOrUpdateResult<IpPrefix, RouteInfo> routeDiff = new CompareOrUpdateResult<>(
                 oldLp != null ? oldLp.getAllRoutes() : null,
-                newLp != null ? newLp.getAllRoutes() : null);
+                newLp != null ? newLp.getAllRoutes() : null,
+                getDestination);
 
         // add routes before removing old in case it helps with continuous connectivity
 
@@ -5968,10 +6003,10 @@
             if (route.hasGateway()) continue;
             if (VDBG || DDBG) log("Adding Route [" + route + "] to network " + netId);
             try {
-                mNMS.addRoute(netId, route);
+                mNetd.networkAddRouteParcel(netId, convertRouteInfo(route));
             } catch (Exception e) {
                 if ((route.getDestination().getAddress() instanceof Inet4Address) || VDBG) {
-                    loge("Exception in addRoute for non-gateway: " + e);
+                    loge("Exception in networkAddRouteParcel for non-gateway: " + e);
                 }
             }
         }
@@ -5979,10 +6014,10 @@
             if (!route.hasGateway()) continue;
             if (VDBG || DDBG) log("Adding Route [" + route + "] to network " + netId);
             try {
-                mNMS.addRoute(netId, route);
+                mNetd.networkAddRouteParcel(netId, convertRouteInfo(route));
             } catch (Exception e) {
                 if ((route.getGateway() instanceof Inet4Address) || VDBG) {
-                    loge("Exception in addRoute for gateway: " + e);
+                    loge("Exception in networkAddRouteParcel for gateway: " + e);
                 }
             }
         }
@@ -5990,12 +6025,22 @@
         for (RouteInfo route : routeDiff.removed) {
             if (VDBG || DDBG) log("Removing Route [" + route + "] from network " + netId);
             try {
-                mNMS.removeRoute(netId, route);
+                mNetd.networkRemoveRouteParcel(netId, convertRouteInfo(route));
             } catch (Exception e) {
-                loge("Exception in removeRoute: " + e);
+                loge("Exception in networkRemoveRouteParcel: " + e);
             }
         }
-        return !routeDiff.added.isEmpty() || !routeDiff.removed.isEmpty();
+
+        for (RouteInfo route : routeDiff.updated) {
+            if (VDBG || DDBG) log("Updating Route [" + route + "] from network " + netId);
+            try {
+                mNetd.networkUpdateRouteParcel(netId, convertRouteInfo(route));
+            } catch (Exception e) {
+                loge("Exception in networkUpdateRouteParcel: " + e);
+            }
+        }
+        return !routeDiff.added.isEmpty() || !routeDiff.removed.isEmpty()
+                || !routeDiff.updated.isEmpty();
     }
 
     private void updateDnses(LinkProperties newLp, LinkProperties oldLp, int netId) {
@@ -7864,7 +7909,7 @@
 
     private void clearNetworkCapabilitiesUids(@NonNull NetworkCapabilities nc) {
         nc.setUids(null);
-        nc.setAdministratorUids(Collections.EMPTY_LIST);
+        nc.setAdministratorUids(new int[0]);
         nc.setOwnerUid(Process.INVALID_UID);
     }
 
@@ -7911,8 +7956,9 @@
         }
 
         // Administrator UIDs also contains the Owner UID
-        if (nai.networkCapabilities.getAdministratorUids().contains(callbackUid)) {
-            return true;
+        final int[] administratorUids = nai.networkCapabilities.getAdministratorUids();
+        for (final int uid : administratorUids) {
+            if (uid == callbackUid) return true;
         }
 
         return false;
diff --git a/services/core/java/com/android/server/TestNetworkService.java b/services/core/java/com/android/server/TestNetworkService.java
index f772a4a..0ea7346 100644
--- a/services/core/java/com/android/server/TestNetworkService.java
+++ b/services/core/java/com/android/server/TestNetworkService.java
@@ -16,6 +16,9 @@
 
 package com.android.server;
 
+import static android.net.TestNetworkManager.TEST_TAP_PREFIX;
+import static android.net.TestNetworkManager.TEST_TUN_PREFIX;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
@@ -53,7 +56,6 @@
 import java.net.NetworkInterface;
 import java.net.SocketException;
 import java.util.ArrayList;
-import java.util.List;
 import java.util.Objects;
 import java.util.concurrent.atomic.AtomicInteger;
 
@@ -61,8 +63,6 @@
 class TestNetworkService extends ITestNetworkManager.Stub {
     @NonNull private static final String TAG = TestNetworkService.class.getSimpleName();
     @NonNull private static final String TEST_NETWORK_TYPE = "TEST_NETWORK";
-    @NonNull private static final String TEST_TUN_PREFIX = "testtun";
-    @NonNull private static final String TEST_TAP_PREFIX = "testtap";
     @NonNull private static final AtomicInteger sTestTunIndex = new AtomicInteger();
 
     @NonNull private final Context mContext;
@@ -250,7 +250,7 @@
         nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED);
         nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
         nc.setNetworkSpecifier(new StringNetworkSpecifier(iface));
-        nc.setAdministratorUids(intArrayToList(administratorUids));
+        nc.setAdministratorUids(administratorUids);
         if (!isMetered) {
             nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
         }
@@ -293,14 +293,6 @@
         return new TestNetworkAgent(looper, context, ni, nc, lp, callingUid, binder);
     }
 
-    private List<Integer> intArrayToList(@NonNull int[] array) {
-        final List<Integer> list = new ArrayList<>(array.length);
-        for (final int i : array) {
-            list.add(i);
-        }
-        return list;
-    }
-
     /**
      * Sets up a Network with extremely limited privileges, guarded by the MANAGE_TEST_NETWORKS
      * permission.
diff --git a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
index 0925de8..34b0aa2 100644
--- a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
+++ b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
@@ -150,7 +150,7 @@
         if (nai != null) {
             transportType = approximateTransportType(nai);
             final String extraInfo = nai.networkInfo.getExtraInfo();
-            name = TextUtils.isEmpty(extraInfo) ? nai.networkCapabilities.getSSID() : extraInfo;
+            name = TextUtils.isEmpty(extraInfo) ? nai.networkCapabilities.getSsid() : extraInfo;
             // Only notify for Internet-capable networks.
             if (!nai.networkCapabilities.hasCapability(NET_CAPABILITY_INTERNET)) return;
         } else {
@@ -183,14 +183,14 @@
         int icon = getIcon(transportType);
         if (notifyType == NotificationType.NO_INTERNET && transportType == TRANSPORT_WIFI) {
             title = r.getString(R.string.wifi_no_internet,
-                    WifiInfo.sanitizeSsid(nai.networkCapabilities.getSSID()));
+                    WifiInfo.sanitizeSsid(nai.networkCapabilities.getSsid()));
             details = r.getString(R.string.wifi_no_internet_detailed);
         } else if (notifyType == NotificationType.PRIVATE_DNS_BROKEN) {
             if (transportType == TRANSPORT_CELLULAR) {
                 title = r.getString(R.string.mobile_no_internet);
             } else if (transportType == TRANSPORT_WIFI) {
                 title = r.getString(R.string.wifi_no_internet,
-                        WifiInfo.sanitizeSsid(nai.networkCapabilities.getSSID()));
+                        WifiInfo.sanitizeSsid(nai.networkCapabilities.getSsid()));
             } else {
                 title = r.getString(R.string.other_networks_no_internet);
             }
@@ -198,19 +198,19 @@
         } else if (notifyType == NotificationType.PARTIAL_CONNECTIVITY
                 && transportType == TRANSPORT_WIFI) {
             title = r.getString(R.string.network_partial_connectivity,
-                    WifiInfo.sanitizeSsid(nai.networkCapabilities.getSSID()));
+                    WifiInfo.sanitizeSsid(nai.networkCapabilities.getSsid()));
             details = r.getString(R.string.network_partial_connectivity_detailed);
         } else if (notifyType == NotificationType.LOST_INTERNET &&
                 transportType == TRANSPORT_WIFI) {
             title = r.getString(R.string.wifi_no_internet,
-                    WifiInfo.sanitizeSsid(nai.networkCapabilities.getSSID()));
+                    WifiInfo.sanitizeSsid(nai.networkCapabilities.getSsid()));
             details = r.getString(R.string.wifi_no_internet_detailed);
         } else if (notifyType == NotificationType.SIGN_IN) {
             switch (transportType) {
                 case TRANSPORT_WIFI:
                     title = r.getString(R.string.wifi_available_sign_in, 0);
                     details = r.getString(R.string.network_available_sign_in_detailed,
-                            WifiInfo.sanitizeSsid(nai.networkCapabilities.getSSID()));
+                            WifiInfo.sanitizeSsid(nai.networkCapabilities.getSsid()));
                     break;
                 case TRANSPORT_CELLULAR:
                     title = r.getString(R.string.network_available_sign_in, 0);
diff --git a/tests/net/common/Android.bp b/tests/net/common/Android.bp
index e44d460..46d680f 100644
--- a/tests/net/common/Android.bp
+++ b/tests/net/common/Android.bp
@@ -20,6 +20,7 @@
     name: "FrameworksNetCommonTests",
     srcs: ["java/**/*.java", "java/**/*.kt"],
     static_libs: [
+        "androidx.core_core",
         "androidx.test.rules",
         "junit",
         "mockito-target-minus-junit4",
diff --git a/tests/net/common/java/android/net/LinkPropertiesTest.java b/tests/net/common/java/android/net/LinkPropertiesTest.java
index 48b65e5..2b5720a 100644
--- a/tests/net/common/java/android/net/LinkPropertiesTest.java
+++ b/tests/net/common/java/android/net/LinkPropertiesTest.java
@@ -29,12 +29,19 @@
 
 import android.net.LinkProperties.ProvisioningChange;
 import android.net.util.LinkPropertiesUtils.CompareResult;
+import android.os.Build;
 import android.system.OsConstants;
 import android.util.ArraySet;
 
+import androidx.core.os.BuildCompat;
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
+
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -50,6 +57,9 @@
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class LinkPropertiesTest {
+    @Rule
+    public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule();
+
     private static final InetAddress ADDRV4 = address("75.208.6.1");
     private static final InetAddress ADDRV6 = address("2001:0db8:85a3:0000:0000:8a2e:0370:7334");
     private static final InetAddress DNS1 = address("75.208.7.1");
@@ -76,13 +86,23 @@
     private static final LinkAddress LINKADDRV6 = new LinkAddress(ADDRV6, 128);
     private static final LinkAddress LINKADDRV6LINKLOCAL = new LinkAddress("fe80::1/64");
     private static final Uri CAPPORT_API_URL = Uri.parse("https://test.example.com/capportapi");
-    private static final CaptivePortalData CAPPORT_DATA = new CaptivePortalData.Builder()
-            .setVenueInfoUrl(Uri.parse("https://test.example.com/venue")).build();
+
+    // CaptivePortalData cannot be in a constant as it does not exist on Q.
+    // The test runner also crashes when scanning for tests if it is a return type.
+    private static Object getCaptivePortalData() {
+        return new CaptivePortalData.Builder()
+                .setVenueInfoUrl(Uri.parse("https://test.example.com/venue")).build();
+    }
 
     private static InetAddress address(String addrString) {
         return InetAddresses.parseNumericAddress(addrString);
     }
 
+    private static boolean isAtLeastR() {
+        // BuildCompat.isAtLeastR is documented to return false on release SDKs (including R)
+        return Build.VERSION.SDK_INT > Build.VERSION_CODES.Q || BuildCompat.isAtLeastR();
+    }
+
     private void checkEmpty(final LinkProperties lp) {
         assertEquals(0, lp.getAllInterfaceNames().size());
         assertEquals(0, lp.getAllAddresses().size());
@@ -98,14 +118,17 @@
         assertNull(lp.getHttpProxy());
         assertNull(lp.getTcpBufferSizes());
         assertNull(lp.getNat64Prefix());
-        assertNull(lp.getDhcpServerAddress());
         assertFalse(lp.isProvisioned());
         assertFalse(lp.isIpv4Provisioned());
         assertFalse(lp.isIpv6Provisioned());
         assertFalse(lp.isPrivateDnsActive());
-        assertFalse(lp.isWakeOnLanSupported());
-        assertNull(lp.getCaptivePortalApiUrl());
-        assertNull(lp.getCaptivePortalData());
+
+        if (isAtLeastR()) {
+            assertNull(lp.getDhcpServerAddress());
+            assertFalse(lp.isWakeOnLanSupported());
+            assertNull(lp.getCaptivePortalApiUrl());
+            assertNull(lp.getCaptivePortalData());
+        }
     }
 
     private LinkProperties makeTestObject() {
@@ -127,10 +150,12 @@
         lp.setMtu(MTU);
         lp.setTcpBufferSizes(TCP_BUFFER_SIZES);
         lp.setNat64Prefix(new IpPrefix("2001:db8:0:64::/96"));
-        lp.setDhcpServerAddress(DHCPSERVER);
-        lp.setWakeOnLanSupported(true);
-        lp.setCaptivePortalApiUrl(CAPPORT_API_URL);
-        lp.setCaptivePortalData(CAPPORT_DATA);
+        if (isAtLeastR()) {
+            lp.setDhcpServerAddress(DHCPSERVER);
+            lp.setWakeOnLanSupported(true);
+            lp.setCaptivePortalApiUrl(CAPPORT_API_URL);
+            lp.setCaptivePortalData((CaptivePortalData) getCaptivePortalData());
+        }
         return lp;
     }
 
@@ -169,14 +194,19 @@
         assertTrue(source.isIdenticalTcpBufferSizes(target));
         assertTrue(target.isIdenticalTcpBufferSizes(source));
 
-        assertTrue(source.isIdenticalWakeOnLan(target));
-        assertTrue(target.isIdenticalWakeOnLan(source));
+        if (isAtLeastR()) {
+            assertTrue(source.isIdenticalDhcpServerAddress(target));
+            assertTrue(source.isIdenticalDhcpServerAddress(source));
 
-        assertTrue(source.isIdenticalCaptivePortalApiUrl(target));
-        assertTrue(target.isIdenticalCaptivePortalApiUrl(source));
+            assertTrue(source.isIdenticalWakeOnLan(target));
+            assertTrue(target.isIdenticalWakeOnLan(source));
 
-        assertTrue(source.isIdenticalCaptivePortalData(target));
-        assertTrue(target.isIdenticalCaptivePortalData(source));
+            assertTrue(source.isIdenticalCaptivePortalApiUrl(target));
+            assertTrue(target.isIdenticalCaptivePortalApiUrl(source));
+
+            assertTrue(source.isIdenticalCaptivePortalData(target));
+            assertTrue(target.isIdenticalCaptivePortalData(source));
+        }
 
         // Check result of equals().
         assertTrue(source.equals(target));
@@ -943,8 +973,7 @@
         assertEquals(new ArraySet<>(expectRemoved), (new ArraySet<>(result.removed)));
     }
 
-    @Test
-    public void testLinkPropertiesParcelable() throws Exception {
+    private static LinkProperties makeLinkPropertiesForParceling() {
         LinkProperties source = new LinkProperties();
         source.setInterfaceName(NAME);
 
@@ -978,17 +1007,29 @@
 
         source.setNat64Prefix(new IpPrefix("2001:db8:1:2:64:64::/96"));
 
-        source.setWakeOnLanSupported(true);
-        source.setCaptivePortalApiUrl(CAPPORT_API_URL);
-        source.setCaptivePortalData(CAPPORT_DATA);
-
-        source.setDhcpServerAddress((Inet4Address) GATEWAY1);
-
         final LinkProperties stacked = new LinkProperties();
         stacked.setInterfaceName("test-stacked");
         source.addStackedLink(stacked);
 
-        assertParcelSane(source.makeSensitiveFieldsParcelingCopy(), 18 /* fieldCount */);
+        return source;
+    }
+
+    @Test @IgnoreAfter(Build.VERSION_CODES.Q)
+    public void testLinkPropertiesParcelable_Q() throws Exception {
+        final LinkProperties source = makeLinkPropertiesForParceling();
+        assertParcelSane(source, 14 /* fieldCount */);
+    }
+
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+    public void testLinkPropertiesParcelable() throws Exception {
+        final LinkProperties source = makeLinkPropertiesForParceling();
+
+        source.setWakeOnLanSupported(true);
+        source.setCaptivePortalApiUrl(CAPPORT_API_URL);
+        source.setCaptivePortalData((CaptivePortalData) getCaptivePortalData());
+        source.setDhcpServerAddress((Inet4Address) GATEWAY1);
+        assertParcelSane(new LinkProperties(source, true /* parcelSensitiveFields */),
+                18 /* fieldCount */);
 
         // Verify that without using a sensitiveFieldsParcelingCopy, sensitive fields are cleared.
         final LinkProperties sanitized = new LinkProperties(source);
@@ -997,7 +1038,8 @@
         assertEquals(sanitized, parcelingRoundTrip(source));
     }
 
-    @Test
+    // Parceling of the scope was broken until Q-QPR2
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
     public void testLinkLocalDnsServerParceling() throws Exception {
         final String strAddress = "fe80::1%lo";
         final LinkProperties lp = new LinkProperties();
@@ -1120,7 +1162,7 @@
         assertFalse(lp.isPrivateDnsActive());
     }
 
-    @Test
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
     public void testDhcpServerAddress() {
         final LinkProperties lp = makeTestObject();
         assertEquals(DHCPSERVER, lp.getDhcpServerAddress());
@@ -1129,7 +1171,7 @@
         assertNull(lp.getDhcpServerAddress());
     }
 
-    @Test
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
     public void testWakeOnLanSupported() {
         final LinkProperties lp = makeTestObject();
         assertTrue(lp.isWakeOnLanSupported());
@@ -1138,7 +1180,7 @@
         assertFalse(lp.isWakeOnLanSupported());
     }
 
-    @Test
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
     public void testCaptivePortalApiUrl() {
         final LinkProperties lp = makeTestObject();
         assertEquals(CAPPORT_API_URL, lp.getCaptivePortalApiUrl());
@@ -1147,10 +1189,10 @@
         assertNull(lp.getCaptivePortalApiUrl());
     }
 
-    @Test
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
     public void testCaptivePortalData() {
         final LinkProperties lp = makeTestObject();
-        assertEquals(CAPPORT_DATA, lp.getCaptivePortalData());
+        assertEquals(getCaptivePortalData(), lp.getCaptivePortalData());
 
         lp.clear();
         assertNull(lp.getCaptivePortalData());
diff --git a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
index efea91a..b2e8c37 100644
--- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
@@ -48,9 +48,11 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import android.os.Build;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.util.ArraySet;
 
+import androidx.core.os.BuildCompat;
 import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
@@ -64,6 +66,13 @@
     private static final String TEST_SSID = "TEST_SSID";
     private static final String DIFFERENT_TEST_SSID = "DIFFERENT_TEST_SSID";
 
+    private boolean isAtLeastR() {
+        // BuildCompat.isAtLeastR() is used to check the Android version before releasing Android R.
+        // Build.VERSION.SDK_INT > Build.VERSION_CODES.Q is used to check the Android version after
+        // releasing Android R.
+        return BuildCompat.isAtLeastR() || Build.VERSION.SDK_INT > Build.VERSION_CODES.Q;
+    }
+
     @Test
     public void testMaybeMarkCapabilitiesRestricted() {
         // verify EIMS is restricted
@@ -269,25 +278,36 @@
             .setUids(uids)
             .addCapability(NET_CAPABILITY_EIMS)
             .addCapability(NET_CAPABILITY_NOT_METERED);
-        netCap.setOwnerUid(123);
+        if (isAtLeastR()) {
+            netCap.setOwnerUid(123);
+        }
         assertParcelingIsLossless(netCap);
         netCap.setSSID(TEST_SSID);
-        assertParcelSane(netCap, 15);
+        testParcelSane(netCap);
     }
 
     @Test
     public void testParcelNetworkCapabilitiesWithRequestorUidAndPackageName() {
         final NetworkCapabilities netCap = new NetworkCapabilities()
                 .addCapability(NET_CAPABILITY_INTERNET)
-                .setRequestorUid(9304)
-                .setRequestorPackageName("com.android.test")
                 .addCapability(NET_CAPABILITY_EIMS)
                 .addCapability(NET_CAPABILITY_NOT_METERED);
+        if (isAtLeastR()) {
+            netCap.setRequestorPackageName("com.android.test");
+            netCap.setRequestorUid(9304);
+        }
         assertParcelingIsLossless(netCap);
         netCap.setSSID(TEST_SSID);
-        assertParcelSane(netCap, 15);
+        testParcelSane(netCap);
     }
 
+    private void testParcelSane(NetworkCapabilities cap) {
+        if (isAtLeastR()) {
+            assertParcelSane(cap, 15);
+        } else {
+            assertParcelSane(cap, 11);
+        }
+    }
 
     @Test
     public void testOemPaid() {
@@ -443,7 +463,7 @@
 
         nc1.setSSID(TEST_SSID);
         nc2.combineCapabilities(nc1);
-        assertTrue(TEST_SSID.equals(nc2.getSSID()));
+        assertTrue(TEST_SSID.equals(nc2.getSsid()));
 
         // Because they now have the same SSID, the following call should not throw
         nc2.combineCapabilities(nc1);
@@ -581,12 +601,12 @@
         // from nc2.
         assertFalse(nc2.hasCapability(NET_CAPABILITY_NOT_ROAMING));
         assertTrue(nc2.hasUnwantedCapability(NET_CAPABILITY_NOT_ROAMING));
-        assertTrue(TEST_SSID.equals(nc2.getSSID()));
+        assertTrue(TEST_SSID.equals(nc2.getSsid()));
 
         nc1.setSSID(DIFFERENT_TEST_SSID);
         nc2.set(nc1);
         assertEquals(nc1, nc2);
-        assertTrue(DIFFERENT_TEST_SSID.equals(nc2.getSSID()));
+        assertTrue(DIFFERENT_TEST_SSID.equals(nc2.getSsid()));
 
         nc1.setUids(uidRange(10, 13));
         nc2.set(nc1);  // Overwrites, as opposed to combineCapabilities
diff --git a/tests/net/java/android/net/NetworkTemplateTest.kt b/tests/net/java/android/net/NetworkTemplateTest.kt
new file mode 100644
index 0000000..5dd0fda
--- /dev/null
+++ b/tests/net/java/android/net/NetworkTemplateTest.kt
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2020 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.content.Context
+import android.net.ConnectivityManager.TYPE_MOBILE
+import android.net.ConnectivityManager.TYPE_WIFI
+import android.net.NetworkIdentity.SUBTYPE_COMBINED
+import android.net.NetworkIdentity.buildNetworkIdentity
+import android.net.NetworkStats.DEFAULT_NETWORK_ALL
+import android.net.NetworkStats.METERED_ALL
+import android.net.NetworkStats.ROAMING_ALL
+import android.net.NetworkTemplate.MATCH_MOBILE
+import android.net.NetworkTemplate.MATCH_WIFI
+import android.net.NetworkTemplate.NETWORK_TYPE_ALL
+import android.net.NetworkTemplate.buildTemplateMobileWithRatType
+import android.telephony.TelephonyManager
+import com.android.testutils.assertParcelSane
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.mock
+import org.mockito.MockitoAnnotations
+import kotlin.test.assertFalse
+import kotlin.test.assertNotEquals
+import kotlin.test.assertTrue
+
+private const val TEST_IMSI1 = "imsi1"
+private const val TEST_IMSI2 = "imsi2"
+private const val TEST_SSID1 = "ssid1"
+
+@RunWith(JUnit4::class)
+class NetworkTemplateTest {
+    private val mockContext = mock(Context::class.java)
+
+    private fun buildMobileNetworkState(subscriberId: String): NetworkState =
+            buildNetworkState(TYPE_MOBILE, subscriberId = subscriberId)
+    private fun buildWifiNetworkState(ssid: String): NetworkState =
+            buildNetworkState(TYPE_WIFI, ssid = ssid)
+
+    private fun buildNetworkState(
+        type: Int,
+        subscriberId: String? = null,
+        ssid: String? = null
+    ): NetworkState {
+        val info = mock(NetworkInfo::class.java)
+        doReturn(type).`when`(info).type
+        doReturn(NetworkInfo.State.CONNECTED).`when`(info).state
+        val lp = LinkProperties()
+        val caps = NetworkCapabilities().apply {
+            setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false)
+            setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true)
+        }
+        return NetworkState(info, lp, caps, mock(Network::class.java), subscriberId, ssid)
+    }
+
+    private fun NetworkTemplate.assertMatches(ident: NetworkIdentity) =
+            assertTrue(matches(ident), "$this does not match $ident")
+
+    private fun NetworkTemplate.assertDoesNotMatch(ident: NetworkIdentity) =
+            assertFalse(matches(ident), "$this should match $ident")
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+    }
+
+    @Test
+    fun testRatTypeGroupMatches() {
+        val stateMobile = buildMobileNetworkState(TEST_IMSI1)
+        // Build UMTS template that matches mobile identities with RAT in the same
+        // group with any IMSI. See {@link NetworkTemplate#getCollapsedRatType}.
+        val templateUmts = buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_UMTS)
+        // Build normal template that matches mobile identities with any RAT and IMSI.
+        val templateAll = buildTemplateMobileWithRatType(null, NETWORK_TYPE_ALL)
+        // Build template with UNKNOWN RAT that matches mobile identities with RAT that
+        // cannot be determined.
+        val templateUnknown =
+                buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_UNKNOWN)
+
+        val identUmts = buildNetworkIdentity(
+                mockContext, stateMobile, false, TelephonyManager.NETWORK_TYPE_UMTS)
+        val identHsdpa = buildNetworkIdentity(
+                mockContext, stateMobile, false, TelephonyManager.NETWORK_TYPE_HSDPA)
+        val identLte = buildNetworkIdentity(
+                mockContext, stateMobile, false, TelephonyManager.NETWORK_TYPE_LTE)
+        val identCombined = buildNetworkIdentity(
+                mockContext, stateMobile, false, SUBTYPE_COMBINED)
+        val identImsi2 = buildNetworkIdentity(mockContext, buildMobileNetworkState(TEST_IMSI2),
+                false, TelephonyManager.NETWORK_TYPE_UMTS)
+        val identWifi = buildNetworkIdentity(
+                mockContext, buildWifiNetworkState(TEST_SSID1), true, 0)
+
+        // Assert that identity with the same RAT matches.
+        templateUmts.assertMatches(identUmts)
+        templateAll.assertMatches(identUmts)
+        templateUnknown.assertDoesNotMatch(identUmts)
+        // Assert that identity with the RAT within the same group matches.
+        templateUmts.assertMatches(identHsdpa)
+        templateAll.assertMatches(identHsdpa)
+        templateUnknown.assertDoesNotMatch(identHsdpa)
+        // Assert that identity with the RAT out of the same group only matches template with
+        // NETWORK_TYPE_ALL.
+        templateUmts.assertDoesNotMatch(identLte)
+        templateAll.assertMatches(identLte)
+        templateUnknown.assertDoesNotMatch(identLte)
+        // Assert that identity with combined RAT only matches with template with NETWORK_TYPE_ALL
+        // and NETWORK_TYPE_UNKNOWN.
+        templateUmts.assertDoesNotMatch(identCombined)
+        templateAll.assertMatches(identCombined)
+        templateUnknown.assertMatches(identCombined)
+        // Assert that identity with different IMSI matches.
+        templateUmts.assertMatches(identImsi2)
+        templateAll.assertMatches(identImsi2)
+        templateUnknown.assertDoesNotMatch(identImsi2)
+        // Assert that wifi identity does not match.
+        templateUmts.assertDoesNotMatch(identWifi)
+        templateAll.assertDoesNotMatch(identWifi)
+        templateUnknown.assertDoesNotMatch(identWifi)
+    }
+
+    @Test
+    fun testParcelUnparcel() {
+        val templateMobile = NetworkTemplate(MATCH_MOBILE, TEST_IMSI1, null, null, METERED_ALL,
+                ROAMING_ALL, DEFAULT_NETWORK_ALL, TelephonyManager.NETWORK_TYPE_LTE)
+        val templateWifi = NetworkTemplate(MATCH_WIFI, null, null, TEST_SSID1, METERED_ALL,
+                ROAMING_ALL, DEFAULT_NETWORK_ALL, 0)
+        assertParcelSane(templateMobile, 8)
+        assertParcelSane(templateWifi, 8)
+    }
+
+    // Verify NETWORK_TYPE_ALL does not conflict with TelephonyManager#NETWORK_TYPE_* constants.
+    @Test
+    fun testNetworkTypeAll() {
+        for (ratType in TelephonyManager.getAllNetworkTypes()) {
+            assertNotEquals(NETWORK_TYPE_ALL, ratType)
+        }
+    }
+}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index c21772a..4bfb51b 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -25,7 +25,6 @@
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.net.ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN;
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
-import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_SUPL;
 import static android.net.ConnectivityManager.EXTRA_NETWORK_INFO;
 import static android.net.ConnectivityManager.EXTRA_NETWORK_TYPE;
 import static android.net.ConnectivityManager.NETID_UNSET;
@@ -175,6 +174,7 @@
 import android.net.ProxyInfo;
 import android.net.ResolverParamsParcel;
 import android.net.RouteInfo;
+import android.net.RouteInfoParcel;
 import android.net.SocketKeepalive;
 import android.net.UidRange;
 import android.net.Uri;
@@ -427,7 +427,6 @@
         public Object getSystemService(String name) {
             if (Context.CONNECTIVITY_SERVICE.equals(name)) return mCm;
             if (Context.NOTIFICATION_SERVICE.equals(name)) return mNotificationManager;
-            if (Context.NETWORK_STACK_SERVICE.equals(name)) return mNetworkStack;
             if (Context.USER_SERVICE.equals(name)) return mUserManager;
             if (Context.ALARM_SERVICE.equals(name)) return mAlarmManager;
             if (Context.LOCATION_SERVICE.equals(name)) return mLocationManager;
@@ -1374,7 +1373,6 @@
             @NonNull final Predicate<Intent> filter) {
         final ConditionVariable cv = new ConditionVariable();
         final IntentFilter intentFilter = new IntentFilter(CONNECTIVITY_ACTION);
-        intentFilter.addAction(CONNECTIVITY_ACTION_SUPL);
         final BroadcastReceiver receiver = new BroadcastReceiver() {
                     private int remaining = count;
                     public void onReceive(Context context, Intent intent) {
@@ -1422,56 +1420,28 @@
         final NetworkRequest legacyRequest = new NetworkRequest(legacyCaps, TYPE_MOBILE_SUPL,
                 ConnectivityManager.REQUEST_ID_UNSET, NetworkRequest.Type.REQUEST);
 
-        // Send request and check that the legacy broadcast for SUPL is sent correctly.
+        // File request, withdraw it and make sure no broadcast is sent
+        final ConditionVariable cv2 = registerConnectivityBroadcast(1);
         final TestNetworkCallback callback = new TestNetworkCallback();
-        final ConditionVariable cv2 = registerConnectivityBroadcastThat(1,
-                intent -> intent.getIntExtra(EXTRA_NETWORK_TYPE, -1) == TYPE_MOBILE_SUPL);
         mCm.requestNetwork(legacyRequest, callback);
         callback.expectCallback(CallbackEntry.AVAILABLE, mCellNetworkAgent);
-        waitFor(cv2);
-
-        // File another request, withdraw it and make sure no broadcast is sent
-        final ConditionVariable cv3 = registerConnectivityBroadcast(1);
-        final TestNetworkCallback callback2 = new TestNetworkCallback();
-        mCm.requestNetwork(legacyRequest, callback2);
-        callback2.expectCallback(CallbackEntry.AVAILABLE, mCellNetworkAgent);
-        mCm.unregisterNetworkCallback(callback2);
-        assertFalse(cv3.block(800)); // 800ms long enough to at least flake if this is sent
+        mCm.unregisterNetworkCallback(callback);
+        assertFalse(cv2.block(800)); // 800ms long enough to at least flake if this is sent
         // As the broadcast did not fire, the receiver was not unregistered. Do this now.
         mServiceContext.clearRegisteredReceivers();
 
-        // Withdraw the request and check that the broadcast for disconnection is sent.
-        final ConditionVariable cv4 = registerConnectivityBroadcastThat(1, intent ->
-                !((NetworkInfo) intent.getExtra(EXTRA_NETWORK_INFO, -1)).isConnected()
-                        && intent.getIntExtra(EXTRA_NETWORK_TYPE, -1) == TYPE_MOBILE_SUPL);
-        mCm.unregisterNetworkCallback(callback);
-        waitFor(cv4);
-
-        // Re-file the request and expect the connected broadcast again
-        final ConditionVariable cv5 = registerConnectivityBroadcastThat(1,
-                intent -> intent.getIntExtra(EXTRA_NETWORK_TYPE, -1) == TYPE_MOBILE_SUPL);
-        final TestNetworkCallback callback3 = new TestNetworkCallback();
-        mCm.requestNetwork(legacyRequest, callback3);
-        callback3.expectCallback(CallbackEntry.AVAILABLE, mCellNetworkAgent);
-        waitFor(cv5);
-
-        // Disconnect the network and expect two disconnected broadcasts, one for SUPL and one
-        // for mobile. Use a small hack to check that both have been sent, but the order is
-        // not contractual.
+        // Disconnect the network and expect mobile disconnected broadcast. Use a small hack to
+        // check that has been sent.
         final AtomicBoolean vanillaAction = new AtomicBoolean(false);
-        final AtomicBoolean suplAction = new AtomicBoolean(false);
-        final ConditionVariable cv6 = registerConnectivityBroadcastThat(2, intent -> {
+        final ConditionVariable cv3 = registerConnectivityBroadcastThat(1, intent -> {
             if (intent.getAction().equals(CONNECTIVITY_ACTION)) {
                 vanillaAction.set(true);
-            } else if (intent.getAction().equals(CONNECTIVITY_ACTION_SUPL)) {
-                suplAction.set(true);
             }
             return !((NetworkInfo) intent.getExtra(EXTRA_NETWORK_INFO, -1)).isConnected();
         });
         mCellNetworkAgent.disconnect();
-        waitFor(cv6);
+        waitFor(cv3);
         assertTrue(vanillaAction.get());
-        assertTrue(suplAction.get());
     }
 
     @Test
@@ -2464,8 +2434,8 @@
             final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(),
                     mServiceContext, "testFactory", filter);
             // Register the factory and don't be surprised when the default request arrives.
-            testFactory.register();
             testFactory.expectAddRequestsWithScores(0);
+            testFactory.register();
             testFactory.waitForNetworkRequests(1);
 
             testFactory.setScoreFilter(42);
@@ -6064,6 +6034,7 @@
             verify(mBatteryStatsService).noteNetworkInterfaceType(stackedLp.getInterfaceName(),
                     TYPE_MOBILE);
         }
+        reset(mMockNetd);
 
         // Add ipv4 address, expect that clatd and prefix discovery are stopped and stacked
         // linkproperties are cleaned up.
@@ -6115,7 +6086,6 @@
         networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
         verify(mMockNetd, times(1)).clatdStart(MOBILE_IFNAME, kNat64Prefix.toString());
 
-
         // Clat iface comes up. Expect stacked link to be added.
         clat.interfaceLinkStateChanged(CLAT_PREFIX + MOBILE_IFNAME, true);
         networkCallback.expectLinkPropertiesThat(mCellNetworkAgent,
@@ -6701,17 +6671,45 @@
         }
     }
 
+    private void assertRouteInfoParcelMatches(RouteInfo route, RouteInfoParcel parcel) {
+        assertEquals(route.getDestination().toString(), parcel.destination);
+        assertEquals(route.getInterface(), parcel.ifName);
+        assertEquals(route.getMtu(), parcel.mtu);
+
+        switch (route.getType()) {
+            case RouteInfo.RTN_UNICAST:
+                if (route.hasGateway()) {
+                    assertEquals(route.getGateway().getHostAddress(), parcel.nextHop);
+                } else {
+                    assertEquals(INetd.NEXTHOP_NONE, parcel.nextHop);
+                }
+                break;
+            case RouteInfo.RTN_UNREACHABLE:
+                assertEquals(INetd.NEXTHOP_UNREACHABLE, parcel.nextHop);
+                break;
+            case RouteInfo.RTN_THROW:
+                assertEquals(INetd.NEXTHOP_THROW, parcel.nextHop);
+                break;
+            default:
+                assertEquals(INetd.NEXTHOP_NONE, parcel.nextHop);
+                break;
+        }
+    }
+
     private void assertRoutesAdded(int netId, RouteInfo... routes) throws Exception {
-        InOrder inOrder = inOrder(mNetworkManagementService);
+        ArgumentCaptor<RouteInfoParcel> captor = ArgumentCaptor.forClass(RouteInfoParcel.class);
+        verify(mMockNetd, times(routes.length)).networkAddRouteParcel(eq(netId), captor.capture());
         for (int i = 0; i < routes.length; i++) {
-            inOrder.verify(mNetworkManagementService).addRoute(eq(netId), eq(routes[i]));
+            assertRouteInfoParcelMatches(routes[i], captor.getAllValues().get(i));
         }
     }
 
     private void assertRoutesRemoved(int netId, RouteInfo... routes) throws Exception {
-        InOrder inOrder = inOrder(mNetworkManagementService);
+        ArgumentCaptor<RouteInfoParcel> captor = ArgumentCaptor.forClass(RouteInfoParcel.class);
+        verify(mMockNetd, times(routes.length)).networkRemoveRouteParcel(eq(netId),
+                captor.capture());
         for (int i = 0; i < routes.length; i++) {
-            inOrder.verify(mNetworkManagementService).removeRoute(eq(netId), eq(routes[i]));
+            assertRouteInfoParcelMatches(routes[i], captor.getAllValues().get(i));
         }
     }
 
@@ -6851,7 +6849,7 @@
     @Test
     public void testCheckConnectivityDiagnosticsPermissionsNetworkAdministrator() throws Exception {
         final NetworkCapabilities nc = new NetworkCapabilities();
-        nc.setAdministratorUids(Arrays.asList(Process.myUid()));
+        nc.setAdministratorUids(new int[] {Process.myUid()});
         final NetworkAgentInfo naiWithUid =
                 new NetworkAgentInfo(
                         null, null, null, null, null, nc, 0, mServiceContext, null, null,
@@ -6873,7 +6871,7 @@
     public void testCheckConnectivityDiagnosticsPermissionsFails() throws Exception {
         final NetworkCapabilities nc = new NetworkCapabilities();
         nc.setOwnerUid(Process.myUid());
-        nc.setAdministratorUids(Arrays.asList(Process.myUid()));
+        nc.setAdministratorUids(new int[] {Process.myUid()});
         final NetworkAgentInfo naiWithUid =
                 new NetworkAgentInfo(
                         null, null, null, null, null, nc, 0, mServiceContext, null, null,
@@ -6926,7 +6924,7 @@
                 argThat(report -> {
                     final NetworkCapabilities nc = report.getNetworkCapabilities();
                     return nc.getUids() == null
-                            && nc.getAdministratorUids().isEmpty()
+                            && nc.getAdministratorUids().length == 0
                             && nc.getOwnerUid() == Process.INVALID_UID;
                 }));
     }
@@ -6947,7 +6945,7 @@
                 argThat(report -> {
                     final NetworkCapabilities nc = report.getNetworkCapabilities();
                     return nc.getUids() == null
-                            && nc.getAdministratorUids().isEmpty()
+                            && nc.getAdministratorUids().length == 0
                             && nc.getOwnerUid() == Process.INVALID_UID;
                 }));
     }
@@ -6977,4 +6975,60 @@
         verify(mConnectivityDiagnosticsCallback)
                 .onNetworkConnectivityReported(eq(n), eq(noConnectivity));
     }
+
+    @Test
+    public void testRouteAddDeleteUpdate() throws Exception {
+        final NetworkRequest request = new NetworkRequest.Builder().build();
+        final TestNetworkCallback networkCallback = new TestNetworkCallback();
+        mCm.registerNetworkCallback(request, networkCallback);
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+        reset(mMockNetd);
+        mCellNetworkAgent.connect(false);
+        networkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
+        final int netId = mCellNetworkAgent.getNetwork().netId;
+
+        final String iface = "rmnet_data0";
+        final InetAddress gateway = InetAddress.getByName("fe80::5678");
+        RouteInfo direct = RouteInfo.makeHostRoute(gateway, iface);
+        RouteInfo rio1 = new RouteInfo(new IpPrefix("2001:db8:1::/48"), gateway, iface);
+        RouteInfo rio2 = new RouteInfo(new IpPrefix("2001:db8:2::/48"), gateway, iface);
+        RouteInfo defaultRoute = new RouteInfo((IpPrefix) null, gateway, iface);
+        RouteInfo defaultWithMtu = new RouteInfo(null, gateway, iface, RouteInfo.RTN_UNICAST,
+                                                 1280 /* mtu */);
+
+        // Send LinkProperties and check that we ask netd to add routes.
+        LinkProperties lp = new LinkProperties();
+        lp.setInterfaceName(iface);
+        lp.addRoute(direct);
+        lp.addRoute(rio1);
+        lp.addRoute(defaultRoute);
+        mCellNetworkAgent.sendLinkProperties(lp);
+        networkCallback.expectLinkPropertiesThat(mCellNetworkAgent, x -> x.getRoutes().size() == 3);
+
+        assertRoutesAdded(netId, direct, rio1, defaultRoute);
+        reset(mMockNetd);
+
+        // Send updated LinkProperties and check that we ask netd to add, remove, update routes.
+        assertTrue(lp.getRoutes().contains(defaultRoute));
+        lp.removeRoute(rio1);
+        lp.addRoute(rio2);
+        lp.addRoute(defaultWithMtu);
+        // Ensure adding the same route with a different MTU replaces the previous route.
+        assertFalse(lp.getRoutes().contains(defaultRoute));
+        assertTrue(lp.getRoutes().contains(defaultWithMtu));
+
+        mCellNetworkAgent.sendLinkProperties(lp);
+        networkCallback.expectLinkPropertiesThat(mCellNetworkAgent,
+                x -> x.getRoutes().contains(rio2));
+
+        assertRoutesRemoved(netId, rio1);
+        assertRoutesAdded(netId, rio2);
+
+        ArgumentCaptor<RouteInfoParcel> captor = ArgumentCaptor.forClass(RouteInfoParcel.class);
+        verify(mMockNetd).networkUpdateRouteParcel(eq(netId), captor.capture());
+        assertRouteInfoParcelMatches(defaultWithMtu, captor.getValue());
+
+
+        mCm.unregisterNetworkCallback(networkCallback);
+    }
 }
diff --git a/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java b/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java
index 8f90f13..551498f 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java
@@ -319,33 +319,33 @@
             assertEntry(18322, 75, 15031, 75, history.getValues(i++, null));
             assertEntry(527798, 761, 78570, 652, history.getValues(i++, null));
             assertEntry(527797, 760, 78570, 651, history.getValues(i++, null));
-            assertEntry(10747, 50, 16838, 55, history.getValues(i++, null));
-            assertEntry(10747, 49, 16838, 54, history.getValues(i++, null));
+            assertEntry(10747, 50, 16839, 55, history.getValues(i++, null));
+            assertEntry(10747, 49, 16837, 54, history.getValues(i++, null));
             assertEntry(89191, 151, 18021, 140, history.getValues(i++, null));
             assertEntry(89190, 150, 18020, 139, history.getValues(i++, null));
-            assertEntry(3821, 22, 4525, 26, history.getValues(i++, null));
-            assertEntry(3820, 22, 4524, 26, history.getValues(i++, null));
-            assertEntry(91686, 159, 18575, 146, history.getValues(i++, null));
-            assertEntry(91685, 159, 18575, 146, history.getValues(i++, null));
-            assertEntry(8289, 35, 6863, 38, history.getValues(i++, null));
-            assertEntry(8289, 35, 6863, 38, history.getValues(i++, null));
+            assertEntry(3821, 23, 4525, 26, history.getValues(i++, null));
+            assertEntry(3820, 21, 4524, 26, history.getValues(i++, null));
+            assertEntry(91686, 159, 18576, 146, history.getValues(i++, null));
+            assertEntry(91685, 159, 18574, 146, history.getValues(i++, null));
+            assertEntry(8289, 36, 6864, 39, history.getValues(i++, null));
+            assertEntry(8289, 34, 6862, 37, history.getValues(i++, null));
             assertEntry(113914, 174, 18364, 157, history.getValues(i++, null));
             assertEntry(113913, 173, 18364, 157, history.getValues(i++, null));
-            assertEntry(11378, 49, 9261, 49, history.getValues(i++, null));
-            assertEntry(11377, 48, 9261, 49, history.getValues(i++, null));
-            assertEntry(201765, 328, 41808, 291, history.getValues(i++, null));
-            assertEntry(201765, 328, 41807, 290, history.getValues(i++, null));
-            assertEntry(106106, 218, 39917, 201, history.getValues(i++, null));
-            assertEntry(106105, 217, 39917, 201, history.getValues(i++, null));
+            assertEntry(11378, 49, 9261, 50, history.getValues(i++, null));
+            assertEntry(11377, 48, 9261, 48, history.getValues(i++, null));
+            assertEntry(201766, 328, 41808, 291, history.getValues(i++, null));
+            assertEntry(201764, 328, 41807, 290, history.getValues(i++, null));
+            assertEntry(106106, 219, 39918, 202, history.getValues(i++, null));
+            assertEntry(106105, 216, 39916, 200, history.getValues(i++, null));
             assertEquals(history.size(), i);
 
             // Slice from middle should be untouched
             history = getHistory(collection, plan, TIME_B - HOUR_IN_MILLIS,
                     TIME_B + HOUR_IN_MILLIS); i = 0;
-            assertEntry(3821, 22, 4525, 26, history.getValues(i++, null));
-            assertEntry(3820, 22, 4524, 26, history.getValues(i++, null));
-            assertEntry(91686, 159, 18575, 146, history.getValues(i++, null));
-            assertEntry(91685, 159, 18575, 146, history.getValues(i++, null));
+            assertEntry(3821, 23, 4525, 26, history.getValues(i++, null));
+            assertEntry(3820, 21, 4524, 26, history.getValues(i++, null));
+            assertEntry(91686, 159, 18576, 146, history.getValues(i++, null));
+            assertEntry(91685, 159, 18574, 146, history.getValues(i++, null));
             assertEquals(history.size(), i);
         }
 
@@ -373,25 +373,25 @@
             assertEntry(527797, 760, 78570, 651, history.getValues(i++, null));
             // Cycle point; start data normalization
             assertEntry(7507, 0, 11763, 0, history.getValues(i++, null));
-            assertEntry(7507, 0, 11763, 0, history.getValues(i++, null));
+            assertEntry(7507, 0, 11762, 0, history.getValues(i++, null));
             assertEntry(62309, 0, 12589, 0, history.getValues(i++, null));
             assertEntry(62309, 0, 12588, 0, history.getValues(i++, null));
             assertEntry(2669, 0, 3161, 0, history.getValues(i++, null));
             assertEntry(2668, 0, 3160, 0, history.getValues(i++, null));
             // Anchor point; end data normalization
-            assertEntry(91686, 159, 18575, 146, history.getValues(i++, null));
-            assertEntry(91685, 159, 18575, 146, history.getValues(i++, null));
-            assertEntry(8289, 35, 6863, 38, history.getValues(i++, null));
-            assertEntry(8289, 35, 6863, 38, history.getValues(i++, null));
+            assertEntry(91686, 159, 18576, 146, history.getValues(i++, null));
+            assertEntry(91685, 159, 18574, 146, history.getValues(i++, null));
+            assertEntry(8289, 36, 6864, 39, history.getValues(i++, null));
+            assertEntry(8289, 34, 6862, 37, history.getValues(i++, null));
             assertEntry(113914, 174, 18364, 157, history.getValues(i++, null));
             assertEntry(113913, 173, 18364, 157, history.getValues(i++, null));
             // Cycle point
-            assertEntry(11378, 49, 9261, 49, history.getValues(i++, null));
-            assertEntry(11377, 48, 9261, 49, history.getValues(i++, null));
-            assertEntry(201765, 328, 41808, 291, history.getValues(i++, null));
-            assertEntry(201765, 328, 41807, 290, history.getValues(i++, null));
-            assertEntry(106106, 218, 39917, 201, history.getValues(i++, null));
-            assertEntry(106105, 217, 39917, 201, history.getValues(i++, null));
+            assertEntry(11378, 49, 9261, 50, history.getValues(i++, null));
+            assertEntry(11377, 48, 9261, 48, history.getValues(i++, null));
+            assertEntry(201766, 328, 41808, 291, history.getValues(i++, null));
+            assertEntry(201764, 328, 41807, 290, history.getValues(i++, null));
+            assertEntry(106106, 219, 39918, 202, history.getValues(i++, null));
+            assertEntry(106105, 216, 39916, 200, history.getValues(i++, null));
             assertEquals(history.size(), i);
 
             // Slice from middle should be augmented
@@ -399,8 +399,8 @@
                     TIME_B + HOUR_IN_MILLIS); i = 0;
             assertEntry(2669, 0, 3161, 0, history.getValues(i++, null));
             assertEntry(2668, 0, 3160, 0, history.getValues(i++, null));
-            assertEntry(91686, 159, 18575, 146, history.getValues(i++, null));
-            assertEntry(91685, 159, 18575, 146, history.getValues(i++, null));
+            assertEntry(91686, 159, 18576, 146, history.getValues(i++, null));
+            assertEntry(91685, 159, 18574, 146, history.getValues(i++, null));
             assertEquals(history.size(), i);
         }
 
@@ -427,34 +427,34 @@
             assertEntry(527798, 761, 78570, 652, history.getValues(i++, null));
             assertEntry(527797, 760, 78570, 651, history.getValues(i++, null));
             // Cycle point; start data normalization
-            assertEntry(15015, 0, 23526, 0, history.getValues(i++, null));
-            assertEntry(15015, 0, 23526, 0, history.getValues(i++, null));
+            assertEntry(15015, 0, 23527, 0, history.getValues(i++, null));
+            assertEntry(15015, 0, 23524, 0, history.getValues(i++, null));
             assertEntry(124619, 0, 25179, 0, history.getValues(i++, null));
             assertEntry(124618, 0, 25177, 0, history.getValues(i++, null));
             assertEntry(5338, 0, 6322, 0, history.getValues(i++, null));
             assertEntry(5337, 0, 6320, 0, history.getValues(i++, null));
             // Anchor point; end data normalization
-            assertEntry(91686, 159, 18575, 146, history.getValues(i++, null));
-            assertEntry(91685, 159, 18575, 146, history.getValues(i++, null));
-            assertEntry(8289, 35, 6863, 38, history.getValues(i++, null));
-            assertEntry(8289, 35, 6863, 38, history.getValues(i++, null));
+            assertEntry(91686, 159, 18576, 146, history.getValues(i++, null));
+            assertEntry(91685, 159, 18574, 146, history.getValues(i++, null));
+            assertEntry(8289, 36, 6864, 39, history.getValues(i++, null));
+            assertEntry(8289, 34, 6862, 37, history.getValues(i++, null));
             assertEntry(113914, 174, 18364, 157, history.getValues(i++, null));
             assertEntry(113913, 173, 18364, 157, history.getValues(i++, null));
             // Cycle point
-            assertEntry(11378, 49, 9261, 49, history.getValues(i++, null));
-            assertEntry(11377, 48, 9261, 49, history.getValues(i++, null));
-            assertEntry(201765, 328, 41808, 291, history.getValues(i++, null));
-            assertEntry(201765, 328, 41807, 290, history.getValues(i++, null));
-            assertEntry(106106, 218, 39917, 201, history.getValues(i++, null));
-            assertEntry(106105, 217, 39917, 201, history.getValues(i++, null));
+            assertEntry(11378, 49, 9261, 50, history.getValues(i++, null));
+            assertEntry(11377, 48, 9261, 48, history.getValues(i++, null));
+            assertEntry(201766, 328, 41808, 291, history.getValues(i++, null));
+            assertEntry(201764, 328, 41807, 290, history.getValues(i++, null));
+            assertEntry(106106, 219, 39918, 202, history.getValues(i++, null));
+            assertEntry(106105, 216, 39916, 200, history.getValues(i++, null));
 
             // Slice from middle should be augmented
             history = getHistory(collection, plan, TIME_B - HOUR_IN_MILLIS,
                     TIME_B + HOUR_IN_MILLIS); i = 0;
             assertEntry(5338, 0, 6322, 0, history.getValues(i++, null));
             assertEntry(5337, 0, 6320, 0, history.getValues(i++, null));
-            assertEntry(91686, 159, 18575, 146, history.getValues(i++, null));
-            assertEntry(91685, 159, 18575, 146, history.getValues(i++, null));
+            assertEntry(91686, 159, 18576, 146, history.getValues(i++, null));
+            assertEntry(91685, 159, 18574, 146, history.getValues(i++, null));
             assertEquals(history.size(), i);
         }
     }
diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
index b346c92..6e63313 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -42,6 +42,7 @@
 import static android.net.NetworkStats.UID_ALL;
 import static android.net.NetworkStatsHistory.FIELD_ALL;
 import static android.net.NetworkTemplate.buildTemplateMobileAll;
+import static android.net.NetworkTemplate.buildTemplateMobileWithRatType;
 import static android.net.NetworkTemplate.buildTemplateWifiWildcard;
 import static android.net.TrafficStats.MB_IN_BYTES;
 import static android.net.TrafficStats.UID_REMOVED;
@@ -60,11 +61,13 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.AlarmManager;
 import android.app.usage.NetworkStatsManager;
 import android.content.Context;
@@ -92,6 +95,8 @@
 import android.os.Messenger;
 import android.os.PowerManager;
 import android.os.SimpleClock;
+import android.telephony.PhoneStateListener;
+import android.telephony.ServiceState;
 import android.telephony.TelephonyManager;
 
 import androidx.test.InstrumentationRegistry;
@@ -163,11 +168,14 @@
     private @Mock NetworkStatsSettings mSettings;
     private @Mock IBinder mBinder;
     private @Mock AlarmManager mAlarmManager;
+    private @Mock TelephonyManager mTelephonyManager;
     private HandlerThread mHandlerThread;
 
     private NetworkStatsService mService;
     private INetworkStatsSession mSession;
     private INetworkManagementEventObserver mNetworkObserver;
+    @Nullable
+    private PhoneStateListener mPhoneStateListener;
 
     private final Clock mClock = new SimpleClock(ZoneOffset.UTC) {
         @Override
@@ -195,7 +203,7 @@
         mHandlerThread = new HandlerThread("HandlerThread");
         final NetworkStatsService.Dependencies deps = makeDependencies();
         mService = new NetworkStatsService(mServiceContext, mNetManager, mAlarmManager, wakeLock,
-                mClock, mServiceContext.getSystemService(TelephonyManager.class), mSettings,
+                mClock, mTelephonyManager, mSettings,
                 mStatsFactory, new NetworkStatsObservers(), mStatsDir, getBaseDir(mStatsDir), deps);
 
         mElapsedRealtime = 0L;
@@ -216,6 +224,12 @@
                 ArgumentCaptor.forClass(INetworkManagementEventObserver.class);
         verify(mNetManager).registerObserver(networkObserver.capture());
         mNetworkObserver = networkObserver.getValue();
+
+        // Capture the phone state listener that created by service.
+        final ArgumentCaptor<PhoneStateListener> phoneStateListenerCaptor =
+                ArgumentCaptor.forClass(PhoneStateListener.class);
+        verify(mTelephonyManager).listen(phoneStateListenerCaptor.capture(), anyInt());
+        mPhoneStateListener = phoneStateListenerCaptor.getValue();
     }
 
     @NonNull
@@ -534,7 +548,7 @@
     }
 
     @Test
-    public void testUid3g4gCombinedByTemplate() throws Exception {
+    public void testUid3gWimaxCombinedByTemplate() throws Exception {
         // pretend that network comes online
         expectDefaultSettings();
         NetworkState[] states = new NetworkState[] {buildMobile3gState(IMSI_1)};
@@ -558,10 +572,10 @@
         assertUidTotal(sTemplateImsi1, UID_RED, 1024L, 8L, 1024L, 8L, 5);
 
 
-        // now switch over to 4g network
+        // now switch over to wimax network
         incrementCurrentTime(HOUR_IN_MILLIS);
         expectDefaultSettings();
-        states = new NetworkState[] {buildMobile4gState(TEST_IFACE2)};
+        states = new NetworkState[] {buildWimaxState(TEST_IFACE2)};
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
                 .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
@@ -589,6 +603,89 @@
     }
 
     @Test
+    public void testMobileStatsByRatType() throws Exception {
+        final NetworkTemplate template3g =
+                buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_UMTS);
+        final NetworkTemplate template4g =
+                buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_LTE);
+        final NetworkTemplate template5g =
+                buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_NR);
+        final NetworkState[] states = new NetworkState[]{buildMobile3gState(IMSI_1)};
+
+        // 3G network comes online.
+        expectNetworkStatsSummary(buildEmptyStats());
+        expectNetworkStatsUidDetail(buildEmptyStats());
+
+        setMobileRatTypeAndWaitForIdle(TelephonyManager.NETWORK_TYPE_UMTS);
+        mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states),
+                new VpnInfo[0]);
+
+        // Create some traffic.
+        incrementCurrentTime(MINUTE_IN_MILLIS);
+        expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
+                .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE,
+                        12L, 18L, 14L, 1L, 0L)));
+        forcePollAndWaitForIdle();
+
+        // Verify 3g templates gets stats.
+        assertUidTotal(sTemplateImsi1, UID_RED, 12L, 18L, 14L, 1L, 0);
+        assertUidTotal(template3g, UID_RED, 12L, 18L, 14L, 1L, 0);
+        assertUidTotal(template4g, UID_RED, 0L, 0L, 0L, 0L, 0);
+        assertUidTotal(template5g, UID_RED, 0L, 0L, 0L, 0L, 0);
+
+        // 4G network comes online.
+        incrementCurrentTime(MINUTE_IN_MILLIS);
+        setMobileRatTypeAndWaitForIdle(TelephonyManager.NETWORK_TYPE_LTE);
+        expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
+                // Append more traffic on existing 3g stats entry.
+                .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE,
+                        16L, 22L, 17L, 2L, 0L))
+                // Add entry that is new on 4g.
+                .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE,
+                        33L, 27L, 8L, 10L, 1L)));
+        forcePollAndWaitForIdle();
+
+        // Verify ALL_MOBILE template gets all. 3g template counters do not increase.
+        assertUidTotal(sTemplateImsi1, UID_RED, 49L, 49L, 25L, 12L, 1);
+        assertUidTotal(template3g, UID_RED, 12L, 18L, 14L, 1L, 0);
+        // Verify 4g template counts appended stats on existing entry and newly created entry.
+        assertUidTotal(template4g, UID_RED, 4L + 33L, 4L + 27L, 3L + 8L, 1L + 10L, 1);
+        // Verify 5g template doesn't get anything since no traffic is generated on 5g.
+        assertUidTotal(template5g, UID_RED, 0L, 0L, 0L, 0L, 0);
+
+        // 5g network comes online.
+        incrementCurrentTime(MINUTE_IN_MILLIS);
+        setMobileRatTypeAndWaitForIdle(TelephonyManager.NETWORK_TYPE_NR);
+        expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
+                // Existing stats remains.
+                .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE,
+                        16L, 22L, 17L, 2L, 0L))
+                .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE,
+                        33L, 27L, 8L, 10L, 1L))
+                // Add some traffic on 5g.
+                .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE,
+                5L, 13L, 31L, 9L, 2L)));
+        forcePollAndWaitForIdle();
+
+        // Verify ALL_MOBILE template gets all.
+        assertUidTotal(sTemplateImsi1, UID_RED, 54L, 62L, 56L, 21L, 3);
+        // 3g/4g template counters do not increase.
+        assertUidTotal(template3g, UID_RED, 12L, 18L, 14L, 1L, 0);
+        assertUidTotal(template4g, UID_RED, 4L + 33L, 4L + 27L, 3L + 8L, 1L + 10L, 1);
+        // Verify 5g template gets the 5g count.
+        assertUidTotal(template5g, UID_RED, 5L, 13L, 31L, 9L, 2);
+    }
+
+    // TODO: support per IMSI state
+    private void setMobileRatTypeAndWaitForIdle(int ratType) {
+        final ServiceState mockSs = mock(ServiceState.class);
+        when(mockSs.getDataNetworkType()).thenReturn(ratType);
+        mPhoneStateListener.onServiceStateChanged(mockSs);
+
+        HandlerUtilsKt.waitForIdle(mHandlerThread, WAIT_TIMEOUT);
+    }
+
+    @Test
     public void testSummaryForAllUid() throws Exception {
         // pretend that network comes online
         expectDefaultSettings();
@@ -1197,6 +1294,7 @@
         when(mSettings.getPollInterval()).thenReturn(HOUR_IN_MILLIS);
         when(mSettings.getPollDelay()).thenReturn(0L);
         when(mSettings.getSampleEnabled()).thenReturn(true);
+        when(mSettings.getCombineSubtypeEnabled()).thenReturn(false);
 
         final Config config = new Config(bucketDuration, deleteAge, deleteAge);
         when(mSettings.getDevConfig()).thenReturn(config);
@@ -1242,6 +1340,7 @@
         final NetworkCapabilities capabilities = new NetworkCapabilities();
         capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, !isMetered);
         capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true);
+        capabilities.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
         return new NetworkState(info, prop, capabilities, WIFI_NETWORK, null, TEST_SSID);
     }
 
@@ -1259,10 +1358,11 @@
         final NetworkCapabilities capabilities = new NetworkCapabilities();
         capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false);
         capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, !isRoaming);
+        capabilities.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
         return new NetworkState(info, prop, capabilities, MOBILE_NETWORK, subscriberId, null);
     }
 
-    private static NetworkState buildMobile4gState(String iface) {
+    private static NetworkState buildWimaxState(@NonNull String iface) {
         final NetworkInfo info = new NetworkInfo(TYPE_WIMAX, 0, null, null);
         info.setDetailedState(DetailedState.CONNECTED, null, null);
         final LinkProperties prop = new LinkProperties();