Merge "Shedule task with current run if there is a service in the cache" into main
diff --git a/netbpfload/NetBpfLoad.cpp b/netbpfload/NetBpfLoad.cpp
index afb44cc..00362b4 100644
--- a/netbpfload/NetBpfLoad.cpp
+++ b/netbpfload/NetBpfLoad.cpp
@@ -250,7 +250,7 @@
     vector<char> rel_data;
     optional<struct bpf_prog_def> prog_def;
 
-    unique_fd prog_fd; /* fd after loading */
+    unique_fd prog_fd; // fd after loading
 } codeSection;
 
 static int readElfHeader(ifstream& elfFile, Elf64_Ehdr* eh) {
@@ -262,7 +262,7 @@
     return 0;
 }
 
-/* Reads all section header tables into an Shdr array */
+// Reads all section header tables into an Shdr array
 static int readSectionHeadersAll(ifstream& elfFile, vector<Elf64_Shdr>& shTable) {
     Elf64_Ehdr eh;
     int ret = 0;
@@ -273,7 +273,7 @@
     elfFile.seekg(eh.e_shoff);
     if (elfFile.fail()) return -1;
 
-    /* Read shdr table entries */
+    // Read shdr table entries
     shTable.resize(eh.e_shnum);
 
     if (!elfFile.read((char*)shTable.data(), (eh.e_shnum * eh.e_shentsize))) return -ENOMEM;
@@ -281,7 +281,7 @@
     return 0;
 }
 
-/* Read a section by its index - for ex to get sec hdr strtab blob */
+// Read a section by its index - for ex to get sec hdr strtab blob
 static int readSectionByIdx(ifstream& elfFile, int id, vector<char>& sec) {
     vector<Elf64_Shdr> shTable;
     int ret = readSectionHeadersAll(elfFile, shTable);
@@ -296,7 +296,7 @@
     return 0;
 }
 
-/* Read whole section header string table */
+// Read whole section header string table
 static int readSectionHeaderStrtab(ifstream& elfFile, vector<char>& strtab) {
     Elf64_Ehdr eh;
     int ret = readElfHeader(elfFile, &eh);
@@ -308,7 +308,7 @@
     return 0;
 }
 
-/* Get name from offset in strtab */
+// Get name from offset in strtab
 static int getSymName(ifstream& elfFile, int nameOff, string& name) {
     int ret;
     vector<char> secStrTab;
@@ -322,7 +322,7 @@
     return 0;
 }
 
-/* Reads a full section by name - example to get the GPL license */
+// Reads a full section by name - example to get the GPL license
 static int readSectionByName(const char* name, ifstream& elfFile, vector<char>& data) {
     vector<char> secStrTab;
     vector<Elf64_Shdr> shTable;
@@ -354,15 +354,15 @@
     return -2;
 }
 
