Merge "Skip hostside tethering test if not SdkLevel.isAtLeastB()" into main
diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java b/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
index 3c91a1b..5f0e5d0 100644
--- a/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
+++ b/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
@@ -24,6 +24,7 @@
 import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
 
 import static com.android.networkstack.apishim.ConstantsShim.KEY_CARRIER_SUPPORTS_TETHERING_BOOL;
+import static com.android.net.module.util.SdkUtil.isAtLeast25Q2;
 
 import android.content.ContentResolver;
 import android.content.Context;
@@ -395,7 +396,7 @@
      * use the async state machine.
      */
     public void readEnableSyncSM(final Context ctx) {
-        USE_SYNC_SM = mDeps.isFeatureNotChickenedOut(ctx, TETHER_ENABLE_SYNC_SM);
+        USE_SYNC_SM = isAtLeast25Q2() || mDeps.isFeatureNotChickenedOut(ctx, TETHER_ENABLE_SYNC_SM);
     }
 
     /** Does the dumping.*/
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
index 0159573..c4c7c9c 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
@@ -63,6 +63,7 @@
 import com.android.internal.util.test.FakeSettingsProvider;
 import com.android.modules.utils.build.SdkLevel;
 import com.android.net.module.util.DeviceConfigUtils;
+import com.android.net.module.util.SdkUtil;
 import com.android.net.module.util.SharedLog;
 import com.android.testutils.DevSdkIgnoreRule;
 import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
@@ -779,7 +780,10 @@
         setTetherEnableSyncSMFlagEnabled(true);
         assertEnableSyncSM(true);
 
-        setTetherEnableSyncSMFlagEnabled(false);
-        assertEnableSyncSM(false);
+        // Feature is enabled by default after 25Q2 release.
+        if (!SdkUtil.isAtLeast25Q2()) {
+            setTetherEnableSyncSMFlagEnabled(false);
+            assertEnableSyncSM(false);
+        }
     }
 }
diff --git a/bpf/loader/NetBpfLoad.cpp b/bpf/loader/NetBpfLoad.cpp
index b9ef766..f11b9a3 100644
--- a/bpf/loader/NetBpfLoad.cpp
+++ b/bpf/loader/NetBpfLoad.cpp
@@ -17,6 +17,7 @@
 #define LOG_TAG "NetBpfLoad"
 
 #include <arpa/inet.h>
+#include <bpf/btf.h>
 #include <bpf/libbpf.h>
 #include <dirent.h>
 #include <elf.h>
@@ -51,6 +52,7 @@
 #include <android-base/logging.h>
 #include <android-base/macros.h>
 #include <android-base/properties.h>
+#include <android-base/scopeguard.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
@@ -649,10 +651,185 @@
     return false;
 }
 
+static int setBtfDatasecSize(ifstream &elfFile, struct btf *btf,
+                             struct btf_type *bt) {
+    const char *name = btf__name_by_offset(btf, bt->name_off);
+    if (!name) {
+        ALOGE("Couldn't resolve section name, errno: %d", errno);
+        return -errno;
+    }
+
+    vector<char> data;
+    int ret = readSectionByName(name, elfFile, data);
+    if (ret) {
+        ALOGE("Couldn't read section %s, ret: %d", name, ret);
+        return ret;
+    }
+    bt->size = data.size();
+    return 0;
+}
+
+static int getSymOffsetByName(ifstream &elfFile, const char *name, int *off) {
+    vector<Elf64_Sym> symtab;
+    int ret = readSymTab(elfFile, 1 /* sort */, symtab);
+    if (ret) return ret;
+    for (int i = 0; i < (int)symtab.size(); i++) {
+        string s;
+        ret = getSymName(elfFile, symtab[i].st_name, s);
+        if (ret) continue;
+        if (!strcmp(s.c_str(), name)) {
+            *off = symtab[i].st_value;
+            return 0;
+        }
+    }
+    return -1;
+}
+
+static int setBtfVarOffset(ifstream &elfFile, struct btf *btf,
+                           struct btf_type *datasecBt) {
+    int i, vars = btf_vlen(datasecBt);
+    struct btf_var_secinfo *vsi;
+    const char *datasecName = btf__name_by_offset(btf, datasecBt->name_off);
+    if (!datasecName) {
+        ALOGE("Couldn't resolve section name, errno: %d", errno);
+        return -errno;
+    }
+
+    for (i = 0, vsi = btf_var_secinfos(datasecBt); i < vars; i++, vsi++) {
+        const struct btf_type *varBt = btf__type_by_id(btf, vsi->type);
+        if (!varBt || !btf_is_var(varBt)) {
+            ALOGE("Found non VAR kind btf_type, section: %s id: %d", datasecName,
+                  vsi->type);
+            return -1;
+        }
+
+        const struct btf_var *var = btf_var(varBt);
+        if (var->linkage == BTF_VAR_STATIC) continue;
+
+        const char *varName = btf__name_by_offset(btf, varBt->name_off);
+        if (!varName) {
+            ALOGE("Failed to resolve var name, section: %s", datasecName);
+            return -1;
+        }
+
+        int off;
+        int ret = getSymOffsetByName(elfFile, varName, &off);
+        if (ret) {
+            ALOGE("No offset found in symbol table, section: %s, var: %s, ret: %d",
+                  datasecName, varName, ret);
+            return ret;
+        }
+        vsi->offset = off;
+    }
+    return 0;
+}
+
+static int loadBtf(ifstream &elfFile, struct btf *btf) {
+    int ret;
+    for (unsigned int i = 1; i < btf__type_cnt(btf); ++i) {
+        struct btf_type *bt = (struct btf_type *)btf__type_by_id(btf, i);
+        if (!btf_is_datasec(bt)) continue;
+        ret = setBtfDatasecSize(elfFile, btf, bt);
+        if (ret) return ret;
+        ret = setBtfVarOffset(elfFile, btf, bt);
+        if (ret) return ret;
+    }
+
+    ret = btf__load_into_kernel(btf);
+    if (ret) {
+        if (errno != EINVAL) {
+            ALOGE("btf__load_into_kernel failed, errno: %d", errno);
+            return ret;
+        };
+        // For BTF_KIND_FUNC, newer kernels can read the BTF_INFO_VLEN bits of
+        // struct btf_type to distinguish static vs. global vs. extern
+        // functions, but older kernels enforce that only the BTF_INFO_KIND bits
+        // can be set. Retry with non-BTF_INFO_KIND bits zeroed out to handle
+        // this case.
+        for (unsigned int i = 1; i < btf__type_cnt(btf); ++i) {
+            struct btf_type *bt = (struct btf_type *)btf__type_by_id(btf, i);
+            if (btf_is_func(bt)) {
+                bt->info = (BTF_INFO_KIND(bt->info)) << 24;
+            }
+        }
+        ret = btf__load_into_kernel(btf);
+        if (ret) {
+            ALOGE("btf__load_into_kernel retry failed, errno: %d", errno);
+            return ret;
+        };
+    }
+    return 0;
+}
+
+int getKeyValueTids(const struct btf *btf, const char *mapName,
+                    uint32_t expectedKeySize, uint32_t expectedValueSize,
+                    uint32_t *keyTypeId, uint32_t *valueTypeId) {
+    const struct btf_type *kvBt;
+    const struct btf_member *key, *value;
+    const size_t max_name = 256;
+    char kvTypeName[max_name];
+    int64_t keySize, valueSize;
+    uint32_t kvId;
+
+    if (snprintf(kvTypeName, max_name, "____btf_map_%s", mapName) == max_name) {
+        ALOGE("____btf_map_%s is too long", mapName);
+        return -1;
+    }
+
+    kvId = btf__find_by_name(btf, kvTypeName);
+    if (kvId < 0) {
+        ALOGE("section not found, map: %s typeName: %s", mapName, kvTypeName);
+        return -1;
+    }
+
+    kvBt = btf__type_by_id(btf, kvId);
+    if (!kvBt) {
+        ALOGE("Couldn't find BTF type, map: %s id: %u", mapName, kvId);
+        return -1;
+    }
+
+    if (!btf_is_struct(kvBt) || btf_vlen(kvBt) < 2) {
+        ALOGE("Non Struct kind or invalid vlen, map: %s id: %u", mapName, kvId);
+        return -1;
+    }
+
+    key = btf_members(kvBt);
+    value = key + 1;
+
+    keySize = btf__resolve_size(btf, key->type);
+    if (keySize < 0) {
+        ALOGE("Couldn't get key size, map: %s errno: %d", mapName, errno);
+        return -1;
+    }
+
+    valueSize = btf__resolve_size(btf, value->type);
+    if (valueSize < 0) {
+        ALOGE("Couldn't get value size, map: %s errno: %d", mapName, errno);
+        return -1;
+    }
+
+    if (expectedKeySize != keySize || expectedValueSize != valueSize) {
+        ALOGE("Key value size mismatch, map: %s key size: %d expected key size: "
+              "%d value size: %d expected value size: %d",
+              mapName, (uint32_t)keySize, expectedKeySize, (uint32_t)valueSize,
+              expectedValueSize);
+        return -1;
+    }
+
+    *keyTypeId = key->type;
+    *valueTypeId = value->type;
+
+    return 0;
+}
+
+static bool isBtfSupported(enum bpf_map_type type) {
+    return type != BPF_MAP_TYPE_DEVMAP_HASH && type != BPF_MAP_TYPE_RINGBUF;
+}
+
 static int createMaps(const char* elfPath, ifstream& elfFile, vector<unique_fd>& mapFds,
                       const char* prefix, const unsigned int bpfloader_ver) {
     int ret;
-    vector<char> mdData;
+    vector<char> mdData, btfData;
     vector<struct bpf_map_def> md;
     vector<string> mapNames;
     string objName = pathToObjName(string(elfPath));
@@ -679,6 +856,21 @@
     ret = getSectionSymNames(elfFile, "maps", mapNames);
     if (ret) return ret;
 
+    ret = readSectionByName(".BTF", elfFile, btfData);
+    if (ret) {
+        ALOGE("Failed to read .BTF section, ret:%d", ret);
+        return ret;
+    }
+    struct btf *btf = btf__new(btfData.data(), btfData.size());
+    if (btf == NULL) {
+        ALOGE("btf__new failed, errno: %d", errno);
+        return -errno;
+    }
+    auto scopeGuard = base::make_scope_guard([btf] { btf__free(btf); });
+
+    ret = loadBtf(elfFile, btf);
+    if (ret) return ret;
+
     unsigned kvers = kernelVersion();
 
     for (int i = 0; i < (int)mapNames.size(); i++) {
@@ -804,12 +996,26 @@
             };
             if (isAtLeastKernelVersion(4, 15, 0))
                 strlcpy(req.map_name, mapNames[i].c_str(), sizeof(req.map_name));
+
+            bool haveBtf = isBtfSupported(type);
+            if (haveBtf) {
+                uint32_t kTid, vTid;
+                ret = getKeyValueTids(btf, mapNames[i].c_str(), md[i].key_size,
+                                      md[i].value_size, &kTid, &vTid);
+                if (ret) return ret;
+                req.btf_fd = btf__fd(btf);
+                req.btf_key_type_id = kTid;
+                req.btf_value_type_id = vTid;
+            }
+
             fd.reset(bpf(BPF_MAP_CREATE, req));
             saved_errno = errno;
             if (fd.ok()) {
-              ALOGD("bpf_create_map[%s] -> %d", mapNames[i].c_str(), fd.get());
+                ALOGD("bpf_create_map[%s] btf:%d -> %d",
+                      mapNames[i].c_str(), haveBtf, fd.get());
             } else {
-              ALOGE("bpf_create_map[%s] -> %d errno:%d", mapNames[i].c_str(), fd.get(), saved_errno);
+                ALOGE("bpf_create_map[%s] btf:%d -> %d errno:%d",
+                      mapNames[i].c_str(), haveBtf, fd.get(), saved_errno);
             }
         }
 
@@ -1414,6 +1620,29 @@
     return wear;
 }
 
