Report more advertising metrics data

Report more advertising metrics data below when the service is
unregistered.
- Replied request count (sum across interfaces)
- Sent packet count (including announcements and probes)
- Number of conflicts during probing
- Nubmer of conflicts after probing

Bug: 287546772
Test: atest FrameworksNetTestCases NsdManagerTest
Merged-In: I50c54a35dc523422e3a7302c059bbbc38eac5631
Change-Id: I50c54a35dc523422e3a7302c059bbbc38eac5631
This commit is contained in:
Paul Hu
2023-07-14 16:38:25 +08:00
parent a337d95cc4
commit 043bcd4537
15 changed files with 333 additions and 38 deletions

View File

@@ -21,12 +21,15 @@ import android.stats.connectivity.MdnsQueryResult
import android.stats.connectivity.NsdEventType
import com.android.testutils.DevSdkIgnoreRule
import com.android.testutils.DevSdkIgnoreRunner
import java.util.Random
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Mockito.doReturn
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
@@ -34,6 +37,12 @@ import org.mockito.Mockito.verify
@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
class NetworkNsdReportedMetricsTest {
private val deps = mock(NetworkNsdReportedMetrics.Dependencies::class.java)
private val random = mock(Random::class.java)
@Before
fun setUp() {
doReturn(random).`when`(deps).makeRandomGenerator()
}
@Test
fun testReportServiceRegistrationSucceeded() {
@@ -80,8 +89,13 @@ class NetworkNsdReportedMetricsTest {
val clientId = 99
val transactionId = 100
val durationMs = 10L
val repliedRequestsCount = 25
val sentPacketCount = 50
val conflictDuringProbingCount = 2
val conflictAfterProbingCount = 1
val metrics = NetworkNsdReportedMetrics(true /* isLegacy */, clientId, deps)
metrics.reportServiceUnregistration(transactionId, durationMs)
metrics.reportServiceUnregistration(transactionId, durationMs, repliedRequestsCount,
sentPacketCount, conflictDuringProbingCount, conflictAfterProbingCount)
val eventCaptor = ArgumentCaptor.forClass(NetworkNsdReported::class.java)
verify(deps).statsWrite(eventCaptor.capture())
@@ -92,6 +106,10 @@ class NetworkNsdReportedMetricsTest {
assertEquals(NsdEventType.NET_REGISTER, it.type)
assertEquals(MdnsQueryResult.MQR_SERVICE_UNREGISTERED, it.queryResult)
assertEquals(durationMs, it.eventDurationMillisec)
assertEquals(repliedRequestsCount, it.repliedRequestsCount)
assertEquals(sentPacketCount, it.sentPacketCount)
assertEquals(conflictDuringProbingCount, it.conflictDuringProbingCount)
assertEquals(conflictAfterProbingCount, it.conflictAfterProbingCount)
}
}

View File

@@ -1222,6 +1222,8 @@ public class NsdServiceTest {
verify(mMockMDnsM).stopOperation(legacyIdCaptor.getValue());
verify(mAdvertiser, never()).removeService(anyInt());
doReturn(mock(MdnsAdvertiser.AdvertiserMetrics.class))
.when(mAdvertiser).getAdvertiserMetrics(anyInt());
client.unregisterService(regListenerWithFeature);
waitForIdle();
verify(mAdvertiser).removeService(serviceIdCaptor.getValue());
@@ -1312,14 +1314,20 @@ public class NsdServiceTest {
new NsdServiceInfo(regInfo.getServiceName(), null))));
verify(mMetrics).reportServiceRegistrationSucceeded(regId, 10L /* durationMs */);
final MdnsAdvertiser.AdvertiserMetrics metrics = new MdnsAdvertiser.AdvertiserMetrics(
50 /* repliedRequestCount */, 100 /* sentPacketCount */,
3 /* conflictDuringProbingCount */, 2 /* conflictAfterProbingCount */);
doReturn(TEST_TIME_MS + 100L).when(mClock).elapsedRealtime();
doReturn(metrics).when(mAdvertiser).getAdvertiserMetrics(regId);
client.unregisterService(regListener);
waitForIdle();
verify(mAdvertiser).removeService(idCaptor.getValue());
verify(regListener, timeout(TIMEOUT_MS)).onServiceUnregistered(
argThat(info -> matches(info, regInfo)));
verify(mSocketProvider, timeout(TIMEOUT_MS)).requestStopWhenInactive();
verify(mMetrics).reportServiceUnregistration(regId, 100L /* durationMs */);
verify(mMetrics).reportServiceUnregistration(regId, 100L /* durationMs */,
50 /* repliedRequestCount */, 100 /* sentPacketCount */,
3 /* conflictDuringProbingCount */, 2 /* conflictAfterProbingCount */);
}
@Test

View File

