Merge "Move fail in testFirewallBlocking"
diff --git a/Cronet/tests/mts/Android.bp b/Cronet/tests/mts/Android.bp
index 1502b0b..d13f695 100644
--- a/Cronet/tests/mts/Android.bp
+++ b/Cronet/tests/mts/Android.bp
@@ -19,6 +19,7 @@
java_genrule {
name: "net-http-test-jarjar-rules",
+ defaults: ["CronetTestJavaDefaults"],
tool_files: [
":NetHttpTestsLibPreJarJar{.jar}",
"jarjar_excludes.txt",
@@ -36,6 +37,7 @@
android_library {
name: "NetHttpTestsLibPreJarJar",
+ defaults: ["CronetTestJavaDefaults"],
srcs: [":cronet_aml_javatests_sources"],
sdk_version: "module_current",
min_sdk_version: "30",
diff --git a/Tethering/src/com/android/networkstack/tethering/OffloadHalHidlImpl.java b/Tethering/src/com/android/networkstack/tethering/OffloadHalHidlImpl.java
index 3e02543..e0a9878 100644
--- a/Tethering/src/com/android/networkstack/tethering/OffloadHalHidlImpl.java
+++ b/Tethering/src/com/android/networkstack/tethering/OffloadHalHidlImpl.java
@@ -288,9 +288,12 @@
IOffloadConfig config = null;
try {
config = IOffloadConfig.getService(true /*retry*/);
- } catch (RemoteException | NoSuchElementException e) {
+ } catch (RemoteException e) {
log.e("getIOffloadConfig error " + e);
return null;
+ } catch (NoSuchElementException e) {
+ log.i("getIOffloadConfig Tether Offload HAL not present/implemented");
+ return null;
}
IOffloadControl control = null;
diff --git a/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java b/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java
index ea20063..de15c5b 100644
--- a/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java
+++ b/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java
@@ -307,7 +307,7 @@
if (mIOffload == null) {
mIOffload = mDeps.getOffload();
if (mIOffload == null) {
- mLog.e("No tethering offload HAL service found.");
+ mLog.i("No tethering offload HAL service found.");
return OFFLOAD_HAL_VERSION_NONE;
}
mLog.i("Tethering offload version "
diff --git a/Tethering/src/com/android/networkstack/tethering/metrics/TetheringMetrics.java b/Tethering/src/com/android/networkstack/tethering/metrics/TetheringMetrics.java
index c181994..de25ff5 100644
--- a/Tethering/src/com/android/networkstack/tethering/metrics/TetheringMetrics.java
+++ b/Tethering/src/com/android/networkstack/tethering/metrics/TetheringMetrics.java
@@ -20,7 +20,6 @@
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
import static android.net.NetworkCapabilities.TRANSPORT_LOWPAN;
-import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE;
import static android.net.TetheringManager.TETHERING_BLUETOOTH;
@@ -160,23 +159,13 @@
}
/**
- * Returns the greater of two start times.
- * @param first the first start time
- * @param second the second start time
- * @return the greater start time
- */
- private long getGreaterStartTime(long first, long second) {
- return first > second ? first : second;
- }
-
- /**
* Updates the upstream events builder with a new upstream event.
* @param upstreamEventsBuilder the builder for the upstream events list
* @param start the start time of the upstream event
* @param stop the stop time of the upstream event
* @param upstream the type of upstream type (e.g. Wifi, Cellular, Bluetooth, ...)
*/
- private void updateUpstreamEvents(final UpstreamEvents.Builder upstreamEventsBuilder,
+ private void addUpstreamEvent(final UpstreamEvents.Builder upstreamEventsBuilder,
final long start, final long stop, @Nullable final UpstreamType upstream) {
final UpstreamEvent.Builder upstreamEventBuilder = UpstreamEvent.newBuilder()
.setUpstreamType(upstream == null ? UpstreamType.UT_NO_NETWORK : upstream)
@@ -201,21 +190,21 @@
* @param downstreamStartTime the start time of the downstream event to find relevant upstream
* events for
*/
- private void updateStatsBuilderToWrite(final NetworkTetheringReported.Builder statsBuilder,
+ private void noteDownstreamStopped(final NetworkTetheringReported.Builder statsBuilder,
final long downstreamStartTime) {
UpstreamEvents.Builder upstreamEventsBuilder = UpstreamEvents.newBuilder();
for (RecordUpstreamEvent event : mUpstreamEventList) {
if (downstreamStartTime > event.mStopTime) continue;
- final long startTime = getGreaterStartTime(downstreamStartTime, event.mStartTime);
+ final long startTime = Math.max(downstreamStartTime, event.mStartTime);
// Handle completed upstream events.
- updateUpstreamEvents(upstreamEventsBuilder, startTime, event.mStopTime,
+ addUpstreamEvent(upstreamEventsBuilder, startTime, event.mStopTime,
event.mUpstreamType);
}
- final long startTime = getGreaterStartTime(downstreamStartTime, mCurrentUpStreamStartTime);
+ final long startTime = Math.max(downstreamStartTime, mCurrentUpStreamStartTime);
final long stopTime = timeNow();
// Handle the last upstream event.
- updateUpstreamEvents(upstreamEventsBuilder, startTime, stopTime, mCurrentUpstream);
+ addUpstreamEvent(upstreamEventsBuilder, startTime, stopTime, mCurrentUpstream);
statsBuilder.setUpstreamEvents(upstreamEventsBuilder);
statsBuilder.setDurationMillis(stopTime - downstreamStartTime);
}
@@ -237,7 +226,7 @@
return;
}
- updateStatsBuilderToWrite(statsBuilder, mDownstreamStartTime.get(downstreamType));
+ noteDownstreamStopped(statsBuilder, mDownstreamStartTime.get(downstreamType));
write(statsBuilder.build());
mBuilderMap.remove(downstreamType);
@@ -365,34 +354,19 @@
if (nc == null) return UpstreamType.UT_NO_NETWORK;
final int typeCount = nc.getTransportTypes().length;
+ // It's possible for a VCN network to be mapped to UT_UNKNOWN, as it may consist of both
+ // Wi-Fi and cellular transport.
+ // TODO: It's necessary to define a new upstream type for VCN, which can be identified by
+ // NET_CAPABILITY_NOT_VCN_MANAGED.
+ if (typeCount > 1) return UpstreamType.UT_UNKNOWN;
- boolean hasCellular = nc.hasTransport(TRANSPORT_CELLULAR);
- boolean hasWifi = nc.hasTransport(TRANSPORT_WIFI);
- boolean hasBT = nc.hasTransport(TRANSPORT_BLUETOOTH);
- boolean hasEthernet = nc.hasTransport(TRANSPORT_ETHERNET);
- boolean hasVpn = nc.hasTransport(TRANSPORT_VPN);
- boolean hasWifiAware = nc.hasTransport(TRANSPORT_WIFI_AWARE);
- boolean hasLopan = nc.hasTransport(TRANSPORT_LOWPAN);
+ if (nc.hasTransport(TRANSPORT_CELLULAR)) return UpstreamType.UT_CELLULAR;
+ if (nc.hasTransport(TRANSPORT_WIFI)) return UpstreamType.UT_WIFI;
+ if (nc.hasTransport(TRANSPORT_BLUETOOTH)) return UpstreamType.UT_BLUETOOTH;
+ if (nc.hasTransport(TRANSPORT_ETHERNET)) return UpstreamType.UT_ETHERNET;
+ if (nc.hasTransport(TRANSPORT_WIFI_AWARE)) return UpstreamType.UT_WIFI_AWARE;
+ if (nc.hasTransport(TRANSPORT_LOWPAN)) return UpstreamType.UT_LOWPAN;
- if (typeCount == 3 && hasCellular && hasWifi && hasVpn) {
- return UpstreamType.UT_WIFI_CELLULAR_VPN;
- }
-
- if (typeCount == 2 && hasVpn) {
- if (hasCellular) return UpstreamType.UT_CELLULAR_VPN;
- if (hasWifi) return UpstreamType.UT_WIFI_VPN;
- if (hasBT) return UpstreamType.UT_BLUETOOTH_VPN;
- if (hasEthernet) return UpstreamType.UT_ETHERNET_VPN;
- }
-
- if (typeCount == 1) {
- if (hasCellular) return UpstreamType.UT_CELLULAR;
- if (hasWifi) return UpstreamType.UT_WIFI;
- if (hasBT) return UpstreamType.UT_BLUETOOTH;
- if (hasEthernet) return UpstreamType.UT_ETHERNET;
- if (hasWifiAware) return UpstreamType.UT_WIFI_AWARE;
- if (hasLopan) return UpstreamType.UT_LOWPAN;
- }
return UpstreamType.UT_UNKNOWN;
}
}
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/metrics/TetheringMetricsTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/metrics/TetheringMetricsTest.java
index aa2d16c..77950ac 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/metrics/TetheringMetricsTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/metrics/TetheringMetricsTest.java
@@ -20,7 +20,6 @@
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
import static android.net.NetworkCapabilities.TRANSPORT_LOWPAN;
-import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE;
import static android.net.TetheringManager.TETHERING_BLUETOOTH;
@@ -278,17 +277,7 @@
runUpstreamTypesTest(buildUpstreamState(TRANSPORT_ETHERNET), UpstreamType.UT_ETHERNET);
runUpstreamTypesTest(buildUpstreamState(TRANSPORT_WIFI_AWARE), UpstreamType.UT_WIFI_AWARE);
runUpstreamTypesTest(buildUpstreamState(TRANSPORT_LOWPAN), UpstreamType.UT_LOWPAN);
- runUpstreamTypesTest(buildUpstreamState(TRANSPORT_CELLULAR, TRANSPORT_VPN),
- UpstreamType.UT_CELLULAR_VPN);
- runUpstreamTypesTest(buildUpstreamState(TRANSPORT_WIFI, TRANSPORT_VPN),
- UpstreamType.UT_WIFI_VPN);
- runUpstreamTypesTest(buildUpstreamState(TRANSPORT_BLUETOOTH, TRANSPORT_VPN),
- UpstreamType.UT_BLUETOOTH_VPN);
- runUpstreamTypesTest(buildUpstreamState(TRANSPORT_ETHERNET, TRANSPORT_VPN),
- UpstreamType.UT_ETHERNET_VPN);
- runUpstreamTypesTest(buildUpstreamState(TRANSPORT_CELLULAR, TRANSPORT_WIFI, TRANSPORT_VPN),
- UpstreamType.UT_WIFI_CELLULAR_VPN);
- runUpstreamTypesTest(buildUpstreamState(TRANSPORT_CELLULAR, TRANSPORT_WIFI, TRANSPORT_VPN,
+ runUpstreamTypesTest(buildUpstreamState(TRANSPORT_CELLULAR, TRANSPORT_WIFI,
TRANSPORT_BLUETOOTH), UpstreamType.UT_UNKNOWN);
}
diff --git a/bpf_progs/netd.c b/bpf_progs/netd.c
index e068d8a..ce3315b 100644
--- a/bpf_progs/netd.c
+++ b/bpf_progs/netd.c
@@ -300,7 +300,8 @@
bpf_packet_trace_ringbuf_submit(pkt);
}
-static __always_inline inline bool skip_owner_match(struct __sk_buff* skb, const unsigned kver) {
+static __always_inline inline bool skip_owner_match(struct __sk_buff* skb, bool egress,
+ const unsigned kver) {
uint32_t flag = 0;
if (skb->protocol == htons(ETH_P_IP)) {
uint8_t proto;
@@ -330,7 +331,8 @@
} else {
return false;
}
- return flag & TCP_FLAG_RST; // false on read failure
+ // Always allow RST's, and additionally allow ingress FINs
+ return flag & (TCP_FLAG_RST | (egress ? 0 : TCP_FLAG_FIN)); // false on read failure
}
static __always_inline inline BpfConfig getConfig(uint32_t configKey) {
@@ -352,7 +354,7 @@
bool egress, const unsigned kver) {
if (is_system_uid(uid)) return PASS;
- if (skip_owner_match(skb, kver)) return PASS;
+ if (skip_owner_match(skb, egress, kver)) return PASS;
BpfConfig enabledRules = getConfig(UID_RULES_CONFIGURATION_KEY);
@@ -446,11 +448,6 @@
return match;
}
- if (key.tag) {
- update_stats_with_config(skb, egress, &key, *selectedMap);
- key.tag = 0;
- }
-
do_packet_tracing(skb, egress, uid, tag, enable_tracing, kver);
update_stats_with_config(skb, egress, &key, *selectedMap);
update_app_uid_stats_map(skb, egress, &uid);
diff --git a/service-t/native/libs/libnetworkstats/BpfNetworkStats.cpp b/service-t/native/libs/libnetworkstats/BpfNetworkStats.cpp
index cdcb0f8..1bc8ca5 100644
--- a/service-t/native/libs/libnetworkstats/BpfNetworkStats.cpp
+++ b/service-t/native/libs/libnetworkstats/BpfNetworkStats.cpp
@@ -109,12 +109,12 @@
return newLine;
}
-int parseBpfNetworkStatsDetailInternal(std::vector<stats_line>* lines,
+int parseBpfNetworkStatsDetailInternal(std::vector<stats_line>& lines,
const BpfMap<StatsKey, StatsValue>& statsMap,
const BpfMap<uint32_t, IfaceValue>& ifaceMap) {
int64_t unknownIfaceBytesTotal = 0;
const auto processDetailUidStats =
- [lines, &unknownIfaceBytesTotal, &ifaceMap](
+ [&lines, &unknownIfaceBytesTotal, &ifaceMap](
const StatsKey& key,
const BpfMap<StatsKey, StatsValue>& statsMap) -> Result<void> {
char ifname[IFNAMSIZ];
@@ -126,7 +126,13 @@
if (!statsEntry.ok()) {
return base::ResultError(statsEntry.error().message(), statsEntry.error().code());
}
- lines->push_back(populateStatsEntry(key, statsEntry.value(), ifname));
+ stats_line newLine = populateStatsEntry(key, statsEntry.value(), ifname);
+ lines.push_back(newLine);
+ if (newLine.tag) {
+ // account tagged traffic in the untagged stats (for historical reasons?)
+ newLine.tag = 0;
+ lines.push_back(newLine);
+ }
return Result<void>();
};
Result<void> res = statsMap.iterate(processDetailUidStats);
@@ -180,7 +186,7 @@
// TODO: the above comment feels like it may be obsolete / out of date,
// since we no longer swap the map via netd binder rpc - though we do
// still swap it.
- int ret = parseBpfNetworkStatsDetailInternal(lines, *inactiveStatsMap, ifaceIndexNameMap);
+ int ret = parseBpfNetworkStatsDetailInternal(*lines, *inactiveStatsMap, ifaceIndexNameMap);
if (ret) {
ALOGE("parse detail network stats failed: %s", strerror(errno));
return ret;
@@ -195,11 +201,11 @@
return 0;
}
-int parseBpfNetworkStatsDevInternal(std::vector<stats_line>* lines,
+int parseBpfNetworkStatsDevInternal(std::vector<stats_line>& lines,
const BpfMap<uint32_t, StatsValue>& statsMap,
const BpfMap<uint32_t, IfaceValue>& ifaceMap) {
int64_t unknownIfaceBytesTotal = 0;
- const auto processDetailIfaceStats = [lines, &unknownIfaceBytesTotal, &ifaceMap, &statsMap](
+ const auto processDetailIfaceStats = [&lines, &unknownIfaceBytesTotal, &ifaceMap, &statsMap](
const uint32_t& key, const StatsValue& value,
const BpfMap<uint32_t, StatsValue>&) {
char ifname[IFNAMSIZ];
@@ -211,7 +217,7 @@
.tag = (uint32_t)TAG_NONE,
.counterSet = (uint32_t)SET_ALL,
};
- lines->push_back(populateStatsEntry(fakeKey, value, ifname));
+ lines.push_back(populateStatsEntry(fakeKey, value, ifname));
return Result<void>();
};
Result<void> res = statsMap.iterateWithValue(processDetailIfaceStats);
@@ -228,29 +234,28 @@
int parseBpfNetworkStatsDev(std::vector<stats_line>* lines) {
static BpfMapRO<uint32_t, IfaceValue> ifaceIndexNameMap(IFACE_INDEX_NAME_MAP_PATH);
static BpfMapRO<uint32_t, StatsValue> ifaceStatsMap(IFACE_STATS_MAP_PATH);
- return parseBpfNetworkStatsDevInternal(lines, ifaceStatsMap, ifaceIndexNameMap);
+ return parseBpfNetworkStatsDevInternal(*lines, ifaceStatsMap, ifaceIndexNameMap);
}
-void groupNetworkStats(std::vector<stats_line>* lines) {
- if (lines->size() <= 1) return;
- std::sort(lines->begin(), lines->end());
+void groupNetworkStats(std::vector<stats_line>& lines) {
+ if (lines.size() <= 1) return;
+ std::sort(lines.begin(), lines.end());
// Similar to std::unique(), but aggregates the duplicates rather than discarding them.
- size_t nextOutput = 0;
- for (size_t i = 1; i < lines->size(); i++) {
- if (lines->at(nextOutput) == lines->at(i)) {
- lines->at(nextOutput) += lines->at(i);
+ size_t currentOutput = 0;
+ for (size_t i = 1; i < lines.size(); i++) {
+ // note that == operator only compares the 'key' portion: iface/uid/tag/set
+ if (lines[currentOutput] == lines[i]) {
+ // while += operator only affects the 'data' portion: {rx,tx}{Bytes,Packets}
+ lines[currentOutput] += lines[i];
} else {
- nextOutput++;
- if (nextOutput != i) {
- lines->at(nextOutput) = lines->at(i);
- }
+ // okay, we're done aggregating the current line, move to the next one
+ lines[++currentOutput] = lines[i];
}
}
- if (lines->size() != nextOutput + 1) {
- lines->resize(nextOutput + 1);
- }
+ // possibly shrink the vector - currentOutput is the last line with valid data
+ lines.resize(currentOutput + 1);
}
// True if lhs equals to rhs, only compare iface, uid, tag and set.
diff --git a/service-t/native/libs/libnetworkstats/BpfNetworkStatsTest.cpp b/service-t/native/libs/libnetworkstats/BpfNetworkStatsTest.cpp
index bf42b62..ccd3f5e 100644
--- a/service-t/native/libs/libnetworkstats/BpfNetworkStatsTest.cpp
+++ b/service-t/native/libs/libnetworkstats/BpfNetworkStatsTest.cpp
@@ -228,7 +228,7 @@
populateFakeStats(TEST_UID1, 0, IFACE_INDEX1, TEST_COUNTERSET0, value1, mFakeStatsMap);
populateFakeStats(TEST_UID1, 0, IFACE_INDEX2, TEST_COUNTERSET1, value1, mFakeStatsMap);
populateFakeStats(TEST_UID2, 0, IFACE_INDEX3, TEST_COUNTERSET1, value1, mFakeStatsMap);
- ASSERT_EQ(0, parseBpfNetworkStatsDetailInternal(&lines, mFakeStatsMap, mFakeIfaceIndexNameMap));
+ ASSERT_EQ(0, parseBpfNetworkStatsDetailInternal(lines, mFakeStatsMap, mFakeIfaceIndexNameMap));
ASSERT_EQ((unsigned long)3, lines.size());
}
@@ -290,8 +290,8 @@
mFakeStatsMap);
populateFakeStats(TEST_UID2, TEST_TAG, IFACE_INDEX1, TEST_COUNTERSET0, value1, mFakeStatsMap);
std::vector<stats_line> lines;
- ASSERT_EQ(0, parseBpfNetworkStatsDetailInternal(&lines, mFakeStatsMap, mFakeIfaceIndexNameMap));
- ASSERT_EQ((unsigned long)4, lines.size());
+ ASSERT_EQ(0, parseBpfNetworkStatsDetailInternal(lines, mFakeStatsMap, mFakeIfaceIndexNameMap));
+ ASSERT_EQ((unsigned long)7, lines.size());
}
TEST_F(BpfNetworkStatsHelperTest, TestGetStatsWithSkippedIface) {
@@ -310,7 +310,7 @@
populateFakeStats(TEST_UID1, 0, IFACE_INDEX1, TEST_COUNTERSET1, value1, mFakeStatsMap);
populateFakeStats(TEST_UID2, 0, IFACE_INDEX1, TEST_COUNTERSET0, value1, mFakeStatsMap);
std::vector<stats_line> lines;
- ASSERT_EQ(0, parseBpfNetworkStatsDetailInternal(&lines, mFakeStatsMap, mFakeIfaceIndexNameMap));
+ ASSERT_EQ(0, parseBpfNetworkStatsDetailInternal(lines, mFakeStatsMap, mFakeIfaceIndexNameMap));
ASSERT_EQ((unsigned long)4, lines.size());
}
@@ -349,7 +349,7 @@
ASSERT_EQ(-1, unknownIfaceBytesTotal);
std::vector<stats_line> lines;
// TODO: find a way to test the total of unknown Iface Bytes go above limit.
- ASSERT_EQ(0, parseBpfNetworkStatsDetailInternal(&lines, mFakeStatsMap, mFakeIfaceIndexNameMap));
+ ASSERT_EQ(0, parseBpfNetworkStatsDetailInternal(lines, mFakeStatsMap, mFakeIfaceIndexNameMap));
ASSERT_EQ((unsigned long)1, lines.size());
expectStatsLineEqual(value1, IFACE_NAME1, TEST_UID1, TEST_COUNTERSET0, 0, lines.front());
}
@@ -381,7 +381,7 @@
EXPECT_RESULT_OK(mFakeIfaceStatsMap.writeValue(ifaceStatsKey, value2, BPF_ANY));
std::vector<stats_line> lines;
ASSERT_EQ(0,
- parseBpfNetworkStatsDevInternal(&lines, mFakeIfaceStatsMap, mFakeIfaceIndexNameMap));
+ parseBpfNetworkStatsDevInternal(lines, mFakeIfaceStatsMap, mFakeIfaceIndexNameMap));
ASSERT_EQ((unsigned long)4, lines.size());
expectStatsLineEqual(value1, IFACE_NAME1, UID_ALL, SET_ALL, TAG_NONE, lines[0]);
@@ -409,25 +409,32 @@
.txPackets = TEST_PACKET0,
.txBytes = TEST_BYTES0,
};
- StatsValue value3 = {
+ StatsValue value3 = { // value1 *2
.rxPackets = TEST_PACKET0 * 2,
.rxBytes = TEST_BYTES0 * 2,
.txPackets = TEST_PACKET1 * 2,
.txBytes = TEST_BYTES1 * 2,
};
+ StatsValue value5 = { // value2 + value3
+ .rxPackets = TEST_PACKET1 + TEST_PACKET0 * 2,
+ .rxBytes = TEST_BYTES1 + TEST_BYTES0 * 2,
+ .txPackets = TEST_PACKET0 + TEST_PACKET1 * 2,
+ .txBytes = TEST_BYTES0 + TEST_BYTES1 * 2,
+ };
std::vector<stats_line> lines;
// Test empty stats.
- ASSERT_EQ(0, parseBpfNetworkStatsDetailInternal(&lines, mFakeStatsMap, mFakeIfaceIndexNameMap));
+ ASSERT_EQ(0, parseBpfNetworkStatsDetailInternal(lines, mFakeStatsMap, mFakeIfaceIndexNameMap));
ASSERT_EQ((size_t) 0, lines.size());
lines.clear();
// Test 1 line stats.
populateFakeStats(TEST_UID1, TEST_TAG, IFACE_INDEX1, TEST_COUNTERSET0, value1, mFakeStatsMap);
- ASSERT_EQ(0, parseBpfNetworkStatsDetailInternal(&lines, mFakeStatsMap, mFakeIfaceIndexNameMap));
- ASSERT_EQ((size_t) 1, lines.size());
- expectStatsLineEqual(value1, IFACE_NAME1, TEST_UID1, TEST_COUNTERSET0, TEST_TAG, lines[0]);
+ ASSERT_EQ(0, parseBpfNetworkStatsDetailInternal(lines, mFakeStatsMap, mFakeIfaceIndexNameMap));
+ ASSERT_EQ((size_t) 2, lines.size()); // TEST_TAG != 0 -> 1 entry becomes 2 lines
+ expectStatsLineEqual(value1, IFACE_NAME1, TEST_UID1, TEST_COUNTERSET0, 0, lines[0]);
+ expectStatsLineEqual(value1, IFACE_NAME1, TEST_UID1, TEST_COUNTERSET0, TEST_TAG, lines[1]);
lines.clear();
// These items should not be grouped.
@@ -436,23 +443,27 @@
populateFakeStats(TEST_UID1, TEST_TAG + 1, IFACE_INDEX1, TEST_COUNTERSET0, value2,
mFakeStatsMap);
populateFakeStats(TEST_UID2, TEST_TAG, IFACE_INDEX1, TEST_COUNTERSET0, value1, mFakeStatsMap);
- ASSERT_EQ(0, parseBpfNetworkStatsDetailInternal(&lines, mFakeStatsMap, mFakeIfaceIndexNameMap));
- ASSERT_EQ((size_t) 5, lines.size());
+ ASSERT_EQ(0, parseBpfNetworkStatsDetailInternal(lines, mFakeStatsMap, mFakeIfaceIndexNameMap));
+ ASSERT_EQ((size_t) 9, lines.size());
lines.clear();
// These items should be grouped.
populateFakeStats(TEST_UID1, TEST_TAG, IFACE_INDEX3, TEST_COUNTERSET0, value1, mFakeStatsMap);
populateFakeStats(TEST_UID2, TEST_TAG, IFACE_INDEX3, TEST_COUNTERSET0, value1, mFakeStatsMap);
- ASSERT_EQ(0, parseBpfNetworkStatsDetailInternal(&lines, mFakeStatsMap, mFakeIfaceIndexNameMap));
- ASSERT_EQ((size_t) 5, lines.size());
+ ASSERT_EQ(0, parseBpfNetworkStatsDetailInternal(lines, mFakeStatsMap, mFakeIfaceIndexNameMap));
+ ASSERT_EQ((size_t) 9, lines.size());
// Verify Sorted & Grouped.
- expectStatsLineEqual(value3, IFACE_NAME1, TEST_UID1, TEST_COUNTERSET0, TEST_TAG, lines[0]);
- expectStatsLineEqual(value2, IFACE_NAME1, TEST_UID1, TEST_COUNTERSET1, TEST_TAG, lines[1]);
- expectStatsLineEqual(value2, IFACE_NAME1, TEST_UID1, TEST_COUNTERSET0, TEST_TAG + 1, lines[2]);
- expectStatsLineEqual(value3, IFACE_NAME1, TEST_UID2, TEST_COUNTERSET0, TEST_TAG, lines[3]);
- expectStatsLineEqual(value2, IFACE_NAME2, TEST_UID1, TEST_COUNTERSET0, TEST_TAG, lines[4]);
+ expectStatsLineEqual(value5, IFACE_NAME1, TEST_UID1, TEST_COUNTERSET0, 0, lines[0]);
+ expectStatsLineEqual(value2, IFACE_NAME1, TEST_UID1, TEST_COUNTERSET1, 0, lines[1]);
+ expectStatsLineEqual(value3, IFACE_NAME1, TEST_UID1, TEST_COUNTERSET0, TEST_TAG, lines[2]);
+ expectStatsLineEqual(value2, IFACE_NAME1, TEST_UID1, TEST_COUNTERSET1, TEST_TAG, lines[3]);
+ expectStatsLineEqual(value2, IFACE_NAME1, TEST_UID1, TEST_COUNTERSET0, TEST_TAG + 1, lines[4]);
+ expectStatsLineEqual(value3, IFACE_NAME1, TEST_UID2, TEST_COUNTERSET0, 0, lines[5]);
+ expectStatsLineEqual(value3, IFACE_NAME1, TEST_UID2, TEST_COUNTERSET0, TEST_TAG, lines[6]);
+ expectStatsLineEqual(value2, IFACE_NAME2, TEST_UID1, TEST_COUNTERSET0, 0, lines[7]);
+ expectStatsLineEqual(value2, IFACE_NAME2, TEST_UID1, TEST_COUNTERSET0, TEST_TAG, lines[8]);
lines.clear();
// Perform test on IfaceStats.
@@ -466,7 +477,7 @@
EXPECT_RESULT_OK(mFakeIfaceStatsMap.writeValue(ifaceStatsKey, value1, BPF_ANY));
ASSERT_EQ(0,
- parseBpfNetworkStatsDevInternal(&lines, mFakeIfaceStatsMap, mFakeIfaceIndexNameMap));
+ parseBpfNetworkStatsDevInternal(lines, mFakeIfaceStatsMap, mFakeIfaceIndexNameMap));
ASSERT_EQ((size_t) 2, lines.size());
expectStatsLineEqual(value3, IFACE_NAME1, UID_ALL, SET_ALL, TAG_NONE, lines[0]);
@@ -485,39 +496,48 @@
.txPackets = TEST_PACKET1,
.txBytes = TEST_BYTES1,
};
+ StatsValue value4 = { // value1 * 4
+ .rxPackets = TEST_PACKET0 * 4,
+ .rxBytes = TEST_BYTES0 * 4,
+ .txPackets = TEST_PACKET1 * 4,
+ .txBytes = TEST_BYTES1 * 4,
+ };
// Mutate uid, 0 < TEST_UID1 < INT_MAX < INT_MIN < UINT_MAX.
- populateFakeStats(0, TEST_TAG, IFACE_INDEX1, TEST_COUNTERSET0, value1, mFakeStatsMap);
- populateFakeStats(UINT_MAX, TEST_TAG, IFACE_INDEX1, TEST_COUNTERSET0, value1, mFakeStatsMap);
- populateFakeStats(INT_MIN, TEST_TAG, IFACE_INDEX1, TEST_COUNTERSET0, value1, mFakeStatsMap);
- populateFakeStats(INT_MAX, TEST_TAG, IFACE_INDEX1, TEST_COUNTERSET0, value1, mFakeStatsMap);
+ populateFakeStats(0, TEST_TAG, IFACE_INDEX1, TEST_COUNTERSET0, value1, mFakeStatsMap);
+ populateFakeStats(UINT_MAX, TEST_TAG, IFACE_INDEX1, TEST_COUNTERSET0, value1, mFakeStatsMap);
+ populateFakeStats(INT_MIN, TEST_TAG, IFACE_INDEX1, TEST_COUNTERSET0, value1, mFakeStatsMap);
+ populateFakeStats(INT_MAX, TEST_TAG, IFACE_INDEX1, TEST_COUNTERSET0, value1, mFakeStatsMap);
// Mutate tag, 0 < TEST_TAG < INT_MAX < INT_MIN < UINT_MAX.
- populateFakeStats(TEST_UID1, INT_MAX, IFACE_INDEX1, TEST_COUNTERSET0, value1, mFakeStatsMap);
- populateFakeStats(TEST_UID1, INT_MIN, IFACE_INDEX1, TEST_COUNTERSET0, value1, mFakeStatsMap);
- populateFakeStats(TEST_UID1, 0, IFACE_INDEX1, TEST_COUNTERSET0, value1, mFakeStatsMap);
+ populateFakeStats(TEST_UID1, INT_MAX, IFACE_INDEX1, TEST_COUNTERSET0, value1, mFakeStatsMap);
+ populateFakeStats(TEST_UID1, INT_MIN, IFACE_INDEX1, TEST_COUNTERSET0, value1, mFakeStatsMap);
+ populateFakeStats(TEST_UID1, 0, IFACE_INDEX1, TEST_COUNTERSET0, value1, mFakeStatsMap);
populateFakeStats(TEST_UID1, UINT_MAX, IFACE_INDEX1, TEST_COUNTERSET0, value1, mFakeStatsMap);
// TODO: Mutate counterSet and enlarge TEST_MAP_SIZE if overflow on counterSet is possible.
std::vector<stats_line> lines;
- ASSERT_EQ(0, parseBpfNetworkStatsDetailInternal(&lines, mFakeStatsMap, mFakeIfaceIndexNameMap));
- ASSERT_EQ((size_t) 8, lines.size());
+ ASSERT_EQ(0, parseBpfNetworkStatsDetailInternal(lines, mFakeStatsMap, mFakeIfaceIndexNameMap));
+ ASSERT_EQ((size_t) 12, lines.size());
// Uid 0 first
- expectStatsLineEqual(value1, IFACE_NAME1, 0, TEST_COUNTERSET0, TEST_TAG, lines[0]);
+ expectStatsLineEqual(value1, IFACE_NAME1, 0, TEST_COUNTERSET0, 0, lines[0]);
+ expectStatsLineEqual(value1, IFACE_NAME1, 0, TEST_COUNTERSET0, TEST_TAG, lines[1]);
// Test uid, mutate tag.
- expectStatsLineEqual(value1, IFACE_NAME1, TEST_UID1, TEST_COUNTERSET0, 0, lines[1]);
- expectStatsLineEqual(value1, IFACE_NAME1, TEST_UID1, TEST_COUNTERSET0, INT_MAX, lines[2]);
- expectStatsLineEqual(value1, IFACE_NAME1, TEST_UID1, TEST_COUNTERSET0, INT_MIN, lines[3]);
- expectStatsLineEqual(value1, IFACE_NAME1, TEST_UID1, TEST_COUNTERSET0, UINT_MAX, lines[4]);
+ expectStatsLineEqual(value4, IFACE_NAME1, TEST_UID1, TEST_COUNTERSET0, 0, lines[2]);
+ expectStatsLineEqual(value1, IFACE_NAME1, TEST_UID1, TEST_COUNTERSET0, INT_MAX, lines[3]);
+ expectStatsLineEqual(value1, IFACE_NAME1, TEST_UID1, TEST_COUNTERSET0, INT_MIN, lines[4]);
+ expectStatsLineEqual(value1, IFACE_NAME1, TEST_UID1, TEST_COUNTERSET0, UINT_MAX, lines[5]);
// Mutate uid.
- expectStatsLineEqual(value1, IFACE_NAME1, INT_MAX, TEST_COUNTERSET0, TEST_TAG, lines[5]);
- expectStatsLineEqual(value1, IFACE_NAME1, INT_MIN, TEST_COUNTERSET0, TEST_TAG, lines[6]);
- expectStatsLineEqual(value1, IFACE_NAME1, UINT_MAX, TEST_COUNTERSET0, TEST_TAG, lines[7]);
- lines.clear();
+ expectStatsLineEqual(value1, IFACE_NAME1, INT_MAX, TEST_COUNTERSET0, 0, lines[6]);
+ expectStatsLineEqual(value1, IFACE_NAME1, INT_MAX, TEST_COUNTERSET0, TEST_TAG, lines[7]);
+ expectStatsLineEqual(value1, IFACE_NAME1, INT_MIN, TEST_COUNTERSET0, 0, lines[8]);
+ expectStatsLineEqual(value1, IFACE_NAME1, INT_MIN, TEST_COUNTERSET0, TEST_TAG, lines[9]);
+ expectStatsLineEqual(value1, IFACE_NAME1, UINT_MAX, TEST_COUNTERSET0, 0, lines[10]);
+ expectStatsLineEqual(value1, IFACE_NAME1, UINT_MAX, TEST_COUNTERSET0, TEST_TAG, lines[11]);
}
} // namespace bpf
} // namespace android
diff --git a/service-t/native/libs/libnetworkstats/include/netdbpf/BpfNetworkStats.h b/service-t/native/libs/libnetworkstats/include/netdbpf/BpfNetworkStats.h
index 1ffa927..133009f 100644
--- a/service-t/native/libs/libnetworkstats/include/netdbpf/BpfNetworkStats.h
+++ b/service-t/native/libs/libnetworkstats/include/netdbpf/BpfNetworkStats.h
@@ -63,7 +63,7 @@
const BpfMap<uint32_t, StatsValue>& ifaceStatsMap,
const BpfMap<uint32_t, IfaceValue>& ifaceNameMap);
// For test only
-int parseBpfNetworkStatsDetailInternal(std::vector<stats_line>* lines,
+int parseBpfNetworkStatsDetailInternal(std::vector<stats_line>& lines,
const BpfMap<StatsKey, StatsValue>& statsMap,
const BpfMap<uint32_t, IfaceValue>& ifaceMap);
// For test only
@@ -106,7 +106,7 @@
}
// For test only
-int parseBpfNetworkStatsDevInternal(std::vector<stats_line>* lines,
+int parseBpfNetworkStatsDevInternal(std::vector<stats_line>& lines,
const BpfMap<uint32_t, StatsValue>& statsMap,
const BpfMap<uint32_t, IfaceValue>& ifaceMap);
@@ -115,7 +115,7 @@
int parseBpfNetworkStatsDetail(std::vector<stats_line>* lines);
int parseBpfNetworkStatsDev(std::vector<stats_line>* lines);
-void groupNetworkStats(std::vector<stats_line>* lines);
+void groupNetworkStats(std::vector<stats_line>& lines);
int cleanStatsMap();
} // namespace bpf
} // namespace android
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsServiceTypeClient.java b/service-t/src/com/android/server/connectivity/mdns/MdnsServiceTypeClient.java
index f87804b..5298aef 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsServiceTypeClient.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsServiceTypeClient.java
@@ -174,6 +174,7 @@
this.searchOptions = searchOptions;
if (listeners.put(listener, searchOptions) == null) {
for (MdnsResponse existingResponse : instanceNameToResponse.values()) {
+ if (!responseMatchesOptions(existingResponse, searchOptions)) continue;
final MdnsServiceInfo info =
buildMdnsServiceInfoFromResponse(existingResponse, serviceTypeLabels);
listener.onServiceNameDiscovered(info);
@@ -199,6 +200,13 @@
}
}
+ private boolean responseMatchesOptions(@NonNull MdnsResponse response,
+ @NonNull MdnsSearchOptions options) {
+ if (options.getResolveInstanceName() == null) return true;
+ // DNS is case-insensitive, so ignore case in the comparison
+ return options.getResolveInstanceName().equalsIgnoreCase(response.getServiceInstanceName());
+ }
+
/**
* Unregisters {@code listener} from receiving discovery event of mDNS service instances.
*
@@ -274,6 +282,7 @@
buildMdnsServiceInfoFromResponse(response, serviceTypeLabels);
for (int i = 0; i < listeners.size(); i++) {
+ if (!responseMatchesOptions(response, listeners.valueAt(i))) continue;
final MdnsServiceBrowserListener listener = listeners.keyAt(i);
if (newServiceFound) {
listener.onServiceNameDiscovered(serviceInfo);
@@ -295,6 +304,7 @@
return;
}
for (int i = 0; i < listeners.size(); i++) {
+ if (!responseMatchesOptions(response, listeners.valueAt(i))) continue;
final MdnsServiceBrowserListener listener = listeners.keyAt(i);
final MdnsServiceInfo serviceInfo =
buildMdnsServiceInfoFromResponse(response, serviceTypeLabels);
@@ -512,6 +522,10 @@
== 0) {
iter.remove();
for (int i = 0; i < listeners.size(); i++) {
+ if (!responseMatchesOptions(existingResponse,
+ listeners.valueAt(i))) {
+ continue;
+ }
final MdnsServiceBrowserListener listener = listeners.keyAt(i);
String serviceInstanceName =
existingResponse.getServiceInstanceName();
diff --git a/service/jni/com_android_server_connectivity_ClatCoordinator.cpp b/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
index 19f2fb8..e63e423 100644
--- a/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
+++ b/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
@@ -233,7 +233,7 @@
}
struct ifreq ifr = {
- .ifr_flags = IFF_TUN,
+ .ifr_flags = static_cast<short>(IFF_TUN | IFF_TUN_EXCL),
};
strlcpy(ifr.ifr_name, v4interface.c_str(), sizeof(ifr.ifr_name));
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index 8786638..9d234d3 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -2176,15 +2176,12 @@
c -> c instanceof CallbackEntry.Available);
}
- private void waitForAvailable(
+ private void waitForTransport(
@NonNull final TestableNetworkCallback cb, final int expectedTransport) {
- cb.eventuallyExpect(
- CallbackEntry.AVAILABLE, NETWORK_CALLBACK_TIMEOUT_MS,
- entry -> {
- final NetworkCapabilities nc = mCm.getNetworkCapabilities(entry.getNetwork());
- return nc.hasTransport(expectedTransport);
- }
- );
+ cb.eventuallyExpect(CallbackEntry.NETWORK_CAPS_UPDATED,
+ NETWORK_CALLBACK_TIMEOUT_MS,
+ entry -> ((CallbackEntry.CapabilitiesChanged) entry).getCaps()
+ .hasTransport(expectedTransport));
}
private void waitForAvailable(
@@ -2672,7 +2669,8 @@
// Validate that an unmetered network is used over other networks.
waitForAvailable(defaultCallback, wifiNetwork);
- waitForAvailable(systemDefaultCallback, wifiNetwork);
+ systemDefaultCallback.eventuallyExpect(CallbackEntry.AVAILABLE,
+ NETWORK_CALLBACK_TIMEOUT_MS, cb -> wifiNetwork.equals(cb.getNetwork()));
// Validate that when setting unmetered to metered, unmetered is lost and replaced by
// the network with the TEST transport. Also wait for validation here, in case there
@@ -2684,11 +2682,14 @@
// callbacks may be received. Eventually, metered Wi-Fi should be the final available
// callback in any case therefore confirm its receipt before continuing to assure the
// system is in the expected state.
- waitForAvailable(systemDefaultCallback, TRANSPORT_WIFI);
+ waitForTransport(systemDefaultCallback, TRANSPORT_WIFI);
}, /* cleanup */ () -> {
- // Validate that removing the test network will fallback to the default network.
+ // Validate that removing the test network will fallback to the default network.
runWithShellPermissionIdentity(tnt::teardown);
- defaultCallback.expect(CallbackEntry.LOST, tnt, NETWORK_CALLBACK_TIMEOUT_MS);
+ // The other callbacks (LP or NC changes) would receive before LOST callback. Use
+ // eventuallyExpect to check callback for avoiding test flake.
+ defaultCallback.eventuallyExpect(CallbackEntry.LOST, NETWORK_CALLBACK_TIMEOUT_MS,
+ lost -> tnt.getNetwork().equals(lost.getNetwork()));
waitForAvailable(defaultCallback);
}, /* cleanup */ () -> {
setWifiMeteredStatusAndWait(ssid, oldMeteredValue, false /* waitForValidation */);
diff --git a/tests/unit/java/com/android/server/connectivity/VpnTest.java b/tests/unit/java/com/android/server/connectivity/VpnTest.java
index 5227c2f..c599d9d 100644
--- a/tests/unit/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/unit/java/com/android/server/connectivity/VpnTest.java
@@ -1656,14 +1656,14 @@
ArgumentCaptor.forClass(IkeSessionCallback.class);
// Verify retry is scheduled
- final long expectedDelay = mTestDeps.getNextRetryDelaySeconds(retryIndex) * 1000;
+ final long expectedDelayMs = mTestDeps.getNextRetryDelayMs(retryIndex);
final ArgumentCaptor<Long> delayCaptor = ArgumentCaptor.forClass(Long.class);
verify(mExecutor, atLeastOnce()).schedule(any(Runnable.class), delayCaptor.capture(),
eq(TimeUnit.MILLISECONDS));
final List<Long> delays = delayCaptor.getAllValues();
- assertEquals(expectedDelay, (long) delays.get(delays.size() - 1));
+ assertEquals(expectedDelayMs, (long) delays.get(delays.size() - 1));
- verify(mIkev2SessionCreator, timeout(TEST_TIMEOUT_MS + expectedDelay))
+ verify(mIkev2SessionCreator, timeout(TEST_TIMEOUT_MS + expectedDelayMs))
.createIkeSession(any(), any(), any(), any(), ikeCbCaptor.capture(), any());
// Forget the mIkev2SessionCreator#createIkeSession call and mExecutor#schedule call
@@ -2966,9 +2966,9 @@
}
@Override
- public long getNextRetryDelaySeconds(int retryCount) {
+ public long getNextRetryDelayMs(int retryCount) {
// Simply return retryCount as the delay seconds for retrying.
- return retryCount;
+ return retryCount * 1000;
}
@Override
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsServiceTypeClientTests.java b/tests/unit/java/com/android/server/connectivity/mdns/MdnsServiceTypeClientTests.java
index 5d58f5d..746994f 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsServiceTypeClientTests.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsServiceTypeClientTests.java
@@ -992,6 +992,71 @@
mockNetwork);
}
+ @Test
+ public void testProcessResponse_ResolveExcludesOtherServices() {
+ client = new MdnsServiceTypeClient(
+ SERVICE_TYPE, mockSocketClient, currentThreadExecutor, mockNetwork);
+
+ final String requestedInstance = "instance1";
+ final String otherInstance = "instance2";
+ final String ipV4Address = "192.0.2.0";
+ final String ipV6Address = "2001:db8::";
+
+ final MdnsSearchOptions resolveOptions = MdnsSearchOptions.newBuilder()
+ // Use different case in the options
+ .setResolveInstanceName("Instance1").build();
+
+ client.startSendAndReceive(mockListenerOne, resolveOptions);
+ client.startSendAndReceive(mockListenerTwo, MdnsSearchOptions.getDefaultOptions());
+
+ // Complete response from instanceName
+ client.processResponse(createResponse(
+ requestedInstance, ipV4Address, 5353, SERVICE_TYPE_LABELS,
+ Collections.emptyMap() /* textAttributes */, TEST_TTL),
+ INTERFACE_INDEX, mockNetwork);
+
+ // Complete response from otherInstanceName
+ client.processResponse(createResponse(
+ otherInstance, ipV4Address, 5353, SERVICE_TYPE_LABELS,
+ Collections.emptyMap() /* textAttributes */, TEST_TTL),
+ INTERFACE_INDEX, mockNetwork);
+
+ // Address update from otherInstanceName
+ client.processResponse(createResponse(
+ otherInstance, ipV6Address, 5353, SERVICE_TYPE_LABELS,
+ Collections.emptyMap(), TEST_TTL), INTERFACE_INDEX, mockNetwork);
+
+ // Goodbye from otherInstanceName
+ client.processResponse(createResponse(
+ otherInstance, ipV6Address, 5353, SERVICE_TYPE_LABELS,
+ Collections.emptyMap(), 0L /* ttl */), INTERFACE_INDEX, mockNetwork);
+
+ // mockListenerOne gets notified for the requested instance
+ verify(mockListenerOne).onServiceNameDiscovered(matchServiceName(requestedInstance));
+ verify(mockListenerOne).onServiceFound(matchServiceName(requestedInstance));
+
+ // ...but does not get any callback for the other instance
+ verify(mockListenerOne, never()).onServiceFound(matchServiceName(otherInstance));
+ verify(mockListenerOne, never()).onServiceNameDiscovered(matchServiceName(otherInstance));
+ verify(mockListenerOne, never()).onServiceUpdated(matchServiceName(otherInstance));
+ verify(mockListenerOne, never()).onServiceRemoved(matchServiceName(otherInstance));
+
+ // mockListenerTwo gets notified for both though
+ final InOrder inOrder = inOrder(mockListenerTwo);
+ inOrder.verify(mockListenerTwo).onServiceNameDiscovered(
+ matchServiceName(requestedInstance));
+ inOrder.verify(mockListenerTwo).onServiceFound(matchServiceName(requestedInstance));
+
+ inOrder.verify(mockListenerTwo).onServiceNameDiscovered(matchServiceName(otherInstance));
+ inOrder.verify(mockListenerTwo).onServiceFound(matchServiceName(otherInstance));
+ inOrder.verify(mockListenerTwo).onServiceUpdated(matchServiceName(otherInstance));
+ inOrder.verify(mockListenerTwo).onServiceRemoved(matchServiceName(otherInstance));
+ }
+
+ private static MdnsServiceInfo matchServiceName(String name) {
+ return argThat(info -> info.getServiceInstanceName().equals(name));
+ }
+
// verifies that the right query was enqueued with the right delay, and send query by executing
// the runnable.
private void verifyAndSendQuery(int index, long timeInMs, boolean expectsUnicastResponse) {