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));
}