-unsigned int readSectionUint(const char* name, ifstream& elfFile, unsigned int defVal) {
+unsigned int readSectionUint(const char* name, ifstream& elfFile) {
     vector<char> theBytes;
     int ret = readSectionByName(name, elfFile, theBytes);
     if (ret) {
-        ALOGD("Couldn't find section %s (defaulting to %u [0x%x]).", name, defVal, defVal);
-        return defVal;
+        ALOGE("Couldn't find section %s.", name);
+        abort();
     } else if (theBytes.size() < sizeof(unsigned int)) {
-        ALOGE("Section %s too short (defaulting to %u [0x%x]).", name, defVal, defVal);
-        return defVal;
+        ALOGE("Section %s is too short.", name);
+        abort();
     } else {
         // decode first 4 bytes as LE32 uint, there will likely be more bytes due to alignment.
         unsigned int value = static_cast<unsigned char>(theBytes[3]);
@@ -428,43 +428,24 @@
     return BPF_PROG_TYPE_UNSPEC;
 }
 
-/*
-static string getSectionName(enum bpf_prog_type type)
-{
-    for (auto& snt : sectionNameTypes)
-        if (snt.type == type)
-            return string(snt.name);
-
-    return "UNKNOWN SECTION NAME " + std::to_string(type);
-}
-*/
-
-static int readProgDefs(ifstream& elfFile, vector<struct bpf_prog_def>& pd,
-                        size_t sizeOfBpfProgDef) {
+static int readProgDefs(ifstream& elfFile, vector<struct bpf_prog_def>& pd) {
     vector<char> pdData;
     int ret = readSectionByName("progs", elfFile, pdData);
     if (ret) return ret;
 
-    if (pdData.size() % sizeOfBpfProgDef) {
+    if (pdData.size() % sizeof(struct bpf_prog_def)) {
         ALOGE("readProgDefs failed due to improper sized progs section, %zu %% %zu != 0",
-              pdData.size(), sizeOfBpfProgDef);
+              pdData.size(), sizeof(struct bpf_prog_def));
         return -1;
     };
 
-    int progCount = pdData.size() / sizeOfBpfProgDef;
-    pd.resize(progCount);
-    size_t trimmedSize = std::min(sizeOfBpfProgDef, sizeof(struct bpf_prog_def));
+    pd.resize(pdData.size() / sizeof(struct bpf_prog_def));
 
     const char* dataPtr = pdData.data();
     for (auto& p : pd) {
-        // First we zero initialize
-        memset(&p, 0, sizeof(p));
-        // Then we set non-zero defaults
-        p.bpfloader_max_ver = DEFAULT_BPFLOADER_MAX_VER;  // v1.0
-        // Then we copy over the structure prefix from the ELF file.
-        memcpy(&p, dataPtr, trimmedSize);
-        // Move to next struct in the ELF file
-        dataPtr += sizeOfBpfProgDef;
+        // Copy the structure from the ELF file and move to the next one.
+        memcpy(&p, dataPtr, sizeof(struct bpf_prog_def));
+        dataPtr += sizeof(struct bpf_prog_def);
     }
     return 0;
 }
@@ -479,7 +460,7 @@
     ret = readSymTab(elfFile, 1 /* sort */, symtab);
     if (ret) return ret;
 
-    /* Get index of section */
+    // Get index of section
     ret = readSectionHeadersAll(elfFile, shTable);
     if (ret) return ret;
 
@@ -494,7 +475,7 @@
         }
     }
 
-    /* No section found with matching name*/
+    // No section found with matching name
     if (sec_idx == -1) {
         ALOGW("No %s section could be found in elf object", sectionName.c_str());
         return -1;
@@ -514,8 +495,8 @@
     return 0;
 }
 
-/* Read a section by its index - for ex to get sec hdr strtab blob */
-static int readCodeSections(ifstream& elfFile, vector<codeSection>& cs, size_t sizeOfBpfProgDef) {
+// Read a section by its index - for ex to get sec hdr strtab blob
+static int readCodeSections(ifstream& elfFile, vector<codeSection>& cs) {
     vector<Elf64_Shdr> shTable;
     int entries, ret = 0;
 
@@ -524,7 +505,7 @@
     entries = shTable.size();
 
     vector<struct bpf_prog_def> pd;
-    ret = readProgDefs(elfFile, pd, sizeOfBpfProgDef);
+    ret = readProgDefs(elfFile, pd);
     if (ret) return ret;
     vector<string> progDefNames;
     ret = getSectionSymNames(elfFile, "progs", progDefNames);
@@ -568,7 +549,7 @@
             }
         }
 
-        /* Check for rel section */
+        // Check for rel section
         if (cs_temp.data.size() > 0 && i < entries) {
             ret = getSymName(elfFile, shTable[i + 1].sh_name, name);
             if (ret) return ret;
@@ -657,8 +638,7 @@
 }
 
 static int createMaps(const char* elfPath, ifstream& elfFile, vector<unique_fd>& mapFds,
-                      const char* prefix, const size_t sizeOfBpfMapDef,
-                      const unsigned int bpfloader_ver) {
+                      const char* prefix, const unsigned int bpfloader_ver) {
     int ret;
     vector<char> mdData;
     vector<struct bpf_map_def> md;
@@ -669,27 +649,19 @@
     if (ret == -2) return 0;  // no maps to read
     if (ret) return ret;
 
-    if (mdData.size() % sizeOfBpfMapDef) {
+    if (mdData.size() % sizeof(struct bpf_map_def)) {
         ALOGE("createMaps failed due to improper sized maps section, %zu %% %zu != 0",
-              mdData.size(), sizeOfBpfMapDef);
+              mdData.size(), sizeof(struct bpf_map_def));
         return -1;
     };
 
-    int mapCount = mdData.size() / sizeOfBpfMapDef;
-    md.resize(mapCount);
-    size_t trimmedSize = std::min(sizeOfBpfMapDef, sizeof(struct bpf_map_def));
+    md.resize(mdData.size() / sizeof(struct bpf_map_def));
 
     const char* dataPtr = mdData.data();
     for (auto& m : md) {
-        // First we zero initialize
-        memset(&m, 0, sizeof(m));
-        // Then we set non-zero defaults
-        m.bpfloader_max_ver = DEFAULT_BPFLOADER_MAX_VER;  // v1.0
-        m.max_kver = 0xFFFFFFFFu;                         // matches KVER_INF from bpf_helpers.h
-        // Then we copy over the structure prefix from the ELF file.
-        memcpy(&m, dataPtr, trimmedSize);
-        // Move to next struct in the ELF file
-        dataPtr += sizeOfBpfMapDef;
+        // Copy the structure from the ELF file and move to the next one.
+        memcpy(&m, dataPtr, sizeof(struct bpf_map_def));
+        dataPtr += sizeof(struct bpf_map_def);
     }
 
     ret = getSectionSymNames(elfFile, "maps", mapNames);
@@ -779,14 +751,14 @@
 
         domain selinux_context = getDomainFromSelinuxContext(md[i].selinux_context);
         if (specified(selinux_context)) {
-            ALOGI("map %s selinux_context [%-32s] -> %d -> '%s' (%s)", mapNames[i].c_str(),
+            ALOGV("map %s selinux_context [%-32s] -> %d -> '%s' (%s)", mapNames[i].c_str(),
                   md[i].selinux_context, static_cast<int>(selinux_context),
                   lookupSelinuxContext(selinux_context), lookupPinSubdir(selinux_context));
         }
 
         domain pin_subdir = getDomainFromPinSubdir(md[i].pin_subdir);
         if (specified(pin_subdir)) {
-            ALOGI("map %s pin_subdir [%-32s] -> %d -> '%s'", mapNames[i].c_str(), md[i].pin_subdir,
+            ALOGV("map %s pin_subdir [%-32s] -> %d -> '%s'", mapNames[i].c_str(), md[i].pin_subdir,
                   static_cast<int>(pin_subdir), lookupPinSubdir(pin_subdir));
         }
 
@@ -881,26 +853,6 @@
     return ret;
 }
 
-/* For debugging, dump all instructions */
-static void dumpIns(char* ins, int size) {
-    for (int row = 0; row < size / 8; row++) {
-        ALOGE("%d: ", row);
-        for (int j = 0; j < 8; j++) {
-            ALOGE("%3x ", ins[(row * 8) + j]);
-        }
-        ALOGE("\n");
-    }
-}
-
-/* For debugging, dump all code sections from cs list */
-static void dumpAllCs(vector<codeSection>& cs) {
-    for (int i = 0; i < (int)cs.size(); i++) {
-        ALOGE("Dumping cs %d, name %s", int(i), cs[i].name.c_str());
-        dumpIns((char*)cs[i].data.data(), cs[i].data.size());
-        ALOGE("-----------");
-    }
-}
-
 static void applyRelo(void* insnsPtr, Elf64_Addr offset, int fd) {
     int insnIndex;
     struct bpf_insn *insn, *insns;
@@ -918,9 +870,7 @@
     }
 
     if (insn->code != (BPF_LD | BPF_IMM | BPF_DW)) {
-        ALOGE("Dumping all instructions till ins %d", insnIndex);
         ALOGE("invalid relo for insn %d: code 0x%x", insnIndex, insn->code);
-        dumpIns((char*)insnsPtr, (insnIndex + 3) * 8);
         return;
     }
 
@@ -945,7 +895,7 @@
             ret = getSymNameByIdx(elfFile, symIndex, symName);
             if (ret) return;
 
-            /* Find the map fd and apply relo */
+            // Find the map fd and apply relo
             for (int j = 0; j < (int)mapNames.size(); j++) {
                 if (!mapNames[j].compare(symName)) {
                     applyRelo(cs[k].data.data(), rel[i].r_offset, mapFds[j]);
@@ -1012,13 +962,13 @@
         }
 
         if (specified(selinux_context)) {
-            ALOGI("prog %s selinux_context [%-32s] -> %d -> '%s' (%s)", name.c_str(),
+            ALOGV("prog %s selinux_context [%-32s] -> %d -> '%s' (%s)", name.c_str(),
                   cs[i].prog_def->selinux_context, static_cast<int>(selinux_context),
                   lookupSelinuxContext(selinux_context), lookupPinSubdir(selinux_context));
         }
 
         if (specified(pin_subdir)) {
-            ALOGI("prog %s pin_subdir [%-32s] -> %d -> '%s'", name.c_str(),
+            ALOGV("prog %s pin_subdir [%-32s] -> %d -> '%s'", name.c_str(),
                   cs[i].prog_def->pin_subdir, static_cast<int>(pin_subdir),
                   lookupPinSubdir(pin_subdir));
         }
@@ -1128,7 +1078,7 @@
 }
 
 int loadProg(const char* const elfPath, bool* const isCritical, const unsigned int bpfloader_ver,
-             const Location& location) {
+             const char* const prefix) {
     vector<char> license;
     vector<char> critical;
     vector<codeSection> cs;
@@ -1154,17 +1104,8 @@
               elfPath, (char*)license.data());
     }
 
-    // the following default values are for bpfloader V0.0 format which does not include them
-    unsigned int bpfLoaderMinVer =
-            readSectionUint("bpfloader_min_ver", elfFile, DEFAULT_BPFLOADER_MIN_VER);
-    unsigned int bpfLoaderMaxVer =
-            readSectionUint("bpfloader_max_ver", elfFile, DEFAULT_BPFLOADER_MAX_VER);
-    unsigned int bpfLoaderMinRequiredVer =
-            readSectionUint("bpfloader_min_required_ver", elfFile, 0);
-    size_t sizeOfBpfMapDef =
-            readSectionUint("size_of_bpf_map_def", elfFile, DEFAULT_SIZEOF_BPF_MAP_DEF);
-    size_t sizeOfBpfProgDef =
-            readSectionUint("size_of_bpf_prog_def", elfFile, DEFAULT_SIZEOF_BPF_PROG_DEF);
+    unsigned int bpfLoaderMinVer = readSectionUint("bpfloader_min_ver", elfFile);
+    unsigned int bpfLoaderMaxVer = readSectionUint("bpfloader_max_ver", elfFile);
 
     // inclusive lower bound check
     if (bpfloader_ver < bpfLoaderMinVer) {
@@ -1180,37 +1121,16 @@
         return 0;
     }
 
-    if (bpfloader_ver < bpfLoaderMinRequiredVer) {
-        ALOGI("BpfLoader version 0x%05x failing due to ELF object %s with required min ver 0x%05x",
-              bpfloader_ver, elfPath, bpfLoaderMinRequiredVer);
-        return -1;
-    }
-
     ALOGI("BpfLoader version 0x%05x processing ELF object %s with ver [0x%05x,0x%05x)",
           bpfloader_ver, elfPath, bpfLoaderMinVer, bpfLoaderMaxVer);
 
-    if (sizeOfBpfMapDef < DEFAULT_SIZEOF_BPF_MAP_DEF) {
-        ALOGE("sizeof(bpf_map_def) of %zu is too small (< %d)", sizeOfBpfMapDef,
-              DEFAULT_SIZEOF_BPF_MAP_DEF);
-        return -1;
-    }
-
-    if (sizeOfBpfProgDef < DEFAULT_SIZEOF_BPF_PROG_DEF) {
-        ALOGE("sizeof(bpf_prog_def) of %zu is too small (< %d)", sizeOfBpfProgDef,
-              DEFAULT_SIZEOF_BPF_PROG_DEF);
-        return -1;
-    }
-
-    ret = readCodeSections(elfFile, cs, sizeOfBpfProgDef);
+    ret = readCodeSections(elfFile, cs);
     if (ret) {
         ALOGE("Couldn't read all code sections in %s", elfPath);
         return ret;
     }
 
-    /* Just for future debugging */
-    if (0) dumpAllCs(cs);
-
-    ret = createMaps(elfPath, elfFile, mapFds, location.prefix, sizeOfBpfMapDef, bpfloader_ver);
+    ret = createMaps(elfPath, elfFile, mapFds, prefix, bpfloader_ver);
     if (ret) {
         ALOGE("Failed to create maps: (ret=%d) in %s", ret, elfPath);
         return ret;
@@ -1221,7 +1141,7 @@
 
     applyMapRelo(elfFile, mapFds, cs);
 
-    ret = loadCodeSections(elfPath, cs, string(license.data()), location.prefix, bpfloader_ver);
+    ret = loadCodeSections(elfPath, cs, string(license.data()), prefix, bpfloader_ver);
     if (ret) ALOGE("Failed to load programs, loadCodeSections ret=%d", ret);
 
     return ret;
@@ -1280,7 +1200,7 @@
             progPath += s;
 
             bool critical;
-            int ret = loadProg(progPath.c_str(), &critical, bpfloader_ver, location);
+            int ret = loadProg(progPath.c_str(), &critical, bpfloader_ver, location.prefix);
             if (ret) {
                 if (critical) retVal = ret;
                 ALOGE("Failed to load object: %s, ret: %s", progPath.c_str(), std::strerror(-ret));
diff --git a/netbpfload/netbpfload.33rc b/netbpfload/netbpfload.33rc
index 493731f..eb937dd 100644
--- a/netbpfload/netbpfload.33rc
+++ b/netbpfload/netbpfload.33rc
@@ -18,4 +18,3 @@
     rlimit memlock 1073741824 1073741824
     oneshot
     reboot_on_failure reboot,netbpfload-failed
-    override
diff --git a/networksecurity/service/Android.bp b/networksecurity/service/Android.bp
index 66d201a..e33abd5 100644
--- a/networksecurity/service/Android.bp
+++ b/networksecurity/service/Android.bp
@@ -27,6 +27,7 @@
     ],
 
     libs: [
+        "framework-configinfrastructure",
         "framework-connectivity-pre-jarjar",
         "service-connectivity-pre-jarjar",
     ],
diff --git a/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyFlagsListener.java b/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyFlagsListener.java
new file mode 100644
index 0000000..8dd5951
--- /dev/null
+++ b/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyFlagsListener.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.net.ct;
+
+import static android.provider.DeviceConfig.NAMESPACE_TETHERING;
+
+import android.content.Context;
+import android.provider.DeviceConfig;
+import android.provider.DeviceConfig.Properties;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.modules.utils.build.SdkLevel;
+
+import java.util.concurrent.Executors;
+
+/** Listener class for the Certificate Transparency Phenotype flags. */
+class CertificateTransparencyFlagsListener implements DeviceConfig.OnPropertiesChangedListener {
+
+    private static final String TAG = "CertificateTransparency";
+
+    private static final String VERSION = "version";
+    private static final String CONTENT_URL = "content_url";
+    private static final String METADATA_URL = "metadata_url";
+
+    CertificateTransparencyFlagsListener(Context context) {}
+
+    void initialize() {
+        DeviceConfig.addOnPropertiesChangedListener(
+                NAMESPACE_TETHERING, Executors.newSingleThreadExecutor(), this);
+        // TODO: handle property changes triggering on boot before registering this listener.
+    }
+
+    @Override
+    public void onPropertiesChanged(Properties properties) {
+        if (!SdkLevel.isAtLeastV() || !NAMESPACE_TETHERING.equals(properties.getNamespace())) {
+            return;
+        }
+
+        String newVersion = DeviceConfig.getString(NAMESPACE_TETHERING, VERSION, "");
+        String newContentUrl = DeviceConfig.getString(NAMESPACE_TETHERING, CONTENT_URL, "");
+        String newMetadataUrl = DeviceConfig.getString(NAMESPACE_TETHERING, METADATA_URL, "");
+        if (TextUtils.isEmpty(newVersion)
+                || TextUtils.isEmpty(newContentUrl)
+                || TextUtils.isEmpty(newMetadataUrl)) {
+            return;
+        }
+
+        Log.d(TAG, "newVersion=" + newVersion);
+        Log.d(TAG, "newContentUrl=" + newContentUrl);
+        Log.d(TAG, "newMetadataUrl=" + newMetadataUrl);
+        // TODO: start download of URLs.
+    }
+}
diff --git a/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyService.java b/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyService.java
index 8c53bf7..406a57f 100644
--- a/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyService.java
+++ b/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyService.java
@@ -23,6 +23,7 @@
 
 import com.android.net.ct.flags.Flags;
 import com.android.net.module.util.DeviceConfigUtils;
+import com.android.server.SystemService;
 
 /** Implementation of the Certificate Transparency service. */
 @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
@@ -32,6 +33,8 @@
     private static final String CERTIFICATE_TRANSPARENCY_ENABLED =
             "certificate_transparency_service_enabled";
 
+    private final CertificateTransparencyFlagsListener mFlagsListener;
+
     /**
      * @return true if the CertificateTransparency service is enabled.
      */
@@ -43,7 +46,9 @@
     }
 
     /** Creates a new {@link CertificateTransparencyService} object. */
-    public CertificateTransparencyService(Context context) {}
+    public CertificateTransparencyService(Context context) {
+        mFlagsListener = new CertificateTransparencyFlagsListener(context);
+    }
 
     /**
      * Called by {@link com.android.server.ConnectivityServiceInitializer}.
@@ -51,6 +56,13 @@
      * @see com.android.server.SystemService#onBootPhase
      */
     public void onBootPhase(int phase) {
-        Log.d(TAG, "CertificateTransparencyService#onBootPhase " + phase);
+
+        switch (phase) {
+            case SystemService.PHASE_BOOT_COMPLETED:
+                Log.d(TAG, "setting up flags listeners");
+                mFlagsListener.initialize();
+                break;
+            default:
+        }
     }
 }
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsDiscoveryManager.java b/service-t/src/com/android/server/connectivity/mdns/MdnsDiscoveryManager.java
index 8123d27..7fa605a 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsDiscoveryManager.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsDiscoveryManager.java
@@ -390,12 +390,18 @@
      */
     public void dump(PrintWriter pw) {
         discoveryExecutor.checkAndRunOnHandlerThread(() -> {
-            pw.println();
+            pw.println("Clients:");
             // Dump ServiceTypeClients
             for (MdnsServiceTypeClient serviceTypeClient
                     : perSocketServiceTypeClients.getAllMdnsServiceTypeClient()) {
                 serviceTypeClient.dump(pw);
             }
+            pw.println();
+            // Dump ServiceCache
+            pw.println("Cached services:");
+            if (serviceCache != null) {
+                serviceCache.dump(pw, "  ");
+            }
         });
     }
 }
\ No newline at end of file
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsResponse.java b/service-t/src/com/android/server/connectivity/mdns/MdnsResponse.java
index 3636644..2957da5 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsResponse.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsResponse.java
@@ -19,11 +19,14 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.net.Network;
+import android.os.SystemClock;
+import android.text.TextUtils;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.net.module.util.DnsUtils;
 
 import java.io.IOException;
+import java.time.Instant;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Iterator;
@@ -426,4 +429,18 @@
 
         return count;
     }
+
+    @Override
+    public String toString() {
+        return "Name: " + TextUtils.join(".", serviceName)
+                + ", pointerRecords: " + pointerRecords
+                + ", serviceRecord: " + serviceRecord
+                + ", textRecord: " + textRecord
+                + ", inet4AddressRecords: " + inet4AddressRecords
+                + ", inet6AddressRecords: " + inet6AddressRecords
+                + ", interfaceIndex: " + interfaceIndex
+                + ", network: " + network
+                + ", lastUpdateTime: " + Instant.now().minusMillis(
+                        SystemClock.elapsedRealtime() - lastUpdateTime);
+    }
 }
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsServiceCache.java b/service-t/src/com/android/server/connectivity/mdns/MdnsServiceCache.java
index a8a4ef1..591ed8b 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsServiceCache.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsServiceCache.java
@@ -32,6 +32,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.connectivity.mdns.util.MdnsUtils;
 
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Iterator;
@@ -71,6 +72,11 @@
             return Objects.equals(mUpperCaseServiceType, ((CacheKey) other).mUpperCaseServiceType)
                     && Objects.equals(mSocketKey, ((CacheKey) other).mSocketKey);
         }
