From 9cb8a29436eb255bd720e5e5b0654e732ff7a91a Mon Sep 17 00:00:00 2001 From: Yuyang Huang Date: Thu, 6 Feb 2025 23:01:00 +0900 Subject: [PATCH] Exclude Google Cast app subtypes from mDNS offload This commit excludes Google Cast app subtypes from mDNS offloading. The large number of subtypes would consume excessive firmware resources if offloaded. Test: TH Change-Id: I562a94187dc5c710ca37d4997e9ed6c1602918b6 --- .../connectivity/mdns/MdnsAdvertiser.java | 15 +++++- .../connectivity/mdns/MdnsAdvertiserTest.kt | 54 +++++++++++++++++++ 2 files changed, 68 insertions(+), 1 deletion(-) 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 =