From f1ae69a950afa325da88a637bf0bf55473b30842 Mon Sep 17 00:00:00 2001 From: Paul Hu Date: Tue, 13 May 2025 07:28:59 +0000 Subject: [PATCH] Provide offload service information update from DiscoveryManager With this change, the OffloadEngine can receive updates when offload service information changes from DiscoveryManager. This allows the OffloadEngine to maintain offload services and to adjust its behavior accordingly. Bug: 410734687 Test: atest ConnectivityCoverageTests Flag: com.android.tethering.mainline.beta.nsd_selective_mdns_response_offload Change-Id: I6c6ba4e76b4a2a94cbfef37bec9d6f87357bec5f --- common/mainline_beta.aconfig | 7 +++ .../src/com/android/server/NsdService.java | 43 +++++++-------- .../mdns/MdnsDiscoveryManager.java | 8 ++- .../connectivity/mdns/MdnsFeatureFlags.java | 24 +++++++++ .../mdns/MdnsServiceTypeClient.java | 23 ++++++-- .../connectivity/mdns/util/MdnsUtils.java | 31 +++++++++++ .../com/android/server/NsdServiceTest.java | 53 ++++++++++++++----- .../mdns/MdnsDiscoveryManagerTests.java | 6 ++- .../mdns/MdnsServiceTypeClientTests.java | 49 ++++++++++++++++- 9 files changed, 198 insertions(+), 46 deletions(-) diff --git a/common/mainline_beta.aconfig b/common/mainline_beta.aconfig index 4d493a5398..f0c913b355 100644 --- a/common/mainline_beta.aconfig +++ b/common/mainline_beta.aconfig @@ -27,4 +27,11 @@ flag { namespace: "com_android_tethering" description: "The flag enables the Tethering and WI-Fi P2p Group Owner mode local network agent." bug: "420590461" +} + +flag { + name: "nsd_selective_mdns_response_offload" + namespace: "com_android_tethering" + description: "A flag to control whether the selective mDNS response offload should be enabled." + bug: "410734687" } diff --git a/service-t/src/com/android/server/NsdService.java b/service-t/src/com/android/server/NsdService.java index 3b73f82687..ebd5caa901 100644 --- a/service-t/src/com/android/server/NsdService.java +++ b/service-t/src/com/android/server/NsdService.java @@ -43,6 +43,7 @@ import static com.android.server.connectivity.mdns.MdnsRecord.MAX_LABEL_LENGTH; import static com.android.server.connectivity.mdns.MdnsSearchOptions.AGGRESSIVE_QUERY_MODE; import static com.android.server.connectivity.mdns.MdnsSearchOptions.PASSIVE_QUERY_MODE; import static com.android.server.connectivity.mdns.util.MdnsUtils.Clock; +import static com.android.server.connectivity.mdns.util.MdnsUtils.createOffloadServiceInfoFromFilterReplies; import android.annotation.NonNull; import android.annotation.Nullable; @@ -116,6 +117,7 @@ import com.android.server.connectivity.mdns.MdnsServiceTypeClient.FilterRepliesI import com.android.server.connectivity.mdns.MdnsSocketProvider; import com.android.server.connectivity.mdns.OffloadCallback; import com.android.server.connectivity.mdns.util.MdnsUtils; +import com.android.tethering.mainline.beta.Flags; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -1991,6 +1993,8 @@ public class NsdService extends INsdManager.Stub { mContext, MdnsFeatureFlags.NSD_CACHE_FLUSH_PER_ADDRESS_TYPE)) .setIsIgnoreTemporaryIPv6AddressesEnabled(mDeps.isTetheringFeatureNotChickenedOut( mContext, MdnsFeatureFlags.NSD_IGNORE_TEMPORARY_IPV6_ADDRESSES)) + .setIsSelectiveMdnsResponseOffloadEnabled(mDeps.isAconfigFlagEnabled( + Flags.FLAG_NSD_SELECTIVE_MDNS_RESPONSE_OFFLOAD)) .setOverrideProvider(new MdnsFeatureFlags.FlagOverrideProvider() { @Override public boolean isForceEnabledForTest(@NonNull String flag) { @@ -2006,16 +2010,17 @@ public class NsdService extends INsdManager.Stub { } }) .build(); + final MdnsOffloadCallback offloadCallback = new MdnsOffloadCallback(); mMdnsSocketClient = new MdnsMultinetworkSocketClient(handler.getLooper(), mMdnsSocketProvider, LOGGER.forSubComponent("MdnsMultinetworkSocketClient"), mMdnsFeatureFlags); mMdnsDiscoveryManager = deps.makeMdnsDiscoveryManager(new ExecutorProvider(), mMdnsSocketClient, LOGGER.forSubComponent("MdnsDiscoveryManager"), - mMdnsFeatureFlags); + mMdnsFeatureFlags, offloadCallback); handler.post(() -> mMdnsSocketClient.setCallback(mMdnsDiscoveryManager)); mAdvertiser = deps.makeMdnsAdvertiser(handler.getLooper(), mMdnsSocketProvider, new AdvertiserCallback(), LOGGER.forSubComponent("MdnsAdvertiser"), - mMdnsFeatureFlags, mContext, new MdnsOffloadCallback()); + mMdnsFeatureFlags, mContext, offloadCallback); mClock = deps.makeClock(); } @@ -2070,6 +2075,15 @@ public class NsdService extends INsdManager.Stub { return DeviceConfigUtils.isTetheringFeatureNotChickenedOut(context, feature); } + /** Get whether a feature config is enabled. */ + public boolean isAconfigFlagEnabled(String feature) { + return switch (feature) { + case Flags.FLAG_NSD_SELECTIVE_MDNS_RESPONSE_OFFLOAD -> + Flags.nsdSelectiveMdnsResponseOffload(); + default -> throw new IllegalStateException("Unknown flag " + feature); + }; + } + /** * @see DeviceConfigUtils#getDeviceConfigPropertyInt */ @@ -2084,9 +2098,9 @@ public class NsdService extends INsdManager.Stub { public MdnsDiscoveryManager makeMdnsDiscoveryManager( @NonNull ExecutorProvider executorProvider, @NonNull MdnsMultinetworkSocketClient socketClient, @NonNull SharedLog sharedLog, - @NonNull MdnsFeatureFlags featureFlags) { + @NonNull MdnsFeatureFlags featureFlags, @NonNull OffloadCallback cb) { return new MdnsDiscoveryManager( - executorProvider, socketClient, sharedLog, featureFlags); + executorProvider, socketClient, sharedLog, featureFlags, cb); } /** @@ -2224,27 +2238,6 @@ public class NsdService extends INsdManager.Stub { } } - /** - * Creates an {@link OffloadServiceInfo} object from a {@link FilterRepliesInfo} instance. - * This method facilitates the conversion of filtering criteria into a service information - * object suitable for offloading mechanisms. - * - * @param info The {@link FilterRepliesInfo} containing the filtering criteria. - * @return A new {@link OffloadServiceInfo} instance populated with data from the - * {@code FilterRepliesInfo}. - */ - @VisibleForTesting - static OffloadServiceInfo createOffloadServiceInfoFromFilterReplies( - @NonNull FilterRepliesInfo info) { - return new OffloadServiceInfo( - new OffloadServiceInfo.Key(info.serviceName, info.serviceType), - new ArrayList<>(info.subtypes), - info.hostname, - null /* offloadPayload */, - 0 /* priority */, - OffloadEngine.OFFLOAD_TYPE_FILTER_REPLIES); - } - private void sendAllOffloadServiceInfos(@NonNull OffloadEngineInfo offloadEngineInfo) { final String targetInterface = offloadEngineInfo.mInterfaceName; final IOffloadEngine offloadEngine = offloadEngineInfo.mOffloadEngine; diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsDiscoveryManager.java b/service-t/src/com/android/server/connectivity/mdns/MdnsDiscoveryManager.java index 4ff65dc725..bc154c1f7c 100644 --- a/service-t/src/com/android/server/connectivity/mdns/MdnsDiscoveryManager.java +++ b/service-t/src/com/android/server/connectivity/mdns/MdnsDiscoveryManager.java @@ -51,6 +51,7 @@ public class MdnsDiscoveryManager implements MdnsSocketClientBase.Callback { @NonNull private final PerSocketServiceTypeClients perSocketServiceTypeClients; @NonNull private final DiscoveryExecutor discoveryExecutor; @NonNull private final MdnsFeatureFlags mdnsFeatureFlags; + @NonNull private final OffloadCallback offloadCallback; // Only accessed on the handler thread, initialized before first use @Nullable @@ -131,13 +132,15 @@ public class MdnsDiscoveryManager implements MdnsSocketClientBase.Callback { public MdnsDiscoveryManager(@NonNull ExecutorProvider executorProvider, @NonNull MdnsSocketClientBase socketClient, @NonNull SharedLog sharedLog, - @NonNull MdnsFeatureFlags mdnsFeatureFlags) { + @NonNull MdnsFeatureFlags mdnsFeatureFlags, + @NonNull OffloadCallback offloadCallback) { this.executorProvider = executorProvider; this.socketClient = socketClient; this.sharedLog = sharedLog; this.perSocketServiceTypeClients = new PerSocketServiceTypeClients(); this.mdnsFeatureFlags = mdnsFeatureFlags; this.discoveryExecutor = new DiscoveryExecutor(socketClient.getLooper(), mdnsFeatureFlags); + this.offloadCallback = offloadCallback; } /** @@ -387,7 +390,8 @@ public class MdnsDiscoveryManager implements MdnsSocketClientBase.Callback { return new MdnsServiceTypeClient( serviceType, socketClient, executorProvider.newServiceTypeClientSchedulerExecutor(), socketKey, - sharedLog.forSubComponent(tag), looper, serviceCache, mdnsFeatureFlags); + sharedLog.forSubComponent(tag), looper, serviceCache, mdnsFeatureFlags, + offloadCallback); } private List getMdnsServiceTypeClientByInterfaceName( diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsFeatureFlags.java b/service-t/src/com/android/server/connectivity/mdns/MdnsFeatureFlags.java index 5ca65ca336..b8c961a154 100644 --- a/service-t/src/com/android/server/connectivity/mdns/MdnsFeatureFlags.java +++ b/service-t/src/com/android/server/connectivity/mdns/MdnsFeatureFlags.java @@ -17,6 +17,9 @@ package com.android.server.connectivity.mdns; import android.annotation.NonNull; import android.annotation.Nullable; +import android.os.Build; + +import androidx.annotation.ChecksSdkIntAtLeast; /** * The class that contains mDNS feature flags; @@ -180,6 +183,11 @@ public class MdnsFeatureFlags { // Flag for ignoring temporary IPv6 addresses in advertising public final boolean mIsIgnoreTemporaryIPv6AddressesEnabled; + // Flag for selective mDns response offload + // This feature can only be enabled if the OffloadServiceInfo system API is available + @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.TIRAMISU) + public final boolean mIsSelectiveMdnsResponseOffloadEnabled; + // Thread stats tag for MdnsSocketClient public final int mMdnsSocketThreadStatsTag; @@ -319,6 +327,7 @@ public class MdnsFeatureFlags { boolean isCacheFlushPerAddressTypeEnabled, boolean isOptimizedExpiredServiceRemovalEnabled, boolean isIgnoreTemporaryIPv6AddressesEnabled, + boolean isSelectiveMdnsResponseOffloadEnabled, int mdnsSocketThreadStatsTag, @Nullable FlagOverrideProvider overrideProvider) { mIsMdnsOffloadFeatureEnabled = isOffloadFeatureEnabled; @@ -339,6 +348,7 @@ public class MdnsFeatureFlags { mIsOptimizedExpiredServiceRemovalEnabled = isOptimizedExpiredServiceRemovalEnabled; mMdnsSocketThreadStatsTag = mdnsSocketThreadStatsTag; mIsIgnoreTemporaryIPv6AddressesEnabled = isIgnoreTemporaryIPv6AddressesEnabled; + mIsSelectiveMdnsResponseOffloadEnabled = isSelectiveMdnsResponseOffloadEnabled; mOverrideProvider = overrideProvider; } @@ -368,6 +378,7 @@ public class MdnsFeatureFlags { private boolean mIsCacheFlushPerAddressTypeEnabled; private boolean mIsOptimizedExpiredServiceRemovalEnabled; private boolean mIsIgnoreTemporaryIPv6AddressesEnabled; + private boolean mIsSelectiveMdnsResponseOffloadEnabled; private int mMdnsSocketThreadStatsTag; private FlagOverrideProvider mOverrideProvider; @@ -392,6 +403,7 @@ public class MdnsFeatureFlags { mIsCacheFlushPerAddressTypeEnabled = true; // Default enabled. mIsOptimizedExpiredServiceRemovalEnabled = false; mIsIgnoreTemporaryIPv6AddressesEnabled = true; // Default enabled. + mIsSelectiveMdnsResponseOffloadEnabled = false; mMdnsSocketThreadStatsTag = MDNS_SOCKET_THREAD_STATS_TAG_NONE; mOverrideProvider = null; } @@ -588,6 +600,17 @@ public class MdnsFeatureFlags { return this; } + /** + * Set whether the selective mDns response offload is enabled. + * + * @see #NSD_SELECTIVE_MDNS_RESPONSE_OFFLOAD + */ + public Builder setIsSelectiveMdnsResponseOffloadEnabled( + boolean isSelectiveMdnsResponseOffloadEnabled) { + mIsSelectiveMdnsResponseOffloadEnabled = isSelectiveMdnsResponseOffloadEnabled; + return this; + } + /** * Builds a {@link MdnsFeatureFlags} with the arguments supplied to this builder. */ @@ -609,6 +632,7 @@ public class MdnsFeatureFlags { mIsCacheFlushPerAddressTypeEnabled, mIsOptimizedExpiredServiceRemovalEnabled, mIsIgnoreTemporaryIPv6AddressesEnabled, + mIsSelectiveMdnsResponseOffloadEnabled, mMdnsSocketThreadStatsTag, mOverrideProvider); } 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 6c9d25e2f1..7feaac36f1 100644 --- a/service-t/src/com/android/server/connectivity/mdns/MdnsServiceTypeClient.java +++ b/service-t/src/com/android/server/connectivity/mdns/MdnsServiceTypeClient.java @@ -23,6 +23,7 @@ import static com.android.server.connectivity.mdns.MdnsServiceCache.findMatchedR import static com.android.server.connectivity.mdns.MdnsQueryScheduler.ScheduledQueryTaskArgs; import static com.android.server.connectivity.mdns.util.MdnsUtils.Clock; import static com.android.server.connectivity.mdns.util.MdnsUtils.buildMdnsServiceInfoFromResponse; +import static com.android.server.connectivity.mdns.util.MdnsUtils.createOffloadServiceInfoFromFilterReplies; import static com.android.server.connectivity.mdns.util.MdnsUtils.responseMatchesInstanceNameAndSubtypes; import android.annotation.NonNull; @@ -97,6 +98,7 @@ public class MdnsServiceTypeClient { } }; @NonNull private final MdnsFeatureFlags featureFlags; + @NonNull private final OffloadCallback offloadCallback; private final ArrayMap listeners = new ArrayMap<>(); /** @@ -444,9 +446,10 @@ public class MdnsServiceTypeClient { @NonNull SharedLog sharedLog, @NonNull Looper looper, @NonNull MdnsServiceCache serviceCache, - @NonNull MdnsFeatureFlags featureFlags) { + @NonNull MdnsFeatureFlags featureFlags, + @NonNull OffloadCallback offloadCallback) { this(serviceType, socketClient, executor, new Clock(), socketKey, sharedLog, looper, - new Dependencies(), serviceCache, featureFlags); + new Dependencies(), serviceCache, featureFlags, offloadCallback); } @VisibleForTesting @@ -460,7 +463,8 @@ public class MdnsServiceTypeClient { @NonNull Looper looper, @NonNull Dependencies dependencies, @NonNull MdnsServiceCache serviceCache, - @NonNull MdnsFeatureFlags featureFlags) { + @NonNull MdnsFeatureFlags featureFlags, + @NonNull OffloadCallback offloadCallback) { this.serviceType = serviceType; this.socketClient = socketClient; this.executor = executor; @@ -477,6 +481,7 @@ public class MdnsServiceTypeClient { this.featureFlags = featureFlags; this.scheduler = featureFlags.isAccurateDelayCallbackEnabled() ? dependencies.createScheduler(handler) : null; + this.offloadCallback = offloadCallback; } /** @@ -535,10 +540,22 @@ public class MdnsServiceTypeClient { combinedInfo = getDiscoveryFilterRepliesInfo(); } + final FilterRepliesInfo oldInfo = offloadInfo.get(serviceName); if (combinedInfo == null) { offloadInfo.remove(serviceName); + if (featureFlags.mIsSelectiveMdnsResponseOffloadEnabled && oldInfo != null) { + offloadCallback.onOffloadStop( + socketKey.getInterfaceName(), + createOffloadServiceInfoFromFilterReplies(oldInfo)); + } } else { offloadInfo.put(serviceName, combinedInfo); + if (featureFlags.mIsSelectiveMdnsResponseOffloadEnabled + && (oldInfo == null || !oldInfo.equals(combinedInfo))) { + offloadCallback.onOffloadStartOrUpdate( + socketKey.getInterfaceName(), + createOffloadServiceInfoFromFilterReplies(combinedInfo)); + } } } diff --git a/service-t/src/com/android/server/connectivity/mdns/util/MdnsUtils.java b/service-t/src/com/android/server/connectivity/mdns/util/MdnsUtils.java index 957fe0fa81..12650e27b1 100644 --- a/service-t/src/com/android/server/connectivity/mdns/util/MdnsUtils.java +++ b/service-t/src/com/android/server/connectivity/mdns/util/MdnsUtils.java @@ -22,7 +22,10 @@ import static com.android.server.connectivity.mdns.MdnsConstants.FLAG_TRUNCATED; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresApi; import android.net.Network; +import android.net.nsd.OffloadEngine; +import android.net.nsd.OffloadServiceInfo; import android.os.Build; import android.os.SystemClock; import android.util.ArraySet; @@ -37,6 +40,7 @@ import com.android.server.connectivity.mdns.MdnsPacketWriter; import com.android.server.connectivity.mdns.MdnsRecord; import com.android.server.connectivity.mdns.MdnsResponse; import com.android.server.connectivity.mdns.MdnsServiceInfo; +import com.android.server.connectivity.mdns.MdnsServiceTypeClient; import java.io.IOException; import java.net.DatagramPacket; @@ -393,4 +397,31 @@ public class MdnsUtils { MdnsConstants.SUBTYPE_PREFIX + requiredSub, actualSub))); return matchesInstanceName && matchesSubtype; } + + /** + * Creates an {@link OffloadServiceInfo} object from a + * {@link MdnsServiceTypeClient.FilterRepliesInfo} instance. + * + * This method facilitates the conversion of filtering criteria into a service information + * object suitable for offloading mechanisms. + * + * @param info The {@link MdnsServiceTypeClient.FilterRepliesInfo} containing the filtering + * criteria. + * @return A new {@link OffloadServiceInfo} instance populated with data from the + * {@code FilterRepliesInfo}. + */ + @RequiresApi(Build.VERSION_CODES.TIRAMISU) + public static OffloadServiceInfo createOffloadServiceInfoFromFilterReplies( + @NonNull MdnsServiceTypeClient.FilterRepliesInfo info) { + return new OffloadServiceInfo( + new OffloadServiceInfo.Key(info.serviceName, info.serviceType), + new ArrayList<>(info.subtypes), + info.hostname, + null /* offloadPayload */, + // Set the priority to 0 because APF will not prioritize some services. Instead, it + // would simply allow every mDNS reply to pass through if there are too many + // offloaded services, so there is no point in setting priorities. + 0 /* priority */, + OffloadEngine.OFFLOAD_TYPE_FILTER_REPLIES); + } } \ No newline at end of file diff --git a/tests/unit/java/com/android/server/NsdServiceTest.java b/tests/unit/java/com/android/server/NsdServiceTest.java index a8116eb7d5..d19b74d1d3 100644 --- a/tests/unit/java/com/android/server/NsdServiceTest.java +++ b/tests/unit/java/com/android/server/NsdServiceTest.java @@ -46,8 +46,8 @@ import static com.android.server.NsdService.DEFAULT_RUNNING_APP_ACTIVE_IMPORTANC import static com.android.server.NsdService.MdnsListener; import static com.android.server.NsdService.NO_TRANSACTION; import static com.android.server.NsdService.checkHostname; -import static com.android.server.NsdService.createOffloadServiceInfoFromFilterReplies; import static com.android.server.NsdService.parseTypeAndSubtype; +import static com.android.server.connectivity.mdns.util.MdnsUtils.createOffloadServiceInfoFromFilterReplies; import static com.android.testutils.ContextUtils.mockService; import static libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges; @@ -249,8 +249,10 @@ public class NsdServiceTest { doReturn(true).when(mMockMDnsM).resolve( anyInt(), anyString(), anyString(), anyString(), anyInt()); doReturn(false).when(mDeps).isMdnsDiscoveryManagerEnabled(any(Context.class)); - doReturn(mDiscoveryManager).when(mDeps) - .makeMdnsDiscoveryManager(any(), any(), any(), any()); + doAnswer(inv -> { + mOffloadCallback = (OffloadCallback) inv.getArguments()[4]; + return mDiscoveryManager; + }).when(mDeps).makeMdnsDiscoveryManager(any(), any(), any(), any(), any()); doReturn(mMulticastLock).when(mWifiManager).createMulticastLock(any()); doReturn(mSocketProvider).when(mDeps).makeMdnsSocketProvider(any(), any(), any(), any()); doReturn(DEFAULT_RUNNING_APP_ACTIVE_IMPORTANCE_CUTOFF).when(mDeps).getDeviceConfigInt( @@ -2141,10 +2143,22 @@ public class NsdServiceTest { verify(offloadEngine).onOffloadServiceUpdated(discoveryInfo); } + private static void verifyOffloadServiceUpdatedAndRemoved(String interfaceName, + OffloadServiceInfo info, OffloadCallback cb, OffloadEngine offloadEngine) { + // onOffloadStartOrUpdate callback triggered. The OffloadServiceInfo update should be sent + // to the OffloadEngine. + cb.onOffloadStartOrUpdate(interfaceName, info); + verify(offloadEngine).onOffloadServiceUpdated(info); + // onOffloadStop callback triggered. The OffloadServiceInfo removal should be sent to the + // OffloadEngine. + cb.onOffloadStop(interfaceName, info); + verify(offloadEngine).onOffloadServiceRemoved(info); + } + @Test @EnableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND) @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) - public void testRegisterOffloadEngine_OffloadServiceUpdatedAndRemoved() { + public void testRegisterOffloadEngine_OffloadServiceUpdatedAndRemoved_Advertiser() { final String interfaceName = "iface"; final OffloadServiceInfo info = new OffloadServiceInfo( new OffloadServiceInfo.Key("_testService", "_testType"), List.of("_sub1", "_sub2"), @@ -2157,14 +2171,29 @@ public class NsdServiceTest { // sent to the OffloadEngine. verify(mAdvertiser).getAllInterfaceOffloadServiceInfos(interfaceName); verify(offloadEngine, never()).onOffloadServiceUpdated(any()); - // onOffloadStartOrUpdate callback triggered. The OffloadServiceInfo update should be sent - // to the OffloadEngine. - mOffloadCallback.onOffloadStartOrUpdate(interfaceName, info); - verify(offloadEngine).onOffloadServiceUpdated(info); - // onOffloadStop callback triggered. The OffloadServiceInfo removal should be sent to the - // OffloadEngine. - mOffloadCallback.onOffloadStop(interfaceName, info); - verify(offloadEngine).onOffloadServiceRemoved(info); + verifyOffloadServiceUpdatedAndRemoved( + interfaceName, info, mOffloadCallback, offloadEngine); + } + + @Test + @EnableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND) + @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + public void testRegisterOffloadEngine_OffloadServiceUpdatedAndRemoved_DiscoveryManager() { + final String interfaceName = "iface"; + final OffloadServiceInfo info = new OffloadServiceInfo( + new OffloadServiceInfo.Key("", "_testType"), List.of("_sub1", "_sub2"), + "Android.local", new byte[]{0x1, 0x2, 0x3}, 1 /* priority */, + OFFLOAD_TYPE_FILTER_REPLIES); + doReturn(Collections.emptyList()).when(mDiscoveryManager) + .notifyOffloadStart(eq(interfaceName)); + final OffloadEngine offloadEngine = registerOffloadEngine(interfaceName); + // Verify that the OffloadServiceInfo retrieves from the DiscoveryManager and that no info + // is sent to the OffloadEngine. + verify(mDiscoveryManager).notifyOffloadStart(eq(interfaceName)); + verify(offloadEngine, never()).onOffloadServiceUpdated(any()); + + verifyOffloadServiceUpdatedAndRemoved( + interfaceName, info, mOffloadCallback, offloadEngine); } private void waitForIdle() { diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsDiscoveryManagerTests.java b/tests/unit/java/com/android/server/connectivity/mdns/MdnsDiscoveryManagerTests.java index 30b3f44f8f..df9e544cc6 100644 --- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsDiscoveryManagerTests.java +++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsDiscoveryManagerTests.java @@ -100,6 +100,7 @@ public class MdnsDiscoveryManagerTests { @Mock MdnsServiceBrowserListener mockListenerTwo; @Mock SharedLog sharedLog; @Mock MdnsServiceCache mockServiceCache; + @Mock OffloadCallback mockCallback; private MdnsDiscoveryManager discoveryManager; private HandlerThread thread; private Handler handler; @@ -117,7 +118,7 @@ public class MdnsDiscoveryManagerTests { doReturn(true).when(socketClient).supportsRequestingSpecificNetworks(); createdServiceTypeClientCount = 0; discoveryManager = new MdnsDiscoveryManager(executorProvider, socketClient, - sharedLog, MdnsFeatureFlags.newBuilder().build()) { + sharedLog, MdnsFeatureFlags.newBuilder().build(), mockCallback) { @Override MdnsServiceTypeClient createServiceTypeClient(@NonNull String serviceType, @NonNull SocketKey socketKey) { @@ -157,7 +158,8 @@ public class MdnsDiscoveryManagerTests { } private MdnsDiscoveryManager makeDiscoveryManager(@NonNull MdnsFeatureFlags featureFlags) { - return new MdnsDiscoveryManager(executorProvider, socketClient, sharedLog, featureFlags) { + return new MdnsDiscoveryManager(executorProvider, socketClient, sharedLog, featureFlags, + mockCallback) { @Override MdnsServiceTypeClient createServiceTypeClient(@NonNull String serviceType, @NonNull SocketKey socketKey) { 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 79896ea29c..d5b5080e04 100644 --- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsServiceTypeClientTests.java +++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsServiceTypeClientTests.java @@ -28,6 +28,7 @@ import static com.android.server.connectivity.mdns.MdnsServiceTypeClient.EVENT_S import static com.android.server.connectivity.mdns.MdnsServiceTypeClient.NO_HOSTNAME; import static com.android.server.connectivity.mdns.MdnsServiceTypeClient.REMOVE_SERVICE_AFTER_QUERY_SENT_TIME; import static com.android.server.connectivity.mdns.MdnsServiceTypeClient.SERVICE_NAME_DISCOVERY; +import static com.android.server.connectivity.mdns.util.MdnsUtils.createOffloadServiceInfoFromFilterReplies; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; @@ -58,6 +59,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.net.InetAddresses; import android.net.Network; +import android.net.nsd.OffloadServiceInfo; import android.os.Build; import android.os.Handler; import android.os.HandlerThread; @@ -138,6 +140,8 @@ public class MdnsServiceTypeClientTests { private MdnsServiceTypeClient.Dependencies mockDeps; @Mock private Scheduler mockScheduler; + @Mock + private OffloadCallback mockCallback; @Captor private ArgumentCaptor serviceInfoCaptor; @@ -277,7 +281,7 @@ public class MdnsServiceTypeClientTests { private MdnsServiceTypeClient makeMdnsServiceTypeClient(MdnsFeatureFlags featureFlags) { return new MdnsServiceTypeClient(SERVICE_TYPE, mockSocketClient, currentThreadExecutor, mockDecoderClock, socketKey, mockSharedLog, thread.getLooper(), mockDeps, - serviceCache, featureFlags); + serviceCache, featureFlags, mockCallback); } @After @@ -2357,7 +2361,7 @@ public class MdnsServiceTypeClientTests { final FilterRepliesInfo resolveInfo = new FilterRepliesInfo( instanceName, SERVICE_TYPE, List.of(), "hostname"); final FilterRepliesInfo discoverInfo = new FilterRepliesInfo( - SERVICE_NAME_DISCOVERY, SERVICE_TYPE, List.of(subtype), "" /* hostname */); + SERVICE_NAME_DISCOVERY, SERVICE_TYPE, List.of(subtype), NO_HOSTNAME); assertTrue(offloadInfo.containsAll(Set.of(resolveInfo, discoverInfo))); // Stop the resolution listener @@ -2423,6 +2427,47 @@ public class MdnsServiceTypeClientTests { SERVICE_NAME_DISCOVERY, SERVICE_TYPE, List.of(subtype2), NO_HOSTNAME))); } + @Test + public void testOffloadServiceInfoUpdate() { + final MdnsFeatureFlags flags = MdnsFeatureFlags.newBuilder() + .setIsSelectiveMdnsResponseOffloadEnabled(true).build(); + client = makeMdnsServiceTypeClient(flags); + final String instanceName = "instance1"; + final String subtype = "subtype"; + final MdnsSearchOptions resolveOptions = MdnsSearchOptions.newBuilder() + .setResolveInstanceName(instanceName).build(); + final MdnsSearchOptions discoverOptions = MdnsSearchOptions.newBuilder() + .addSubtype(subtype).build(); + // Register two listener, one is for service resolution and one is for service discovery. + startSendAndReceive(mockListenerOne, resolveOptions); + startSendAndReceive(mockListenerTwo, discoverOptions); + + final OffloadServiceInfo resolveInfo1 = createOffloadServiceInfoFromFilterReplies( + new FilterRepliesInfo( + instanceName, SERVICE_TYPE, List.of(), NO_HOSTNAME)); + verify(mockCallback).onOffloadStartOrUpdate(socketKey.getInterfaceName(), resolveInfo1); + final OffloadServiceInfo discoverInfo = createOffloadServiceInfoFromFilterReplies( + new FilterRepliesInfo( + SERVICE_NAME_DISCOVERY, SERVICE_TYPE, List.of(subtype), NO_HOSTNAME)); + verify(mockCallback).onOffloadStartOrUpdate(socketKey.getInterfaceName(), discoverInfo); + + // Get a service response + processResponse(createResponse(instanceName, "192.0.2.0", 5353, + MdnsUtils.constructFullSubtype(SERVICE_TYPE_LABELS, SUBTYPE), + Collections.emptyMap() /* textAttributes */, TEST_TTL), socketKey); + + final OffloadServiceInfo resolveInfo2 = createOffloadServiceInfoFromFilterReplies( + new FilterRepliesInfo( + instanceName, SERVICE_TYPE, List.of(), "hostname")); + verify(mockCallback).onOffloadStartOrUpdate(socketKey.getInterfaceName(), resolveInfo2); + + stopSendAndReceive(mockListenerOne); + verify(mockCallback).onOffloadStop(socketKey.getInterfaceName(), resolveInfo2); + + stopSendAndReceive(mockListenerTwo); + verify(mockCallback).onOffloadStop(socketKey.getInterfaceName(), discoverInfo); + } + private static MdnsServiceInfo matchServiceName(String name) { return argThat(info -> info.getServiceInstanceName().equals(name)); }