+
+        @Override
+        public String toString() {
+            return "CacheKey{ ServiceType=" + mUpperCaseServiceType + ", " + mSocketKey + " }";
+        }
     }
     /**
      * A map of cached services. Key is composed of service type and socket. Value is the list of
@@ -353,6 +359,22 @@
         mNextExpirationTime = getNextExpirationTime(now);
     }
 
+    /**
+     * Dump ServiceCache state.
+     */
+    public void dump(PrintWriter pw, String indent) {
+        ensureRunningOnHandlerThread(mHandler);
+        // IndentingPrintWriter cannot be used on the mDNS stack build. So, manually add an indent.
+        for (int i = 0; i < mCachedServices.size(); i++) {
+            final CacheKey key = mCachedServices.keyAt(i);
+            pw.println(indent + key);
+            for (MdnsResponse response : mCachedServices.valueAt(i)) {
+                pw.println(indent + "  Response{ " + response + " }");
+            }
+            pw.println();
+        }
+    }
+
     /*** Callbacks for listening service expiration */
     public interface ServiceExpiredCallback {
         /*** Notify the service is expired */
diff --git a/service/src/com/android/server/connectivity/DnsManager.java b/service/src/com/android/server/connectivity/DnsManager.java
index ac02229..b95e3b1 100644
--- a/service/src/com/android/server/connectivity/DnsManager.java
+++ b/service/src/com/android/server/connectivity/DnsManager.java
@@ -41,6 +41,7 @@
 import android.net.NetworkCapabilities;
 import android.net.ResolverParamsParcel;
 import android.net.Uri;
+import android.net.resolv.aidl.DohParamsParcel;
 import android.net.shared.PrivateDnsConfig;
 import android.os.Binder;
 import android.os.RemoteException;
@@ -52,16 +53,17 @@
 import android.util.Pair;
 
 import java.net.InetAddress;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
-import java.util.stream.Collectors;
 
 /**
  * Encapsulate the management of DNS settings for networks.
@@ -382,15 +384,14 @@
         paramsParcel.domains = getDomainStrings(lp.getDomains());
         paramsParcel.tlsName = strictMode ? privateDnsCfg.hostname : "";
         paramsParcel.tlsServers =
-                strictMode ? makeStrings(
-                        Arrays.stream(privateDnsCfg.ips)
-                              .filter((ip) -> lp.isReachable(ip))
-                              .collect(Collectors.toList()))
+                strictMode ? makeStrings(getReachableAddressList(privateDnsCfg.ips, lp))
                 : useTls ? paramsParcel.servers  // Opportunistic
                 : new String[0];            // Off
         paramsParcel.transportTypes = nc.getTransportTypes();
         paramsParcel.meteredNetwork = nc.isMetered();
         paramsParcel.interfaceNames = lp.getAllInterfaceNames().toArray(new String[0]);
+        paramsParcel.dohParams = makeDohParamsParcel(privateDnsCfg, lp);
+
         // Prepare to track the validation status of the DNS servers in the
         // resolver config when private DNS is in opportunistic or strict mode.
         if (useTls) {
@@ -404,15 +405,16 @@
         }
 
         Log.d(TAG, String.format("sendDnsConfigurationForNetwork(%d, %s, %s, %d, %d, %d, %d, "
-                + "%d, %d, %s, %s, %s, %b, %s)", paramsParcel.netId,
+                + "%d, %d, %s, %s, %s, %b, %s, %s, %s, %s, %d)", paramsParcel.netId,
                 Arrays.toString(paramsParcel.servers), Arrays.toString(paramsParcel.domains),
                 paramsParcel.sampleValiditySeconds, paramsParcel.successThreshold,
                 paramsParcel.minSamples, paramsParcel.maxSamples, paramsParcel.baseTimeoutMsec,
                 paramsParcel.retryCount, paramsParcel.tlsName,
                 Arrays.toString(paramsParcel.tlsServers),
                 Arrays.toString(paramsParcel.transportTypes), paramsParcel.meteredNetwork,
-                Arrays.toString(paramsParcel.interfaceNames)));
-
+                Arrays.toString(paramsParcel.interfaceNames),
+                paramsParcel.dohParams.name, Arrays.toString(paramsParcel.dohParams.ips),
+                paramsParcel.dohParams.dohpath, paramsParcel.dohParams.port));
         try {
             mDnsResolver.setResolverConfiguration(paramsParcel);
         } catch (RemoteException | ServiceSpecificException e) {
@@ -498,4 +500,26 @@
     private static String[] getDomainStrings(String domains) {
         return (TextUtils.isEmpty(domains)) ? new String[0] : domains.split(" ");
     }
+
+    @NonNull
+    private List<InetAddress> getReachableAddressList(@NonNull InetAddress[] ips,
+            @NonNull LinkProperties lp) {
+        final ArrayList<InetAddress> out = new ArrayList<InetAddress>(Arrays.asList(ips));
+        out.removeIf(ip -> !lp.isReachable(ip));
+        return out;
+    }
+
+    @NonNull
+    private DohParamsParcel makeDohParamsParcel(@NonNull PrivateDnsConfig cfg,
+            @NonNull LinkProperties lp) {
+        if (cfg.mode == PRIVATE_DNS_MODE_OFF) {
+            return new DohParamsParcel.Builder().build();
+        }
+        return new DohParamsParcel.Builder()
+                .setName(cfg.dohName)
+                .setIps(makeStrings(getReachableAddressList(cfg.dohIps, lp)))
+                .setDohpath(cfg.dohPath)
+                .setPort(cfg.dohPort)
+                .build();
+    }
 }
diff --git a/staticlibs/Android.bp b/staticlibs/Android.bp
index 94a2411..f47a23a 100644
--- a/staticlibs/Android.bp
+++ b/staticlibs/Android.bp
@@ -296,9 +296,6 @@
         "framework-connectivity-t.stubs.module_lib",
         "framework-location.stubs.module_lib",
     ],
-    static_libs: [
-        "modules-utils-build",
-    ],
     jarjar_rules: "jarjar-rules-shared.txt",
     visibility: [
         "//cts/tests/tests/net",
@@ -534,6 +531,19 @@
     ],
 }
 
+// net-utils-connectivity-apks is only for NetworkStack, CaptivePortalLogin and
+// Tethering.apk
+// It includes all the static libraries in this directory, which is safe because
+// these APKs use R8 to strip out unused code, and they do not depend on
+// bootclasspath jars that may have duplicate copies of the included classes
+// with the same jarjaring.
+// Tethering.apk does depend on a bootclasspath jar (framework-tethering.jar),
+// however it does not use any of the static libraries. If it did, Tethering.apk
+// would need to use another variant that excludes classes that are already
+// included in framework-tethering.jar (similarly to how framework-connectivity
+// and service-connectivity do it). Otherwise, static libs included in
+// framework-tethering and Tethering.apk and jarjared the same way would
+// conflict.
 java_library {
     name: "net-utils-connectivity-apks",
     srcs: [
diff --git a/staticlibs/framework/com/android/net/module/util/NetworkStatsUtils.java b/staticlibs/framework/com/android/net/module/util/NetworkStatsUtils.java
index 04b6174..41a9428 100644
--- a/staticlibs/framework/com/android/net/module/util/NetworkStatsUtils.java
+++ b/staticlibs/framework/com/android/net/module/util/NetworkStatsUtils.java
@@ -19,9 +19,6 @@
 import android.app.usage.NetworkStats;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.modules.utils.build.SdkLevel;
-
-import java.util.ArrayList;
 
 /**
  * Various utilities used for NetworkStats related code.
@@ -108,21 +105,12 @@
      */
     public static android.net.NetworkStats fromPublicNetworkStats(
             NetworkStats publiceNetworkStats) {
-        final ArrayList<android.net.NetworkStats.Entry> entries = new ArrayList<>();
+        android.net.NetworkStats stats = new android.net.NetworkStats(0L, 0);
         while (publiceNetworkStats.hasNextBucket()) {
             NetworkStats.Bucket bucket = new NetworkStats.Bucket();
             publiceNetworkStats.getNextBucket(bucket);
-            entries.add(fromBucket(bucket));
-        }
-        android.net.NetworkStats stats = new android.net.NetworkStats(0L, 0);
-        // The new API is only supported on devices running the mainline version of `NetworkStats`.
-        // It should always be used when available for memory efficiency.
-        if (SdkLevel.isAtLeastT()) {
-            stats = stats.addEntries(entries);
-        } else {
-            for (android.net.NetworkStats.Entry entry : entries) {
-                stats = stats.addEntry(entry);
-            }
+            final android.net.NetworkStats.Entry entry = fromBucket(bucket);
+            stats = stats.addEntry(entry);
         }
         return stats;
     }
diff --git a/staticlibs/native/bpf_headers/include/bpf/bpf_helpers.h b/staticlibs/native/bpf_headers/include/bpf/bpf_helpers.h
index 4ddec8b..9069038 100644
--- a/staticlibs/native/bpf_headers/include/bpf/bpf_helpers.h
+++ b/staticlibs/native/bpf_headers/include/bpf/bpf_helpers.h
@@ -7,7 +7,7 @@
 #include "bpf_map_def.h"
 
 /******************************************************************************
- * WARNING: CHANGES TO THIS FILE OUTSIDE OF AOSP/MASTER ARE LIKELY TO BREAK   *
+ * WARNING: CHANGES TO THIS FILE OUTSIDE OF AOSP/MAIN ARE LIKELY TO BREAK     *
  * DEVICE COMPATIBILITY WITH MAINLINE MODULES SHIPPING EBPF CODE.             *
  *                                                                            *
  * THIS WILL LIKELY RESULT IN BRICKED DEVICES AT SOME ARBITRARY FUTURE TIME   *
@@ -71,11 +71,11 @@
  * In which case it's just best to use the default.
  */
 #ifndef BPFLOADER_MIN_VER
-#define BPFLOADER_MIN_VER BPFLOADER_PLATFORM_VERSION
+#define BPFLOADER_MIN_VER BPFLOADER_PLATFORM_VERSION  // inclusive, ie. >=
 #endif
 
 #ifndef BPFLOADER_MAX_VER
-#define BPFLOADER_MAX_VER DEFAULT_BPFLOADER_MAX_VER
+#define BPFLOADER_MAX_VER 0x10000u  // exclusive, ie. < v1.0
 #endif
 
 /* place things in different elf sections */
@@ -414,19 +414,10 @@
     SECTION(SECTION_NAME)                                                                \
     int the_prog
 
-#ifndef DEFAULT_BPF_PROG_SELINUX_CONTEXT
-#define DEFAULT_BPF_PROG_SELINUX_CONTEXT ""
-#endif
-
-#ifndef DEFAULT_BPF_PROG_PIN_SUBDIR
-#define DEFAULT_BPF_PROG_PIN_SUBDIR ""
-#endif
-
 #define DEFINE_BPF_PROG_KVER_RANGE_OPT(SECTION_NAME, prog_uid, prog_gid, the_prog, min_kv, max_kv, \
                                        opt)                                                        \
     DEFINE_BPF_PROG_EXT(SECTION_NAME, prog_uid, prog_gid, the_prog, min_kv, max_kv,                \
-                        BPFLOADER_MIN_VER, BPFLOADER_MAX_VER, opt,                                 \
-                        DEFAULT_BPF_PROG_SELINUX_CONTEXT, DEFAULT_BPF_PROG_PIN_SUBDIR,             \
+                        BPFLOADER_MIN_VER, BPFLOADER_MAX_VER, opt, "", "",                         \
                         LOAD_ON_ENG, LOAD_ON_USER, LOAD_ON_USERDEBUG)
 
 // Programs (here used in the sense of functions/sections) marked optional are allowed to fail