+static int libbpfPrint(enum libbpf_print_level lvl, const char *const formatStr,
+                       va_list argList) {
+    int32_t prio;
+    switch (lvl) {
+      case LIBBPF_WARN:
+        prio = ANDROID_LOG_WARN;
+        break;
+      case LIBBPF_INFO:
+        prio = ANDROID_LOG_INFO;
+        break;
+      case LIBBPF_DEBUG:
+        prio = ANDROID_LOG_DEBUG;
+        break;
+    }
+    char *s = strdup(formatStr ?: "(no format string)");
+    int len = strlen(s);
+    if (len && s[len - 1] == '\n')
+        s[len - 1] = 0;
+    LOG_PRI_VA(prio, LOG_TAG, s, argList);
+    free(s);
+    return 0;
+}
+
 static int doLoad(char** argv, char * const envp[]) {
     if (!isAtLeastS) {
         ALOGE("Impossible - not reachable on Android <S.");
@@ -1421,6 +1650,7 @@
         // for any possible busted 'optimized' start everything vendor init hacks on R
         return 0;
     }
+    libbpf_set_print(libbpfPrint);
 
     const bool runningAsRoot = !getuid();  // true iff U QPR3 or V+
 
diff --git a/clatd/clatd.h b/clatd/clatd.h
index daa5ebc..11e9687 100644
--- a/clatd/clatd.h
+++ b/clatd/clatd.h
@@ -48,7 +48,7 @@
 // plus some extra just-in-case headroom, because it doesn't hurt.
 #define MAXDUMPLEN (64 + MAXMTU)
 
-#define CLATD_VERSION "1.7"
+#define CLATD_VERSION "1.8"
 
 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
 
diff --git a/clatd/main.c b/clatd/main.c
index bc29041..9e5710e 100644
--- a/clatd/main.c
+++ b/clatd/main.c
@@ -18,12 +18,17 @@
 
 #include <arpa/inet.h>
 #include <errno.h>
+#include <linux/audit.h>
+#include <linux/filter.h>
+#include <linux/seccomp.h>
+#include <linux/unistd.h>
 #include <netinet/in.h>
 #include <stdbool.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/personality.h>
+#include <sys/prctl.h>
 #include <sys/utsname.h>
 #include <unistd.h>
 
@@ -53,6 +58,131 @@
   printf("-w [write socket descriptor number]\n");
 }
 
+// Load the architecture identifier (AUDIT_ARCH_* constant)
+#define BPF_SECCOMP_LOAD_AUDIT_ARCH \
+	BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, arch))
+
+// Load the system call number
+#define BPF_SECCOMP_LOAD_SYSCALL_NR \
+	BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, nr))
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+// Load the system call argument n, where n is [0..5]
+#define BPF_SECCOMP_LOAD_SYSCALL_ARG_LO32(n) \
+	BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[n]))
+#define BPF_SECCOMP_LOAD_SYSCALL_ARG_HI32(n) \
+	BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[n]) + 4)
+#else
+#error "Not a little endian architecture?"
+#endif
+
+// Allow the system call
+#define BPF_SECCOMP_ALLOW BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW)
+
+// Allow (but 'audit' log) the system call
+#define BPF_SECCOMP_LOG BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_LOG)
+
+// Reject the system call (kill thread)
+#define BPF_SECCOMP_KILL BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL)
+
+// Note arguments to BPF_JUMP(opcode, operand, true_offset, false_offset)
+
+// If not equal, jump over count instructions
+#define BPF_JUMP_IF_NOT_EQUAL(v, count) \
+	BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, (v), 0, (count))
+
+// If equal, jump over count instructions
+#define BPF_JUMP_IF_EQUAL(v, count) \
+	BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, (v), (count), 0)
+
+// *TWO* instructions: compare and if not equal jump over the allow statement
+#define BPF2_SECCOMP_ALLOW_IF_EQUAL(v) \
+	BPF_JUMP_IF_NOT_EQUAL((v), 1), \
+	BPF_SECCOMP_ALLOW
+
+// *TWO* instructions: compare and if not equal jump over the log statement
+#define BPF2_SECCOMP_LOG_IF_EQUAL(v) \
+	BPF_JUMP_IF_NOT_EQUAL((v), 1), \
+	BPF_SECCOMP_LOG
+
+// *TWO* instructions: compare and if equal jump over the kill statement
+#define BPF2_SECCOMP_KILL_IF_NOT_EQUAL(v) \
+	BPF_JUMP_IF_EQUAL((v), 1), \
+	BPF_SECCOMP_KILL
+
+// Android only supports the following 5 little endian architectures
+#if defined(__aarch64__) && defined(__LP64__)
+  #define MY_AUDIT_ARCH AUDIT_ARCH_AARCH64
+#elif defined(__arm__) && defined(__ILP32__)
+  #define MY_AUDIT_ARCH AUDIT_ARCH_ARM
+#elif defined(__i386__) && defined(__ILP32__)
+  #define MY_AUDIT_ARCH AUDIT_ARCH_I386
+#elif defined(__x86_64__) && defined(__LP64__)
+  #define MY_AUDIT_ARCH AUDIT_ARCH_X86_64
+#elif defined(__riscv) && defined(__LP64__)
+  #define MY_AUDIT_ARCH AUDIT_ARCH_RISCV64
+#else
+  #error "Unknown AUDIT_ARCH_* architecture."
+#endif
+
+void enable_seccomp(void) {
+  static const struct sock_filter filter[] = {
+    BPF_SECCOMP_LOAD_AUDIT_ARCH,
+    BPF2_SECCOMP_KILL_IF_NOT_EQUAL(MY_AUDIT_ARCH),
+
+    BPF_SECCOMP_LOAD_SYSCALL_NR,                     // aarch64
+
+    // main event loop:
+    //   ppoll ( read sendmsg | recvmsg writev )
+    BPF2_SECCOMP_ALLOW_IF_EQUAL(__NR_ppoll),         // 73
+    BPF2_SECCOMP_ALLOW_IF_EQUAL(__NR_read),          // 63
+    BPF2_SECCOMP_ALLOW_IF_EQUAL(__NR_sendmsg),       // 211
+    BPF2_SECCOMP_ALLOW_IF_EQUAL(__NR_recvmsg),       // 212
+    BPF2_SECCOMP_ALLOW_IF_EQUAL(__NR_writev),        // 66
+
+    // logging: getuid writev
+    BPF2_SECCOMP_ALLOW_IF_EQUAL(__NR_getuid),        // 174
+
+    // inbound signal (SIGTERM) processing
+    BPF2_SECCOMP_ALLOW_IF_EQUAL(__NR_rt_sigreturn),  // 139
+
+    // sleep(n)
+    BPF2_SECCOMP_ALLOW_IF_EQUAL(__NR_nanosleep),     // 101
+
+    // _exit(0)
+    BPF2_SECCOMP_ALLOW_IF_EQUAL(__NR_exit_group),    // 94
+
+#if defined(__aarch64__)
+    // Pixels are aarch64 - if we break clatd functionality on them,
+    // we *will* notice on GoogleGuest WiFi network (which is ipv6 only)
+    BPF_SECCOMP_KILL,
+#else
+    // All other architectures: generate audit lines visible in dmesg and logcat
+    BPF_SECCOMP_LOG,
+#endif
+  };
+  static const struct sock_fprog prog = {
+    .len = (unsigned short)ARRAY_SIZE(filter),
+    .filter = (struct sock_filter *)filter,
+  };
+
+  // https://man7.org/linux/man-pages/man2/PR_SET_NO_NEW_PRIVS.2const.html
+  // required to allow non-privileged seccomp filter installation
+  int rv = prctl(PR_SET_NO_NEW_PRIVS, 1L, 0L, 0L, 0L);
+  if (rv) {
+    logmsg(ANDROID_LOG_FATAL, "prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) = %d [%d]", rv, errno);
+    exit(1);
+  }
+
+  // https://man7.org/linux/man-pages/man2/PR_SET_SECCOMP.2const.html
+  // but see also https://man7.org/linux/man-pages/man2/seccomp.2.html
+  rv = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog, 0L, 0L);
+  if (rv) {
+    logmsg(ANDROID_LOG_FATAL, "prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog, 0, 0) = %d [%d]", rv, errno);
+    exit(1);
+  }
+}
+
 /* function: main
  * allocate and setup the tun device, then run the event loop
  */
@@ -241,6 +371,8 @@
   // TODO: actually perform true DAD
   send_dad(tunnel.write_fd6, &Global_Clatd_Config.ipv6_local_subnet);
 
+  enable_seccomp();  // WARNING: from this point forward very limited system calls available.
+
   event_loop(&tunnel);
 
   if (sigterm) {
@@ -255,5 +387,7 @@
     logmsg(ANDROID_LOG_INFO, "Clatd on %s %s SIGTERM", uplink_interface,
            sigterm ? "received" : "timed out waiting for");
   }
-  return 0;
+
+  // Using _exit() here avoids 4 mprotect() syscalls triggered via 'exit(0)' or 'return 0'
+  _exit(0);
 }
diff --git a/framework/src/android/net/ConnectivityManager.java b/framework/src/android/net/ConnectivityManager.java
index 4f18fa2..f8a1293 100644
--- a/framework/src/android/net/ConnectivityManager.java
+++ b/framework/src/android/net/ConnectivityManager.java
@@ -1200,11 +1200,14 @@
 
     /** @hide */
     public static final long FEATURE_USE_DECLARED_METHODS_FOR_CALLBACKS = 1L;
+    /** @hide */
+    public static final long FEATURE_QUEUE_NETWORK_AGENT_EVENTS_IN_SYSTEM_SERVER = 1L << 1;
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @LongDef(flag = true, prefix = "FEATURE_", value = {
-            FEATURE_USE_DECLARED_METHODS_FOR_CALLBACKS
+            FEATURE_USE_DECLARED_METHODS_FOR_CALLBACKS,
+            FEATURE_QUEUE_NETWORK_AGENT_EVENTS_IN_SYSTEM_SERVER
     })
     public @interface ConnectivityManagerFeature {}
 
@@ -4881,7 +4884,8 @@
         return 0;
     }
 
