Implement exit announcements
Build ExitAnnouncementInfo in MdnsRecordRepository.exitService. Use a separate class for AnnouncementInfo and ExitAnnouncementInfo, so announcement callbacks can differentiate each case. Bug: 241738458 Test: atest Change-Id: I3b1ad1bef3dc1514479d7c789ef06b6a7de02e59
This commit is contained in:
@@ -22,6 +22,7 @@ import android.os.HandlerThread
|
||||
import android.os.SystemClock
|
||||
import com.android.internal.util.HexDump
|
||||
import com.android.server.connectivity.mdns.MdnsAnnouncer.AnnouncementInfo
|
||||
import com.android.server.connectivity.mdns.MdnsAnnouncer.BaseAnnouncementInfo
|
||||
import com.android.server.connectivity.mdns.MdnsRecordRepository.getReverseDnsAddress
|
||||
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
|
||||
import com.android.testutils.DevSdkIgnoreRunner
|
||||
@@ -67,7 +68,7 @@ class MdnsAnnouncerTest {
|
||||
private class TestAnnouncementInfo(
|
||||
announcedRecords: List<MdnsRecord>,
|
||||
additionalRecords: List<MdnsRecord>
|
||||
) : AnnouncementInfo(announcedRecords, additionalRecords) {
|
||||
) : AnnouncementInfo(1 /* serviceId */, announcedRecords, additionalRecords) {
|
||||
override fun getDelayMs(nextIndex: Int) =
|
||||
if (nextIndex < FIRST_ANNOUNCES_COUNT) {
|
||||
FIRST_ANNOUNCES_DELAY
|
||||
@@ -81,7 +82,7 @@ class MdnsAnnouncerTest {
|
||||
val replySender = MdnsReplySender(thread.looper, socket, buffer)
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val cb = mock(MdnsPacketRepeater.PacketRepeaterCallback::class.java)
|
||||
as MdnsPacketRepeater.PacketRepeaterCallback<AnnouncementInfo>
|
||||
as MdnsPacketRepeater.PacketRepeaterCallback<BaseAnnouncementInfo>
|
||||
val announcer = MdnsAnnouncer("testiface", thread.looper, replySender, cb)
|
||||
/*
|
||||
The expected packet replicates records announced when registering a service, as observed in
|
||||
|
||||
@@ -22,6 +22,8 @@ import android.net.nsd.NsdServiceInfo
|
||||
import android.os.Build
|
||||
import android.os.HandlerThread
|
||||
import com.android.server.connectivity.mdns.MdnsAnnouncer.AnnouncementInfo
|
||||
import com.android.server.connectivity.mdns.MdnsAnnouncer.BaseAnnouncementInfo
|
||||
import com.android.server.connectivity.mdns.MdnsAnnouncer.ExitAnnouncementInfo
|
||||
import com.android.server.connectivity.mdns.MdnsInterfaceAdvertiser.EXIT_ANNOUNCEMENT_DELAY_MS
|
||||
import com.android.server.connectivity.mdns.MdnsPacketRepeater.PacketRepeaterCallback
|
||||
import com.android.server.connectivity.mdns.MdnsProber.ProbingInfo
|
||||
@@ -35,8 +37,10 @@ import org.junit.runner.RunWith
|
||||
import org.mockito.ArgumentCaptor
|
||||
import org.mockito.Mockito.any
|
||||
import org.mockito.Mockito.anyInt
|
||||
import org.mockito.Mockito.doAnswer
|
||||
import org.mockito.Mockito.doReturn
|
||||
import org.mockito.Mockito.mock
|
||||
import org.mockito.Mockito.times
|
||||
import org.mockito.Mockito.verify
|
||||
|
||||
private const val LOG_TAG = "testlogtag"
|
||||
@@ -66,7 +70,7 @@ class MdnsInterfaceAdvertiserTest {
|
||||
private val probeCbCaptor = ArgumentCaptor.forClass(PacketRepeaterCallback::class.java)
|
||||
as ArgumentCaptor<PacketRepeaterCallback<ProbingInfo>>
|
||||
private val announceCbCaptor = ArgumentCaptor.forClass(PacketRepeaterCallback::class.java)
|
||||
as ArgumentCaptor<PacketRepeaterCallback<AnnouncementInfo>>
|
||||
as ArgumentCaptor<PacketRepeaterCallback<BaseAnnouncementInfo>>
|
||||
|
||||
private val probeCb get() = probeCbCaptor.value
|
||||
private val announceCb get() = announceCbCaptor.value
|
||||
@@ -82,7 +86,21 @@ class MdnsInterfaceAdvertiserTest {
|
||||
doReturn(announcer).`when`(deps).makeMdnsAnnouncer(any(), any(), any(), any())
|
||||
doReturn(prober).`when`(deps).makeMdnsProber(any(), any(), any(), any())
|
||||
|
||||
doReturn(-1).`when`(repository).addService(anyInt(), any())
|
||||
val knownServices = mutableSetOf<Int>()
|
||||
doAnswer { inv ->
|
||||
knownServices.add(inv.getArgument(0))
|
||||
-1
|
||||
}.`when`(repository).addService(anyInt(), any())
|
||||
doAnswer { inv ->
|
||||
knownServices.remove(inv.getArgument(0))
|
||||
null
|
||||
}.`when`(repository).removeService(anyInt())
|
||||
doAnswer {
|
||||
knownServices.toIntArray().also { knownServices.clear() }
|
||||
}.`when`(repository).clearServices()
|
||||
doAnswer { inv ->
|
||||
knownServices.contains(inv.getArgument(0))
|
||||
}.`when`(repository).hasActiveService(anyInt())
|
||||
thread.start()
|
||||
advertiser.start()
|
||||
|
||||
@@ -97,18 +115,7 @@ class MdnsInterfaceAdvertiserTest {
|
||||
|
||||
@Test
|
||||
fun testAddRemoveService() {
|
||||
val testProbingInfo = mock(ProbingInfo::class.java)
|
||||
doReturn(TEST_SERVICE_ID_1).`when`(testProbingInfo).serviceId
|
||||
doReturn(testProbingInfo).`when`(repository).setServiceProbing(TEST_SERVICE_ID_1)
|
||||
|
||||
advertiser.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1)
|
||||
verify(repository).addService(TEST_SERVICE_ID_1, TEST_SERVICE_1)
|
||||
verify(prober).startProbing(testProbingInfo)
|
||||
|
||||
// Simulate probing success: continues to announcing
|
||||
val testAnnouncementInfo = mock(AnnouncementInfo::class.java)
|
||||
doReturn(testAnnouncementInfo).`when`(repository).onProbingSucceeded(testProbingInfo)
|
||||
probeCb.onFinished(testProbingInfo)
|
||||
val testAnnouncementInfo = addServiceAndFinishProbing(TEST_SERVICE_ID_1, TEST_SERVICE_1)
|
||||
|
||||
verify(announcer).startSending(TEST_SERVICE_ID_1, testAnnouncementInfo,
|
||||
0L /* initialDelayMs */)
|
||||
@@ -117,7 +124,7 @@ class MdnsInterfaceAdvertiserTest {
|
||||
verify(cb).onRegisterServiceSucceeded(advertiser, TEST_SERVICE_ID_1)
|
||||
|
||||
// Remove the service: expect exit announcements
|
||||
val testExitInfo = mock(AnnouncementInfo::class.java)
|
||||
val testExitInfo = mock(ExitAnnouncementInfo::class.java)
|
||||
doReturn(testExitInfo).`when`(repository).exitService(TEST_SERVICE_ID_1)
|
||||
advertiser.removeService(TEST_SERVICE_ID_1)
|
||||
|
||||
@@ -125,7 +132,45 @@ class MdnsInterfaceAdvertiserTest {
|
||||
verify(announcer).stop(TEST_SERVICE_ID_1)
|
||||
verify(announcer).startSending(TEST_SERVICE_ID_1, testExitInfo, EXIT_ANNOUNCEMENT_DELAY_MS)
|
||||
|
||||
// TODO: after exit announcements are implemented, verify that announceCb.onFinished causes
|
||||
// cb.onDestroyed to be called.
|
||||
// Exit announcements finish: the advertiser has no left service and destroys itself
|
||||
announceCb.onFinished(testExitInfo)
|
||||
thread.waitForIdle(TIMEOUT_MS)
|
||||
verify(cb).onDestroyed(socket)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDoubleRemove() {
|
||||
addServiceAndFinishProbing(TEST_SERVICE_ID_1, TEST_SERVICE_1)
|
||||
|
||||
val testExitInfo = mock(ExitAnnouncementInfo::class.java)
|
||||
doReturn(testExitInfo).`when`(repository).exitService(TEST_SERVICE_ID_1)
|
||||
advertiser.removeService(TEST_SERVICE_ID_1)
|
||||
|
||||
verify(prober).stop(TEST_SERVICE_ID_1)
|
||||
verify(announcer).stop(TEST_SERVICE_ID_1)
|
||||
verify(announcer).startSending(TEST_SERVICE_ID_1, testExitInfo, EXIT_ANNOUNCEMENT_DELAY_MS)
|
||||
|
||||
doReturn(false).`when`(repository).hasActiveService(TEST_SERVICE_ID_1)
|
||||
advertiser.removeService(TEST_SERVICE_ID_1)
|
||||
// Prober, announcer were still stopped only one time
|
||||
verify(prober, times(1)).stop(TEST_SERVICE_ID_1)
|
||||
verify(announcer, times(1)).stop(TEST_SERVICE_ID_1)
|
||||
}
|
||||
|
||||
private fun addServiceAndFinishProbing(serviceId: Int, serviceInfo: NsdServiceInfo):
|
||||
AnnouncementInfo {
|
||||
val testProbingInfo = mock(ProbingInfo::class.java)
|
||||
doReturn(serviceId).`when`(testProbingInfo).serviceId
|
||||
doReturn(testProbingInfo).`when`(repository).setServiceProbing(serviceId)
|
||||
|
||||
advertiser.addService(serviceId, serviceInfo)
|
||||
verify(repository).addService(serviceId, serviceInfo)
|
||||
verify(prober).startProbing(testProbingInfo)
|
||||
|
||||
// Simulate probing success: continues to announcing
|
||||
val testAnnouncementInfo = mock(AnnouncementInfo::class.java)
|
||||
doReturn(testAnnouncementInfo).`when`(repository).onProbingSucceeded(testProbingInfo)
|
||||
probeCb.onFinished(testProbingInfo)
|
||||
return testAnnouncementInfo
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import java.util.Collections
|
||||
import kotlin.test.assertContentEquals
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFailsWith
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertNotNull
|
||||
import kotlin.test.assertTrue
|
||||
import org.junit.After
|
||||
@@ -129,10 +130,63 @@ class MdnsRecordRepositoryTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testHasActiveService() {
|
||||
val repository = MdnsRecordRepository(thread.looper, deps)
|
||||
assertFalse(repository.hasActiveService(TEST_SERVICE_ID_1))
|
||||
|
||||
repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1)
|
||||
assertTrue(repository.hasActiveService(TEST_SERVICE_ID_1))
|
||||
|
||||
val probingInfo = repository.setServiceProbing(TEST_SERVICE_ID_1)
|
||||
repository.onProbingSucceeded(probingInfo)
|
||||
repository.onAdvertisementSent(TEST_SERVICE_ID_1)
|
||||
assertTrue(repository.hasActiveService(TEST_SERVICE_ID_1))
|
||||
|
||||
repository.exitService(TEST_SERVICE_ID_1)
|
||||
assertFalse(repository.hasActiveService(TEST_SERVICE_ID_1))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testExitAnnouncements() {
|
||||
val repository = MdnsRecordRepository(thread.looper, deps)
|
||||
repository.updateAddresses(TEST_ADDRESSES)
|
||||
|
||||
repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1)
|
||||
val probingInfo = repository.setServiceProbing(TEST_SERVICE_ID_1)
|
||||
repository.onProbingSucceeded(probingInfo)
|
||||
repository.onAdvertisementSent(TEST_SERVICE_ID_1)
|
||||
|
||||
val exitAnnouncement = repository.exitService(TEST_SERVICE_ID_1)
|
||||
assertNotNull(exitAnnouncement)
|
||||
assertEquals(1, repository.servicesCount)
|
||||
val packet = exitAnnouncement.getPacket(0)
|
||||
|
||||
assertEquals(0x8400 /* response, authoritative */, packet.flags)
|
||||
assertEquals(0, packet.questions.size)
|
||||
assertEquals(0, packet.authorityRecords.size)
|
||||
assertEquals(0, packet.additionalRecords.size)
|
||||
|
||||
assertContentEquals(listOf(
|
||||
MdnsPointerRecord(
|
||||
arrayOf("_testservice", "_tcp", "local"),
|
||||
0L /* receiptTimeMillis */,
|
||||
true /* cacheFlush */,
|
||||
0L /* ttlMillis */,
|
||||
arrayOf("MyTestService", "_testservice", "_tcp", "local"))
|
||||
), packet.answers)
|
||||
|
||||
repository.removeService(TEST_SERVICE_ID_1)
|
||||
assertEquals(0, repository.servicesCount)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testExitingServiceReAdded() {
|
||||
val repository = MdnsRecordRepository(thread.looper, deps)
|
||||
repository.addService(TEST_SERVICE_ID_1, TEST_SERVICE_1)
|
||||
val probingInfo = repository.setServiceProbing(TEST_SERVICE_ID_1)
|
||||
repository.onProbingSucceeded(probingInfo)
|
||||
repository.onAdvertisementSent(TEST_SERVICE_ID_1)
|
||||
repository.exitService(TEST_SERVICE_ID_1)
|
||||
|
||||
assertEquals(TEST_SERVICE_ID_1, repository.addService(TEST_SERVICE_ID_2, TEST_SERVICE_1))
|
||||
@@ -262,7 +316,7 @@ class MdnsRecordRepositoryTest {
|
||||
|
||||
@Test
|
||||
fun testGetReverseDnsAddress() {
|
||||
val expectedV6 = "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.B.D.1.0.0.2.ip6.arpa"
|
||||
val expectedV6 = "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.B.D.0.1.0.0.2.ip6.arpa"
|
||||
.split(".").toTypedArray()
|
||||
assertContentEquals(expectedV6, getReverseDnsAddress(parseNumericAddress("2001:db8::1")))
|
||||
val expectedV4 = "123.2.0.192.in-addr.arpa".split(".").toTypedArray()
|
||||
|
||||
Reference in New Issue
Block a user