diff --git a/staticlibs/native/bpf_headers/include/bpf/bpf_map_def.h b/staticlibs/native/bpf_headers/include/bpf/bpf_map_def.h
index 00ef91a..2d6736c 100644
--- a/staticlibs/native/bpf_headers/include/bpf/bpf_map_def.h
+++ b/staticlibs/native/bpf_headers/include/bpf/bpf_map_def.h
@@ -29,7 +29,7 @@
  *                                                                            *
  *                          ! ! ! W A R N I N G ! ! !                         *
  *                                                                            *
- * CHANGES TO THESE STRUCTURE DEFINITIONS OUTSIDE OF AOSP/MASTER *WILL* BREAK *
+ * CHANGES TO THESE STRUCTURE DEFINITIONS OUTSIDE OF AOSP/MAIN *WILL* BREAK   *
  * MAINLINE MODULE COMPATIBILITY                                              *
  *                                                                            *
  * AND THUS MAY RESULT IN YOUR DEVICE BRICKING AT SOME ARBITRARY POINT IN     *
@@ -42,12 +42,6 @@
  *                                                                            *
  ******************************************************************************/
 
-// These are the values used if these fields are missing
-#define DEFAULT_BPFLOADER_MIN_VER 0u        // v0.0 (this is inclusive ie. >= v0.0)
-#define DEFAULT_BPFLOADER_MAX_VER 0x10000u  // v1.0 (this is exclusive ie. < v1.0)
-#define DEFAULT_SIZEOF_BPF_MAP_DEF 32       // v0.0 struct: enum (uint sized) + 7 uint
-#define DEFAULT_SIZEOF_BPF_PROG_DEF 20      // v0.0 struct: 4 uint + bool + 3 byte alignment pad
-
 /*
  * The bpf_{map,prog}_def structures are compiled for different architectures.
  * Once by the BPF compiler for the BPF architecture, and once by a C++
diff --git a/staticlibs/tests/unit/src/com/android/net/module/util/NetworkStatsUtilsTest.kt b/staticlibs/tests/unit/src/com/android/net/module/util/NetworkStatsUtilsTest.kt
index 97d3c19..baadad0 100644
--- a/staticlibs/tests/unit/src/com/android/net/module/util/NetworkStatsUtilsTest.kt
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/NetworkStatsUtilsTest.kt
@@ -17,27 +17,15 @@
 package com.android.net.module.util
 
 import android.net.NetworkStats
-import android.net.NetworkStats.DEFAULT_NETWORK_NO
-import android.net.NetworkStats.DEFAULT_NETWORK_YES
-import android.net.NetworkStats.Entry
-import android.net.NetworkStats.METERED_NO
-import android.net.NetworkStats.ROAMING_NO
-import android.net.NetworkStats.ROAMING_YES
-import android.net.NetworkStats.SET_DEFAULT
-import android.net.NetworkStats.TAG_NONE
 import androidx.test.filters.SmallTest
 import androidx.test.runner.AndroidJUnit4
 import com.android.testutils.assertEntryEquals
-import com.android.testutils.assertNetworkStatsEquals
-import com.android.testutils.makePublicStatsFromAndroidNetStats
 import kotlin.test.assertEquals
 import kotlin.test.assertFailsWith
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mockito.doReturn
 import org.mockito.Mockito.mock
-import org.mockito.Mockito.`when`
-
 
 @RunWith(AndroidJUnit4::class)
 @SmallTest
@@ -105,37 +93,6 @@
         assertEntryEquals(expectedEntry, entry)
     }
 
-    @Test
-    fun testPublicStatsToAndroidNetStats() {
-        val uid1 = 10001
-        val uid2 = 10002
-        val testIface = "wlan0"
-        val testAndroidNetStats = NetworkStats(0L, 3)
-                .addEntry(Entry(testIface, uid1, SET_DEFAULT, TAG_NONE,
-                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, 20, 3, 57, 40, 3))
-                .addEntry(Entry(
-                        testIface, uid2, SET_DEFAULT, TAG_NONE,
-                        METERED_NO, ROAMING_YES, DEFAULT_NETWORK_NO, 2, 7, 2, 5, 7))
-                .addEntry(Entry(testIface, uid2, SET_DEFAULT, TAG_NONE,
-                        METERED_NO, ROAMING_YES, DEFAULT_NETWORK_NO, 4, 5, 3, 1, 8))
-        val publicStats: android.app.usage.NetworkStats =
-                makePublicStatsFromAndroidNetStats(testAndroidNetStats)
-        val androidNetStats: NetworkStats =
-                NetworkStatsUtils.fromPublicNetworkStats(publicStats)
-
-        // 1. The public `NetworkStats` class does not include interface information.
-        //    Interface details must be removed and items with duplicated
-        //    keys need to be merged before making any comparisons.
-        // 2. The public `NetworkStats` class lacks an operations field.
-        //    Thus, the information will not be preserved during the conversion.
-        val expectedStats = NetworkStats(0L, 2)
-                .addEntry(Entry(null, uid1, SET_DEFAULT, TAG_NONE,
-                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, 20, 3, 57, 40, 0))
-                .addEntry(Entry(null, uid2, SET_DEFAULT, TAG_NONE,
-                        METERED_NO, ROAMING_YES, DEFAULT_NETWORK_NO, 6, 12, 5, 6, 0))
-        assertNetworkStatsEquals(expectedStats, androidNetStats)
-    }
-
     private fun makeMockBucket(
         uid: Int,
         tag: Int,
diff --git a/staticlibs/testutils/devicetests/com/android/testutils/NetworkStatsUtils.kt b/staticlibs/testutils/devicetests/com/android/testutils/NetworkStatsUtils.kt
index e8c7297..f2b14f5 100644
--- a/staticlibs/testutils/devicetests/com/android/testutils/NetworkStatsUtils.kt
+++ b/staticlibs/testutils/devicetests/com/android/testutils/NetworkStatsUtils.kt
@@ -16,12 +16,7 @@
 
 package com.android.testutils
 
-import android.app.usage.NetworkStatsManager
-import android.content.Context
-import android.net.INetworkStatsService
-import android.net.INetworkStatsSession
 import android.net.NetworkStats
-import android.net.NetworkTemplate
 import android.text.TextUtils
 import com.android.modules.utils.build.SdkLevel
 import kotlin.test.assertTrue
@@ -117,32 +112,3 @@
 fun assertParcelingIsLossless(stats: NetworkStats) {
     assertParcelingIsLossless(stats) { a, b -> orderInsensitiveEquals(a, b) }
 }
-
-/**
- * Make a {@link android.app.usage.NetworkStats} instance from
- * a {@link android.net.NetworkStats} instance.
- */
-// It's not possible to directly create a mocked `NetworkStats` instance
-// because of limitations with `NetworkStats#getNextBucket`.
-// As a workaround for testing, create a mock by controlling the return values
-// from the mocked service that provides the `NetworkStats` data.
-// Notes:
-//   1. The order of records in the final `NetworkStats` object might change or
-//      some records might be merged if there are items with duplicate keys.
-//   2. The interface and operations fields will be empty since there is
-//      no such field in the {@link android.app.usage.NetworkStats}.
-fun makePublicStatsFromAndroidNetStats(androidNetStats: NetworkStats):
-        android.app.usage.NetworkStats {
-    val mockService = Mockito.mock(INetworkStatsService::class.java)
-    val manager = NetworkStatsManager(Mockito.mock(Context::class.java), mockService)
-    val mockStatsSession = Mockito.mock(INetworkStatsSession::class.java)
-
-    Mockito.doReturn(mockStatsSession).`when`(mockService)
-            .openSessionForUsageStats(anyInt(), any())
-    Mockito.doReturn(androidNetStats).`when`(mockStatsSession).getSummaryForAllUid(
-            any(NetworkTemplate::class.java), anyLong(), anyLong(), anyBoolean())
-    return manager.querySummary(
-            Mockito.mock(NetworkTemplate::class.java),
-            Long.MIN_VALUE, Long.MAX_VALUE
-    )
-}
diff --git a/tests/cts/net/src/android/net/cts/DnsTest.java b/tests/cts/net/src/android/net/cts/DnsTest.java
index fb63a19..b1e5680 100644
--- a/tests/cts/net/src/android/net/cts/DnsTest.java
+++ b/tests/cts/net/src/android/net/cts/DnsTest.java
@@ -16,6 +16,10 @@
 
 package android.net.cts;
 
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
 import android.content.pm.PackageManager;
 import android.net.ConnectivityManager;
 import android.net.ConnectivityManager.NetworkCallback;