-    private boolean isFeatureEnabled(@ConnectivityManagerFeature long connectivityManagerFeature) {
+    /** @hide */
+    public boolean isFeatureEnabled(@ConnectivityManagerFeature long connectivityManagerFeature) {
         synchronized (mEnabledConnectivityManagerFeaturesLock) {
             if (mEnabledConnectivityManagerFeatures == null) {
                 try {
diff --git a/framework/src/android/net/NetworkAgent.java b/framework/src/android/net/NetworkAgent.java
index 08f5ecd..95b45ce 100644
--- a/framework/src/android/net/NetworkAgent.java
+++ b/framework/src/android/net/NetworkAgent.java
@@ -37,6 +37,7 @@
 import android.telephony.data.NrQosSessionAttributes;
 import android.util.Log;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.net.module.util.FrameworkConnectivityStatsLog;
 
@@ -116,15 +117,33 @@
     private final ArrayList<RegistryAction> mPreConnectedQueue = new ArrayList<>();
     private volatile long mLastBwRefreshTime = 0;
     private static final long BW_REFRESH_MIN_WIN_MS = 500;
+
+    private final boolean mQueueRemoved;
+
     private boolean mBandwidthUpdateScheduled = false;
     private AtomicBoolean mBandwidthUpdatePending = new AtomicBoolean(false);
     @NonNull
     private NetworkInfo mNetworkInfo;
     @NonNull
     private final Object mRegisterLock = new Object();
-    // TODO : move the preconnected queue to the system server and remove this
+    // TODO : when ConnectivityFlags.QUEUE_NETWORK_AGENT_EVENTS_IN_SYSTEM_SERVER is
+    // not chickened out this is never read. Remove when retiring this flag.
     private boolean mConnected = false;
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "STATE_" }, value = {
+        STATE_CREATED,
+        STATE_REGISTERED,
+        STATE_UNREGISTERED
+    })
+    public @interface NetworkAgentState {}
+    private static final int STATE_CREATED = 0;
+    private static final int STATE_REGISTERED = 1;
+    private static final int STATE_UNREGISTERED = 2;
+    @GuardedBy("mRegisterLock")
+    private int mState = STATE_CREATED;
+
     /**
      * The ID of the {@link NetworkProvider} that created this object, or
      * {@link NetworkProvider#ID_NONE} if unknown.
@@ -506,6 +525,18 @@
         return ni;
     }
 
+    /**
+     * Returns whether a given ConnectivityManager feature is enabled.
+     *
+     * Tests can override this.
+     * @hide
+     */
+    @VisibleForTesting
+    public boolean isFeatureEnabled(@NonNull Context context,
+            @ConnectivityManager.ConnectivityManagerFeature long feature) {
+        return context.getSystemService(ConnectivityManager.class).isFeatureEnabled(feature);
+    }
+
     // Temporary backward compatibility constructor
     public NetworkAgent(@NonNull Context context, @NonNull Looper looper, @NonNull String logTag,
             @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp, int score,
@@ -588,6 +619,10 @@
             @Nullable LocalNetworkConfig localNetworkConfig, @NonNull NetworkScore score,
             @NonNull NetworkAgentConfig config, int providerId, @NonNull NetworkInfo ni) {
         mHandler = new NetworkAgentHandler(looper);
+        // If the feature is enabled, then events are queued in the system
+        // server, and it's removed from this NetworkAgent.
+        mQueueRemoved = isFeatureEnabled(context,
+                ConnectivityManager.FEATURE_QUEUE_NETWORK_AGENT_EVENTS_IN_SYSTEM_SERVER);
         LOG_TAG = logTag;
         mNetworkInfo = new NetworkInfo(ni);
         this.providerId = providerId;
@@ -609,24 +644,31 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case EVENT_AGENT_CONNECTED: {
-                    // 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(mRegistry);
-                                } catch (RemoteException e) {
-                                    Log.wtf(LOG_TAG, "Communication error with registry", e);
-                                    // Fall through
+                    if (mQueueRemoved) {
+                        // No handling. This message is legacy from a time where the
+                        // agent had to wait until the registry was sent to it, which
+                        // would only happen after the corresponding NetworkMonitor
+                        // was created.
+                        mConnected = true; // never read, but mConnected = false would be confusing
+                    } else {
+                        // Feature chickened out, keep the old queueing behavior
+                        synchronized (mRegisterLock) {
+                            if (mConnected) {
+                                log("Received new connection while already connected!");
+                            } else {
+                                if (VDBG) log("NetworkAgent fully connected");
+                                for (RegistryAction a : mPreConnectedQueue) {
+                                    try {
+                                        a.execute(mRegistry);
+                                    } catch (RemoteException e) {
+                                        Log.wtf(LOG_TAG, "Communication error with registry", e);
+                                        // Fall through
+                                    }
                                 }
+                                mPreConnectedQueue.clear();
                             }
-                            mPreConnectedQueue.clear();
+                            mConnected = true;
                         }
-                        mConnected = true;
                     }
                     break;
                 }
@@ -634,7 +676,8 @@
                     if (DBG) log("NetworkAgent channel lost");
                     // let the client know CS is done with us.
                     onNetworkUnwanted();
-                    synchronized (mPreConnectedQueue) {
+                    synchronized (mRegisterLock) {
+                        mState = STATE_UNREGISTERED;
                         mConnected = false;
                     }
                     break;
@@ -757,8 +800,19 @@
     public Network register() {
         if (VDBG) log("Registering NetworkAgent");
         synchronized (mRegisterLock) {
-            if (mNetwork != null) {
-                throw new IllegalStateException("Agent already registered");
+            if (mQueueRemoved) {
+                switch (mState) {
+                    case STATE_REGISTERED:
+                        throw new IllegalStateException("Agent already registered");
+                    case STATE_UNREGISTERED:
+                        throw new IllegalStateException("Agent already unregistered");
+                    default: // CREATED, this is the normal case
+                }
+            } else {
+                // Feature is chickened out, do the old processing
+                if (mNetwork != null) {
+                    throw new IllegalStateException("Agent already registered");
+                }
             }
             final ConnectivityManager cm = (ConnectivityManager) mInitialConfiguration.context
                     .getSystemService(Context.CONNECTIVITY_SERVICE);
@@ -787,6 +841,7 @@
             } else {
                 mNetwork = result.network;
                 mRegistry = result.registry;
+                mState = STATE_REGISTERED;
             }
             mInitialConfiguration = null; // All this memory can now be GC'd
         }
@@ -936,6 +991,7 @@
             mNetwork = network;
             mInitialConfiguration = null;
             mRegistry = registry;
+            mState = STATE_REGISTERED;
         }
         return new NetworkAgentBinder(mHandler);
     }
@@ -961,30 +1017,49 @@
         return mNetwork;
     }
 
-    private void queueOrSendMessage(@NonNull RegistryAction action) {
-        synchronized (mPreConnectedQueue) {
-            if (mNetwork == null && !Process.isApplicationUid(Process.myUid())) {
-                // Theoretically, it should not be valid to queue messages here before
-                // registering the NetworkAgent. However, practically, with the way
-                // queueing works right now, it ends up working out just fine.
-                // Log a statistic so that we know if this is happening in the
-                // wild. The check for isApplicationUid is to prevent logging the
-                // metric from test code.
+    private void logTerribleErrorMessageBeforeConnect() {
+        FrameworkConnectivityStatsLog.write(
+                FrameworkConnectivityStatsLog.CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED,
+                FrameworkConnectivityStatsLog.CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED__ERROR_TYPE__TYPE_MESSAGE_QUEUED_BEFORE_CONNECT
+        );
+    }
 
-                FrameworkConnectivityStatsLog.write(
-                        FrameworkConnectivityStatsLog.CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED,
-                        FrameworkConnectivityStatsLog.CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED__ERROR_TYPE__TYPE_MESSAGE_QUEUED_BEFORE_CONNECT
-                );
-            }
-            if (mConnected) {
-                try {
-                    action.execute(mRegistry);
-                } catch (RemoteException e) {
-                    Log.wtf(LOG_TAG, "Error executing registry action", e);
-                    // Fall through: the channel is asynchronous and does not report errors back
+    private void send(@NonNull RegistryAction action) {
+        synchronized (mRegisterLock) {
+            if (mQueueRemoved) {
+                if (mState <= STATE_CREATED) {
+                    // Log a terrible error. There is nothing to do with this message
+                    // so drop it.
+                    logTerribleErrorMessageBeforeConnect();
+                    Log.e(LOG_TAG, "Agent not yet registered, ignoring command");
+                    return;
+                }
+                if (mState >= STATE_UNREGISTERED) {
+                    // This should not crash for two reasons : first, the agent may
+                    // be disconnected by ConnectivityService at any time and the message
+                    // typically arrives on another thread, so it's not feasible for
+                    // apps to check before sending, they'd have to always catch. Second,
+                    // historically this hasn't thrown and some code may be relying on
+                    // the historical behavior.
+                    Log.e(LOG_TAG, "Agent already unregistered, ignoring command");
+                    return;
                 }
             } else {
-                mPreConnectedQueue.add(action);
+                if (null == mNetwork) {
+                    // Log a terrible error but still enqueue the message for backward
+                    // compatibility.
+                    logTerribleErrorMessageBeforeConnect();
+                }
+                if (!mConnected) {
+                    mPreConnectedQueue.add(action);
+                    return;
+                }
+            }
+            try {
+                action.execute(mRegistry);
+            } catch (RemoteException e) {
+                Log.wtf(LOG_TAG, "Error executing registry action", e);
+                // Fall through: the channel is asynchronous and does not report errors back
             }
         }
     }
@@ -995,8 +1070,9 @@
      */
     public void sendLinkProperties(@NonNull LinkProperties linkProperties) {
         Objects.requireNonNull(linkProperties);
-        final LinkProperties lp = new LinkProperties(linkProperties);
-        queueOrSendMessage(reg -> reg.sendLinkProperties(lp));
+        // Copy the object because if the agent is running in the system server
+        // then the same instance will be seen by the registry
+        send(reg -> reg.sendLinkProperties(new LinkProperties(linkProperties)));
     }
 
     /**
@@ -1022,7 +1098,7 @@
             @SuppressLint("NullableCollection") @Nullable List<Network> underlyingNetworks) {
         final ArrayList<Network> underlyingArray = (underlyingNetworks != null)
                 ? new ArrayList<>(underlyingNetworks) : null;
-        queueOrSendMessage(reg -> reg.sendUnderlyingNetworks(underlyingArray));
+        send(reg -> reg.sendUnderlyingNetworks(underlyingArray));
     }
 
     /**
@@ -1032,7 +1108,7 @@
     public void markConnected() {
         mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null /* reason */,
                 mNetworkInfo.getExtraInfo());
-        queueOrSendNetworkInfo(mNetworkInfo);
+        sendNetworkInfo(mNetworkInfo);
     }
 
     /**
@@ -1045,7 +1121,12 @@
         // When unregistering an agent nobody should use the extrainfo (or reason) any more.
         mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, null /* reason */,
                 null /* extraInfo */);
-        queueOrSendNetworkInfo(mNetworkInfo);
+        synchronized (mRegisterLock) {
+            if (mState >= STATE_REGISTERED) {
+                sendNetworkInfo(mNetworkInfo);
+            }
+            mState = STATE_UNREGISTERED;
+        }
     }
 
     /**
@@ -1068,7 +1149,7 @@
      */
     public void setTeardownDelayMillis(
             @IntRange(from = 0, to = MAX_TEARDOWN_DELAY_MS) int teardownDelayMillis) {
-        queueOrSendMessage(reg -> reg.sendTeardownDelayMs(teardownDelayMillis));
+        send(reg -> reg.sendTeardownDelayMs(teardownDelayMillis));
     }
 
     /**
@@ -1107,7 +1188,7 @@
      */
     public void unregisterAfterReplacement(
             @IntRange(from = 0, to = MAX_TEARDOWN_DELAY_MS) int timeoutMillis) {
-        queueOrSendMessage(reg -> reg.sendUnregisterAfterReplacement(timeoutMillis));
+        send(reg -> reg.sendUnregisterAfterReplacement(timeoutMillis));
     }
 
     /**
@@ -1125,7 +1206,7 @@
     @SystemApi
     public void setLegacySubtype(final int legacySubtype, @NonNull final String legacySubtypeName) {
         mNetworkInfo.setSubtype(legacySubtype, legacySubtypeName);
-        queueOrSendNetworkInfo(mNetworkInfo);
+        sendNetworkInfo(mNetworkInfo);
     }
 
     /**
@@ -1147,7 +1228,7 @@
     @Deprecated
     public void setLegacyExtraInfo(@Nullable final String extraInfo) {
         mNetworkInfo.setExtraInfo(extraInfo);
-        queueOrSendNetworkInfo(mNetworkInfo);
+        sendNetworkInfo(mNetworkInfo);
     }
 
     /**
@@ -1155,13 +1236,9 @@
      * @hide TODO: expose something better.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
-    public final void sendNetworkInfo(NetworkInfo networkInfo) {
-        queueOrSendNetworkInfo(networkInfo);
-    }
-
-    private void queueOrSendNetworkInfo(NetworkInfo networkInfo) {
+    private void sendNetworkInfo(final NetworkInfo networkInfo) {
         final NetworkInfo ni = new NetworkInfo(networkInfo);
-        queueOrSendMessage(reg -> reg.sendNetworkInfo(ni));
+        send(reg -> reg.sendNetworkInfo(ni));
     }
 
     /**
@@ -1174,7 +1251,7 @@
         mLastBwRefreshTime = System.currentTimeMillis();
         final NetworkCapabilities nc =
                 new NetworkCapabilities(networkCapabilities, NetworkCapabilities.REDACT_NONE);
-        queueOrSendMessage(reg -> reg.sendNetworkCapabilities(nc));
+        send(reg -> reg.sendNetworkCapabilities(nc));
     }
 
     /**
@@ -1186,7 +1263,7 @@
         Objects.requireNonNull(config);
         // If the agent doesn't have NET_CAPABILITY_LOCAL_NETWORK, this will be ignored by
         // ConnectivityService with a Log.wtf.
-        queueOrSendMessage(reg -> reg.sendLocalNetworkConfig(config));
+        send(reg -> reg.sendLocalNetworkConfig(config));
     }
 
     /**
@@ -1196,7 +1273,7 @@
      */
     public void sendNetworkScore(@NonNull NetworkScore score) {
         Objects.requireNonNull(score);
-        queueOrSendMessage(reg -> reg.sendScore(score));
+        send(reg -> reg.sendScore(score));
     }
 
     /**
@@ -1246,8 +1323,7 @@
      * @hide should move to NetworkAgentConfig.
      */
     public void explicitlySelected(boolean explicitlySelected, boolean acceptUnvalidated) {
-        queueOrSendMessage(reg -> reg.sendExplicitlySelected(
-                explicitlySelected, acceptUnvalidated));
+        send(reg -> reg.sendExplicitlySelected(explicitlySelected, acceptUnvalidated));
     }
 
     /**
@@ -1387,7 +1463,7 @@
      */
     public final void sendSocketKeepaliveEvent(int slot,
             @SocketKeepalive.KeepaliveEvent int event) {
-        queueOrSendMessage(reg -> reg.sendSocketKeepaliveEvent(slot, event));
+        send(reg -> reg.sendSocketKeepaliveEvent(slot, event));
     }
     /** @hide TODO delete once callers have moved to sendSocketKeepaliveEvent */
     public void onSocketKeepaliveEvent(int slot, int reason) {
@@ -1493,11 +1569,11 @@
             @NonNull final QosSessionAttributes attributes) {
         Objects.requireNonNull(attributes, "The attributes must be non-null");
         if (attributes instanceof EpsBearerQosSessionAttributes) {
-            queueOrSendMessage(ra -> ra.sendEpsQosSessionAvailable(qosCallbackId,
+            send(reg -> reg.sendEpsQosSessionAvailable(qosCallbackId,
                     new QosSession(sessionId, QosSession.TYPE_EPS_BEARER),
                     (EpsBearerQosSessionAttributes)attributes));
         } else if (attributes instanceof NrQosSessionAttributes) {
-            queueOrSendMessage(ra -> ra.sendNrQosSessionAvailable(qosCallbackId,
+            send(reg -> reg.sendNrQosSessionAvailable(qosCallbackId,
                     new QosSession(sessionId, QosSession.TYPE_NR_BEARER),
                     (NrQosSessionAttributes)attributes));
         }
@@ -1512,7 +1588,7 @@
      */
     public final void sendQosSessionLost(final int qosCallbackId,
             final int sessionId, final int qosSessionType) {
-        queueOrSendMessage(ra -> ra.sendQosSessionLost(qosCallbackId,
+        send(reg -> reg.sendQosSessionLost(qosCallbackId,
                 new QosSession(sessionId, qosSessionType)));
     }
 
@@ -1526,7 +1602,7 @@
      */
     public final void sendQosCallbackError(final int qosCallbackId,
             @QosCallbackException.ExceptionType final int exceptionType) {
-        queueOrSendMessage(ra -> ra.sendQosCallbackError(qosCallbackId, exceptionType));
+        send(reg -> reg.sendQosCallbackError(qosCallbackId, exceptionType));
     }
 
     /**
@@ -1543,7 +1619,7 @@
             throw new IllegalArgumentException("Duration must be within ["
                     + MIN_LINGER_TIMER_MS + "," + Integer.MAX_VALUE + "]ms");
         }
-        queueOrSendMessage(ra -> ra.sendLingerDuration((int) durationMs));
+        send(reg -> reg.sendLingerDuration((int) durationMs));
     }
 
     /**
@@ -1552,7 +1628,7 @@
      */
     public void sendAddDscpPolicy(@NonNull final DscpPolicy policy) {
         Objects.requireNonNull(policy);
-        queueOrSendMessage(ra -> ra.sendAddDscpPolicy(policy));
+        send(reg -> reg.sendAddDscpPolicy(policy));
     }
 
     /**
@@ -1560,14 +1636,14 @@
      * @param policyId the ID corresponding to a specific DSCP Policy.
      */
     public void sendRemoveDscpPolicy(final int policyId) {
-        queueOrSendMessage(ra -> ra.sendRemoveDscpPolicy(policyId));
+        send(reg -> reg.sendRemoveDscpPolicy(policyId));
     }
 
     /**
      * Remove all the DSCP policies on this network.
      */
     public void sendRemoveAllDscpPolicies() {
-        queueOrSendMessage(ra -> ra.sendRemoveAllDscpPolicies());
+        send(reg -> reg.sendRemoveAllDscpPolicies());
     }
 
     /** @hide */
diff --git a/service/src/com/android/server/BpfNetMaps.java b/service/src/com/android/server/BpfNetMaps.java
index 25c0617..923920f 100644
--- a/service/src/com/android/server/BpfNetMaps.java
+++ b/service/src/com/android/server/BpfNetMaps.java
@@ -1325,11 +1325,11 @@
                             + value.iif2 + "(" + mDeps.getIfName(value.iif2) + ")");
             if (sLocalNetBlockedUidMap != null) {
                 BpfDump.dumpMap(sLocalNetAccessMap, pw, "sLocalNetAccessMap",
-                        (key, value) -> "[" + key + "]: " + value);
+                        (key, value) -> "" + key + ": " + value.val);
             }
             if (sLocalNetBlockedUidMap != null) {
                 BpfDump.dumpMap(sLocalNetBlockedUidMap, pw, "sLocalNetBlockedUidMap",
-                        (key, value) -> "[" + key + "]: " + value);
+                        (key, value) -> "" + key + ": " + value.val);
             }
             dumpDataSaverConfig(pw);
             pw.decreaseIndent();
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index 329b338..dc4a35b 100644
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -158,6 +158,7 @@
 import static com.android.server.connectivity.ConnectivityFlags.INGRESS_TO_VPN_ADDRESS_FILTERING;
 import static com.android.server.connectivity.ConnectivityFlags.NAMESPACE_TETHERING_BOOT;
 import static com.android.server.connectivity.ConnectivityFlags.QUEUE_CALLBACKS_FOR_FROZEN_APPS;
+import static com.android.server.connectivity.ConnectivityFlags.QUEUE_NETWORK_AGENT_EVENTS_IN_SYSTEM_SERVER;
 import static com.android.server.connectivity.ConnectivityFlags.REQUEST_RESTRICTED_WIFI;
 import static com.android.server.connectivity.ConnectivityFlags.WIFI_DATA_INACTIVITY_TIMEOUT;
 
@@ -4696,6 +4697,10 @@
 
             // If the network has been destroyed, the only thing that it can do is disconnect.
             if (nai.isDestroyed() && !isDisconnectRequest(msg)) {
+                if (DBG) {
+                    log("Message " + eventName(msg.what) + " from destroyed agent with netId "
+                            + nai.network.netId);
+                }
                 return;
             }
 
@@ -4704,6 +4709,10 @@
                 // when registration is complete. It does this by sending all the
                 // messages in the order received immediately after the
                 // EVENT_AGENT_REGISTERED message.
+                if (DBG) {
+                    log("Message " + eventName(msg.what) + " enqueued for agent with netId "
+                            + nai.network.netId);
+                }
                 return;
             }
 
@@ -9418,10 +9427,11 @@
         if (DBG) log("registerNetworkAgent " + nai);
         mDeps.getNetworkStack().makeNetworkMonitor(
                 nai.network, name, new NetworkMonitorCallbacks(nai));
-        // 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. TODO : have NetworkAgentInfo defer them instead.
+        // NetworkAgentInfo registration is done, but CS will only accept messages when the
+        // NetworkMonitor is created. If the network disconnects or sends any other event
+        // before that, messages are deferred by the Tracker Handler until it is (by asking
+        // NetworkAgentInfo to do it). The window is very small unless the NetworkStack
+        // doesn't reply immediately, which would mean a broken system anyway.
         final NetworkAndAgentRegistryParcelable result = new NetworkAndAgentRegistryParcelable();
         result.network = nai.network;
         result.registry = nai.getRegistry();
@@ -9455,6 +9465,7 @@
         NetworkInfo networkInfo = nai.networkInfo;
         updateNetworkInfo(nai, networkInfo);
         updateVpnUids(nai, null, nai.networkCapabilities);
+        nai.processEnqueuedMessages(mTrackerHandler::handleMessage);
     }
 
     private class NetworkOfferInfo implements IBinder.DeathRecipient {
@@ -15048,6 +15059,9 @@
         if (mUseDeclaredMethodsForCallbacksEnabled) {
             features |= ConnectivityManager.FEATURE_USE_DECLARED_METHODS_FOR_CALLBACKS;
         }
+        if (mQueueNetworkAgentEventsInSystemServer) {
+            features |= ConnectivityManager.FEATURE_QUEUE_NETWORK_AGENT_EVENTS_IN_SYSTEM_SERVER;
+        }
         return features;
     }
 
@@ -15056,6 +15070,8 @@
         switch (featureFlag) {
             case INGRESS_TO_VPN_ADDRESS_FILTERING:
                 return mIngressToVpnAddressFiltering;
+            case QUEUE_NETWORK_AGENT_EVENTS_IN_SYSTEM_SERVER:
+                return mQueueNetworkAgentEventsInSystemServer;
             default:
                 throw new IllegalArgumentException("Unknown flag: " + featureFlag);
         }
diff --git a/service/src/com/android/server/connectivity/NetworkAgentInfo.java b/service/src/com/android/server/connectivity/NetworkAgentInfo.java
index 4540f02..abab6ab 100644
--- a/service/src/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/service/src/com/android/server/connectivity/NetworkAgentInfo.java
@@ -25,8 +25,8 @@
 import static android.net.NetworkCapabilities.TRANSPORT_TEST;
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 import static android.net.NetworkCapabilities.transportNamesOf;
-import static android.system.OsConstants.EIO;
 import static android.system.OsConstants.EEXIST;
+import static android.system.OsConstants.EIO;
 import static android.system.OsConstants.ENOENT;
 
 import static com.android.net.module.util.FrameworkConnectivityStatsLog.CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED;
@@ -93,6 +93,7 @@
 import java.util.Objects;
 import java.util.SortedSet;
 import java.util.TreeSet;
+import java.util.function.Consumer;
 
 /**
  * A bag class used by ConnectivityService for holding a collection of most recent
@@ -717,8 +718,20 @@
         }
 
         mHandler.obtainMessage(EVENT_AGENT_REGISTERED, ARG_AGENT_SUCCESS, 0, this).sendToTarget();
+    }
+
+    /**
+     * Pass all enqueued messages to the message processor argument, and clear the queue.
+     *
+     * This is called by ConnectivityService when it is ready to receive messages for this
+     * network agent. The processor may process the messages synchronously or asynchronously
+     * at its option.
+     *
+     * @param messageProcessor a function to process the messages
+     */
+    public void processEnqueuedMessages(final Consumer<Message> messageProcessor) {
         for (final Message enqueued : mMessagesPendingRegistration) {
-            mHandler.sendMessage(enqueued);
+            messageProcessor.accept(enqueued);
         }
         mMessagesPendingRegistration.clear();
     }
diff --git a/staticlibs/framework/com/android/net/module/util/SdkUtil.java b/staticlibs/framework/com/android/net/module/util/SdkUtil.java
index 5006ba9..63558dd 100644
--- a/staticlibs/framework/com/android/net/module/util/SdkUtil.java
+++ b/staticlibs/framework/com/android/net/module/util/SdkUtil.java
@@ -16,7 +16,10 @@
 
 package com.android.net.module.util;
 
+import static android.os.Build.VERSION.SDK_INT;
+
 import android.annotation.Nullable;
+import android.os.Build;
 
 /**
  * Utilities to deal with multiple SDKs in a single mainline module.
@@ -46,4 +49,9 @@
             this.value = value;
         }
     }
+
+    /** Checks if the device is running on a release version of Android Baklava or newer */
+    public static boolean isAtLeast25Q2() {
+        return SDK_INT >= 36  || (SDK_INT == 35 && "Baklava".equals(Build.VERSION.CODENAME));
+    }
 }
diff --git a/staticlibs/testutils/devicetests/com/android/testutils/TestableNetworkAgent.kt b/staticlibs/testutils/devicetests/com/android/testutils/TestableNetworkAgent.kt
index bfbbc34..0413ed4 100644
--- a/staticlibs/testutils/devicetests/com/android/testutils/TestableNetworkAgent.kt
+++ b/staticlibs/testutils/devicetests/com/android/testutils/TestableNetworkAgent.kt
@@ -17,6 +17,7 @@
 package com.android.testutils
 
 import android.content.Context
+import android.net.ConnectivityManager.FEATURE_QUEUE_NETWORK_AGENT_EVENTS_IN_SYSTEM_SERVER
 import android.net.InetAddresses.parseNumericAddress
 import android.net.KeepalivePacketData
 import android.net.LinkAddress
@@ -28,6 +29,7 @@
 import android.net.NetworkCapabilities.TRANSPORT_TEST
 import android.net.NetworkProvider
 import android.net.NetworkRequest
+import android.net.NetworkScore
 import android.net.QosFilter
 import android.net.Uri
 import android.os.Looper
@@ -64,16 +66,21 @@
 import kotlin.test.assertNotNull
 import kotlin.test.assertNull
 import kotlin.test.assertTrue
+import kotlin.test.fail
 import org.junit.Assert.assertArrayEquals
 
 // Any legal score (0~99) for the test network would do, as it is going to be kept up by the
 // requests filed by the test and should never match normal internet requests. 70 is the default
 // score of Ethernet networks, it's as good a value as any other.
-private const val TEST_NETWORK_SCORE = 70
+private val TEST_NETWORK_SCORE = NetworkScore.Builder().setLegacyInt(70).build()
 
 private class Provider(context: Context, looper: Looper) :
             NetworkProvider(context, looper, "NetworkAgentTest NetworkProvider")
 
+private val enabledFeatures = mutableMapOf(
+    FEATURE_QUEUE_NETWORK_AGENT_EVENTS_IN_SYSTEM_SERVER to true
+)
+
 public open class TestableNetworkAgent(
     context: Context,
     looper: Looper,
@@ -81,8 +88,17 @@
     val lp: LinkProperties,
     conf: NetworkAgentConfig
 ) : NetworkAgent(context, looper, TestableNetworkAgent::class.java.simpleName /* tag */,
-        nc, lp, TEST_NETWORK_SCORE, conf, Provider(context, looper)) {
+    nc, lp, TEST_NETWORK_SCORE, conf, Provider(context, looper)) {
+
+    override fun isFeatureEnabled(context: Context, feature: Long): Boolean {
+        when (val it = enabledFeatures.get(feature)) {
+            null -> fail("Unmocked feature $feature, see TestableNetworkAgent.enabledFeatures")
+            else -> return it
+        }
+    }
+
     companion object {
+        fun setFeatureEnabled(flag: Long, enabled: Boolean) = enabledFeatures.set(flag, enabled)
 
         /**
          * Convenience method to create a [NetworkRequest] matching [TestableNetworkAgent]s from
diff --git a/tests/cts/multidevices/apfv6_test.py b/tests/cts/multidevices/apfv6_test.py
index b82a3be..fb45f4a 100644
--- a/tests/cts/multidevices/apfv6_test.py
+++ b/tests/cts/multidevices/apfv6_test.py
@@ -14,6 +14,7 @@
 
 from mobly import asserts
 from scapy.layers.inet import IP, ICMP, IPOption_Router_Alert
+from scapy.layers.inet6 import IPv6, ICMPv6EchoRequest, ICMPv6EchoReply
 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
@@ -102,6 +103,22 @@
         )
 
     @apf_utils.at_least_B()
+    def test_ipv6_icmp_echo_request_offload(self):
+        eth = Ether(src=self.server_mac_address, dst=self.client_mac_address)
+        ip = IPv6(src=self.server_ipv6_addresses[0], dst=self.client_ipv6_addresses[0])
+        icmp = ICMPv6EchoRequest(id=1, seq=123)
+        echo_request = bytes(eth/ip/icmp/b"hello").hex()
+
+        eth = Ether(src=self.client_mac_address, dst=self.server_mac_address)
+        ip = IPv6(src=self.client_ipv6_addresses[0], dst=self.server_ipv6_addresses[0])
+        icmp = ICMPv6EchoReply(id=1, seq=123)
+        expected_echo_reply = bytes(eth/ip/icmp/b"hello").hex()
+
+        self.send_packet_and_expect_reply_received(
+            echo_request, "DROPPED_IPV6_ICMP6_ECHO_REQUEST_REPLIED", expected_echo_reply
+        )
+
+    @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(
diff --git a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
index 1fa9e3a..bd9bd2a 100644
--- a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
@@ -384,9 +384,9 @@
             initialLp = lp,
             initialNc = nc
         )
-        agent.setTeardownDelayMillis(0)
         // Connect the agent and verify initial status callbacks.
         agent.register()
+        agent.setTeardownDelayMillis(0)
         agent.markConnected()
         agent.expectCallback<OnNetworkCreated>()
         agent.expectPostConnectionCallbacks(expectedInitSignalStrengthThresholds)
@@ -1934,4 +1934,19 @@
         // VPN networks are always created as soon as the agent is registered.
         doTestNativeNetworkCreation(expectCreatedImmediately = true, intArrayOf(TRANSPORT_VPN))
     }
+
+    @Test(expected = IllegalStateException::class)
+    fun testRegisterAgain() {
+        val agent = createNetworkAgent()
+        agent.register()
+        agent.unregister()
+        agent.register()
+    }
+
+    @Test
+    fun testUnregisterBeforeRegister() {
+        // For backward compatibility, this shouldn't crash.
+        val agent = createNetworkAgent()
+        agent.unregister()
+    }
 }
diff --git a/tests/unit/java/com/android/server/connectivityservice/CSKeepConnectedTest.kt b/tests/unit/java/com/android/server/connectivityservice/CSKeepConnectedTest.kt
index 94c68c0..4aeae19 100644
--- a/tests/unit/java/com/android/server/connectivityservice/CSKeepConnectedTest.kt
+++ b/tests/unit/java/com/android/server/connectivityservice/CSKeepConnectedTest.kt
@@ -45,20 +45,29 @@
                 .addTransportType(TRANSPORT_WIFI)
                 .addCapability(NET_CAPABILITY_LOCAL_NETWORK)
                 .build()
-        val keepConnectedAgent = Agent(nc = nc, score = FromS(NetworkScore.Builder()
-                .setKeepConnectedReason(KEEP_CONNECTED_LOCAL_NETWORK)
-                .build()),
-                lnc = FromS(LocalNetworkConfig.Builder().build()))
-        val dontKeepConnectedAgent = Agent(nc = nc,
-                lnc = FromS(LocalNetworkConfig.Builder().build()))
+        val keepConnectedAgent = Agent(
+            nc = nc,
+            score = FromS(
+                    NetworkScore.Builder()
+                            .setKeepConnectedReason(KEEP_CONNECTED_LOCAL_NETWORK)
+                            .build()
+            ),
+            lnc = FromS(LocalNetworkConfig.Builder().build())
+        )
+        val dontKeepConnectedAgent = Agent(
+            nc = nc,
+            lnc = FromS(LocalNetworkConfig.Builder().build())
+        )
         doTestKeepConnected(keepConnectedAgent, dontKeepConnectedAgent)
     }
 
     @Test
     fun testKeepConnectedForTest() {
-        val keepAgent = Agent(score = FromS(NetworkScore.Builder()
-                .setKeepConnectedReason(KEEP_CONNECTED_FOR_TEST)
-                .build()))
+        val keepAgent = Agent(score = FromS(
+                NetworkScore.Builder()
+                        .setKeepConnectedReason(KEEP_CONNECTED_FOR_TEST)
+                        .build()
+        ))
         val dontKeepAgent = Agent()
         doTestKeepConnected(keepAgent, dontKeepAgent)
     }
diff --git a/tests/unit/java/com/android/server/connectivityservice/CSLocalAgentCreationTests.kt b/tests/unit/java/com/android/server/connectivityservice/CSLocalAgentCreationTests.kt
index 16a30aa..6805d9a 100644
--- a/tests/unit/java/com/android/server/connectivityservice/CSLocalAgentCreationTests.kt
+++ b/tests/unit/java/com/android/server/connectivityservice/CSLocalAgentCreationTests.kt
@@ -61,7 +61,8 @@
     data class TestParams(
             val sdkLevel: Int,
             val isTv: Boolean = false,
-            val addLocalNetCapToRequest: Boolean = true)
+            val addLocalNetCapToRequest: Boolean = true
+    )
 
     companion object {
         @JvmStatic
@@ -81,8 +82,14 @@
     }
 
     private fun makeNativeNetworkConfigLocal(netId: Int, permission: Int) =
-            NativeNetworkConfig(netId, NativeNetworkType.PHYSICAL_LOCAL, permission,
-                    false /* secure */, VpnManager.TYPE_VPN_NONE, false /* excludeLocalRoutes */)
+            NativeNetworkConfig(
+                    netId,
+                    NativeNetworkType.PHYSICAL_LOCAL,
+                    permission,
+                    false /* secure */,
+                    VpnManager.TYPE_VPN_NONE,
+                    false /* excludeLocalRoutes */
+            )
 
     @Test
     fun testLocalAgents() {
@@ -99,8 +106,8 @@
             addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
             addCapability(NET_CAPABILITY_LOCAL_NETWORK)
         }.build()
-        val localAgent = if (params.sdkLevel >= VERSION_V
-                || params.sdkLevel == VERSION_U && params.isTv) {
+        val localAgent = if (params.sdkLevel >= VERSION_V ||
+                params.sdkLevel == VERSION_U && params.isTv) {
             Agent(nc = ncTemplate, score = keepConnectedScore(), lnc = defaultLnc())
         } else {
             assertFailsWith<IllegalArgumentException> { Agent(nc = ncTemplate, lnc = defaultLnc()) }
@@ -109,7 +116,8 @@
         }
         localAgent.connect()
         netdInOrder.verify(netd).networkCreate(
-                makeNativeNetworkConfigLocal(localAgent.network.netId, INetd.PERMISSION_NONE))
+                makeNativeNetworkConfigLocal(localAgent.network.netId, INetd.PERMISSION_NONE)
+        )
         if (params.addLocalNetCapToRequest) {
             assertEquals(localAgent.network, allNetworksCb.expect<Available>().network)
         } else {
@@ -123,10 +131,12 @@
     @Test
     fun testBadAgents() {
         assertFailsWith<IllegalArgumentException> {
-            Agent(nc = NetworkCapabilities.Builder()
-                    .addCapability(NET_CAPABILITY_LOCAL_NETWORK)
-                    .build(),
-                    lnc = null)
+            Agent(
+                    nc = NetworkCapabilities.Builder()
+                            .addCapability(NET_CAPABILITY_LOCAL_NETWORK)
+                            .build(),
+                    lnc = null
+            )
         }
         assertFailsWith<IllegalArgumentException> {
             Agent(nc = NetworkCapabilities.Builder().build(), lnc = defaultLnc())
diff --git a/tests/unit/java/com/android/server/connectivityservice/CSLocalAgentTests.kt b/tests/unit/java/com/android/server/connectivityservice/CSLocalAgentTests.kt
index 3583f84..d835155 100644
--- a/tests/unit/java/com/android/server/connectivityservice/CSLocalAgentTests.kt
+++ b/tests/unit/java/com/android/server/connectivityservice/CSLocalAgentTests.kt
@@ -111,14 +111,18 @@
         deps.setBuildSdk(VERSION_V)
 
         assertFailsWith<IllegalArgumentException> {
-            Agent(nc = NetworkCapabilities.Builder()
-                    .addCapability(NET_CAPABILITY_LOCAL_NETWORK)
-                    .build(),
-                    lnc = null)
+            Agent(
+                    nc = NetworkCapabilities.Builder()
+                            .addCapability(NET_CAPABILITY_LOCAL_NETWORK)
+                            .build(),
+                    lnc = null
+            )
         }
         assertFailsWith<IllegalArgumentException> {
-            Agent(nc = NetworkCapabilities.Builder().build(),
-                    lnc = FromS(LocalNetworkConfig.Builder().build()))
+            Agent(
+                    nc = NetworkCapabilities.Builder().build(),
+                    lnc = FromS(LocalNetworkConfig.Builder().build())
+            )
         }
     }
 
@@ -127,27 +131,31 @@
         deps.setBuildSdk(VERSION_V)
 
         val cb = TestableNetworkCallback()
-        cm.requestNetwork(NetworkRequest.Builder()
-                .clearCapabilities()
-                .build(),
-                cb)
-        val agent = Agent(nc = NetworkCapabilities.Builder()
-                .addCapability(NET_CAPABILITY_LOCAL_NETWORK)
-                .build(),
-                lnc = FromS(LocalNetworkConfig.Builder().build()))
+        cm.requestNetwork(
+                NetworkRequest.Builder()
+                        .clearCapabilities()
+                        .build(),
+                cb
+        )
+        val agent = Agent(
+                nc = NetworkCapabilities.Builder()
+                        .addCapability(NET_CAPABILITY_LOCAL_NETWORK)
+                        .build(),
+                lnc = FromS(LocalNetworkConfig.Builder().build())
+        )
         agent.connect()
         cb.expectAvailableCallbacks(agent.network, validated = false)
         agent.sendNetworkCapabilities(NetworkCapabilities.Builder().build())
         cb.expect<Lost>(agent.network)
 
-        val agent2 = Agent(nc = NetworkCapabilities.Builder()
-                .build(),
-                lnc = null)
+        val agent2 = Agent(nc = NetworkCapabilities.Builder().build(), lnc = null)
         agent2.connect()
         cb.expectAvailableCallbacks(agent2.network, validated = false)
-        agent2.sendNetworkCapabilities(NetworkCapabilities.Builder()
-                .addCapability(NET_CAPABILITY_LOCAL_NETWORK)
-                .build())
+        agent2.sendNetworkCapabilities(
+                NetworkCapabilities.Builder()
+                        .addCapability(NET_CAPABILITY_LOCAL_NETWORK)
+                        .build()
+        )
         cb.expect<Lost>(agent2.network)
     }
 
@@ -156,10 +164,12 @@
         deps.setBuildSdk(VERSION_V)
 
         val cb = TestableNetworkCallback()
-        cm.requestNetwork(NetworkRequest.Builder()
-                .addCapability(NET_CAPABILITY_LOCAL_NETWORK)
-                .build(),
-                cb)
+        cm.requestNetwork(
+                NetworkRequest.Builder()
+                        .addCapability(NET_CAPABILITY_LOCAL_NETWORK)
+                        .build(),
+                cb
+        )
 
         // Set up a local agent that should forward its traffic to the best DUN upstream.
         val localAgent = Agent(
@@ -171,15 +181,20 @@
 
         cb.expectAvailableCallbacks(localAgent.network, validated = false)
 
-        val wifiAgent = Agent(score = keepScore(), lp = lp("wifi0"),
-                nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET))
+        val wifiAgent = Agent(
+                score = keepScore(),
+                lp = lp("wifi0"),
+                nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET)
+        )
         wifiAgent.connect()
 
         val newLnc = LocalNetworkConfig.Builder()
-                .setUpstreamSelector(NetworkRequest.Builder()
-                        .addTransportType(TRANSPORT_WIFI)
-                        .addForbiddenCapability(NET_CAPABILITY_LOCAL_NETWORK)
-                        .build())
+                .setUpstreamSelector(
+                        NetworkRequest.Builder()
+                                .addTransportType(TRANSPORT_WIFI)
+                                .addForbiddenCapability(NET_CAPABILITY_LOCAL_NETWORK)
+                                .build()
+                )
                 .build()
         localAgent.sendLocalNetworkConfig(newLnc)
 
@@ -207,21 +222,29 @@
                 nc = nc(TRANSPORT_THREAD, NET_CAPABILITY_LOCAL_NETWORK),
                 lp = lp(name),
                 lnc = localNetworkConfig,
-                score = FromS(NetworkScore.Builder()
+                score = FromS(
+                    NetworkScore.Builder()
                         .setKeepConnectedReason(KEEP_CONNECTED_LOCAL_NETWORK)
-                        .build())
+                        .build()
+                )
         )
         return localAgent
     }
 
     private fun createWifiAgent(name: String): CSAgentWrapper {
-        return Agent(score = keepScore(), lp = lp(name),
-                nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET))
+        return Agent(
+                score = keepScore(),
+                lp = lp(name),
+                nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET)
+        )
     }
 
     private fun createCellAgent(name: String): CSAgentWrapper {
-        return Agent(score = keepScore(), lp = lp(name),
-                nc = nc(TRANSPORT_CELLULAR, NET_CAPABILITY_INTERNET))
+        return Agent(
+                score = keepScore(),
+                lp = lp(name),
+                nc = nc(TRANSPORT_CELLULAR, NET_CAPABILITY_INTERNET)
+        )
     }
 
     private fun sendLocalNetworkConfig(
@@ -245,11 +268,12 @@
         cm.registerNetworkCallback(NetworkRequest.Builder().clearCapabilities().build(), cb)
         val inOrder = inOrder(multicastRoutingCoordinatorService)
 
-        val lnc = FromS(LocalNetworkConfig.Builder()
-                .setUpstreamSelector(upstreamSelectorWifi)
-                .setUpstreamMulticastRoutingConfig(multicastRoutingConfigMinScope)
-                .setDownstreamMulticastRoutingConfig(multicastRoutingConfigSelected)
-                .build()
+        val lnc = FromS(
+                LocalNetworkConfig.Builder()
+                        .setUpstreamSelector(upstreamSelectorWifi)
+                        .setUpstreamMulticastRoutingConfig(multicastRoutingConfigMinScope)
+                        .setDownstreamMulticastRoutingConfig(multicastRoutingConfigSelected)
+                        .build()
         )
         val localAgent = createLocalAgent("local0", lnc)
         localAgent.connect()
@@ -264,9 +288,15 @@
         }
 
         inOrder.verify(multicastRoutingCoordinatorService).applyMulticastRoutingConfig(
-                "local0", "wifi0", multicastRoutingConfigMinScope)
+                "local0",
+                "wifi0",
+                multicastRoutingConfigMinScope
+        )
         inOrder.verify(multicastRoutingCoordinatorService).applyMulticastRoutingConfig(
-                "wifi0", "local0", multicastRoutingConfigSelected)
+                "wifi0",
+                "local0",
+                multicastRoutingConfigSelected
+        )
 
         wifiAgent.disconnect()
 