@@ -33,7 +33,10 @@ import com.android.testutils.DevSdkIgnoreRunner
import com.android.testutils.waitForIdle
import java.net.NetworkInterface
import java.util.Objects
import java.util.concurrent.CompletableFuture
import java.util.concurrent.TimeUnit
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -206,6 +209,18 @@ class MdnsAdvertiserTest {
verify(cb).onRegisterServiceSucceeded(eq(SERVICE_ID_1), argThat { it.matches(SERVICE_1) })
verify(cb).onOffloadStartOrUpdate(eq(TEST_INTERFACE1), eq(OFFLOAD_SERVICEINFO_NO_SUBTYPE))
// Service is conflicted.
postSync { intAdvCbCaptor.value.onServiceConflict(mockInterfaceAdvertiser1, SERVICE_ID_1) }
// Verify the metrics data
doReturn(25).`when`(mockInterfaceAdvertiser1).getServiceRepliedRequestsCount(SERVICE_ID_1)
doReturn(40).`when`(mockInterfaceAdvertiser1).getSentPacketCount(SERVICE_ID_1)
val metrics = postReturn { advertiser.getAdvertiserMetrics(SERVICE_ID_1) }
assertEquals(25, metrics.mRepliedRequestsCount)
assertEquals(40, metrics.mSentPacketCount)
assertEquals(0, metrics.mConflictDuringProbingCount)
assertEquals(1, metrics.mConflictAfterProbingCount)
doReturn(TEST_OFFLOAD_PACKET2).`when`(mockInterfaceAdvertiser1)
.getRawOffloadPayload(
SERVICE_ID_1
@@ -265,6 +280,22 @@ class MdnsAdvertiserTest {
verify(cb).onRegisterServiceSucceeded(eq(SERVICE_ID_1),
argThat { it.matches(ALL_NETWORKS_SERVICE) })
// Services are conflicted.
postSync { intAdvCbCaptor1.value.onServiceConflict(mockInterfaceAdvertiser1, SERVICE_ID_1) }
postSync { intAdvCbCaptor1.value.onServiceConflict(mockInterfaceAdvertiser1, SERVICE_ID_1) }
postSync { intAdvCbCaptor2.value.onServiceConflict(mockInterfaceAdvertiser2, SERVICE_ID_1) }
// Verify the metrics data
doReturn(10).`when`(mockInterfaceAdvertiser1).getServiceRepliedRequestsCount(SERVICE_ID_1)
doReturn(5).`when`(mockInterfaceAdvertiser2).getServiceRepliedRequestsCount(SERVICE_ID_1)
doReturn(22).`when`(mockInterfaceAdvertiser1).getSentPacketCount(SERVICE_ID_1)
doReturn(12).`when`(mockInterfaceAdvertiser2).getSentPacketCount(SERVICE_ID_1)
val metrics = postReturn { advertiser.getAdvertiserMetrics(SERVICE_ID_1) }
assertEquals(15, metrics.mRepliedRequestsCount)
assertEquals(34, metrics.mSentPacketCount)
assertEquals(2, metrics.mConflictDuringProbingCount)
assertEquals(1, metrics.mConflictAfterProbingCount)
// Unregister the service
postSync { advertiser.removeService(SERVICE_ID_1) }
verify(mockInterfaceAdvertiser1).removeService(SERVICE_ID_1)
@@ -376,6 +407,14 @@ class MdnsAdvertiserTest {
handler.post(r)
handler.waitForIdle(TIMEOUT_MS)
}
private fun <T> postReturn(r: (() -> T)): T {
val future = CompletableFuture<T>()
handler.post {
future.complete(r())
}
return future.get(TIMEOUT_MS, TimeUnit.MILLISECONDS)
}
}
// NsdServiceInfo does not implement equals; this is useful to use in argument matchers

View File

@@ -253,7 +253,7 @@ class MdnsAnnouncerTest {
val captor = ArgumentCaptor.forClass(DatagramPacket::class.java)
repeat(FIRST_ANNOUNCES_COUNT) { i ->
verify(cb, timeout(TEST_TIMEOUT_MS)).onSent(i, request)
verify(cb, timeout(TEST_TIMEOUT_MS)).onSent(i, request, 1 /* sentPacketCount */)
verify(socket, atLeast(i + 1)).send(any())
val now = SystemClock.elapsedRealtime()
assertTrue(now > timeStart + startDelay + i * FIRST_ANNOUNCES_DELAY)

View File

@@ -92,7 +92,7 @@ class MdnsProberTest {
private fun assertProbesSent(probeInfo: TestProbeInfo, expectedHex: String) {
repeat(probeInfo.numSends) { i ->
verify(cb, timeout(TEST_TIMEOUT_MS)).onSent(i, probeInfo)
verify(cb, timeout(TEST_TIMEOUT_MS)).onSent(i, probeInfo, 1 /* sentPacketCount */)
// If the probe interval is short, more than (i+1) probes may have been sent already
verify(socket, atLeast(i + 1)).send(any())
}
@@ -190,7 +190,7 @@ class MdnsProberTest {
prober.startProbing(probeInfo)
// Expect the initial probe
verify(cb, timeout(TEST_TIMEOUT_MS)).onSent(0, probeInfo)
verify(cb, timeout(TEST_TIMEOUT_MS)).onSent(0, probeInfo, 1 /* sentPacketCount */)
// Stop probing
val stopResult = CompletableFuture<Boolean>()
@@ -200,7 +200,7 @@ class MdnsProberTest {
// Wait for a bit (more than the probe delay) to ensure no more probes were sent
Thread.sleep(SHORT_TIMEOUT_MS * 2)
verify(cb, never()).onSent(1, probeInfo)
verify(cb, never()).onSent(1, probeInfo, 1 /* sentPacketCount */)
verify(cb, never()).onFinished(probeInfo)
// Only one sent packet

View File

@@ -155,7 +155,7 @@ class MdnsRecordRepositoryTest {
val probingInfo = repository.setServiceProbing(TEST_SERVICE_ID_1)
repository.onProbingSucceeded(probingInfo)
repository.onAdvertisementSent(TEST_SERVICE_ID_1)
repository.onAdvertisementSent(TEST_SERVICE_ID_1, 2 /* sentPacketCount */)
assertTrue(repository.hasActiveService(TEST_SERVICE_ID_1))
repository.exitService(TEST_SERVICE_ID_1)
@@ -166,7 +166,7 @@ class MdnsRecordRepositoryTest {
fun testExitAnnouncements() {
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME)
repository.initWithService(TEST_SERVICE_ID_1, TEST_SERVICE_1)
repository.onAdvertisementSent(TEST_SERVICE_ID_1)
repository.onAdvertisementSent(TEST_SERVICE_ID_1, 2 /* sentPacketCount */)
val exitAnnouncement = repository.exitService(TEST_SERVICE_ID_1)
assertNotNull(exitAnnouncement)
@@ -195,7 +195,7 @@ class MdnsRecordRepositoryTest {
fun testExitAnnouncements_WithSubtype() {
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME)
repository.initWithService(TEST_SERVICE_ID_1, TEST_SERVICE_1, TEST_SUBTYPE)
repository.onAdvertisementSent(TEST_SERVICE_ID_1)
repository.onAdvertisementSent(TEST_SERVICE_ID_1, 2 /* sentPacketCount */)
val exitAnnouncement = repository.exitService(TEST_SERVICE_ID_1)
assertNotNull(exitAnnouncement)
@@ -230,7 +230,7 @@ class MdnsRecordRepositoryTest {
fun testExitingServiceReAdded() {
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME)
repository.initWithService(TEST_SERVICE_ID_1, TEST_SERVICE_1)
repository.onAdvertisementSent(TEST_SERVICE_ID_1)
repository.onAdvertisementSent(TEST_SERVICE_ID_1, 2 /* sentPacketCount */)
repository.exitService(TEST_SERVICE_ID_1)
assertEquals(TEST_SERVICE_ID_1,
@@ -246,7 +246,7 @@ class MdnsRecordRepositoryTest {
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME)
val announcementInfo = repository.initWithService(TEST_SERVICE_ID_1, TEST_SERVICE_1,
TEST_SUBTYPE)
repository.onAdvertisementSent(TEST_SERVICE_ID_1)
repository.onAdvertisementSent(TEST_SERVICE_ID_1, 2 /* sentPacketCount */)
val packet = announcementInfo.getPacket(0)
assertEquals(0x8400 /* response, authoritative */, packet.flags)
@@ -657,6 +657,34 @@ class MdnsRecordRepositoryTest {
// Above records are identical to the actual registrations: no conflict
assertEquals(emptySet(), repository.getConflictingServices(packet))
}
@Test
fun testGetServiceRepliedRequestsCount() {
val repository = MdnsRecordRepository(thread.looper, deps, TEST_HOSTNAME)
repository.initWithService(TEST_SERVICE_ID_1, TEST_SERVICE_1)
// Verify that there is no packet replied.
assertEquals(MdnsConstants.NO_PACKET,
repository.getServiceRepliedRequestsCount(TEST_SERVICE_ID_1))
val questions = listOf(MdnsPointerRecord(arrayOf("_testservice", "_tcp", "local"),
0L /* receiptTimeMillis */,
false /* cacheFlush */,
// TTL and data is empty for a question
0L /* ttlMillis */,
null /* pointer */))
val query = MdnsPacket(0 /* flags */, questions, listOf() /* answers */,
listOf() /* authorityRecords */, listOf() /* additionalRecords */)
val src = InetSocketAddress(parseNumericAddress("192.0.2.123"), 5353)
// Reply to the question and verify there is one packet replied.
val reply = repository.getReply(query, src)
assertNotNull(reply)
assertEquals(1, repository.getServiceRepliedRequestsCount(TEST_SERVICE_ID_1))
// No package replied for unknown service.
assertEquals(MdnsConstants.NO_PACKET,
repository.getServiceRepliedRequestsCount(TEST_SERVICE_ID_2))
}
}
private fun MdnsRecordRepository.initWithService(