Merge changes Id3eba714,I595ba207 into main

* changes:
  Add CtsTetheringUtils methods for wifi tethering without permission
  Skip stopTetheringRequest tests if tethering isn't supported
diff --git a/clatd/clatd.c b/clatd/clatd.c
index bac8b1d..9b1d987 100644
--- a/clatd/clatd.c
+++ b/clatd/clatd.c
@@ -49,7 +49,8 @@
 
 struct clat_config Global_Clatd_Config;
 
-volatile sig_atomic_t running = 1;
+volatile sig_atomic_t sigterm = 0;
+bool running = true;
 
 // reads IPv6 packet from AF_PACKET socket, translates to IPv4, writes to tun
 void process_packet_6_to_4(struct tun_data *tunnel) {
@@ -78,10 +79,11 @@
     if (errno != EAGAIN) {
       logmsg(ANDROID_LOG_WARN, "%s: read error: %s", __func__, strerror(errno));
     }
+    if (errno == ENETDOWN) running = false;
     return;
   } else if (readlen == 0) {
     logmsg(ANDROID_LOG_WARN, "%s: packet socket removed?", __func__);
-    running = 0;
+    running = false;
     return;
   } else if (readlen >= sizeof(buf)) {
     logmsg(ANDROID_LOG_WARN, "%s: read truncation - ignoring pkt", __func__);
@@ -161,10 +163,11 @@
     if (errno != EAGAIN) {
       logmsg(ANDROID_LOG_WARN, "%s: read error: %s", __func__, strerror(errno));
     }
+    if (errno == ENETDOWN) running = false;  // not sure if this can happen
     return;
   } else if (readlen == 0) {
     logmsg(ANDROID_LOG_WARN, "%s: tun interface removed", __func__);
-    running = 0;
+    running = false;
     return;
   } else if (readlen >= sizeof(buf)) {
     logmsg(ANDROID_LOG_WARN, "%s: read truncation - ignoring pkt", __func__);
@@ -273,23 +276,12 @@
  *   tunnel - tun device data
  */
 void event_loop(struct tun_data *tunnel) {
-  // Apparently some network gear will refuse to perform NS for IPs that aren't DAD'ed,
-  // this would then result in an ipv6-only network with working native ipv6, working
-  // IPv4 via DNS64, but non-functioning IPv4 via CLAT (ie. IPv4 literals + IPv4 only apps).
-  // The kernel itself doesn't do DAD for anycast ips (but does handle IPV6 MLD and handle ND).
-  // So we'll spoof dad here, and yeah, we really should check for a response and in
-  // case of failure pick a different IP.  Seeing as 48-bits of the IP are utterly random
-  // (with the other 16 chosen to guarantee checksum neutrality) this seems like a remote
-  // concern...
-  // TODO: actually perform true DAD
-  send_dad(tunnel->write_fd6, &Global_Clatd_Config.ipv6_local_subnet);
-
   struct pollfd wait_fd[] = {
     { tunnel->read_fd6, POLLIN, 0 },
     { tunnel->fd4, POLLIN, 0 },
   };
 
-  while (running) {
+  while (running && !sigterm) {
     if (poll(wait_fd, ARRAY_SIZE(wait_fd), -1) == -1) {
       if (errno != EINTR) {
         logmsg(ANDROID_LOG_WARN, "event_loop/poll returned an error: %s", strerror(errno));
diff --git a/clatd/clatd.h b/clatd/clatd.h
index e170c58..daa5ebc 100644
--- a/clatd/clatd.h
+++ b/clatd/clatd.h
@@ -52,7 +52,9 @@
 
 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
 
-extern volatile sig_atomic_t running;
+extern volatile sig_atomic_t sigterm;
+
+void send_dad(int fd, const struct in6_addr* tgt);
 
 void event_loop(struct tun_data *tunnel);
 
diff --git a/clatd/main.c b/clatd/main.c
index 7aa1671..bc29041 100644
--- a/clatd/main.c
+++ b/clatd/main.c
@@ -34,10 +34,10 @@
 
 #define DEVICEPREFIX "v4-"
 
-/* function: stop_loop
+/* function: handle_sigterm
  * signal handler: stop the event loop
  */
-static void stop_loop(__attribute__((unused)) int unused) { running = 0; };
+static void handle_sigterm(__attribute__((unused)) int unused) { sigterm = 1; };
 
 /* function: print_help
  * in case the user is running this on the command line
@@ -64,6 +64,48 @@
        *write_sock_str = NULL;
   unsigned len;
 
+  // Clatd binary is setuid/gid CLAT, thus when we reach here we have:
+  //   $ adb shell ps | grep clat
+  //                [pid] [ppid]
+  //   clat          7650  1393   10785364   2612 do_sys_poll         0 S clatd-wlan0
+  //   $ adb shell cat /proc/7650/status | egrep -i '^(Uid:|Gid:|Groups:)'
+  //         [real][effective][saved][filesystem]
+  //          [uid]   [euid]  [suid]  [fsuid]
+  //   Uid:    1000    1029    1029    1029
+  //          [gid]   [egid]  [sgid]  [fsgid]
+  //   Gid:    1000    1029    1029    1029
+  //   Groups: 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1018 1021 1023 1024 1032 1065 3001 3002 3003 3005 3006 3007 3009 3010 3011 3012
+  // This mismatch between uid & euid appears to cause periodic (every 5 minutes):
+  //                                                  objhash pid  ppid             uid
+  //   W ActivityManager: Stale PhantomProcessRecord {xxxxxxx 7650:1393:clatd-wlan0/1000}, removing
+  // This is due to:
+  //   $ adbz shell ls -ld /proc/7650
+  //   dr-xr-xr-x 9 clat clat 0 2025-03-14 11:37 /proc/7650
+  // which is used by
+  //   //frameworks/base/core/java/com/android/internal/os/ProcessCpuTracker.java
+  // which thus returns the uid 'clat' vs
+  //   //frameworks/base/core/java/android/os/Process.java
+  // getUidForPid() which grabs *real* 'uid' from /proc/<pid>/status and is used in:
+  //   //frameworks/base/services/core/java/com/android/server/am/PhantomProcessList.java
+  // (perhaps this should grab euid instead? unclear)
+  //
+  // However, we want to drop as many privs as possible, hence:
+  gid_t egid = getegid();  // documented to never fail, hence should return AID_CLAT == 1029
+  uid_t euid = geteuid();  // (ditto)
+  setresgid(egid, egid, egid);  // ignore any failure
+  setresuid(euid, euid, euid);  // ignore any failure
+  // ideally we'd somehow drop supplementary groups too...
+  // but for historical reasons that actually requires CAP_SETGID which we don't have
+  // (see man 2 setgroups)
+  //
+  // Now we (should) have:
+  // $ adb shell ps | grep clat
+  // clat          5370  1479   10785364   2528 do_sys_poll         0 S clatd-wlan0
+  // # adb shell cat /proc/5370/status | egrep -i '^(Uid:|Gid:|Groups:)'
+  // Uid:    1029    1029    1029    1029
+  // Gid:    1029    1029    1029    1029
+  // Groups: 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1018 1021 1023 1024 1032 1065 3001 3002 3003 3005 3006 3007 3009 3010 3011 3012
+
   while ((opt = getopt(argc, argv, "i:p:4:6:t:r:w:h")) != -1) {
     switch (opt) {
       case 'i':
@@ -150,7 +192,7 @@
     exit(1);
   }
 
-  logmsg(ANDROID_LOG_INFO, "Starting clat version %s on %s plat=%s v4=%s v6=%s", CLATD_VERSION,
+  logmsg(ANDROID_LOG_INFO, "Starting clat version " CLATD_VERSION " on %s plat=%s v4=%s v6=%s",
          uplink_interface, plat_prefix ? plat_prefix : "(none)", v4_addr ? v4_addr : "(none)",
          v6_addr ? v6_addr : "(none)");
 
@@ -183,25 +225,35 @@
   }
 
   // Loop until someone sends us a signal or brings down the tun interface.
-  if (signal(SIGTERM, stop_loop) == SIG_ERR) {
+  if (signal(SIGTERM, handle_sigterm) == SIG_ERR) {
     logmsg(ANDROID_LOG_FATAL, "sigterm handler failed: %s", strerror(errno));
     exit(1);
   }
 
+  // Apparently some network gear will refuse to perform NS for IPs that aren't DAD'ed,
+  // this would then result in an ipv6-only network with working native ipv6, working
+  // IPv4 via DNS64, but non-functioning IPv4 via CLAT (ie. IPv4 literals + IPv4 only apps).
+  // The kernel itself doesn't do DAD for anycast ips (but does handle IPV6 MLD and handle ND).
+  // So we'll spoof dad here, and yeah, we really should check for a response and in
+  // case of failure pick a different IP.  Seeing as 48-bits of the IP are utterly random
+  // (with the other 16 chosen to guarantee checksum neutrality) this seems like a remote
+  // concern...
+  // TODO: actually perform true DAD
+  send_dad(tunnel.write_fd6, &Global_Clatd_Config.ipv6_local_subnet);
+
   event_loop(&tunnel);
 
-  logmsg(ANDROID_LOG_INFO, "Shutting down clat on %s", uplink_interface);
-
-  if (running) {
-    logmsg(ANDROID_LOG_INFO, "Clatd on %s waiting for SIGTERM", uplink_interface);
+  if (sigterm) {
+    logmsg(ANDROID_LOG_INFO, "Shutting down clatd on %s, already received SIGTERM", uplink_interface);
+  } else {
+    // this implies running == false, ie. we received EOF or ENETDOWN error.
+    logmsg(ANDROID_LOG_INFO, "Shutting down clatd on %s, waiting for SIGTERM", uplink_interface);
     // let's give higher level java code 15 seconds to kill us,
     // but eventually terminate anyway, in case system server forgets about us...
-    // sleep() should be interrupted by SIGTERM, the handler should clear running
+    // sleep() should be interrupted by SIGTERM, the handler should set 'sigterm'
     sleep(15);
     logmsg(ANDROID_LOG_INFO, "Clatd on %s %s SIGTERM", uplink_interface,
-           running ? "timed out waiting for" : "received");
-  } else {
-    logmsg(ANDROID_LOG_INFO, "Clatd on %s already received SIGTERM", uplink_interface);
+           sigterm ? "received" : "timed out waiting for");
   }
   return 0;
 }
diff --git a/framework/src/android/net/ConnectivityManager.java b/framework/src/android/net/ConnectivityManager.java
index 3d7ea69..4f18fa2 100644
--- a/framework/src/android/net/ConnectivityManager.java
+++ b/framework/src/android/net/ConnectivityManager.java
@@ -1252,9 +1252,13 @@
          * {@link ConnectivityManager#registerNetworkAgent}
          * @hide
          */
-        public static Network registerNetworkAgentResult(
+        public static NetworkAndAgentRegistryParcelable registerNetworkAgentResult(
                 @Nullable final Network network, @Nullable final INetworkAgentRegistry registry) {
-            return network;
+            final NetworkAndAgentRegistryParcelable result =
+                    new NetworkAndAgentRegistryParcelable();
+            result.network = network;
+            result.registry = registry;
+            return result;
         }
     }
 
@@ -3968,7 +3972,8 @@
     @RequiresPermission(anyOf = {
             NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
             android.Manifest.permission.NETWORK_FACTORY})
-    public Network registerNetworkAgent(@NonNull INetworkAgent na, @NonNull NetworkInfo ni,
+    public NetworkAndAgentRegistryParcelable registerNetworkAgent(
+            @NonNull INetworkAgent na, @NonNull NetworkInfo ni,
             @NonNull LinkProperties lp, @NonNull NetworkCapabilities nc,
             @NonNull NetworkScore score, @NonNull NetworkAgentConfig config, int providerId) {
         return registerNetworkAgent(na, ni, lp, nc, null /* localNetworkConfig */, score, config,
@@ -3983,7 +3988,8 @@
     @RequiresPermission(anyOf = {
             NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
             android.Manifest.permission.NETWORK_FACTORY})
-    public Network registerNetworkAgent(@NonNull INetworkAgent na, @NonNull NetworkInfo ni,
+    public NetworkAndAgentRegistryParcelable registerNetworkAgent(
+            @NonNull INetworkAgent na, @NonNull NetworkInfo ni,
             @NonNull LinkProperties lp, @NonNull NetworkCapabilities nc,
             @Nullable LocalNetworkConfig localNetworkConfig, @NonNull NetworkScore score,
             @NonNull NetworkAgentConfig config, int providerId) {
diff --git a/framework/src/android/net/IConnectivityManager.aidl b/framework/src/android/net/IConnectivityManager.aidl
index 47b3316..a270684 100644
--- a/framework/src/android/net/IConnectivityManager.aidl
+++ b/framework/src/android/net/IConnectivityManager.aidl
@@ -30,6 +30,7 @@
 import android.net.LocalNetworkConfig;
 import android.net.Network;
 import android.net.NetworkAgentConfig;
+import android.net.NetworkAndAgentRegistryParcelable;
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
 import android.net.NetworkRequest;
@@ -146,7 +147,8 @@
 
     void declareNetworkRequestUnfulfillable(in NetworkRequest request);
 
-    Network registerNetworkAgent(in INetworkAgent na, in NetworkInfo ni, in LinkProperties lp,
+    NetworkAndAgentRegistryParcelable registerNetworkAgent(
+            in INetworkAgent na, in NetworkInfo ni, in LinkProperties lp,
             in NetworkCapabilities nc, in NetworkScore score,
             in LocalNetworkConfig localNetworkConfig, in NetworkAgentConfig config,
             in int factorySerialNumber);
diff --git a/framework/src/android/net/INetworkAgent.aidl b/framework/src/android/net/INetworkAgent.aidl
index fa5175c..c6beeca 100644
--- a/framework/src/android/net/INetworkAgent.aidl
+++ b/framework/src/android/net/INetworkAgent.aidl
@@ -26,7 +26,7 @@
  * @hide
  */
 oneway interface INetworkAgent {
-    void onRegistered(in INetworkAgentRegistry registry);
+    void onRegistered();
     void onDisconnected();
     void onBandwidthUpdateRequested();
     void onValidationStatusChanged(int validationStatus,
diff --git a/framework/src/android/net/INetworkAgentRegistry.aidl b/framework/src/android/net/INetworkAgentRegistry.aidl
index 61b27b5..afdd1ee 100644
--- a/framework/src/android/net/INetworkAgentRegistry.aidl
+++ b/framework/src/android/net/INetworkAgentRegistry.aidl
@@ -30,7 +30,7 @@
  * Interface for NetworkAgents to send network properties.
  * @hide
  */
-oneway interface INetworkAgentRegistry {
+interface INetworkAgentRegistry {
     void sendNetworkCapabilities(in NetworkCapabilities nc);
     void sendLinkProperties(in LinkProperties lp);
     // TODO: consider replacing this by "markConnected()" and removing
diff --git a/framework/src/android/net/NetworkAgent.java b/framework/src/android/net/NetworkAgent.java
index cefa1ea..08f5ecd 100644
--- a/framework/src/android/net/NetworkAgent.java
+++ b/framework/src/android/net/NetworkAgent.java
@@ -98,6 +98,7 @@
     @Nullable
     private volatile Network mNetwork;
 
+    // Null before the agent is registered
     @Nullable
     private volatile INetworkAgentRegistry mRegistry;
 
@@ -121,6 +122,8 @@
     private NetworkInfo mNetworkInfo;
     @NonNull
     private final Object mRegisterLock = new Object();
+    // TODO : move the preconnected queue to the system server and remove this
+    private boolean mConnected = false;
 
     /**
      * The ID of the {@link NetworkProvider} that created this object, or
@@ -606,16 +609,16 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case EVENT_AGENT_CONNECTED: {
-                    if (mRegistry != null) {
-                        log("Received new connection while already connected!");
-                    } else {
-                        if (VDBG) log("NetworkAgent fully connected");
-                        synchronized (mPreConnectedQueue) {
-                            final INetworkAgentRegistry registry = (INetworkAgentRegistry) msg.obj;
-                            mRegistry = registry;
+                    // TODO : move the pre-connected queue to the system server, and remove
+                    // handling this EVENT_AGENT_CONNECTED message.
+                    synchronized (mPreConnectedQueue) {
+                        if (mConnected) {
+                            log("Received new connection while already connected!");
+                        } else {
+                            if (VDBG) log("NetworkAgent fully connected");
                             for (RegistryAction a : mPreConnectedQueue) {
                                 try {
-                                    a.execute(registry);
+                                    a.execute(mRegistry);
                                 } catch (RemoteException e) {
                                     Log.wtf(LOG_TAG, "Communication error with registry", e);
                                     // Fall through
@@ -623,6 +626,7 @@
                             }
                             mPreConnectedQueue.clear();
                         }
+                        mConnected = true;
                     }
                     break;
                 }
@@ -631,7 +635,7 @@
                     // let the client know CS is done with us.
                     onNetworkUnwanted();
                     synchronized (mPreConnectedQueue) {
-                        mRegistry = null;
+                        mConnected = false;
                     }
                     break;
                 }
@@ -758,20 +762,32 @@
             }
             final ConnectivityManager cm = (ConnectivityManager) mInitialConfiguration.context
                     .getSystemService(Context.CONNECTIVITY_SERVICE);
+            final NetworkAndAgentRegistryParcelable result;
             if (mInitialConfiguration.localNetworkConfig == null) {
                 // Call registerNetworkAgent without localNetworkConfig argument to pass
                 // android.net.cts.NetworkAgentTest#testAgentStartsInConnecting in old cts
-                mNetwork = cm.registerNetworkAgent(new NetworkAgentBinder(mHandler),
+                result = cm.registerNetworkAgent(new NetworkAgentBinder(mHandler),
                         new NetworkInfo(mInitialConfiguration.info),
                         mInitialConfiguration.properties, mInitialConfiguration.capabilities,
                         mInitialConfiguration.score, mInitialConfiguration.config, providerId);
             } else {
-                mNetwork = cm.registerNetworkAgent(new NetworkAgentBinder(mHandler),
+                result = cm.registerNetworkAgent(new NetworkAgentBinder(mHandler),
                         new NetworkInfo(mInitialConfiguration.info),
                         mInitialConfiguration.properties, mInitialConfiguration.capabilities,
                         mInitialConfiguration.localNetworkConfig, mInitialConfiguration.score,
                         mInitialConfiguration.config, providerId);
             }
+            if (null == result && Process.isApplicationUid(Process.myUid())) {
+                // Let it slide in tests to allow mocking, since NetworkAndAgentRegistryParcelable
+                // is not public and can't be instantiated by CTS. The danger here is that if
+                // this happens in production for some reason the code will crash later instead
+                // of here. If this is a system app, it will still crash as expected.
+                Log.e(LOG_TAG, "registerNetworkAgent returned null. This agent will not work. "
+                        + "Is ConnectivityManager a mock ?");
+            } else {
+                mNetwork = result.network;
+                mRegistry = result.registry;
+            }
             mInitialConfiguration = null; // All this memory can now be GC'd
         }
         return mNetwork;
@@ -787,8 +803,8 @@
         }
 
         @Override
-        public void onRegistered(@NonNull INetworkAgentRegistry registry) {
-            mHandler.sendMessage(mHandler.obtainMessage(EVENT_AGENT_CONNECTED, registry));
+        public void onRegistered() {
+            mHandler.sendMessage(mHandler.obtainMessage(EVENT_AGENT_CONNECTED));
         }
 
         @Override
@@ -913,11 +929,13 @@
      *
      * @hide
      */
-    public INetworkAgent registerForTest(final Network network) {
+    public INetworkAgent registerForTest(final Network network,
+            final INetworkAgentRegistry registry) {
         log("Registering NetworkAgent for test");
         synchronized (mRegisterLock) {
             mNetwork = network;
             mInitialConfiguration = null;
+            mRegistry = registry;
         }
         return new NetworkAgentBinder(mHandler);
     }
@@ -958,7 +976,7 @@
                         FrameworkConnectivityStatsLog.CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED__ERROR_TYPE__TYPE_MESSAGE_QUEUED_BEFORE_CONNECT
                 );
             }
-            if (mRegistry != null) {
+            if (mConnected) {
                 try {
                     action.execute(mRegistry);
                 } catch (RemoteException e) {
diff --git a/framework/src/android/net/NetworkAndAgentRegistryParcelable.aidl b/framework/src/android/net/NetworkAndAgentRegistryParcelable.aidl
new file mode 100644
index 0000000..8c01bbc
--- /dev/null
+++ b/framework/src/android/net/NetworkAndAgentRegistryParcelable.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2025 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.net.INetworkAgentRegistry;
+import android.net.Network;
+
+/**
+ * A pair of Network and NetworkAgentRegistry.
+ *
+ * {@hide}
+ */
+@JavaDerive(toString=true)
+parcelable NetworkAndAgentRegistryParcelable {
+  Network network;
+  INetworkAgentRegistry registry;
+}
diff --git a/networksecurity/TEST_MAPPING b/networksecurity/TEST_MAPPING
index 448ee84..f75bf9a 100644
--- a/networksecurity/TEST_MAPPING
+++ b/networksecurity/TEST_MAPPING
@@ -1,4 +1,9 @@
 {
+  "tethering-mainline-presubmit": [
+    {
+      "name": "NetworkSecurityUnitTests"
+    }
+  ],
   "presubmit": [
     {
       "name": "CtsNetSecConfigCertificateTransparencyTestCases"
diff --git a/networksecurity/tests/unit/Android.bp b/networksecurity/tests/unit/Android.bp
index 11263cf..1336acc 100644
--- a/networksecurity/tests/unit/Android.bp
+++ b/networksecurity/tests/unit/Android.bp
@@ -41,4 +41,5 @@
     ],
 
     sdk_version: "test_current",
+    min_sdk_version: "VanillaIceCream",
 }
diff --git a/networksecurity/tests/unit/AndroidTest.xml b/networksecurity/tests/unit/AndroidTest.xml
index 3c94df7..fc0943e 100644
--- a/networksecurity/tests/unit/AndroidTest.xml
+++ b/networksecurity/tests/unit/AndroidTest.xml
@@ -17,6 +17,12 @@
 <configuration description="Runs NetworkSecurity Mainline unit Tests.">
     <option name="test-tag" value="NetworkSecurityUnitTests" />
 
+    <!--
+        Only run tests if the device under test is SDK version 35 (Android 15) or above.
+    -->
+    <object type="module_controller"
+            class="com.android.tradefed.testtype.suite.module.Sdk35ModuleController" />
+
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="test-file-name" value="NetworkSecurityUnitTests.apk" />
     </target_preparer>
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index 3ce3f02..329b338 100644
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -228,6 +228,7 @@
 import android.net.Network;
 import android.net.NetworkAgent;
 import android.net.NetworkAgentConfig;
+import android.net.NetworkAndAgentRegistryParcelable;
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.DetailedState;
@@ -526,6 +527,7 @@
     private final boolean mBackgroundFirewallChainEnabled;
 
     private final boolean mUseDeclaredMethodsForCallbacksEnabled;
+    private final boolean mQueueNetworkAgentEventsInSystemServer;
 
     // Flag to delay callbacks for frozen apps, suppressing duplicate and stale callbacks.
     private final boolean mQueueCallbacksForFrozenApps;
@@ -1928,6 +1930,9 @@
         mUseDeclaredMethodsForCallbacksEnabled =
                 mDeps.isFeatureNotChickenedOut(context,
                         ConnectivityFlags.USE_DECLARED_METHODS_FOR_CALLBACKS);
+        mQueueNetworkAgentEventsInSystemServer =
+                mDeps.isFeatureNotChickenedOut(context,
+                        ConnectivityFlags.QUEUE_NETWORK_AGENT_EVENTS_IN_SYSTEM_SERVER);
         // registerUidFrozenStateChangedCallback is only available on U+
         mQueueCallbacksForFrozenApps = mDeps.isAtLeastU()
                 && mDeps.isFeatureNotChickenedOut(context, QUEUE_CALLBACKS_FOR_FROZEN_APPS);
@@ -4688,18 +4693,30 @@
         private void maybeHandleNetworkAgentMessage(Message msg) {
             final Pair<NetworkAgentInfo, Object> arg = (Pair<NetworkAgentInfo, Object>) msg.obj;
             final NetworkAgentInfo nai = arg.first;
-            if (!mNetworkAgentInfos.contains(nai)) {
-                if (VDBG) {
-                    log(String.format("%s from unknown NetworkAgent", eventName(msg.what)));
-                }
-                return;
-            }
 
             // If the network has been destroyed, the only thing that it can do is disconnect.
             if (nai.isDestroyed() && !isDisconnectRequest(msg)) {
                 return;
             }
 
+            if (mQueueNetworkAgentEventsInSystemServer && nai.maybeEnqueueMessage(msg)) {
+                // If the message is enqueued, the NAI will replay it immediately
+                // when registration is complete. It does this by sending all the
+                // messages in the order received immediately after the
+                // EVENT_AGENT_REGISTERED message.
+                return;
+            }
+
+            // If the nai has been registered (and doesn't enqueue), it should now be
+            // in the list of NAIs.
+            if (!mNetworkAgentInfos.contains(nai)) {
+                // TODO : this is supposed to be impossible
+                if (VDBG) {
+                    log(String.format("%s from unknown NetworkAgent", eventName(msg.what)));
+                }
+                return;
+            }
+
             switch (msg.what) {
                 case NetworkAgent.EVENT_NETWORK_CAPABILITIES_CHANGED: {
                     final NetworkCapabilities proposed = (NetworkCapabilities) arg.second;
@@ -7494,7 +7511,7 @@
 
     private boolean isLegacyLockdownNai(NetworkAgentInfo nai) {
         return mLockdownEnabled
-                && getVpnType(nai) == VpnManager.TYPE_VPN_LEGACY
+                && isLegacyVpn(nai)
                 && nai.networkCapabilities.appliesToUid(Process.FIRST_APPLICATION_UID);
     }
 
@@ -9328,7 +9345,7 @@
      * @param providerId the ID of the provider owning this NetworkAgent.
      * @return the network created for this agent.
      */
-    public Network registerNetworkAgent(INetworkAgent na,
+    public NetworkAndAgentRegistryParcelable registerNetworkAgent(INetworkAgent na,
             NetworkInfo networkInfo,
             LinkProperties linkProperties,
             NetworkCapabilities networkCapabilities,
@@ -9371,7 +9388,8 @@
         }
     }
 
-    private Network registerNetworkAgentInternal(INetworkAgent na, NetworkInfo networkInfo,
+    private NetworkAndAgentRegistryParcelable registerNetworkAgentInternal(
+            INetworkAgent na, NetworkInfo networkInfo,
             LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
             NetworkScore currentScore, NetworkAgentConfig networkAgentConfig,
             @Nullable LocalNetworkConfig localNetworkConfig, int providerId,
@@ -9403,8 +9421,11 @@
         // NetworkAgentInfo registration will finish when the NetworkMonitor is created.
         // If the network disconnects or sends any other event before that, messages are deferred by
         // NetworkAgent until nai.connect(), which will be called when finalizing the
-        // registration.
-        return nai.network;
+        // registration. TODO : have NetworkAgentInfo defer them instead.
+        final NetworkAndAgentRegistryParcelable result = new NetworkAndAgentRegistryParcelable();
+        result.network = nai.network;
+        result.registry = nai.getRegistry();
+        return result;
     }
 
     private void handleRegisterNetworkAgent(NetworkAgentInfo nai, INetworkMonitor networkMonitor) {
@@ -9416,8 +9437,6 @@
                 nai.getDeclaredCapabilitiesSanitized(mCarrierPrivilegeAuthenticator)));
         processLinkPropertiesFromAgent(nai, nai.linkProperties);
 
-        nai.onNetworkMonitorCreated(networkMonitor);
-
         mNetworkAgentInfos.add(nai);
         synchronized (mNetworkForNetId) {
             mNetworkForNetId.put(nai.network.getNetId(), nai);
@@ -9432,7 +9451,7 @@
         if (nai.isLocalNetwork()) {
             handleUpdateLocalNetworkConfig(nai, null /* oldConfig */, nai.localNetworkConfig);
         }
-        nai.notifyRegistered();
+        nai.notifyRegistered(networkMonitor);
         NetworkInfo networkInfo = nai.networkInfo;
         updateNetworkInfo(nai, networkInfo);
         updateVpnUids(nai, null, nai.networkCapabilities);
@@ -10133,8 +10152,8 @@
      * interfaces.
      * Ingress discard rule is added to the address iff
      *   1. The address is not a link local address
-     *   2. The address is used by a single interface of VPN whose VPN type is not TYPE_VPN_LEGACY
-     *      or TYPE_VPN_OEM and the address is not used by any other interfaces even non-VPN ones
+     *   2. The address is used by a single interface of VPN whose VPN type is not LEGACY, OEM or
+     *      OEM_LEGACY and the address is not used by any other interfaces even non-VPN ones
      * Ingress discard rule is not be added to TYPE_VPN_LEGACY or TYPE_VPN_OEM VPN since these VPNs
      * might need to receive packet to VPN address via non-VPN interface.
      * This method can be called during network disconnects, when nai has already been removed from
@@ -10172,9 +10191,7 @@
         final Set<Pair<InetAddress, String>> ingressDiscardRules = new ArraySet<>();
         for (final NetworkAgentInfo agent : nais) {
             final int vpnType = getVpnType(agent);
-            if (!agent.isVPN() || agent.isDestroyed()
-                    || vpnType == VpnManager.TYPE_VPN_LEGACY
-                    || vpnType == VpnManager.TYPE_VPN_OEM) {
+            if (!agent.isVPN() || agent.isDestroyed() || !vpnSupportsInterfaceFiltering(agent)) {
                 continue;
             }
             final LinkProperties agentLp = (nai == agent) ? lp : agent.linkProperties;
@@ -12817,6 +12834,23 @@
         return ((VpnTransportInfo) ti).getType();
     }
 
+    private boolean isVpnServiceVpn(NetworkAgentInfo nai) {
+        final int vpnType = getVpnType(nai);
+        return vpnType == VpnManager.TYPE_VPN_SERVICE || vpnType == VpnManager.TYPE_VPN_OEM_SERVICE;
+    }
+
+    private boolean isLegacyVpn(NetworkAgentInfo nai) {
+        final int vpnType = getVpnType(nai);
+        return vpnType == VpnManager.TYPE_VPN_LEGACY || vpnType == VpnManager.TYPE_VPN_OEM_LEGACY;
+    }
+
+    private boolean vpnSupportsInterfaceFiltering(NetworkAgentInfo vpn) {
+        final int vpnType = getVpnType(vpn);
+        return vpnType != VpnManager.TYPE_VPN_LEGACY
+                && vpnType != VpnManager.TYPE_VPN_OEM
+                && vpnType != VpnManager.TYPE_VPN_OEM_LEGACY;
+    }
+
     private void maybeUpdateWifiRoamTimestamp(@NonNull NetworkAgentInfo nai,
             @NonNull NetworkCapabilities nc) {
         final TransportInfo prevInfo = nai.networkCapabilities.getTransportInfo();
@@ -12850,7 +12884,7 @@
         if (hasNetworkStackPermission()) return uid;
 
         final NetworkAgentInfo vpn = getVpnForUid(uid);
-        if (vpn == null || getVpnType(vpn) != VpnManager.TYPE_VPN_SERVICE
+        if (vpn == null || !isVpnServiceVpn(vpn)
                 || vpn.networkCapabilities.getOwnerUid() != mDeps.getCallingUid()) {
             return INVALID_UID;
         }
diff --git a/service/src/com/android/server/connectivity/ConnectivityFlags.java b/service/src/com/android/server/connectivity/ConnectivityFlags.java
index 136ea81..74bd235 100644
--- a/service/src/com/android/server/connectivity/ConnectivityFlags.java
+++ b/service/src/com/android/server/connectivity/ConnectivityFlags.java
@@ -62,6 +62,9 @@
     public static final String QUEUE_CALLBACKS_FOR_FROZEN_APPS =
             "queue_callbacks_for_frozen_apps";
 
+    public static final String QUEUE_NETWORK_AGENT_EVENTS_IN_SYSTEM_SERVER =
+            "queue_network_agent_events_in_system_server";
+
     private boolean mNoRematchAllRequestsOnRegister;
 
     /**
diff --git a/service/src/com/android/server/connectivity/NetworkAgentInfo.java b/service/src/com/android/server/connectivity/NetworkAgentInfo.java
index e762a8e..4540f02 100644
--- a/service/src/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/service/src/com/android/server/connectivity/NetworkAgentInfo.java
@@ -62,6 +62,7 @@
 import android.net.TcpKeepalivePacketData;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.Message;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
 import android.os.SystemClock;
@@ -630,6 +631,7 @@
     // Used by ConnectivityService to keep track of 464xlat.
     public final Nat464Xlat clatd;
 
+    private final ArrayList<Message> mMessagesPendingRegistration = new ArrayList<>();
     // Set after asynchronous creation of the NetworkMonitor.
     private volatile NetworkMonitorManager mNetworkMonitor;
 
@@ -639,6 +641,7 @@
     private final ConnectivityService.Dependencies mConnServiceDeps;
     private final Context mContext;
     private final Handler mHandler;
+    private final NetworkAgentMessageHandler mRegistry;
     private final QosCallbackTracker mQosCallbackTracker;
     private final INetd mNetd;
 
@@ -673,6 +676,7 @@
         mNetd = netd;
         mContext = context;
         mHandler = handler;
+        mRegistry = new NetworkAgentMessageHandler(mHandler);
         this.factorySerialNumber = factorySerialNumber;
         this.creatorUid = creatorUid;
         mLingerDurationMs = lingerDurationMs;
@@ -698,10 +702,12 @@
      * Must be called from the ConnectivityService handler thread. A NetworkAgent can only be
      * registered once.
      */
-    public void notifyRegistered() {
+    public void notifyRegistered(final INetworkMonitor nm) {
+        HandlerUtils.ensureRunningOnHandlerThread(mHandler);
+        mNetworkMonitor = new NetworkMonitorManager(nm);
         try {
             networkAgent.asBinder().linkToDeath(mDeathMonitor, 0);
-            networkAgent.onRegistered(new NetworkAgentMessageHandler(mHandler));
+            networkAgent.onRegistered();
         } catch (RemoteException e) {
             Log.e(TAG, "Error registering NetworkAgent", e);
             maybeUnlinkDeathMonitor();
@@ -711,6 +717,29 @@
         }
 
         mHandler.obtainMessage(EVENT_AGENT_REGISTERED, ARG_AGENT_SUCCESS, 0, this).sendToTarget();
+        for (final Message enqueued : mMessagesPendingRegistration) {
+            mHandler.sendMessage(enqueued);
+        }
+        mMessagesPendingRegistration.clear();
+    }
+
+    /**
+     * Enqueues a message if it needs to be enqueued, and returns whether it was enqueued.
+     *
+     * The message is enqueued iff it can't be sent just yet. If it can be sent
+     * immediately, this method returns false and doesn't enqueue.
+     *
+     * If it enqueues, this method will make a copy of the message for enqueuing since
+     * messages can't be reused or recycled before the end of their processing by the
+     * handler.
+     */
+    public boolean maybeEnqueueMessage(final Message msg) {
+        HandlerUtils.ensureRunningOnHandlerThread(mHandler);
+        if (null != mNetworkMonitor) return false;
+        final Message m = mHandler.obtainMessage();
+        m.copyFrom(msg);
+        mMessagesPendingRegistration.add(m);
+        return true;
     }
 
     /**
@@ -1036,13 +1065,6 @@
     }
 
     /**
-     * Inform NetworkAgentInfo that a new NetworkMonitor was created.
-     */
-    public void onNetworkMonitorCreated(INetworkMonitor networkMonitor) {
-        mNetworkMonitor = new NetworkMonitorManager(networkMonitor);
-    }
-
-    /**
      * Set the NetworkCapabilities on this NetworkAgentInfo. Also attempts to notify NetworkMonitor
      * of the new capabilities, if NetworkMonitor has been created.
      *
@@ -1117,6 +1139,13 @@
         return mNetworkMonitor;
     }
 
+    /**
+     * Get the registry in this NetworkAgentInfo.
+     */
+    public INetworkAgentRegistry getRegistry() {
+        return mRegistry;
+    }
+
     // Functions for manipulating the requests satisfied by this network.
     //
     // These functions must only called on ConnectivityService's main thread.
diff --git a/staticlibs/netd/Android.bp b/staticlibs/netd/Android.bp
index 03f5f06..9222b17 100644
--- a/staticlibs/netd/Android.bp
+++ b/staticlibs/netd/Android.bp
@@ -22,7 +22,7 @@
     sdk_version: "system_current",
     min_sdk_version: "30",
     static_libs: [
-        "netd_aidl_interface-V16-java",
+        "netd_aidl_interface-V17-java",
     ],
     apex_available: [
         "//apex_available:platform", // used from services.net
@@ -45,7 +45,7 @@
 cc_library_static {
     name: "netd_aidl_interface-lateststable-ndk",
     whole_static_libs: [
-        "netd_aidl_interface-V16-ndk",
+        "netd_aidl_interface-V17-ndk",
     ],
     apex_available: [
         "com.android.resolv",
@@ -56,12 +56,12 @@
 
 cc_defaults {
     name: "netd_aidl_interface_lateststable_cpp_static",
-    static_libs: ["netd_aidl_interface-V16-cpp"],
+    static_libs: ["netd_aidl_interface-V17-cpp"],
 }
 
 cc_defaults {
     name: "netd_aidl_interface_lateststable_cpp_shared",
-    shared_libs: ["netd_aidl_interface-V16-cpp"],
+    shared_libs: ["netd_aidl_interface-V17-cpp"],
 }
 
 aidl_interface {
@@ -175,6 +175,10 @@
             version: "16",
             imports: [],
         },
+        {
+            version: "17",
+            imports: [],
+        },
 
     ],
     frozen: true,
diff --git a/staticlibs/netd/aidl_api/netd_aidl_interface/17/.hash b/staticlibs/netd/aidl_api/netd_aidl_interface/17/.hash
new file mode 100644
index 0000000..f69c88b
--- /dev/null
+++ b/staticlibs/netd/aidl_api/netd_aidl_interface/17/.hash
@@ -0,0 +1 @@
+a786da2fe41bda627a8c1e63b99264a415e769c8
diff --git a/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/INetd.aidl b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/INetd.aidl
new file mode 100644
index 0000000..8351b56
--- /dev/null
+++ b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/INetd.aidl
@@ -0,0 +1,272 @@
+/**
+ * Copyright (c) 2016, 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+/* @hide */
+interface INetd {
+  boolean isAlive();
+  /**
+   * @deprecated unimplemented on T+.
+   */
+  boolean firewallReplaceUidChain(in @utf8InCpp String chainName, boolean isAllowlist, in int[] uids);
+  boolean bandwidthEnableDataSaver(boolean enable);
+  /**
+   * @deprecated use networkCreate() instead.
+   */
+  void networkCreatePhysical(int netId, int permission);
+  /**
+   * @deprecated use networkCreate() instead.
+   */
+  void networkCreateVpn(int netId, boolean secure);
+  void networkDestroy(int netId);
+  void networkAddInterface(int netId, in @utf8InCpp String iface);
+  void networkRemoveInterface(int netId, in @utf8InCpp String iface);
+  void networkAddUidRanges(int netId, in android.net.UidRangeParcel[] uidRanges);
+  void networkRemoveUidRanges(int netId, in android.net.UidRangeParcel[] uidRanges);
+  void networkRejectNonSecureVpn(boolean add, in android.net.UidRangeParcel[] uidRanges);
+  void socketDestroy(in android.net.UidRangeParcel[] uidRanges, in int[] exemptUids);
+  boolean tetherApplyDnsInterfaces();
+  android.net.TetherStatsParcel[] tetherGetStats();
+  void interfaceAddAddress(in @utf8InCpp String ifName, in @utf8InCpp String addrString, int prefixLength);
+  void interfaceDelAddress(in @utf8InCpp String ifName, in @utf8InCpp String addrString, int prefixLength);
+  @utf8InCpp String getProcSysNet(int ipversion, int which, in @utf8InCpp String ifname, in @utf8InCpp String parameter);
+  void setProcSysNet(int ipversion, int which, in @utf8InCpp String ifname, in @utf8InCpp String parameter, in @utf8InCpp String value);
+  void ipSecSetEncapSocketOwner(in ParcelFileDescriptor socket, int newUid);
+  int ipSecAllocateSpi(int transformId, in @utf8InCpp String sourceAddress, in @utf8InCpp String destinationAddress, int spi);
+  void ipSecAddSecurityAssociation(int transformId, int mode, in @utf8InCpp String sourceAddress, in @utf8InCpp String destinationAddress, int underlyingNetId, int spi, int markValue, int markMask, in @utf8InCpp String authAlgo, in byte[] authKey, in int authTruncBits, in @utf8InCpp String cryptAlgo, in byte[] cryptKey, in int cryptTruncBits, in @utf8InCpp String aeadAlgo, in byte[] aeadKey, in int aeadIcvBits, int encapType, int encapLocalPort, int encapRemotePort, int interfaceId);
+  void ipSecDeleteSecurityAssociation(int transformId, in @utf8InCpp String sourceAddress, in @utf8InCpp String destinationAddress, int spi, int markValue, int markMask, int interfaceId);
+  void ipSecApplyTransportModeTransform(in ParcelFileDescriptor socket, int transformId, int direction, in @utf8InCpp String sourceAddress, in @utf8InCpp String destinationAddress, int spi);
+  void ipSecRemoveTransportModeTransform(in ParcelFileDescriptor socket);
+  void ipSecAddSecurityPolicy(int transformId, int selAddrFamily, int direction, in @utf8InCpp String tmplSrcAddress, in @utf8InCpp String tmplDstAddress, int spi, int markValue, int markMask, int interfaceId);
+  void ipSecUpdateSecurityPolicy(int transformId, int selAddrFamily, int direction, in @utf8InCpp String tmplSrcAddress, in @utf8InCpp String tmplDstAddress, int spi, int markValue, int markMask, int interfaceId);
+  void ipSecDeleteSecurityPolicy(int transformId, int selAddrFamily, int direction, int markValue, int markMask, int interfaceId);
+  void ipSecAddTunnelInterface(in @utf8InCpp String deviceName, in @utf8InCpp String localAddress, in @utf8InCpp String remoteAddress, int iKey, int oKey, int interfaceId);
+  void ipSecUpdateTunnelInterface(in @utf8InCpp String deviceName, in @utf8InCpp String localAddress, in @utf8InCpp String remoteAddress, int iKey, int oKey, int interfaceId);
+  void ipSecRemoveTunnelInterface(in @utf8InCpp String deviceName);
+  void wakeupAddInterface(in @utf8InCpp String ifName, in @utf8InCpp String prefix, int mark, int mask);
+  void wakeupDelInterface(in @utf8InCpp String ifName, in @utf8InCpp String prefix, int mark, int mask);
+  void setIPv6AddrGenMode(in @utf8InCpp String ifName, int mode);
+  void idletimerAddInterface(in @utf8InCpp String ifName, int timeout, in @utf8InCpp String classLabel);
+  void idletimerRemoveInterface(in @utf8InCpp String ifName, int timeout, in @utf8InCpp String classLabel);
+  void strictUidCleartextPenalty(int uid, int policyPenalty);
+  /**
+   * @deprecated This method has no effect and throws UnsupportedOperationException. The clatd control plane moved to the mainline module starting in T. See ClatCoordinator.
+   */
+  @utf8InCpp String clatdStart(in @utf8InCpp String ifName, in @utf8InCpp String nat64Prefix);
+  /**
+   * @deprecated This method has no effect and throws UnsupportedOperationException. The clatd control plane moved to the mainline module starting in T. See ClatCoordinator.
+   */
+  void clatdStop(in @utf8InCpp String ifName);
+  boolean ipfwdEnabled();
+  @utf8InCpp String[] ipfwdGetRequesterList();
+  void ipfwdEnableForwarding(in @utf8InCpp String requester);
+  void ipfwdDisableForwarding(in @utf8InCpp String requester);
+  void ipfwdAddInterfaceForward(in @utf8InCpp String fromIface, in @utf8InCpp String toIface);
+  void ipfwdRemoveInterfaceForward(in @utf8InCpp String fromIface, in @utf8InCpp String toIface);
+  void bandwidthSetInterfaceQuota(in @utf8InCpp String ifName, long bytes);
+  void bandwidthRemoveInterfaceQuota(in @utf8InCpp String ifName);
+  void bandwidthSetInterfaceAlert(in @utf8InCpp String ifName, long bytes);
+  void bandwidthRemoveInterfaceAlert(in @utf8InCpp String ifName);
+  void bandwidthSetGlobalAlert(long bytes);
+  /**
+   * @deprecated unimplemented on T+.
+   */
+  void bandwidthAddNaughtyApp(int uid);
+  /**
+   * @deprecated unimplemented on T+.
+   */
+  void bandwidthRemoveNaughtyApp(int uid);
+  /**
+   * @deprecated unimplemented on T+.
+   */
+  void bandwidthAddNiceApp(int uid);
+  /**
+   * @deprecated unimplemented on T+.
+   */
+  void bandwidthRemoveNiceApp(int uid);
+  void tetherStart(in @utf8InCpp String[] dhcpRanges);
+  void tetherStop();
+  boolean tetherIsEnabled();
+  void tetherInterfaceAdd(in @utf8InCpp String ifName);
+  void tetherInterfaceRemove(in @utf8InCpp String ifName);
+  @utf8InCpp String[] tetherInterfaceList();
+  void tetherDnsSet(int netId, in @utf8InCpp String[] dnsAddrs);
+  @utf8InCpp String[] tetherDnsList();
+  void networkAddRoute(int netId, in @utf8InCpp String ifName, in @utf8InCpp String destination, in @utf8InCpp String nextHop);
+  void networkRemoveRoute(int netId, in @utf8InCpp String ifName, in @utf8InCpp String destination, in @utf8InCpp String nextHop);
+  void networkAddLegacyRoute(int netId, in @utf8InCpp String ifName, in @utf8InCpp String destination, in @utf8InCpp String nextHop, int uid);
+  void networkRemoveLegacyRoute(int netId, in @utf8InCpp String ifName, in @utf8InCpp String destination, in @utf8InCpp String nextHop, int uid);
+  int networkGetDefault();
+  void networkSetDefault(int netId);
+  void networkClearDefault();
+  void networkSetPermissionForNetwork(int netId, int permission);
+  void networkSetPermissionForUser(int permission, in int[] uids);
+  void networkClearPermissionForUser(in int[] uids);
+  /**
+   * @deprecated unimplemented on T+.
+   */
+  void trafficSetNetPermForUids(int permission, in int[] uids);
+  void networkSetProtectAllow(int uid);
+  void networkSetProtectDeny(int uid);
+  boolean networkCanProtect(int uid);
+  void firewallSetFirewallType(int firewalltype);
+  void firewallSetInterfaceRule(in @utf8InCpp String ifName, int firewallRule);
+  /**
+   * @deprecated unimplemented on T+.
+   */
+  void firewallSetUidRule(int childChain, int uid, int firewallRule);
+  /**
+   * @deprecated unimplemented on T+.
+   */
+  void firewallEnableChildChain(int childChain, boolean enable);
+  @utf8InCpp String[] interfaceGetList();
+  android.net.InterfaceConfigurationParcel interfaceGetCfg(in @utf8InCpp String ifName);
+  void interfaceSetCfg(in android.net.InterfaceConfigurationParcel cfg);
+  void interfaceSetIPv6PrivacyExtensions(in @utf8InCpp String ifName, boolean enable);
+  void interfaceClearAddrs(in @utf8InCpp String ifName);
+  void interfaceSetEnableIPv6(in @utf8InCpp String ifName, boolean enable);
+  void interfaceSetMtu(in @utf8InCpp String ifName, int mtu);
+  void tetherAddForward(in @utf8InCpp String intIface, in @utf8InCpp String extIface);
+  void tetherRemoveForward(in @utf8InCpp String intIface, in @utf8InCpp String extIface);
+  void setTcpRWmemorySize(in @utf8InCpp String rmemValues, in @utf8InCpp String wmemValues);
+  void registerUnsolicitedEventListener(android.net.INetdUnsolicitedEventListener listener);
+  /**
+   * @deprecated unimplemented on T+.
+   */
+  void firewallAddUidInterfaceRules(in @utf8InCpp String ifName, in int[] uids);
+  /**
+   * @deprecated unimplemented on T+.
+   */
+  void firewallRemoveUidInterfaceRules(in int[] uids);
+  /**
+   * @deprecated unimplemented on T+.
+   */
+  void trafficSwapActiveStatsMap();
+  IBinder getOemNetd();
+  void tetherStartWithConfiguration(in android.net.TetherConfigParcel config);
+  android.net.MarkMaskParcel getFwmarkForNetwork(int netId);
+  void networkAddRouteParcel(int netId, in android.net.RouteInfoParcel routeInfo);
+  void networkUpdateRouteParcel(int netId, in android.net.RouteInfoParcel routeInfo);
+  void networkRemoveRouteParcel(int netId, in android.net.RouteInfoParcel routeInfo);
+  /**
+   * @deprecated This method has no effect and throws UnsupportedOperationException. The mainline module accesses the BPF map directly starting in S. See BpfCoordinator.
+   */
+  void tetherOffloadRuleAdd(in android.net.TetherOffloadRuleParcel rule);
+  /**
+   * @deprecated This method has no effect and throws UnsupportedOperationException. The mainline module accesses the BPF map directly starting in S. See BpfCoordinator.
+   */
+  void tetherOffloadRuleRemove(in android.net.TetherOffloadRuleParcel rule);
+  /**
+   * @deprecated This method has no effect and throws UnsupportedOperationException. The mainline module accesses the BPF map directly starting in S. See BpfCoordinator.
+   */
+  android.net.TetherStatsParcel[] tetherOffloadGetStats();
+  /**
+   * @deprecated This method has no effect and throws UnsupportedOperationException. The mainline module accesses the BPF map directly starting in S. See BpfCoordinator.
+   */
+  void tetherOffloadSetInterfaceQuota(int ifIndex, long quotaBytes);
+  /**
+   * @deprecated This method has no effect and throws UnsupportedOperationException. The mainline module accesses the BPF map directly starting in S. See BpfCoordinator.
+   */
+  android.net.TetherStatsParcel tetherOffloadGetAndClearStats(int ifIndex);
+  void networkCreate(in android.net.NativeNetworkConfig config);
+  void networkAddUidRangesParcel(in android.net.netd.aidl.NativeUidRangeConfig uidRangesConfig);
+  void networkRemoveUidRangesParcel(in android.net.netd.aidl.NativeUidRangeConfig uidRangesConfig);
+  void ipSecMigrate(in android.net.IpSecMigrateInfoParcel migrateInfo);
+  void setNetworkAllowlist(in android.net.netd.aidl.NativeUidRangeConfig[] allowedNetworks);
+  void networkAllowBypassVpnOnNetwork(boolean allow, int uid, int netId);
+  const int IPV4 = 4;
+  const int IPV6 = 6;
+  const int CONF = 1;
+  const int NEIGH = 2;
+  const String IPSEC_INTERFACE_PREFIX = "ipsec";
+  const int IPV6_ADDR_GEN_MODE_EUI64 = 0;
+  const int IPV6_ADDR_GEN_MODE_NONE = 1;
+  const int IPV6_ADDR_GEN_MODE_STABLE_PRIVACY = 2;
+  const int IPV6_ADDR_GEN_MODE_RANDOM = 3;
+  const int IPV6_ADDR_GEN_MODE_DEFAULT = 0;
+  const int PENALTY_POLICY_ACCEPT = 1;
+  const int PENALTY_POLICY_LOG = 2;
+  const int PENALTY_POLICY_REJECT = 3;
+  const int CLAT_MARK = 0xdeadc1a7;
+  const int LOCAL_NET_ID = 99;
+  const int DUMMY_NET_ID = 51;
+  const int UNREACHABLE_NET_ID = 52;
+  const String NEXTHOP_NONE = "";
+  const String NEXTHOP_UNREACHABLE = "unreachable";
+  const String NEXTHOP_THROW = "throw";
+  const int PERMISSION_NONE = 0;
+  const int PERMISSION_NETWORK = 1;
+  const int PERMISSION_SYSTEM = 2;
+  /**
+   * @deprecated usage is internal to module.
+   */
+  const int NO_PERMISSIONS = 0;
+  /**
+   * @deprecated usage is internal to module.
+   */
+  const int PERMISSION_INTERNET = 4;
+  /**
+   * @deprecated usage is internal to module.
+   */
+  const int PERMISSION_UPDATE_DEVICE_STATS = 8;
+  /**
+   * @deprecated usage is internal to module.
+   */
+  const int PERMISSION_UNINSTALLED = (-1) /* -1 */;
+  /**
+   * @deprecated use FIREWALL_ALLOWLIST.
+   */
+  const int FIREWALL_WHITELIST = 0;
+  const int FIREWALL_ALLOWLIST = 0;
+  /**
+   * @deprecated use FIREWALL_DENYLIST.
+   */
+  const int FIREWALL_BLACKLIST = 1;
+  const int FIREWALL_DENYLIST = 1;
+  const int FIREWALL_RULE_ALLOW = 1;
+  const int FIREWALL_RULE_DENY = 2;
+  const int FIREWALL_CHAIN_NONE = 0;
+  const int FIREWALL_CHAIN_DOZABLE = 1;
+  const int FIREWALL_CHAIN_STANDBY = 2;
+  const int FIREWALL_CHAIN_POWERSAVE = 3;
+  const int FIREWALL_CHAIN_RESTRICTED = 4;
+  const String IF_STATE_UP = "up";
+  const String IF_STATE_DOWN = "down";
+  const String IF_FLAG_BROADCAST = "broadcast";
+  const String IF_FLAG_LOOPBACK = "loopback";
+  const String IF_FLAG_POINTOPOINT = "point-to-point";
+  const String IF_FLAG_RUNNING = "running";
+  const String IF_FLAG_MULTICAST = "multicast";
+  const int IPSEC_DIRECTION_IN = 0;
+  const int IPSEC_DIRECTION_OUT = 1;
+}
diff --git a/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/INetdUnsolicitedEventListener.aidl b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/INetdUnsolicitedEventListener.aidl
new file mode 100644
index 0000000..31775df
--- /dev/null
+++ b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/INetdUnsolicitedEventListener.aidl
@@ -0,0 +1,48 @@
+/**
+ * Copyright (c) 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+/* @hide */
+interface INetdUnsolicitedEventListener {
+  oneway void onInterfaceClassActivityChanged(boolean isActive, int timerLabel, long timestampNs, int uid);
+  oneway void onQuotaLimitReached(@utf8InCpp String alertName, @utf8InCpp String ifName);
+  oneway void onInterfaceDnsServerInfo(@utf8InCpp String ifName, long lifetimeS, in @utf8InCpp String[] servers);
+  oneway void onInterfaceAddressUpdated(@utf8InCpp String addr, @utf8InCpp String ifName, int flags, int scope);
+  oneway void onInterfaceAddressRemoved(@utf8InCpp String addr, @utf8InCpp String ifName, int flags, int scope);
+  oneway void onInterfaceAdded(@utf8InCpp String ifName);
+  oneway void onInterfaceRemoved(@utf8InCpp String ifName);
+  oneway void onInterfaceChanged(@utf8InCpp String ifName, boolean up);
+  oneway void onInterfaceLinkStateChanged(@utf8InCpp String ifName, boolean up);
+  oneway void onRouteChanged(boolean updated, @utf8InCpp String route, @utf8InCpp String gateway, @utf8InCpp String ifName);
+  oneway void onStrictCleartextDetected(int uid, @utf8InCpp String hex);
+}
diff --git a/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/InterfaceConfigurationParcel.aidl b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/InterfaceConfigurationParcel.aidl
new file mode 100644
index 0000000..1869d8d
--- /dev/null
+++ b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/InterfaceConfigurationParcel.aidl
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+/* @hide */
+parcelable InterfaceConfigurationParcel {
+  @utf8InCpp String ifName;
+  @utf8InCpp String hwAddr;
+  @utf8InCpp String ipv4Addr;
+  int prefixLength;
+  @utf8InCpp String[] flags;
+}
diff --git a/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/IpSecMigrateInfoParcel.aidl b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/IpSecMigrateInfoParcel.aidl
new file mode 100644
index 0000000..975a261
--- /dev/null
+++ b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/IpSecMigrateInfoParcel.aidl
@@ -0,0 +1,45 @@
+/**
+ * Copyright (c) 2022, 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+@JavaOnlyImmutable
+parcelable IpSecMigrateInfoParcel {
+  int requestId;
+  int selAddrFamily;
+  int direction;
+  @utf8InCpp String oldSourceAddress;
+  @utf8InCpp String oldDestinationAddress;
+  @utf8InCpp String newSourceAddress;
+  @utf8InCpp String newDestinationAddress;
+  int interfaceId;
+}
diff --git a/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/MarkMaskParcel.aidl b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/MarkMaskParcel.aidl
new file mode 100644
index 0000000..8ea20d1
--- /dev/null
+++ b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/MarkMaskParcel.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+/* @hide */
+parcelable MarkMaskParcel {
+  int mark;
+  int mask;
+}
diff --git a/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/NativeNetworkConfig.aidl b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/NativeNetworkConfig.aidl
new file mode 100644
index 0000000..77d814b
--- /dev/null
+++ b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/NativeNetworkConfig.aidl
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @JavaOnlyImmutable
+parcelable NativeNetworkConfig {
+  int netId;
+  android.net.NativeNetworkType networkType = android.net.NativeNetworkType.PHYSICAL;
+  int permission;
+  boolean secure;
+  android.net.NativeVpnType vpnType = android.net.NativeVpnType.PLATFORM;
+  boolean excludeLocalRoutes = false;
+}
diff --git a/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/NativeNetworkType.aidl b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/NativeNetworkType.aidl
new file mode 100644
index 0000000..e77a143
--- /dev/null
+++ b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/NativeNetworkType.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+@Backing(type="int")
+enum NativeNetworkType {
+  PHYSICAL = 0,
+  VIRTUAL = 1,
+  PHYSICAL_LOCAL = 2,
+}
diff --git a/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/NativeVpnType.aidl b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/NativeVpnType.aidl
new file mode 100644
index 0000000..a6af5f7
--- /dev/null
+++ b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/NativeVpnType.aidl
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+@Backing(type="int")
+enum NativeVpnType {
+  SERVICE = 1,
+  PLATFORM = 2,
+  LEGACY = 3,
+  OEM = 4,
+  OEM_SERVICE = 5,
+  OEM_LEGACY = 6,
+}
diff --git a/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/RouteInfoParcel.aidl b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/RouteInfoParcel.aidl
new file mode 100644
index 0000000..5ef95e6
--- /dev/null
+++ b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/RouteInfoParcel.aidl
@@ -0,0 +1,40 @@
+/**
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+parcelable RouteInfoParcel {
+  @utf8InCpp String destination;
+  @utf8InCpp String ifName;
+  @utf8InCpp String nextHop;
+  int mtu;
+}
diff --git a/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/TetherConfigParcel.aidl b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/TetherConfigParcel.aidl
new file mode 100644
index 0000000..7b39c22
--- /dev/null
+++ b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/TetherConfigParcel.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+/* @hide */
+parcelable TetherConfigParcel {
+  boolean usingLegacyDnsProxy;
+  @utf8InCpp String[] dhcpRanges;
+}
diff --git a/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/TetherOffloadRuleParcel.aidl b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/TetherOffloadRuleParcel.aidl
new file mode 100644
index 0000000..983e986
--- /dev/null
+++ b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/TetherOffloadRuleParcel.aidl
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+/* @hide */
+parcelable TetherOffloadRuleParcel {
+  int inputInterfaceIndex;
+  int outputInterfaceIndex;
+  byte[] destination;
+  int prefixLength;
+  byte[] srcL2Address;
+  byte[] dstL2Address;
+  int pmtu = 1500;
+}
diff --git a/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/TetherStatsParcel.aidl b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/TetherStatsParcel.aidl
new file mode 100644
index 0000000..5f1b722
--- /dev/null
+++ b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/TetherStatsParcel.aidl
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+/* @hide */
+parcelable TetherStatsParcel {
+  @utf8InCpp String iface;
+  long rxBytes;
+  long rxPackets;
+  long txBytes;
+  long txPackets;
+  int ifIndex = 0;
+}
diff --git a/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/UidRangeParcel.aidl b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/UidRangeParcel.aidl
new file mode 100644
index 0000000..72e987a
--- /dev/null
+++ b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/UidRangeParcel.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @JavaOnlyImmutable
+parcelable UidRangeParcel {
+  int start;
+  int stop;
+}
diff --git a/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/netd/aidl/NativeUidRangeConfig.aidl b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/netd/aidl/NativeUidRangeConfig.aidl
new file mode 100644
index 0000000..9bb679f
--- /dev/null
+++ b/staticlibs/netd/aidl_api/netd_aidl_interface/17/android/net/netd/aidl/NativeUidRangeConfig.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net.netd.aidl;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @JavaOnlyImmutable
+parcelable NativeUidRangeConfig {
+  int netId;
+  android.net.UidRangeParcel[] uidRanges;
+  int subPriority;
+}
diff --git a/staticlibs/netd/aidl_api/netd_aidl_interface/current/android/net/NativeVpnType.aidl b/staticlibs/netd/aidl_api/netd_aidl_interface/current/android/net/NativeVpnType.aidl
index 8a8be83..a6af5f7 100644
--- a/staticlibs/netd/aidl_api/netd_aidl_interface/current/android/net/NativeVpnType.aidl
+++ b/staticlibs/netd/aidl_api/netd_aidl_interface/current/android/net/NativeVpnType.aidl
@@ -38,4 +38,6 @@
   PLATFORM = 2,
   LEGACY = 3,
   OEM = 4,
+  OEM_SERVICE = 5,
+  OEM_LEGACY = 6,
 }
diff --git a/staticlibs/netd/binder/android/net/NativeVpnType.aidl b/staticlibs/netd/binder/android/net/NativeVpnType.aidl
index cd1b447..aa0fdc1 100644
--- a/staticlibs/netd/binder/android/net/NativeVpnType.aidl
+++ b/staticlibs/netd/binder/android/net/NativeVpnType.aidl
@@ -34,7 +34,20 @@
   LEGACY = 3,
 
   /**
-   * An VPN created by OEM code through other means than VpnService or VpnManager.
+   * A VPN created by OEM code through other means than VpnService or VpnManager.
    */
   OEM = 4,
-}
\ No newline at end of file
+
+  /**
+   * A VPN created by OEM code using VpnService, and which OEM code desires to differentiate from
+   * other VPN types. The core networking stack will treat this VPN type similarly to SERVICE.
+   */
+  OEM_SERVICE = 5,
+
+  /**
+   * A VPN created by OEM code using the legacy VPN mechanisms, and which OEM code desires to
+   * differentiate from other VPN types. The core networking stack will treat this VPN type
+   * similarly to LEGACY.
+   */
+  OEM_LEGACY = 6,
+}
diff --git a/staticlibs/tests/unit/host/python/apf_utils_test.py b/staticlibs/tests/unit/host/python/apf_utils_test.py
index 8059e22..d4753b7 100644
--- a/staticlibs/tests/unit/host/python/apf_utils_test.py
+++ b/staticlibs/tests/unit/host/python/apf_utils_test.py
@@ -144,7 +144,7 @@
     asserts.assert_equal(ip_addresses, [])
 
   @patch("net_tests_utils.host.python.adb_utils.adb_shell")
-  def test_get_ipv6_addresses_success(
+  def test_get_non_tentative_ipv6_addresses_success(
       self, mock_adb_shell: MagicMock
   ) -> None:
     mock_adb_shell.return_value = """
@@ -156,7 +156,7 @@
     inet6 fe80::3aff:2199:2d8e:20d1/64 scope link noprefixroute
         valid_lft forever preferred_lft forever
 """
-    ip_addresses = get_ipv6_addresses(self.mock_ad, "wlan0")
+    ip_addresses = get_non_tentative_ipv6_addresses(self.mock_ad, "wlan0")
     asserts.assert_equal(ip_addresses,
                          ["fe80::10a3:5dff:fe52:de32",
                           "2001:b400:e53f:164e:9c1e:780e:d1:4658",
@@ -167,7 +167,7 @@
           self, mock_adb_shell: MagicMock
   ) -> None:
     mock_adb_shell.return_value = ""
-    ip_addresses = get_ipv6_addresses(self.mock_ad, "wlan0")
+    ip_addresses = get_non_tentative_ipv6_addresses(self.mock_ad, "wlan0")
     asserts.assert_equal(ip_addresses, [])
 
   @patch("net_tests_utils.host.python.adb_utils.adb_shell")
diff --git a/staticlibs/testutils/devicetests/com/android/testutils/ConnectivityDiagnosticsCollector.kt b/staticlibs/testutils/devicetests/com/android/testutils/ConnectivityDiagnosticsCollector.kt
index 8a255c6..60285a8 100644
--- a/staticlibs/testutils/devicetests/com/android/testutils/ConnectivityDiagnosticsCollector.kt
+++ b/staticlibs/testutils/devicetests/com/android/testutils/ConnectivityDiagnosticsCollector.kt
@@ -45,7 +45,6 @@
 import java.io.ByteArrayOutputStream
 import java.io.CharArrayWriter
 import java.io.File
-import java.io.FileOutputStream
 import java.io.FileReader
 import java.io.OutputStream
 import java.io.OutputStreamWriter
@@ -280,7 +279,7 @@
         }
         val outFile = File(collectorDir, filename + FILENAME_SUFFIX)
         outputFiles.add(filename)
-        FileOutputStream(outFile).use { fos ->
+        getOutputStreamViaShell(outFile).use { fos ->
             failureHeader?.let {
                 fos.write(it.toByteArray())
                 fos.write("\n".toByteArray())
diff --git a/tests/cts/multidevices/apfv6_test.py b/tests/cts/multidevices/apfv6_test.py
index 61f1bfc..b82a3be 100644
--- a/tests/cts/multidevices/apfv6_test.py
+++ b/tests/cts/multidevices/apfv6_test.py
@@ -13,8 +13,9 @@
 #  limitations under the License.
 
 from mobly import asserts
-from scapy.layers.inet import IP, ICMP
+from scapy.layers.inet import IP, ICMP, IPOption_Router_Alert
 from scapy.layers.l2 import Ether
+from scapy.contrib.igmpv3 import IGMPv3, IGMPv3mq, IGMPv3mr, IGMPv3gr
 from net_tests_utils.host.python import apf_test_base, apf_utils, adb_utils, assert_utils, packet_utils
 
 APFV6_VERSION = 6000
@@ -98,4 +99,48 @@
         expected_echo_reply = bytes(eth/ip/icmp/b"hello").hex()
         self.send_packet_and_expect_reply_received(
             echo_request, "DROPPED_IPV4_PING_REQUEST_REPLIED", expected_echo_reply
-        )
\ No newline at end of file
+        )
+
+    @apf_utils.at_least_B()
+    def test_igmpv3_general_query_offload(self):
+        ether = Ether(src=self.server_mac_address, dst='01:00:5e:00:00:01')
+        ip = IP(
+            src=self.server_ipv4_addresses[0],
+            dst='224.0.0.1',
+            options=[IPOption_Router_Alert()]
+        )
+        igmp = IGMPv3(type=0x11)/IGMPv3mq()
+        igmpv3_general_query = bytes(ether/ip/igmp).hex()
+
+        mcast_addrs = ['239.0.0.1', '239.0.0.2', '239.0.0.3']
+
+        for addr in mcast_addrs:
+            adb_utils.adb_shell(
+                self.clientDevice,
+                f'ip addr add {addr}/32 dev {self.client_iface_name} autojoin'
+            )
+
+        ether = Ether(src=self.client_mac_address, dst='01:00:5e:00:00:16')
+        ip = IP(
+            src=self.client_ipv4_addresses[0],
+            dst='224.0.0.22',
+            options=[IPOption_Router_Alert()],
+            id=0,
+            flags="DF"
+        )
+        igmpv3_hdr = IGMPv3(type=0x22)
+        mcast_records = []
+        for addr in mcast_addrs:
+            mcast_records.append(IGMPv3gr(rtype=2, maddr=addr))
+
+        igmp = IGMPv3mr(records=mcast_records)
+        expected_igmpv3_report = bytes(ether/ip/igmpv3_hdr/igmp).hex()
+        self.send_packet_and_expect_reply_received(
+            igmpv3_general_query, "DROPPED_IGMP_V3_GENERAL_QUERY_REPLIED", expected_igmpv3_report
+        )
+
+        for addr in mcast_addrs:
+            adb_utils.adb_shell(
+                self.clientDevice,
+                f'ip addr del {addr}/32 dev {self.client_iface_name}'
+            )
diff --git a/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt b/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt
index 81afabc..7dbb9b2 100644
--- a/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt
+++ b/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt
@@ -372,7 +372,7 @@
 
         if (caps.apfVersionSupported > 4) {
             assertThat(caps.maximumApfProgramSize).isAtLeast(2048)
-            assertThat(caps.apfVersionSupported).isEqualTo(6000) // v6.0000
+            assertThat(caps.apfVersionSupported).isAnyOf(6000, 6100) // v6.000 or v6.100
         }
 
         // DEVICEs launching with Android 15 (AOSP experimental) or higher with CHIPSETs that set
@@ -383,14 +383,22 @@
             assertThat(caps.maximumApfProgramSize).isAtLeast(2048)
         }
 
-        // CHIPSETs (or DEVICES with CHIPSETs) that set ro.board.first_api_level or
-        // ro.board.api_level to 202504 or higher:
-        // - [VSR-5.3.12-018] MUST implement version 6 of the Android Packet Filtering (APF)
-        //   interpreter in the Wi-Fi firmware.
-        // - [VSR-5.3.12-019] MUST provide at least 4000 bytes of APF RAM.
+        // DEVICEs with CHIPSETs that set ro.board.first_api_level or ro.board.api_level to 202504
+        // or higher:
+        // - [VSR-5.3.12-018] MUST implement version 6 or version 6.1 of the Android Packet
+        //   Filtering (APF) interpreter in the Wi-Fi firmware.
+        // - [VSR-5.3.12-019] MUST provide at least 4000 bytes of APF RAM when version 6 is
+        //   implemented OR 3000 bytes when version 6.1 is implemented.
+        // - Note, the APF RAM requirement for APF version 6.1 will become 4000 bytes in Android 17
+        //   with CHIPSETs that set ro.board.first_api_level or ro.board.api_level to 202604 or
+        //   higher.
         if (vsrApiLevel >= 202504) {
-            assertThat(caps.apfVersionSupported).isEqualTo(6000)
-            assertThat(caps.maximumApfProgramSize).isAtLeast(4000)
+            assertThat(caps.apfVersionSupported).isAnyOf(6000, 6100)
+            if (caps.apfVersionSupported == 6000) {
+                assertThat(caps.maximumApfProgramSize).isAtLeast(4000)
+            } else {
+                assertThat(caps.maximumApfProgramSize).isAtLeast(3000)
+            }
         }
 
         // ApfFilter does not support anything but ARPHRD_ETHER.
diff --git a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
index 6dc23ff..1fa9e3a 100644
--- a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
@@ -146,10 +146,10 @@
 import java.nio.ByteBuffer
 import java.time.Duration
 import java.util.Arrays
-import java.util.Random
 import java.util.UUID
 import java.util.concurrent.Executors
 import kotlin.collections.ArrayList
+import kotlin.random.Random
 import kotlin.test.assertEquals
 import kotlin.test.assertFailsWith
 import kotlin.test.assertFalse
@@ -259,7 +259,7 @@
     private class FakeConnectivityService {
         val mockRegistry = mock(INetworkAgentRegistry::class.java)
         private var agentField: INetworkAgent? = null
-        private val registry = object : INetworkAgentRegistry.Stub(),
+        val registry: INetworkAgentRegistry = object : INetworkAgentRegistry.Stub(),
                 INetworkAgentRegistry by mockRegistry {
             // asBinder has implementations in both INetworkAgentRegistry.Stub and mockRegistry, so
             // it needs to be disambiguated. Just fail the test as it should be unused here.
@@ -276,7 +276,7 @@
 
         fun connect(agent: INetworkAgent) {
             this.agentField = agent
-            agent.onRegistered(registry)
+            agent.onRegistered()
         }
 
         fun disconnect() = agent.onDisconnected()
@@ -405,7 +405,8 @@
     }
 
     private fun createNetworkAgentWithFakeCS() = createNetworkAgent().also {
-        mFakeConnectivityService.connect(it.registerForTest(Network(FAKE_NET_ID)))
+        val binder = it.registerForTest(Network(FAKE_NET_ID), mFakeConnectivityService.registry)
+        mFakeConnectivityService.connect(binder)
     }
 
     private fun TestableNetworkAgent.expectPostConnectionCallbacks(
@@ -948,6 +949,47 @@
         callback.expect<Lost>(agent.network!!)
     }
 
+    fun doTestOemVpnType(type: Int) {
+        val mySessionId = "MySession12345"
+        val nc = NetworkCapabilities().apply {
+            addTransportType(TRANSPORT_TEST)
+            addTransportType(TRANSPORT_VPN)
+            addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
+            removeCapability(NET_CAPABILITY_NOT_VPN)
+            setTransportInfo(VpnTransportInfo(type, mySessionId))
+        }
+
+        val agent = createNetworkAgent(initialNc = nc)
+        agent.register()
+        agent.markConnected()
+
+        val request = NetworkRequest.Builder()
+            .clearCapabilities()
+            .addTransportType(TRANSPORT_VPN)
+            .removeCapability(NET_CAPABILITY_NOT_VPN)
+            .build()
+        val callback = TestableNetworkCallback()
+        registerNetworkCallback(request, callback)
+
+        callback.expectAvailableThenValidatedCallbacks(agent.network!!)
+
+        var vpnNc = mCM.getNetworkCapabilities(agent.network!!)
+        assertNotNull(vpnNc)
+        assertEquals(type, (vpnNc!!.transportInfo as VpnTransportInfo).type)
+
+        agent.unregister()
+        callback.expect<Lost>(agent.network!!)
+    }
+
+    @Test
+    @IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+    fun testOemVpnTypes() {
+        // TODO: why is this necessary given the @IgnoreUpTo above?
+        assumeTrue(SdkLevel.isAtLeastB())
+        doTestOemVpnType(VpnManager.TYPE_VPN_OEM_SERVICE)
+        doTestOemVpnType(VpnManager.TYPE_VPN_OEM_LEGACY)
+    }
+
     private fun unregister(agent: TestableNetworkAgent) {
         agent.unregister()
         agent.eventuallyExpect<OnNetworkUnwanted>()
@@ -1520,7 +1562,7 @@
         val s = Os.socket(AF_INET6, SOCK_DGRAM, 0)
         net.bindSocket(s)
         val content = ByteArray(16)
-        Random().nextBytes(content)
+        Random.nextBytes(content)
         Os.sendto(s, ByteBuffer.wrap(content), 0, REMOTE_ADDRESS, 7 /* port */)
         val match = reader.poll(DEFAULT_TIMEOUT_MS) {
             val udpStart = IPV6_HEADER_LEN + UDP_HEADER_LEN
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index c28a0f8..3eefa0f 100755
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -2200,6 +2200,7 @@
                 case ConnectivityFlags.DELAY_DESTROY_SOCKETS:
                 case ConnectivityFlags.USE_DECLARED_METHODS_FOR_CALLBACKS:
                 case ConnectivityFlags.QUEUE_CALLBACKS_FOR_FROZEN_APPS:
+                case ConnectivityFlags.QUEUE_NETWORK_AGENT_EVENTS_IN_SYSTEM_SERVER:
                     return true;
                 default:
                     throw new UnsupportedOperationException("Unknown flag " + name
diff --git a/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt b/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt
index d7e781e..48333c5 100644
--- a/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt
+++ b/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt
@@ -169,6 +169,7 @@
         it[ConnectivityFlags.DELAY_DESTROY_SOCKETS] = true
         it[ConnectivityFlags.USE_DECLARED_METHODS_FOR_CALLBACKS] = true
         it[ConnectivityFlags.QUEUE_CALLBACKS_FOR_FROZEN_APPS] = true
+        it[ConnectivityFlags.QUEUE_NETWORK_AGENT_EVENTS_IN_SYSTEM_SERVER] = true
     }
     fun setFeatureEnabled(flag: String, enabled: Boolean) = enabledFeatures.set(flag, enabled)