@@ -282,11 +312,12 @@
     fun testMulticastRoutingConfig_2LocalNetworks() {
         deps.setBuildSdk(VERSION_V)
         val inOrder = inOrder(multicastRoutingCoordinatorService)
-        val lnc = FromS(LocalNetworkConfig.Builder()
-                .setUpstreamSelector(upstreamSelectorWifi)
-                .setUpstreamMulticastRoutingConfig(multicastRoutingConfigMinScope)
-                .setDownstreamMulticastRoutingConfig(multicastRoutingConfigSelected)
-                .build()
+        val lnc = FromS(
+                LocalNetworkConfig.Builder()
+                        .setUpstreamSelector(upstreamSelectorWifi)
+                        .setUpstreamMulticastRoutingConfig(multicastRoutingConfigMinScope)
+                        .setDownstreamMulticastRoutingConfig(multicastRoutingConfigSelected)
+                        .build()
         )
         val localAgent0 = createLocalAgent("local0", lnc)
         localAgent0.connect()
@@ -296,18 +327,30 @@
         waitForIdle()
 
         inOrder.verify(multicastRoutingCoordinatorService).applyMulticastRoutingConfig(
-                "local0", "wifi0", multicastRoutingConfigMinScope)
+                "local0",
+                "wifi0",
+                multicastRoutingConfigMinScope
+        )
         inOrder.verify(multicastRoutingCoordinatorService).applyMulticastRoutingConfig(
-                "wifi0", "local0", multicastRoutingConfigSelected)
+                "wifi0",
+                "local0",
+                multicastRoutingConfigSelected
+        )
 
         val localAgent1 = createLocalAgent("local1", lnc)
         localAgent1.connect()
         waitForIdle()
 
         inOrder.verify(multicastRoutingCoordinatorService).applyMulticastRoutingConfig(
-                "local1", "wifi0", multicastRoutingConfigMinScope)
+                "local1",
+                "wifi0",
+                multicastRoutingConfigMinScope
+        )
         inOrder.verify(multicastRoutingCoordinatorService).applyMulticastRoutingConfig(
-                "wifi0", "local1", multicastRoutingConfigSelected)
+                "wifi0",
+                "local1",
+                multicastRoutingConfigSelected
+        )
 
         localAgent0.disconnect()
         localAgent1.disconnect()
