diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsAdvertiser.java b/service-t/src/com/android/server/connectivity/mdns/MdnsAdvertiser.java index 54f7ca3f96..c3306bd3b9 100644 --- a/service-t/src/com/android/server/connectivity/mdns/MdnsAdvertiser.java +++ b/service-t/src/com/android/server/connectivity/mdns/MdnsAdvertiser.java @@ -1007,6 +1007,19 @@ public class MdnsAdvertiser { }); } + private List getOffloadSubtype(@NonNull NsdServiceInfo nsdServiceInfo) { + // Workaround: Google Cast doesn't announce subtypes per DNS-SD/mDNS spec. + // Thus, subtypes aren't offloaded; only "_googlecast._tcp" is. + // Subtype responses occur when hardware offload is off. + // This solution works because Google Cast doesn't follow the intended usage of subtypes in + // the spec, as it always discovers for both the subtype+base type, and only uses the mDNS + // subtype as an optimization. + if (nsdServiceInfo.getServiceType().equals("_googlecast._tcp")) { + return new ArrayList<>(); + } + return new ArrayList<>(nsdServiceInfo.getSubtypes()); + } + private OffloadServiceInfoWrapper createOffloadService(int serviceId, @NonNull Registration registration, byte[] rawOffloadPacket) { final NsdServiceInfo nsdServiceInfo = registration.getServiceInfo(); @@ -1017,7 +1030,7 @@ public class MdnsAdvertiser { final OffloadServiceInfo offloadServiceInfo = new OffloadServiceInfo( new OffloadServiceInfo.Key(nsdServiceInfo.getServiceName(), nsdServiceInfo.getServiceType()), - new ArrayList<>(nsdServiceInfo.getSubtypes()), + getOffloadSubtype(nsdServiceInfo), String.join(".", mDeviceHostName), rawOffloadPacket, priority, diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsAdvertiserTest.kt b/tests/unit/java/com/android/server/connectivity/mdns/MdnsAdvertiserTest.kt index c733e6a202..e6e6ecca7c 100644 --- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsAdvertiserTest.kt +++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsAdvertiserTest.kt @@ -92,6 +92,13 @@ private val SERVICE_1 = NsdServiceInfo("TestServiceName", "_advertisertest._tcp" network = TEST_NETWORK_1 } +private val GOOGLECAST_SERVICE = NsdServiceInfo("TestServiceName", "_googlecast._tcp").apply { + subtypes = setOf(TEST_SUBTYPE) + port = 12345 + hostAddresses = listOf(TEST_ADDR) + network = TEST_NETWORK_1 +} + private val SERVICE_1_SUBTYPE = NsdServiceInfo("TestServiceName", "_advertisertest._tcp").apply { subtypes = setOf(TEST_SUBTYPE) port = 12345 @@ -143,6 +150,15 @@ private val OFFLOAD_SERVICEINFO = OffloadServiceInfo( OffloadEngine.OFFLOAD_TYPE_REPLY.toLong() ) +private val OFFLOAD_SERVICE_INFO_GOOGLECAST = OffloadServiceInfo( + OffloadServiceInfo.Key("TestServiceName", "_googlecast._tcp"), + listOf(), + "Android_test.local", + TEST_OFFLOAD_PACKET1, + Int.MAX_VALUE, + OffloadEngine.OFFLOAD_TYPE_REPLY.toLong() +) + private val OFFLOAD_SERVICEINFO_NO_SUBTYPE = OffloadServiceInfo( OffloadServiceInfo.Key("TestServiceName", "_advertisertest._tcp"), listOf(), @@ -507,6 +523,44 @@ class MdnsAdvertiserTest { ))) } + @Test + fun testAddService_NoSubtypeForGoogleCastOffload() { + val advertiser = + MdnsAdvertiser(thread.looper, socketProvider, cb, mockDeps, sharedlog, flags, context) + postSync { + advertiser.addOrUpdateService( + SERVICE_ID_1, + GOOGLECAST_SERVICE, + DEFAULT_ADVERTISING_OPTION, + TEST_CLIENT_UID_1 + ) + } + val socketCbCaptor = ArgumentCaptor.forClass(SocketCallback::class.java) + verify(socketProvider).requestSocket(eq(SERVICE_1.network), socketCbCaptor.capture()) + + val socketCb = socketCbCaptor.value + postSync { socketCb.onSocketCreated(TEST_SOCKETKEY_1, mockSocket1, listOf(TEST_LINKADDR)) } + + val intAdvCbCaptor1 = ArgumentCaptor.forClass(MdnsInterfaceAdvertiser.Callback::class.java) + verify(mockDeps).makeAdvertiser( + eq(mockSocket1), + eq(listOf(TEST_LINKADDR)), + eq(thread.looper), + any(), + intAdvCbCaptor1.capture(), + eq(TEST_HOSTNAME), + any(), + any() + ) + + doReturn(false).`when`(mockInterfaceAdvertiser1).isProbing(SERVICE_ID_1) + postSync { + intAdvCbCaptor1.value.onServiceProbingSucceeded(mockInterfaceAdvertiser1, SERVICE_ID_1) + } + + verify(cb).onOffloadStartOrUpdate(eq(TEST_INTERFACE1), eq(OFFLOAD_SERVICE_INFO_GOOGLECAST)) + } + @Test fun testAddService_Conflicts() { val advertiser =