@@ -23,10 +27,16 @@
 import android.net.Network;
 import android.net.NetworkInfo;
 import android.os.SystemClock;
-import android.test.AndroidTestCase;
 import android.util.Log;
 
 import androidx.test.filters.RequiresDevice;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.testutils.DevSdkIgnoreRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
 import java.net.Inet4Address;
 import java.net.Inet6Address;
@@ -36,7 +46,9 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
-public class DnsTest extends AndroidTestCase {
+@RunWith(DevSdkIgnoreRunner.class)
+@DevSdkIgnoreRunner.RestoreDefaultNetwork
+public class DnsTest {
 
     static {
         System.loadLibrary("nativedns_jni");
@@ -46,10 +58,13 @@
     private static final String TAG = "DnsTest";
     private static final String PROXY_NETWORK_TYPE = "PROXY";
 
+    private Context mContext;
     private ConnectivityManager mCm;
 
+    @Before
     public void setUp() {
-        mCm = getContext().getSystemService(ConnectivityManager.class);
+        mContext = InstrumentationRegistry.getInstrumentation().getContext();
+        mCm = mContext.getSystemService(ConnectivityManager.class);
     }
 
     /**
@@ -69,6 +84,7 @@
      * Perf - measure size of first and second tier caches and their effect
      * Assert requires network permission
      */
+    @Test
     @RequiresDevice // IPv6 support may be missing on presubmit virtual hardware
     public void testDnsWorks() throws Exception {
         ensureIpv6Connectivity();
@@ -91,7 +107,7 @@
 
         // Skip the rest of the test if the active network for watch is PROXY.
         // TODO: Check NetworkInfo type in addition to type name once ag/601257 is merged.
-        if (getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)
+        if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)
                 && activeNetworkInfoIsProxy()) {
             Log.i(TAG, "Skipping test because the active network type name is PROXY.");
             return;
diff --git a/tests/unit/java/com/android/server/connectivity/DnsManagerTest.java b/tests/unit/java/com/android/server/connectivity/DnsManagerTest.java
index ea3d2dd..b47b97d 100644
--- a/tests/unit/java/com/android/server/connectivity/DnsManagerTest.java
+++ b/tests/unit/java/com/android/server/connectivity/DnsManagerTest.java
@@ -54,6 +54,7 @@
 import android.net.ResolverOptionsParcel;
 import android.net.ResolverParamsParcel;
 import android.net.RouteInfo;
+import android.net.resolv.aidl.DohParamsParcel;
 import android.net.shared.PrivateDnsConfig;
 import android.os.Build;
 import android.provider.Settings;
@@ -327,8 +328,16 @@
     @Test
     public void testSendDnsConfiguration() throws Exception {
         reset(mMockDnsResolver);
-        mDnsManager.updatePrivateDns(new Network(TEST_NETID),
-                mDnsManager.getPrivateDnsConfig());
+        final PrivateDnsConfig cfg = new PrivateDnsConfig(
+                PRIVATE_DNS_MODE_OPPORTUNISTIC /* mode */,
+                null /* hostname */,
+                null /* ips */,
+                "doh.com" /* dohName */,
+                null /* dohIps */,
+                "/some-path{?dns}" /* dohPath */,
+                5353 /* dohPort */);
+
+        mDnsManager.updatePrivateDns(new Network(TEST_NETID), cfg);
         final LinkProperties lp = new LinkProperties();
         lp.setInterfaceName(TEST_IFACENAME);
         lp.addDnsServer(InetAddress.getByName("3.3.3.3"));
@@ -352,7 +361,11 @@
         expectedParams.transportTypes = TEST_TRANSPORT_TYPES;
         expectedParams.resolverOptions = null;
         expectedParams.meteredNetwork = true;
-        expectedParams.dohParams = null;
+        expectedParams.dohParams = new DohParamsParcel.Builder()
+                .setName("doh.com")
+                .setDohpath("/some-path{?dns}")
+                .setPort(5353)
+                .build();
         expectedParams.interfaceNames = new String[]{TEST_IFACENAME};
         verify(mMockDnsResolver, times(1)).setResolverConfiguration(eq(expectedParams));
     }