@@ -318,15 +361,19 @@
     fun testMulticastRoutingConfig_UpstreamNetworkCellToWifi() {
         deps.setBuildSdk(VERSION_V)
         val cb = TestableNetworkCallback()
-        cm.registerNetworkCallback(NetworkRequest.Builder().clearCapabilities()
+        cm.registerNetworkCallback(
+                NetworkRequest.Builder().clearCapabilities()
                         .addCapability(NET_CAPABILITY_LOCAL_NETWORK)
-                        .build(), cb)
+                        .build(),
+            cb
+        )
         val inOrder = inOrder(multicastRoutingCoordinatorService)
-        val lnc = FromS(LocalNetworkConfig.Builder()
-                .setUpstreamSelector(upstreamSelectorAny)
-                .setUpstreamMulticastRoutingConfig(multicastRoutingConfigMinScope)
-                .setDownstreamMulticastRoutingConfig(multicastRoutingConfigSelected)
-                .build()
+        val lnc = FromS(
+                LocalNetworkConfig.Builder()
+                        .setUpstreamSelector(upstreamSelectorAny)
+                        .setUpstreamMulticastRoutingConfig(multicastRoutingConfigMinScope)
+                        .setDownstreamMulticastRoutingConfig(multicastRoutingConfigSelected)
+                        .build()
         )
         val localAgent = createLocalAgent("local0", lnc)
         val wifiAgent = createWifiAgent("wifi0")
@@ -341,9 +388,15 @@
         }
 
         inOrder.verify(multicastRoutingCoordinatorService).applyMulticastRoutingConfig(
-                "local0", "cell0", multicastRoutingConfigMinScope)
+                "local0",
+                "cell0",
+                multicastRoutingConfigMinScope
+        )
         inOrder.verify(multicastRoutingCoordinatorService).applyMulticastRoutingConfig(
-                "cell0", "local0", multicastRoutingConfigSelected)
+                "cell0",
+                "local0",
+                multicastRoutingConfigSelected
+        )
 
         wifiAgent.connect()
 
@@ -357,9 +410,15 @@
         inOrder.verify(multicastRoutingCoordinatorService)
                 .applyMulticastRoutingConfig("cell0", "local0", CONFIG_FORWARD_NONE)
         inOrder.verify(multicastRoutingCoordinatorService).applyMulticastRoutingConfig(
-                "local0", "wifi0", multicastRoutingConfigMinScope)
+                "local0",
+                "wifi0",
+                multicastRoutingConfigMinScope
+        )
         inOrder.verify(multicastRoutingCoordinatorService).applyMulticastRoutingConfig(
-                "wifi0", "local0", multicastRoutingConfigSelected)
+                "wifi0",
+                "local0",
+                multicastRoutingConfigSelected
+        )
 
         localAgent.disconnect()
         cellAgent.disconnect()
@@ -370,15 +429,19 @@
     fun testMulticastRoutingConfig_UpstreamSelectorCellToWifi() {
         deps.setBuildSdk(VERSION_V)
         val cb = TestableNetworkCallback()
-        cm.registerNetworkCallback(NetworkRequest.Builder().clearCapabilities()
+        cm.registerNetworkCallback(
+                NetworkRequest.Builder().clearCapabilities()
                         .addCapability(NET_CAPABILITY_LOCAL_NETWORK)
-                        .build(), cb)
+                        .build(),
+                cb
+        )
         val inOrder = inOrder(multicastRoutingCoordinatorService)
-        val lnc = FromS(LocalNetworkConfig.Builder()
-                .setUpstreamSelector(upstreamSelectorCell)
-                .setUpstreamMulticastRoutingConfig(multicastRoutingConfigMinScope)
-                .setDownstreamMulticastRoutingConfig(multicastRoutingConfigSelected)
-                .build()
+        val lnc = FromS(
+                LocalNetworkConfig.Builder()
+                        .setUpstreamSelector(upstreamSelectorCell)
+                        .setUpstreamMulticastRoutingConfig(multicastRoutingConfigMinScope)
+                        .setDownstreamMulticastRoutingConfig(multicastRoutingConfigSelected)
+                        .build()
         )
         val localAgent = createLocalAgent("local0", lnc)
         val wifiAgent = createWifiAgent("wifi0")
@@ -393,12 +456,22 @@
         }
 
         inOrder.verify(multicastRoutingCoordinatorService).applyMulticastRoutingConfig(
-                "local0", "cell0", multicastRoutingConfigMinScope)
+                "local0",
+                "cell0",
+                multicastRoutingConfigMinScope
+        )
         inOrder.verify(multicastRoutingCoordinatorService).applyMulticastRoutingConfig(
-                "cell0", "local0", multicastRoutingConfigSelected)
+                "cell0",
+                "local0",
+                multicastRoutingConfigSelected
+        )
 
-        sendLocalNetworkConfig(localAgent, upstreamSelectorWifi, multicastRoutingConfigMinScope,
-                multicastRoutingConfigSelected)
+        sendLocalNetworkConfig(
+                localAgent,
+                upstreamSelectorWifi,
+                multicastRoutingConfigMinScope,
+                multicastRoutingConfigSelected
+        )
         cb.expect<LocalInfoChanged>(localAgent.network) {
             it.info.upstreamNetwork == wifiAgent.network
         }
@@ -409,9 +482,15 @@
         inOrder.verify(multicastRoutingCoordinatorService)
                 .applyMulticastRoutingConfig("cell0", "local0", CONFIG_FORWARD_NONE)
         inOrder.verify(multicastRoutingCoordinatorService).applyMulticastRoutingConfig(
-                "local0", "wifi0", multicastRoutingConfigMinScope)
+                "local0",
+                "wifi0",
+                multicastRoutingConfigMinScope
+        )
         inOrder.verify(multicastRoutingCoordinatorService).applyMulticastRoutingConfig(
-                "wifi0", "local0", multicastRoutingConfigSelected)
+                "wifi0",
+                "local0",
+                multicastRoutingConfigSelected
+        )
 
         localAgent.disconnect()
         cellAgent.disconnect()
@@ -422,15 +501,19 @@
     fun testMulticastRoutingConfig_UpstreamSelectorWifiToNull() {
         deps.setBuildSdk(VERSION_V)
         val cb = TestableNetworkCallback()
-        cm.registerNetworkCallback(NetworkRequest.Builder().clearCapabilities()
+        cm.registerNetworkCallback(
+                NetworkRequest.Builder().clearCapabilities()
                         .addCapability(NET_CAPABILITY_LOCAL_NETWORK)
-                        .build(), cb)
+                        .build(),
+                cb
+        )
         val inOrder = inOrder(multicastRoutingCoordinatorService)
-        val lnc = FromS(LocalNetworkConfig.Builder()
-                .setUpstreamSelector(upstreamSelectorWifi)
-                .setUpstreamMulticastRoutingConfig(multicastRoutingConfigMinScope)
-                .setDownstreamMulticastRoutingConfig(multicastRoutingConfigSelected)
-                .build()
+        val lnc = FromS(
+                LocalNetworkConfig.Builder()
+                        .setUpstreamSelector(upstreamSelectorWifi)
+                        .setUpstreamMulticastRoutingConfig(multicastRoutingConfigMinScope)
+                        .setDownstreamMulticastRoutingConfig(multicastRoutingConfigSelected)
+                        .build()
         )
         val localAgent = createLocalAgent("local0", lnc)
         localAgent.connect()
@@ -442,12 +525,22 @@
         }
 
         inOrder.verify(multicastRoutingCoordinatorService).applyMulticastRoutingConfig(
-                "local0", "wifi0", multicastRoutingConfigMinScope)
+                "local0",
+                "wifi0",
+                multicastRoutingConfigMinScope
+        )
         inOrder.verify(multicastRoutingCoordinatorService).applyMulticastRoutingConfig(
-                "wifi0", "local0", multicastRoutingConfigSelected)
+                "wifi0",
+                "local0",
+                multicastRoutingConfigSelected
+        )
 
-        sendLocalNetworkConfig(localAgent, null, multicastRoutingConfigMinScope,
-                multicastRoutingConfigSelected)
+        sendLocalNetworkConfig(
+                localAgent,
+                null,
+                multicastRoutingConfigMinScope,
+                multicastRoutingConfigSelected
+        )
         cb.expect<LocalInfoChanged>(localAgent.network) {
             it.info.upstreamNetwork == null
         }
@@ -458,9 +551,15 @@
         inOrder.verify(multicastRoutingCoordinatorService)
                 .applyMulticastRoutingConfig("wifi0", "local0", CONFIG_FORWARD_NONE)
         inOrder.verify(multicastRoutingCoordinatorService, never()).applyMulticastRoutingConfig(
-                eq("local0"), any(), eq(multicastRoutingConfigMinScope))
+                eq("local0"),
+                any(),
+                eq(multicastRoutingConfigMinScope)
+        )
         inOrder.verify(multicastRoutingCoordinatorService, never()).applyMulticastRoutingConfig(
-                any(), eq("local0"), eq(multicastRoutingConfigSelected))
+                any(),
+                eq("local0"),
+                eq(multicastRoutingConfigSelected)
+        )
 
         localAgent.disconnect()
         wifiAgent.disconnect()
@@ -482,23 +581,30 @@
         cm.registerNetworkCallback(NetworkRequest.Builder().clearCapabilities().build(), cb)
 
         // Set up a local agent that should forward its traffic to the best wifi upstream.
-        val localAgent = Agent(nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_LOCAL_NETWORK),
+        val localAgent = Agent(
+                nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_LOCAL_NETWORK),
                 lp = lp("local0"),
-                lnc = FromS(LocalNetworkConfig.Builder()
-                        .setUpstreamSelector(upstreamSelectorWifi)
-                        .setUpstreamMulticastRoutingConfig(multicastRoutingConfigMinScope)
-                        .setDownstreamMulticastRoutingConfig(multicastRoutingConfigSelected)
-                        .build()),
-                score = FromS(NetworkScore.Builder()
-                        .setKeepConnectedReason(KEEP_CONNECTED_LOCAL_NETWORK)
-                        .build())
+                lnc = FromS(
+                        LocalNetworkConfig.Builder()
+                                .setUpstreamSelector(upstreamSelectorWifi)
+                                .setUpstreamMulticastRoutingConfig(multicastRoutingConfigMinScope)
+                                .setDownstreamMulticastRoutingConfig(multicastRoutingConfigSelected)
+                                .build()
+                ),
+                score = FromS(
+                        NetworkScore.Builder()
+                                .setKeepConnectedReason(KEEP_CONNECTED_LOCAL_NETWORK)
+                                .build()
+                )
         )
         localAgent.connect()
 
         cb.expectAvailableCallbacks(localAgent.network, validated = false)
 
-        val wifiAgent = Agent(lp = lp("wifi0"),
-                nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET))
+        val wifiAgent = Agent(
+                lp = lp("wifi0"),
+                nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET)
+        )
         wifiAgent.connect()
 
         cb.expectAvailableCallbacks(wifiAgent.network, validated = false)
@@ -519,8 +625,10 @@
         inOrder.verify(netd).networkDestroy(wifiAgent.network.netId)
 
         val wifiIface2 = if (sameIfaceName) "wifi0" else "wifi1"
-        val wifiAgent2 = Agent(lp = lp(wifiIface2),
-                nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET))
+        val wifiAgent2 = Agent(
+                lp = lp(wifiIface2),
+                nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET)
+        )
         wifiAgent2.connect()
 
         cb.expectAvailableCallbacks(wifiAgent2.network, validated = false)
@@ -529,9 +637,15 @@
 
         inOrder.verify(netd).ipfwdAddInterfaceForward("local0", wifiIface2)
         inOrder.verify(multicastRoutingCoordinatorService).applyMulticastRoutingConfig(
-                "local0", wifiIface2, multicastRoutingConfigMinScope)
+                "local0",
+                wifiIface2,
+                multicastRoutingConfigMinScope
+        )
         inOrder.verify(multicastRoutingCoordinatorService).applyMulticastRoutingConfig(
-                wifiIface2, "local0", multicastRoutingConfigSelected)
+                wifiIface2,
+                "local0",
+                multicastRoutingConfigSelected
+        )
 
         inOrder.verify(netd, never()).ipfwdRemoveInterfaceForward(any(), any())
         inOrder.verify(multicastRoutingCoordinatorService, never())
@@ -547,24 +661,35 @@
         cm.registerNetworkCallback(NetworkRequest.Builder().clearCapabilities().build(), cb)
 
         // Set up a local agent that should forward its traffic to the best wifi upstream.
-        val localAgent = Agent(nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_LOCAL_NETWORK),
+        val localAgent = Agent(
+                nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_LOCAL_NETWORK),
                 lp = lp("local0"),
-                lnc = FromS(LocalNetworkConfig.Builder()
-                        .setUpstreamSelector(NetworkRequest.Builder()
-                                .addTransportType(TRANSPORT_WIFI)
-                                .addForbiddenCapability(NET_CAPABILITY_LOCAL_NETWORK)
-                                .build())
-                        .build()),
-                score = FromS(NetworkScore.Builder()
-                        .setKeepConnectedReason(KEEP_CONNECTED_LOCAL_NETWORK)
-                        .build())
+                lnc = FromS(
+                        LocalNetworkConfig.Builder()
+                                .setUpstreamSelector(
+                                        NetworkRequest.Builder()
+                                                .addTransportType(TRANSPORT_WIFI)
+                                                .addForbiddenCapability(
+                                                        NET_CAPABILITY_LOCAL_NETWORK
+                                                )
+                                                .build()
+                                )
+                                .build()
+                ),
+                score = FromS(
+                        NetworkScore.Builder()
+                                .setKeepConnectedReason(KEEP_CONNECTED_LOCAL_NETWORK)
+                                .build()
+                )
         )
         localAgent.connect()
 
         cb.expectAvailableCallbacks(localAgent.network, validated = false)
 
-        val wifiAgent = Agent(lp = lp("wifi0"),
-                nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET))
+        val wifiAgent = Agent(
+                lp = lp("wifi0"),
+                nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET)
+        )
         wifiAgent.connect()
 
         cb.expectAvailableCallbacks(wifiAgent.network, validated = false)
@@ -587,21 +712,27 @@
         deps.setBuildSdk(VERSION_V)
 
         val localCb = TestableNetworkCallback()
-        cm.requestNetwork(NetworkRequest.Builder().clearCapabilities()
-                .addCapability(NET_CAPABILITY_LOCAL_NETWORK)
-                .build(),
-                localCb)
+        cm.requestNetwork(
+                NetworkRequest.Builder().clearCapabilities()
+                        .addCapability(NET_CAPABILITY_LOCAL_NETWORK)
+                        .build(),
+                localCb
+        )
 
         val cb = TestableNetworkCallback()
         cm.registerNetworkCallback(NetworkRequest.Builder().clearCapabilities().build(), cb)
 
         val localNc = nc(TRANSPORT_WIFI, NET_CAPABILITY_LOCAL_NETWORK)
-        val lnc = FromS(LocalNetworkConfig.Builder()
-                .setUpstreamSelector(NetworkRequest.Builder()
-                        .addTransportType(TRANSPORT_WIFI)
-                        .addForbiddenCapability(NET_CAPABILITY_LOCAL_NETWORK)
-                        .build())
-                .build())
+        val lnc = FromS(
+                LocalNetworkConfig.Builder()
+                        .setUpstreamSelector(
+                                NetworkRequest.Builder()
+                                        .addTransportType(TRANSPORT_WIFI)
+                                        .addForbiddenCapability(NET_CAPABILITY_LOCAL_NETWORK)
+                                        .build()
+                        )
+                        .build()
+        )
         val localScore = FromS(NetworkScore.Builder().build())
 
         // Set up a local agent that should forward its traffic to the best wifi upstream.
@@ -628,10 +759,16 @@
         val localAgent2 = Agent(nc = localNc, lp = lp("local0"), lnc = lnc, score = localScore)
         localAgent2.connect()
 
-        localCb.expectAvailableCallbacks(localAgent2.network,
-                validated = false, upstream = wifiAgent.network)
-        cb.expectAvailableCallbacks(localAgent2.network,
-                validated = false, upstream = wifiAgent.network)
+        localCb.expectAvailableCallbacks(
+                localAgent2.network,
+                validated = false,
+                upstream = wifiAgent.network
+        )
+        cb.expectAvailableCallbacks(
+                localAgent2.network,
+                validated = false,
+                upstream = wifiAgent.network
+        )
         cb.expect<Lost> { it.network == localAgent.network }
     }
 
@@ -648,24 +785,36 @@
         // Unregister wifi pending replacement, then set up a local agent that would have
         // this network as its upstream.
         wifiAgent.unregisterAfterReplacement(LONG_TIMEOUT_MS)
-        val localAgent = Agent(nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_LOCAL_NETWORK),
+        val localAgent = Agent(
+                nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_LOCAL_NETWORK),
                 lp = lp("local0"),
-                lnc = FromS(LocalNetworkConfig.Builder()
-                        .setUpstreamSelector(NetworkRequest.Builder()
-                                .addForbiddenCapability(NET_CAPABILITY_LOCAL_NETWORK)
-                                .addTransportType(TRANSPORT_WIFI)
-                                .build())
-                        .build()),
-                score = FromS(NetworkScore.Builder()
-                        .setKeepConnectedReason(KEEP_CONNECTED_LOCAL_NETWORK)
-                        .build())
+                lnc = FromS(
+                        LocalNetworkConfig.Builder()
+                                .setUpstreamSelector(
+                                        NetworkRequest.Builder()
+                                                .addForbiddenCapability(
+                                                        NET_CAPABILITY_LOCAL_NETWORK
+                                                )
+                                                .addTransportType(TRANSPORT_WIFI)
+                                                .build()
+                                )
+                                .build()
+                ),
+                score = FromS(
+                        NetworkScore.Builder()
+                                .setKeepConnectedReason(KEEP_CONNECTED_LOCAL_NETWORK)
+                                .build()
+                )
         )
 
         // Connect the local agent. The zombie wifi is its upstream, but the stack doesn't
         // tell netd to add the forward since the wifi0 interface has gone.
         localAgent.connect()
-        cb.expectAvailableCallbacks(localAgent.network,
-                validated = false, upstream = wifiAgent.network)
+        cb.expectAvailableCallbacks(
+                localAgent.network,
+                validated = false,
+                upstream = wifiAgent.network
+        )
 
         verify(netd, never()).ipfwdAddInterfaceForward("local0", "wifi0")
 
@@ -679,33 +828,51 @@
     fun testForwardingRules() {
         deps.setBuildSdk(VERSION_V)
         // Set up a local agent that should forward its traffic to the best DUN upstream.
-        val lnc = FromS(LocalNetworkConfig.Builder()
-                .setUpstreamSelector(NetworkRequest.Builder()
-                        .addCapability(NET_CAPABILITY_DUN)
-                        .addForbiddenCapability(NET_CAPABILITY_LOCAL_NETWORK)
-                        .build())
-                .build())
-        val localAgent = Agent(nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_LOCAL_NETWORK),
+        val lnc = FromS(
+                LocalNetworkConfig.Builder()
+                        .setUpstreamSelector(
+                                NetworkRequest.Builder()
+                                        .addCapability(NET_CAPABILITY_DUN)
+                                        .addForbiddenCapability(NET_CAPABILITY_LOCAL_NETWORK)
+                                        .build()
+                        )
+                        .build()
+        )
+        val localAgent = Agent(
+                nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_LOCAL_NETWORK),
                 lp = lp("local0"),
                 lnc = lnc,
-                score = FromS(NetworkScore.Builder()
-                        .setKeepConnectedReason(KEEP_CONNECTED_LOCAL_NETWORK)
-                        .build())
+                score = FromS(
+                        NetworkScore.Builder()
+                                .setKeepConnectedReason(KEEP_CONNECTED_LOCAL_NETWORK)
+                                .build()
+                )
         )
         localAgent.connect()
 
-        val wifiAgent = Agent(score = keepScore(), lp = lp("wifi0"),
-                nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET))
-        val cellAgentDun = Agent(score = keepScore(), lp = lp("cell0"),
-                nc = nc(TRANSPORT_CELLULAR, NET_CAPABILITY_INTERNET, NET_CAPABILITY_DUN))
-        val wifiAgentDun = Agent(score = keepScore(), lp = lp("wifi1"),
-                nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET, NET_CAPABILITY_DUN))
+        val wifiAgent = Agent(
+                score = keepScore(),
+                lp = lp("wifi0"),
+                nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET)
+        )
+        val cellAgentDun = Agent(
+                score = keepScore(),
+                lp = lp("cell0"),
+                nc = nc(TRANSPORT_CELLULAR, NET_CAPABILITY_INTERNET, NET_CAPABILITY_DUN)
+        )
+        val wifiAgentDun = Agent(
+                score = keepScore(),
+                lp = lp("wifi1"),
+                nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET, NET_CAPABILITY_DUN)
+        )
 
         val cb = TestableNetworkCallback()
-        cm.registerNetworkCallback(NetworkRequest.Builder()
-                .addCapability(NET_CAPABILITY_LOCAL_NETWORK)
-                .build(),
-                cb)
+        cm.registerNetworkCallback(
+                NetworkRequest.Builder()
+                        .addCapability(NET_CAPABILITY_LOCAL_NETWORK)
+                        .build(),
+                cb
+        )
         cb.expectAvailableCallbacks(localAgent.network, validated = false)
 
         val inOrder = inOrder(netd)
@@ -750,8 +917,11 @@
         inOrder.verify(netd).ipfwdDisableForwarding(any())
         cb.expect<LocalInfoChanged>(localAgent.network) { it.info.upstreamNetwork == null }
 
-        val wifiAgentDun2 = Agent(score = keepScore(), lp = lp("wifi2"),
-                nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET, NET_CAPABILITY_DUN))
+        val wifiAgentDun2 = Agent(
+                score = keepScore(),
+                lp = lp("wifi2"),
+                nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET, NET_CAPABILITY_DUN)
+        )
         wifiAgentDun2.connect()
         inOrder.verify(netd).ipfwdEnableForwarding(any())
         inOrder.verify(netd).ipfwdAddInterfaceForward("local0", "wifi2")
@@ -764,8 +934,11 @@
         inOrder.verify(netd).ipfwdDisableForwarding(any())
         cb.expect<LocalInfoChanged>(localAgent.network) { it.info.upstreamNetwork == null }
 
-        val wifiAgentDun3 = Agent(score = keepScore(), lp = lp("wifi3"),
-                nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET, NET_CAPABILITY_DUN))
+        val wifiAgentDun3 = Agent(
+                score = keepScore(),
+                lp = lp("wifi3"),
+                nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET, NET_CAPABILITY_DUN)
+        )
         wifiAgentDun3.connect()
         inOrder.verify(netd).ipfwdEnableForwarding(any())
         inOrder.verify(netd).ipfwdAddInterfaceForward("local0", "wifi3")
@@ -800,8 +973,11 @@
         cm.registerNetworkCallback(nr, listenCb)
 
         val upstream = if (haveUpstream) {
-            Agent(score = keepScore(), lp = lp("wifi0"),
-                    nc = nc(TRANSPORT_WIFI)).also { it.connect() }
+            Agent(
+                    score = keepScore(),
+                    lp = lp("wifi0"),
+                    nc = nc(TRANSPORT_WIFI)
+            ).also { it.connect() }
         } else {
             null
         }
@@ -809,23 +985,32 @@
         // Set up a local agent.
         val lnc = FromS(LocalNetworkConfig.Builder().apply {
             if (haveUpstream) {
-                setUpstreamSelector(NetworkRequest.Builder()
-                        .addForbiddenCapability(NET_CAPABILITY_LOCAL_NETWORK)
-                        .addTransportType(TRANSPORT_WIFI)
-                        .build())
+                setUpstreamSelector(
+                        NetworkRequest.Builder()
+                                .addForbiddenCapability(NET_CAPABILITY_LOCAL_NETWORK)
+                                .addTransportType(TRANSPORT_WIFI)
+                                .build()
+                )
             }
         }.build())
-        val localAgent = Agent(nc = nc(TRANSPORT_THREAD, NET_CAPABILITY_LOCAL_NETWORK),
+        val localAgent = Agent(
+                nc = nc(TRANSPORT_THREAD, NET_CAPABILITY_LOCAL_NETWORK),
                 lp = lp("local0"),
                 lnc = lnc,
                 score = FromS(NetworkScore.Builder().build())
         )
         localAgent.connect()
 
-        requestCb.expectAvailableCallbacks(localAgent.network,
-                validated = false, upstream = upstream?.network)
-        listenCb.expectAvailableCallbacks(localAgent.network,
-                validated = false, upstream = upstream?.network)
+        requestCb.expectAvailableCallbacks(
+                localAgent.network,
+                validated = false,
+                upstream = upstream?.network
+        )
+        listenCb.expectAvailableCallbacks(
+                localAgent.network,
+                validated = false,
+                upstream = upstream?.network
+        )
 
         cm.unregisterNetworkCallback(requestCb)
 
@@ -866,12 +1051,14 @@
         doTestLocalNetworkRequest(
                 request,
                 enableMatchLocalNetwork = false,
-                expectCallback = false)
+                expectCallback = false
+        )
         // If ENABLE_MATCH_LOCAL_NETWORK is true, request is satisfied by local network
         doTestLocalNetworkRequest(
                 request,
                 enableMatchLocalNetwork = true,
-                expectCallback = true)
+                expectCallback = true
+        )
     }
 
     @Test
@@ -880,10 +1067,12 @@
         doTestLocalNetworkRequest(
                 request,
                 enableMatchLocalNetwork = false,
-                expectCallback = true)
+                expectCallback = true
+        )
         doTestLocalNetworkRequest(
                 request,
                 enableMatchLocalNetwork = true,
-                expectCallback = true)
+                expectCallback = true
+        )
     }
 }
diff --git a/tests/unit/java/com/android/server/connectivityservice/base/CSAgentWrapper.kt b/tests/unit/java/com/android/server/connectivityservice/base/CSAgentWrapper.kt
index 9be7d11..6e07ac6 100644
--- a/tests/unit/java/com/android/server/connectivityservice/base/CSAgentWrapper.kt
+++ b/tests/unit/java/com/android/server/connectivityservice/base/CSAgentWrapper.kt
@@ -104,7 +104,8 @@
         doNothing().`when`(networkStack).makeNetworkMonitor(
                 nmNetworkCaptor.capture(),
                 any() /* name */,
-                nmCbCaptor.capture())
+                nmCbCaptor.capture()
+        )
 
         // Create the actual agent. NetworkAgent is abstract, so make an anonymous subclass.
         if (deps.isAtLeastS()) {
@@ -157,8 +158,10 @@
                 // Instead of forcefully adding NOT_SUSPENDED to all agents like older tools did,
                 // it's better to let the developer manage it as they see fit but help them
                 // debug if they forget.
-                fail("Could not connect the agent. Did you forget to add " +
-                        "NET_CAPABILITY_NOT_SUSPENDED ?")
+                fail(
+                    "Could not connect the agent. Did you forget to add " +
+                        "NET_CAPABILITY_NOT_SUSPENDED ?"
+                )
             }
             fail("Could not connect the agent. Instrumentation failure ?")
         }
diff --git a/tests/unit/java/com/android/server/connectivityservice/base/CSTestHelpers.kt b/tests/unit/java/com/android/server/connectivityservice/base/CSTestHelpers.kt
index a53d430..5e18843 100644
--- a/tests/unit/java/com/android/server/connectivityservice/base/CSTestHelpers.kt
+++ b/tests/unit/java/com/android/server/connectivityservice/base/CSTestHelpers.kt
@@ -114,8 +114,10 @@
     )
     doReturn(true).`when`(pm).hasSystemFeature(argThat { supported.contains(it) })
     val myPackageName = realContext.packageName
-    val myPackageInfo = realContext.packageManager.getPackageInfo(myPackageName,
-            PackageManager.GET_PERMISSIONS)
+    val myPackageInfo = realContext.packageManager.getPackageInfo(
+        myPackageName,
+            PackageManager.GET_PERMISSIONS
+    )
     // Very high version code so that the checks for the module version will always
     // say that it is recent enough. This is the most sensible default, but if some
     // test needs to test with different version codes they can re-mock this with a
@@ -123,7 +125,10 @@
     myPackageInfo.longVersionCode = 9999999L
     doReturn(arrayOf(myPackageName)).`when`(pm).getPackagesForUid(Binder.getCallingUid())
     doReturn(myPackageInfo).`when`(pm).getPackageInfoAsUser(
-            eq(myPackageName), anyInt(), eq(UserHandle.getCallingUserId()))
+            eq(myPackageName),
+            anyInt(),
+            eq(UserHandle.getCallingUserId())
+    )
     doReturn(listOf(myPackageInfo)).`when`(pm)
             .getInstalledPackagesAsUser(eq(PackageManager.GET_PERMISSIONS), anyInt())
 }
@@ -144,12 +149,19 @@
         handler as Handler
         val delayMs = ((date as Long) - SystemClock.elapsedRealtime()).coerceAtLeast(0)
         if (delayMs > UNREASONABLY_LONG_ALARM_WAIT_MS) {
-            fail("Attempting to send msg more than $UNREASONABLY_LONG_ALARM_WAIT_MS" +
-                    "ms into the future : $delayMs")
+            fail(
+                "Attempting to send msg more than $UNREASONABLY_LONG_ALARM_WAIT_MS" +
+                    "ms into the future : $delayMs"
+            )
         }
         alrmHdlr.postDelayed({ handler.post(wakeupMsg::onAlarm) }, wakeupMsg, delayMs)
-    }.`when`(am).setExact(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), anyLong(), anyString(),
-            any<WakeupMessage>(), any())
+    }.`when`(am).setExact(
+            eq(AlarmManager.ELAPSED_REALTIME_WAKEUP),
+            anyLong(),
+            anyString(),
+            any<WakeupMessage>(),
+            any()
+    )
     doAnswer {
         alrmHdlr.removeCallbacksAndMessages(it.getArgument<WakeupMessage>(0))
     }.`when`(am).cancel(any<WakeupMessage>())
@@ -193,14 +205,20 @@
 
 private val TEST_LINGER_DELAY_MS = 400
 private val TEST_NASCENT_DELAY_MS = 300
-internal fun makeConnectivityService(context: Context, netd: INetd, deps: Dependencies,
-                                     mPermDeps: PermissionMonitor.Dependencies) =
+internal fun makeConnectivityService(
+        context: Context,
+        netd: INetd,
+        deps: Dependencies,
+        mPermDeps: PermissionMonitor.Dependencies
+) =
         ConnectivityService(
                 context,
                 mock<IDnsResolver>(),
                 mock<IpConnectivityLog>(),
                 netd,
-                deps, mPermDeps).also {
+                deps,
+                mPermDeps
+        ).also {
             it.mLingerDelayMs = TEST_LINGER_DELAY_MS
             it.mNascentDelayMs = TEST_NASCENT_DELAY_MS
         }