1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server; 18 19 import static android.Manifest.permission.DEVICE_POWER; 20 import static android.Manifest.permission.NETWORK_SETTINGS; 21 import static android.Manifest.permission.NETWORK_STACK; 22 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED; 23 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND; 24 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE; 25 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE; 26 import static android.content.pm.PackageManager.PERMISSION_DENIED; 27 import static android.content.pm.PackageManager.PERMISSION_GRANTED; 28 import static android.net.InetAddresses.parseNumericAddress; 29 import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET; 30 import static android.net.NetworkCapabilities.TRANSPORT_VPN; 31 import static android.net.NetworkCapabilities.TRANSPORT_WIFI; 32 import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK; 33 import static android.net.connectivity.ConnectivityCompatChanges.ENABLE_PLATFORM_MDNS_BACKEND; 34 import static android.net.connectivity.ConnectivityCompatChanges.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER; 35 import static android.net.nsd.NsdManager.FAILURE_BAD_PARAMETERS; 36 import static android.net.nsd.NsdManager.FAILURE_INTERNAL_ERROR; 37 import static android.net.nsd.NsdManager.FAILURE_MAX_LIMIT; 38 import static android.net.nsd.NsdManager.FAILURE_OPERATION_NOT_RUNNING; 39 40 import static com.android.networkstack.apishim.api33.ConstantsShim.REGISTER_NSD_OFFLOAD_ENGINE; 41 import static com.android.server.NsdService.DEFAULT_RUNNING_APP_ACTIVE_IMPORTANCE_CUTOFF; 42 import static com.android.server.NsdService.MdnsListener; 43 import static com.android.server.NsdService.NO_TRANSACTION; 44 import static com.android.server.NsdService.checkHostname; 45 import static com.android.server.NsdService.parseTypeAndSubtype; 46 import static com.android.testutils.ContextUtils.mockService; 47 48 import static libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges; 49 import static libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges; 50 51 import static org.junit.Assert.assertArrayEquals; 52 import static org.junit.Assert.assertEquals; 53 import static org.junit.Assert.assertNotNull; 54 import static org.junit.Assert.assertNull; 55 import static org.junit.Assert.assertThrows; 56 import static org.junit.Assert.assertTrue; 57 import static org.junit.Assert.assertFalse; 58 import static org.mockito.ArgumentMatchers.anyInt; 59 import static org.mockito.ArgumentMatchers.anyString; 60 import static org.mockito.ArgumentMatchers.argThat; 61 import static org.mockito.Mockito.any; 62 import static org.mockito.Mockito.doCallRealMethod; 63 import static org.mockito.Mockito.doReturn; 64 import static org.mockito.Mockito.eq; 65 import static org.mockito.Mockito.inOrder; 66 import static org.mockito.Mockito.mock; 67 import static org.mockito.Mockito.never; 68 import static org.mockito.Mockito.reset; 69 import static org.mockito.Mockito.timeout; 70 import static org.mockito.Mockito.times; 71 import static org.mockito.Mockito.verify; 72 import static org.mockito.Mockito.verifyNoMoreInteractions; 73 import static org.mockito.Mockito.when; 74 75 import android.app.ActivityManager; 76 import android.app.ActivityManager.OnUidImportanceListener; 77 import android.compat.testing.PlatformCompatChangeRule; 78 import android.content.ContentResolver; 79 import android.content.Context; 80 import android.content.pm.PackageManager; 81 import android.net.INetd; 82 import android.net.Network; 83 import android.net.mdns.aidl.DiscoveryInfo; 84 import android.net.mdns.aidl.GetAddressInfo; 85 import android.net.mdns.aidl.IMDnsEventListener; 86 import android.net.mdns.aidl.RegistrationInfo; 87 import android.net.mdns.aidl.ResolutionInfo; 88 import android.net.nsd.AdvertisingRequest; 89 import android.net.nsd.INsdManagerCallback; 90 import android.net.nsd.INsdServiceConnector; 91 import android.net.nsd.MDnsManager; 92 import android.net.nsd.NsdManager; 93 import android.net.nsd.NsdManager.DiscoveryListener; 94 import android.net.nsd.NsdManager.RegistrationListener; 95 import android.net.nsd.NsdManager.ResolveListener; 96 import android.net.nsd.NsdManager.ServiceInfoCallback; 97 import android.net.nsd.NsdServiceInfo; 98 import android.net.nsd.OffloadEngine; 99 import android.net.wifi.WifiManager; 100 import android.os.Binder; 101 import android.os.Build; 102 import android.os.Handler; 103 import android.os.HandlerThread; 104 import android.os.IBinder; 105 import android.os.Looper; 106 import android.os.Message; 107 import android.os.Process; 108 import android.os.RemoteException; 109 import android.util.Pair; 110 111 import androidx.annotation.NonNull; 112 import androidx.test.filters.SmallTest; 113 114 import com.android.metrics.NetworkNsdReportedMetrics; 115 import com.android.server.NsdService.Dependencies; 116 import com.android.server.connectivity.mdns.MdnsAdvertiser; 117 import com.android.server.connectivity.mdns.MdnsAdvertisingOptions; 118 import com.android.server.connectivity.mdns.MdnsDiscoveryManager; 119 import com.android.server.connectivity.mdns.MdnsInterfaceSocket; 120 import com.android.server.connectivity.mdns.MdnsSearchOptions; 121 import com.android.server.connectivity.mdns.MdnsServiceBrowserListener; 122 import com.android.server.connectivity.mdns.MdnsServiceInfo; 123 import com.android.server.connectivity.mdns.MdnsSocketProvider; 124 import com.android.server.connectivity.mdns.MdnsSocketProvider.SocketRequestMonitor; 125 import com.android.server.connectivity.mdns.util.MdnsUtils; 126 import com.android.testutils.DevSdkIgnoreRule; 127 import com.android.testutils.DevSdkIgnoreRunner; 128 import com.android.testutils.HandlerUtils; 129 130 import org.junit.After; 131 import org.junit.Before; 132 import org.junit.Rule; 133 import org.junit.Test; 134 import org.junit.rules.TestRule; 135 import org.junit.runner.RunWith; 136 import org.mockito.AdditionalAnswers; 137 import org.mockito.ArgumentCaptor; 138 import org.mockito.InOrder; 139 import org.mockito.Mock; 140 import org.mockito.Mockito; 141 import org.mockito.MockitoAnnotations; 142 143 import java.net.InetAddress; 144 import java.net.UnknownHostException; 145 import java.time.Duration; 146 import java.time.Instant; 147 import java.util.ArrayList; 148 import java.util.Collections; 149 import java.util.LinkedList; 150 import java.util.List; 151 import java.util.Objects; 152 import java.util.Queue; 153 import java.util.Set; 154 155 // TODOs: 156 // - test client can send requests and receive replies 157 // - test NSD_ON ENABLE/DISABLED listening 158 @DevSdkIgnoreRunner.MonitorThreadLeak 159 @RunWith(DevSdkIgnoreRunner.class) 160 @SmallTest 161 @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2) 162 public class NsdServiceTest { 163 @Rule 164 public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule(); 165 166 static final int PROTOCOL = NsdManager.PROTOCOL_DNS_SD; 167 private static final long CLEANUP_DELAY_MS = 500; 168 private static final long TIMEOUT_MS = 500; 169 private static final long TEST_TIME_MS = 123L; 170 private static final String SERVICE_NAME = "a_name"; 171 private static final String SERVICE_TYPE = "_test._tcp"; 172 private static final String SERVICE_FULL_NAME = SERVICE_NAME + "." + SERVICE_TYPE; 173 private static final String DOMAIN_NAME = "mytestdevice.local"; 174 private static final int PORT = 2201; 175 private static final int IFACE_IDX_ANY = 0; 176 private static final String IPV4_ADDRESS = "192.0.2.0"; 177 private static final String IPV6_ADDRESS = "2001:db8::"; 178 179 // Records INsdManagerCallback created when NsdService#connect is called. 180 // Only accessed on the test thread, since NsdService#connect is called by the NsdManager 181 // constructor called on the test thread. 182 private final Queue<INsdManagerCallback> mCreatedCallbacks = new LinkedList<>(); 183 184 @Rule 185 public TestRule compatChangeRule = new PlatformCompatChangeRule(); 186 @Rule 187 public TestRule ignoreRule = new DevSdkIgnoreRule(); 188 @Mock Context mContext; 189 @Mock PackageManager mPackageManager; 190 @Mock ContentResolver mResolver; 191 @Mock MDnsManager mMockMDnsM; 192 @Mock Dependencies mDeps; 193 @Mock MdnsDiscoveryManager mDiscoveryManager; 194 @Mock MdnsAdvertiser mAdvertiser; 195 @Mock MdnsSocketProvider mSocketProvider; 196 @Mock WifiManager mWifiManager; 197 @Mock WifiManager.MulticastLock mMulticastLock; 198 @Mock ActivityManager mActivityManager; 199 @Mock NetworkNsdReportedMetrics mMetrics; 200 @Mock MdnsUtils.Clock mClock; 201 SocketRequestMonitor mSocketRequestMonitor; 202 OnUidImportanceListener mUidImportanceListener; 203 HandlerThread mThread; 204 TestHandler mHandler; 205 NsdService mService; 206 207 private static class LinkToDeathRecorder extends Binder { 208 IBinder.DeathRecipient mDr; 209 210 @Override linkToDeath(@onNull DeathRecipient recipient, int flags)211 public void linkToDeath(@NonNull DeathRecipient recipient, int flags) { 212 super.linkToDeath(recipient, flags); 213 mDr = recipient; 214 } 215 } 216 217 @Before setUp()218 public void setUp() throws Exception { 219 MockitoAnnotations.initMocks(this); 220 mThread = new HandlerThread("mock-service-handler"); 221 mThread.start(); 222 mHandler = new TestHandler(mThread.getLooper()); 223 when(mContext.getContentResolver()).thenReturn(mResolver); 224 mockService(mContext, MDnsManager.class, MDnsManager.MDNS_SERVICE, mMockMDnsM); 225 mockService(mContext, WifiManager.class, Context.WIFI_SERVICE, mWifiManager); 226 mockService(mContext, ActivityManager.class, Context.ACTIVITY_SERVICE, mActivityManager); 227 doReturn(mPackageManager).when(mContext).getPackageManager(); 228 if (mContext.getSystemService(MDnsManager.class) == null) { 229 // Test is using mockito-extended 230 doCallRealMethod().when(mContext).getSystemService(MDnsManager.class); 231 doCallRealMethod().when(mContext).getSystemService(WifiManager.class); 232 doCallRealMethod().when(mContext).getSystemService(ActivityManager.class); 233 } 234 doReturn(true).when(mMockMDnsM).registerService( 235 anyInt(), anyString(), anyString(), anyInt(), any(), anyInt()); 236 doReturn(true).when(mMockMDnsM).stopOperation(anyInt()); 237 doReturn(true).when(mMockMDnsM).discover(anyInt(), anyString(), anyInt()); 238 doReturn(true).when(mMockMDnsM).resolve( 239 anyInt(), anyString(), anyString(), anyString(), anyInt()); 240 doReturn(false).when(mDeps).isMdnsDiscoveryManagerEnabled(any(Context.class)); 241 doReturn(mDiscoveryManager).when(mDeps) 242 .makeMdnsDiscoveryManager(any(), any(), any(), any()); 243 doReturn(mMulticastLock).when(mWifiManager).createMulticastLock(any()); 244 doReturn(mSocketProvider).when(mDeps).makeMdnsSocketProvider(any(), any(), any(), any()); 245 doReturn(DEFAULT_RUNNING_APP_ACTIVE_IMPORTANCE_CUTOFF).when(mDeps).getDeviceConfigInt( 246 eq(NsdService.MDNS_CONFIG_RUNNING_APP_ACTIVE_IMPORTANCE_CUTOFF), anyInt()); 247 doReturn(mAdvertiser).when(mDeps).makeMdnsAdvertiser(any(), any(), any(), any(), any(), 248 any()); 249 doReturn(mMetrics).when(mDeps).makeNetworkNsdReportedMetrics(anyInt()); 250 doReturn(mClock).when(mDeps).makeClock(); 251 doReturn(TEST_TIME_MS).when(mClock).elapsedRealtime(); 252 mService = makeService(); 253 final ArgumentCaptor<SocketRequestMonitor> cbMonitorCaptor = 254 ArgumentCaptor.forClass(SocketRequestMonitor.class); 255 verify(mDeps).makeMdnsSocketProvider(any(), any(), any(), cbMonitorCaptor.capture()); 256 mSocketRequestMonitor = cbMonitorCaptor.getValue(); 257 258 final ArgumentCaptor<OnUidImportanceListener> uidListenerCaptor = 259 ArgumentCaptor.forClass(OnUidImportanceListener.class); 260 verify(mActivityManager).addOnUidImportanceListener(uidListenerCaptor.capture(), anyInt()); 261 mUidImportanceListener = uidListenerCaptor.getValue(); 262 } 263 264 @After tearDown()265 public void tearDown() throws Exception { 266 if (mThread != null) { 267 mThread.quitSafely(); 268 mThread.join(); 269 } 270 271 // Clear inline mocks as there are possible memory leaks if not done (see mockito 272 // doc for clearInlineMocks), and some tests create many of them. 273 Mockito.framework().clearInlineMocks(); 274 } 275 276 // Native mdns provided by Netd is removed after U. 277 @DevSdkIgnoreRule.IgnoreAfter(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 278 @Test 279 @DisableCompatChanges({ 280 RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER, 281 ENABLE_PLATFORM_MDNS_BACKEND}) testPreSClients()282 public void testPreSClients() throws Exception { 283 // Pre S client connected, the daemon should be started. 284 connectClient(mService); 285 final INsdManagerCallback cb1 = getCallback(); 286 final IBinder.DeathRecipient deathRecipient1 = verifyLinkToDeath(cb1); 287 verify(mMockMDnsM, times(1)).registerEventListener(any()); 288 verify(mMockMDnsM, times(1)).startDaemon(); 289 290 connectClient(mService); 291 final INsdManagerCallback cb2 = getCallback(); 292 final IBinder.DeathRecipient deathRecipient2 = verifyLinkToDeath(cb2); 293 // Daemon has been started, it should not try to start it again. 294 verify(mMockMDnsM, times(1)).registerEventListener(any()); 295 verify(mMockMDnsM, times(1)).startDaemon(); 296 297 deathRecipient1.binderDied(); 298 // Still 1 client remains, daemon shouldn't be stopped. 299 waitForIdle(); 300 verify(mMockMDnsM, never()).stopDaemon(); 301 302 deathRecipient2.binderDied(); 303 // All clients are disconnected, the daemon should be stopped. 304 verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS); 305 } 306 307 @Test 308 @EnableCompatChanges(RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER) 309 @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND) 310 @DevSdkIgnoreRule.IgnoreAfter(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) testNoDaemonStartedWhenClientsConnect()311 public void testNoDaemonStartedWhenClientsConnect() throws Exception { 312 // Creating an NsdManager will not cause daemon startup. 313 connectClient(mService); 314 verify(mMockMDnsM, never()).registerEventListener(any()); 315 verify(mMockMDnsM, never()).startDaemon(); 316 final INsdManagerCallback cb1 = getCallback(); 317 final IBinder.DeathRecipient deathRecipient1 = verifyLinkToDeath(cb1); 318 319 // Creating another NsdManager will not cause daemon startup either. 320 connectClient(mService); 321 verify(mMockMDnsM, never()).registerEventListener(any()); 322 verify(mMockMDnsM, never()).startDaemon(); 323 final INsdManagerCallback cb2 = getCallback(); 324 final IBinder.DeathRecipient deathRecipient2 = verifyLinkToDeath(cb2); 325 326 // If there is no active request, try to clean up the daemon but should not do it because 327 // daemon has not been started. 328 deathRecipient1.binderDied(); 329 verify(mMockMDnsM, never()).unregisterEventListener(any()); 330 verify(mMockMDnsM, never()).stopDaemon(); 331 deathRecipient2.binderDied(); 332 verify(mMockMDnsM, never()).unregisterEventListener(any()); 333 verify(mMockMDnsM, never()).stopDaemon(); 334 } 335 verifyLinkToDeath(INsdManagerCallback cb)336 private IBinder.DeathRecipient verifyLinkToDeath(INsdManagerCallback cb) 337 throws Exception { 338 final IBinder.DeathRecipient dr = ((LinkToDeathRecorder) cb.asBinder()).mDr; 339 assertNotNull(dr); 340 return dr; 341 } 342 343 @Test 344 @EnableCompatChanges(RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER) 345 @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND) 346 @DevSdkIgnoreRule.IgnoreAfter(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) testClientRequestsAreGCedAtDisconnection()347 public void testClientRequestsAreGCedAtDisconnection() throws Exception { 348 final NsdManager client = connectClient(mService); 349 final INsdManagerCallback cb1 = getCallback(); 350 final IBinder.DeathRecipient deathRecipient = verifyLinkToDeath(cb1); 351 verify(mMockMDnsM, never()).registerEventListener(any()); 352 verify(mMockMDnsM, never()).startDaemon(); 353 354 final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE); 355 request.setPort(PORT); 356 357 // Client registration request 358 final RegistrationListener listener1 = mock(RegistrationListener.class); 359 client.registerService(request, PROTOCOL, listener1); 360 waitForIdle(); 361 verify(mMockMDnsM).registerEventListener(any()); 362 verify(mMockMDnsM).startDaemon(); 363 verify(mMockMDnsM).registerService( 364 eq(2), eq(SERVICE_NAME), eq(SERVICE_TYPE), eq(PORT), any(), eq(IFACE_IDX_ANY)); 365 366 // Client discovery request 367 final DiscoveryListener listener2 = mock(DiscoveryListener.class); 368 client.discoverServices(SERVICE_TYPE, PROTOCOL, listener2); 369 waitForIdle(); 370 verify(mMockMDnsM).discover(3 /* id */, SERVICE_TYPE, IFACE_IDX_ANY); 371 372 // Client resolve request 373 final ResolveListener listener3 = mock(ResolveListener.class); 374 client.resolveService(request, listener3); 375 waitForIdle(); 376 verify(mMockMDnsM).resolve( 377 4 /* id */, SERVICE_NAME, SERVICE_TYPE, "local." /* domain */, IFACE_IDX_ANY); 378 379 // Client disconnects, stop the daemon after CLEANUP_DELAY_MS. 380 deathRecipient.binderDied(); 381 verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS); 382 // checks that request are cleaned 383 verify(mMockMDnsM).stopOperation(2 /* id */); 384 verify(mMockMDnsM).stopOperation(3 /* id */); 385 verify(mMockMDnsM).stopOperation(4 /* id */); 386 } 387 388 @Test 389 @EnableCompatChanges(RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER) 390 @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND) 391 @DevSdkIgnoreRule.IgnoreAfter(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) testCleanupDelayNoRequestActive()392 public void testCleanupDelayNoRequestActive() throws Exception { 393 final NsdManager client = connectClient(mService); 394 395 final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE); 396 request.setPort(PORT); 397 final RegistrationListener listener1 = mock(RegistrationListener.class); 398 client.registerService(request, PROTOCOL, listener1); 399 waitForIdle(); 400 verify(mMockMDnsM).registerEventListener(any()); 401 verify(mMockMDnsM).startDaemon(); 402 final INsdManagerCallback cb1 = getCallback(); 403 final IBinder.DeathRecipient deathRecipient = verifyLinkToDeath(cb1); 404 verify(mMockMDnsM).registerService( 405 eq(2), eq(SERVICE_NAME), eq(SERVICE_TYPE), eq(PORT), any(), eq(IFACE_IDX_ANY)); 406 407 client.unregisterService(listener1); 408 waitForIdle(); 409 verify(mMockMDnsM).stopOperation(2 /* id */); 410 411 verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS); 412 reset(mMockMDnsM); 413 deathRecipient.binderDied(); 414 // Client disconnects, daemon should not be stopped after CLEANUP_DELAY_MS. 415 verify(mMockMDnsM, never()).unregisterEventListener(any()); 416 verify(mMockMDnsM, never()).stopDaemon(); 417 } 418 getEventListener()419 private IMDnsEventListener getEventListener() { 420 final ArgumentCaptor<IMDnsEventListener> listenerCaptor = 421 ArgumentCaptor.forClass(IMDnsEventListener.class); 422 verify(mMockMDnsM).registerEventListener(listenerCaptor.capture()); 423 return listenerCaptor.getValue(); 424 } 425 426 @Test 427 @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND) 428 @DevSdkIgnoreRule.IgnoreAfter(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) testDiscoverOnTetheringDownstream()429 public void testDiscoverOnTetheringDownstream() throws Exception { 430 final NsdManager client = connectClient(mService); 431 final int interfaceIdx = 123; 432 final DiscoveryListener discListener = mock(DiscoveryListener.class); 433 client.discoverServices(SERVICE_TYPE, PROTOCOL, discListener); 434 waitForIdle(); 435 436 final IMDnsEventListener eventListener = getEventListener(); 437 final ArgumentCaptor<Integer> discIdCaptor = ArgumentCaptor.forClass(Integer.class); 438 verify(mMockMDnsM).discover(discIdCaptor.capture(), eq(SERVICE_TYPE), 439 eq(0) /* interfaceIdx */); 440 // NsdManager uses a separate HandlerThread to dispatch callbacks (on ServiceHandler), so 441 // this needs to use a timeout 442 verify(discListener, timeout(TIMEOUT_MS)).onDiscoveryStarted(SERVICE_TYPE); 443 final int discId = discIdCaptor.getValue(); 444 verify(mMetrics).reportServiceDiscoveryStarted(true /* isLegacy */, discId); 445 446 final DiscoveryInfo discoveryInfo = new DiscoveryInfo( 447 discId, 448 IMDnsEventListener.SERVICE_FOUND, 449 SERVICE_NAME, 450 SERVICE_TYPE, 451 DOMAIN_NAME, 452 interfaceIdx, 453 INetd.LOCAL_NET_ID); // LOCAL_NET_ID (99) used on tethering downstreams 454 eventListener.onServiceDiscoveryStatus(discoveryInfo); 455 waitForIdle(); 456 457 final ArgumentCaptor<NsdServiceInfo> discoveredInfoCaptor = 458 ArgumentCaptor.forClass(NsdServiceInfo.class); 459 verify(discListener, timeout(TIMEOUT_MS)).onServiceFound(discoveredInfoCaptor.capture()); 460 final NsdServiceInfo foundInfo = discoveredInfoCaptor.getValue(); 461 assertEquals(SERVICE_NAME, foundInfo.getServiceName()); 462 assertEquals(SERVICE_TYPE, foundInfo.getServiceType()); 463 assertNull(foundInfo.getHost()); 464 assertNull(foundInfo.getNetwork()); 465 assertEquals(interfaceIdx, foundInfo.getInterfaceIndex()); 466 467 // After discovering the service, verify resolving it 468 final ResolveListener resolveListener = mock(ResolveListener.class); 469 client.resolveService(foundInfo, resolveListener); 470 waitForIdle(); 471 472 final ArgumentCaptor<Integer> resolvIdCaptor = ArgumentCaptor.forClass(Integer.class); 473 verify(mMockMDnsM).resolve(resolvIdCaptor.capture(), eq(SERVICE_NAME), eq(SERVICE_TYPE), 474 eq("local.") /* domain */, eq(interfaceIdx)); 475 476 final int servicePort = 10123; 477 final ResolutionInfo resolutionInfo = new ResolutionInfo( 478 resolvIdCaptor.getValue(), 479 IMDnsEventListener.SERVICE_RESOLVED, 480 null /* serviceName */, 481 null /* serviceType */, 482 null /* domain */, 483 SERVICE_FULL_NAME, 484 DOMAIN_NAME, 485 servicePort, 486 new byte[0] /* txtRecord */, 487 interfaceIdx); 488 489 doReturn(true).when(mMockMDnsM).getServiceAddress(anyInt(), any(), anyInt()); 490 eventListener.onServiceResolutionStatus(resolutionInfo); 491 waitForIdle(); 492 493 final ArgumentCaptor<Integer> getAddrIdCaptor = ArgumentCaptor.forClass(Integer.class); 494 verify(mMockMDnsM).getServiceAddress(getAddrIdCaptor.capture(), eq(DOMAIN_NAME), 495 eq(interfaceIdx)); 496 497 final String serviceAddress = "192.0.2.123"; 498 final int getAddrId = getAddrIdCaptor.getValue(); 499 final GetAddressInfo addressInfo = new GetAddressInfo( 500 getAddrId, 501 IMDnsEventListener.SERVICE_GET_ADDR_SUCCESS, 502 SERVICE_FULL_NAME, 503 serviceAddress, 504 interfaceIdx, 505 INetd.LOCAL_NET_ID); 506 doReturn(TEST_TIME_MS + 10L).when(mClock).elapsedRealtime(); 507 eventListener.onGettingServiceAddressStatus(addressInfo); 508 waitForIdle(); 509 510 final ArgumentCaptor<NsdServiceInfo> resInfoCaptor = 511 ArgumentCaptor.forClass(NsdServiceInfo.class); 512 verify(resolveListener, timeout(TIMEOUT_MS)).onServiceResolved(resInfoCaptor.capture()); 513 verify(mMetrics).reportServiceResolved(true /* isLegacy */, getAddrId, 10L /* durationMs */, 514 false /* isServiceFromCache */, 0 /* sentQueryCount */); 515 516 final NsdServiceInfo resolvedService = resInfoCaptor.getValue(); 517 assertEquals(SERVICE_NAME, resolvedService.getServiceName()); 518 assertEquals("." + SERVICE_TYPE, resolvedService.getServiceType()); 519 assertEquals(parseNumericAddress(serviceAddress), resolvedService.getHost()); 520 assertEquals(servicePort, resolvedService.getPort()); 521 assertNull(resolvedService.getNetwork()); 522 assertEquals(interfaceIdx, resolvedService.getInterfaceIndex()); 523 } 524 525 @Test 526 @EnableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND) testDiscoverOnTetheringDownstream_DiscoveryManager()527 public void testDiscoverOnTetheringDownstream_DiscoveryManager() throws Exception { 528 final NsdManager client = connectClient(mService); 529 final DiscoveryListener discListener = mock(DiscoveryListener.class); 530 client.discoverServices(SERVICE_TYPE, PROTOCOL, discListener); 531 waitForIdle(); 532 533 final ArgumentCaptor<MdnsServiceBrowserListener> discoverListenerCaptor = 534 ArgumentCaptor.forClass(MdnsServiceBrowserListener.class); 535 final InOrder discManagerOrder = inOrder(mDiscoveryManager); 536 final String serviceTypeWithLocalDomain = SERVICE_TYPE + ".local"; 537 discManagerOrder.verify(mDiscoveryManager).registerListener(eq(serviceTypeWithLocalDomain), 538 discoverListenerCaptor.capture(), any()); 539 540 final int interfaceIdx = 123; 541 final MdnsServiceInfo mockServiceInfo = new MdnsServiceInfo( 542 SERVICE_NAME, /* serviceInstanceName */ 543 serviceTypeWithLocalDomain.split("\\."), /* serviceType */ 544 List.of(), /* subtypes */ 545 new String[] {"android", "local"}, /* hostName */ 546 12345, /* port */ 547 List.of(IPV4_ADDRESS), 548 List.of(IPV6_ADDRESS), 549 List.of(), /* textStrings */ 550 List.of(), /* textEntries */ 551 interfaceIdx, /* interfaceIndex */ 552 null /* network */, 553 Instant.MAX /* expirationTime */); 554 555 // Verify service is found with the interface index 556 discoverListenerCaptor.getValue().onServiceNameDiscovered( 557 mockServiceInfo, false /* isServiceFromCache */); 558 final ArgumentCaptor<NsdServiceInfo> foundInfoCaptor = 559 ArgumentCaptor.forClass(NsdServiceInfo.class); 560 verify(discListener, timeout(TIMEOUT_MS)).onServiceFound(foundInfoCaptor.capture()); 561 final NsdServiceInfo foundInfo = foundInfoCaptor.getValue(); 562 assertNull(foundInfo.getNetwork()); 563 assertEquals(interfaceIdx, foundInfo.getInterfaceIndex()); 564 565 // Using the returned service info to resolve or register callback uses the interface index 566 client.resolveService(foundInfo, mock(ResolveListener.class)); 567 client.registerServiceInfoCallback(foundInfo, Runnable::run, 568 mock(ServiceInfoCallback.class)); 569 waitForIdle(); 570 571 discManagerOrder.verify(mDiscoveryManager, times(2)).registerListener(any(), any(), argThat( 572 o -> o.getNetwork() == null && o.getInterfaceIndex() == interfaceIdx)); 573 } 574 575 @Test 576 @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND) 577 @DevSdkIgnoreRule.IgnoreAfter(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) testDiscoverOnBlackholeNetwork()578 public void testDiscoverOnBlackholeNetwork() throws Exception { 579 final NsdManager client = connectClient(mService); 580 final DiscoveryListener discListener = mock(DiscoveryListener.class); 581 client.discoverServices(SERVICE_TYPE, PROTOCOL, discListener); 582 waitForIdle(); 583 584 final IMDnsEventListener eventListener = getEventListener(); 585 final ArgumentCaptor<Integer> discIdCaptor = ArgumentCaptor.forClass(Integer.class); 586 verify(mMockMDnsM).discover(discIdCaptor.capture(), eq(SERVICE_TYPE), 587 eq(0) /* interfaceIdx */); 588 // NsdManager uses a separate HandlerThread to dispatch callbacks (on ServiceHandler), so 589 // this needs to use a timeout 590 verify(discListener, timeout(TIMEOUT_MS)).onDiscoveryStarted(SERVICE_TYPE); 591 final int discId = discIdCaptor.getValue(); 592 verify(mMetrics).reportServiceDiscoveryStarted(true /* isLegacy */, discId); 593 594 final DiscoveryInfo discoveryInfo = new DiscoveryInfo( 595 discId, 596 IMDnsEventListener.SERVICE_FOUND, 597 SERVICE_NAME, 598 SERVICE_TYPE, 599 DOMAIN_NAME, 600 123 /* interfaceIdx */, 601 INetd.DUMMY_NET_ID); // netId of the blackhole network 602 eventListener.onServiceDiscoveryStatus(discoveryInfo); 603 waitForIdle(); 604 605 verify(discListener, never()).onServiceFound(any()); 606 } 607 608 @Test 609 @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND) 610 @DevSdkIgnoreRule.IgnoreAfter(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) testServiceRegistrationSuccessfulAndFailed()611 public void testServiceRegistrationSuccessfulAndFailed() throws Exception { 612 final NsdManager client = connectClient(mService); 613 final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE); 614 request.setPort(PORT); 615 final RegistrationListener regListener = mock(RegistrationListener.class); 616 client.registerService(request, PROTOCOL, regListener); 617 waitForIdle(); 618 619 final IMDnsEventListener eventListener = getEventListener(); 620 final ArgumentCaptor<Integer> regIdCaptor = ArgumentCaptor.forClass(Integer.class); 621 verify(mMockMDnsM).registerService(regIdCaptor.capture(), 622 eq(SERVICE_NAME), eq(SERVICE_TYPE), eq(PORT), any(), eq(IFACE_IDX_ANY)); 623 624 // Register service successfully. 625 final int regId = regIdCaptor.getValue(); 626 final RegistrationInfo registrationInfo = new RegistrationInfo( 627 regId, 628 IMDnsEventListener.SERVICE_REGISTERED, 629 SERVICE_NAME, 630 SERVICE_TYPE, 631 PORT, 632 new byte[0] /* txtRecord */, 633 IFACE_IDX_ANY); 634 doReturn(TEST_TIME_MS + 10L).when(mClock).elapsedRealtime(); 635 eventListener.onServiceRegistrationStatus(registrationInfo); 636 637 final ArgumentCaptor<NsdServiceInfo> registeredInfoCaptor = 638 ArgumentCaptor.forClass(NsdServiceInfo.class); 639 verify(regListener, timeout(TIMEOUT_MS)) 640 .onServiceRegistered(registeredInfoCaptor.capture()); 641 final NsdServiceInfo registeredInfo = registeredInfoCaptor.getValue(); 642 assertEquals(SERVICE_NAME, registeredInfo.getServiceName()); 643 verify(mMetrics).reportServiceRegistrationSucceeded( 644 true /* isLegacy */, regId, 10L /* durationMs */); 645 646 // Fail to register service. 647 final RegistrationInfo registrationFailedInfo = new RegistrationInfo( 648 regId, 649 IMDnsEventListener.SERVICE_REGISTRATION_FAILED, 650 null /* serviceName */, 651 null /* registrationType */, 652 0 /* port */, 653 new byte[0] /* txtRecord */, 654 IFACE_IDX_ANY); 655 doReturn(TEST_TIME_MS + 20L).when(mClock).elapsedRealtime(); 656 eventListener.onServiceRegistrationStatus(registrationFailedInfo); 657 verify(regListener, timeout(TIMEOUT_MS)) 658 .onRegistrationFailed(any(), eq(FAILURE_INTERNAL_ERROR)); 659 verify(mMetrics).reportServiceRegistrationFailed( 660 true /* isLegacy */, regId, 20L /* durationMs */); 661 } 662 663 @Test 664 @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND) 665 @DevSdkIgnoreRule.IgnoreAfter(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) testServiceDiscoveryFailed()666 public void testServiceDiscoveryFailed() throws Exception { 667 final NsdManager client = connectClient(mService); 668 final DiscoveryListener discListener = mock(DiscoveryListener.class); 669 client.discoverServices(SERVICE_TYPE, PROTOCOL, discListener); 670 waitForIdle(); 671 672 final IMDnsEventListener eventListener = getEventListener(); 673 final ArgumentCaptor<Integer> discIdCaptor = ArgumentCaptor.forClass(Integer.class); 674 verify(mMockMDnsM).discover(discIdCaptor.capture(), eq(SERVICE_TYPE), eq(IFACE_IDX_ANY)); 675 verify(discListener, timeout(TIMEOUT_MS)).onDiscoveryStarted(SERVICE_TYPE); 676 final int discId = discIdCaptor.getValue(); 677 verify(mMetrics).reportServiceDiscoveryStarted(true /* isLegacy */, discId); 678 679 // Fail to discover service. 680 final DiscoveryInfo discoveryFailedInfo = new DiscoveryInfo( 681 discId, 682 IMDnsEventListener.SERVICE_DISCOVERY_FAILED, 683 null /* serviceName */, 684 null /* registrationType */, 685 null /* domainName */, 686 IFACE_IDX_ANY, 687 0 /* netId */); 688 doReturn(TEST_TIME_MS + 10L).when(mClock).elapsedRealtime(); 689 eventListener.onServiceDiscoveryStatus(discoveryFailedInfo); 690 verify(discListener, timeout(TIMEOUT_MS)) 691 .onStartDiscoveryFailed(SERVICE_TYPE, FAILURE_INTERNAL_ERROR); 692 verify(mMetrics).reportServiceDiscoveryFailed( 693 true /* isLegacy */, discId, 10L /* durationMs */); 694 } 695 696 @Test 697 @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND) 698 @DevSdkIgnoreRule.IgnoreAfter(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) testServiceResolutionFailed()699 public void testServiceResolutionFailed() throws Exception { 700 final NsdManager client = connectClient(mService); 701 final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE); 702 final ResolveListener resolveListener = mock(ResolveListener.class); 703 client.resolveService(request, resolveListener); 704 waitForIdle(); 705 706 final IMDnsEventListener eventListener = getEventListener(); 707 final ArgumentCaptor<Integer> resolvIdCaptor = ArgumentCaptor.forClass(Integer.class); 708 verify(mMockMDnsM).resolve(resolvIdCaptor.capture(), eq(SERVICE_NAME), eq(SERVICE_TYPE), 709 eq("local.") /* domain */, eq(IFACE_IDX_ANY)); 710 711 // Fail to resolve service. 712 final int resolvId = resolvIdCaptor.getValue(); 713 final ResolutionInfo resolutionFailedInfo = new ResolutionInfo( 714 resolvId, 715 IMDnsEventListener.SERVICE_RESOLUTION_FAILED, 716 null /* serviceName */, 717 null /* serviceType */, 718 null /* domain */, 719 null /* serviceFullName */, 720 null /* domainName */, 721 0 /* port */, 722 new byte[0] /* txtRecord */, 723 IFACE_IDX_ANY); 724 doReturn(TEST_TIME_MS + 10L).when(mClock).elapsedRealtime(); 725 eventListener.onServiceResolutionStatus(resolutionFailedInfo); 726 verify(resolveListener, timeout(TIMEOUT_MS)) 727 .onResolveFailed(any(), eq(FAILURE_INTERNAL_ERROR)); 728 verify(mMetrics).reportServiceResolutionFailed( 729 true /* isLegacy */, resolvId, 10L /* durationMs */); 730 } 731 732 @Test 733 @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND) 734 @DevSdkIgnoreRule.IgnoreAfter(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) testGettingAddressFailed()735 public void testGettingAddressFailed() throws Exception { 736 final NsdManager client = connectClient(mService); 737 final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE); 738 final ResolveListener resolveListener = mock(ResolveListener.class); 739 client.resolveService(request, resolveListener); 740 waitForIdle(); 741 742 final IMDnsEventListener eventListener = getEventListener(); 743 final ArgumentCaptor<Integer> resolvIdCaptor = ArgumentCaptor.forClass(Integer.class); 744 verify(mMockMDnsM).resolve(resolvIdCaptor.capture(), eq(SERVICE_NAME), eq(SERVICE_TYPE), 745 eq("local.") /* domain */, eq(IFACE_IDX_ANY)); 746 747 // Resolve service successfully. 748 final ResolutionInfo resolutionInfo = new ResolutionInfo( 749 resolvIdCaptor.getValue(), 750 IMDnsEventListener.SERVICE_RESOLVED, 751 null /* serviceName */, 752 null /* serviceType */, 753 null /* domain */, 754 SERVICE_FULL_NAME, 755 DOMAIN_NAME, 756 PORT, 757 new byte[0] /* txtRecord */, 758 IFACE_IDX_ANY); 759 doReturn(true).when(mMockMDnsM).getServiceAddress(anyInt(), any(), anyInt()); 760 eventListener.onServiceResolutionStatus(resolutionInfo); 761 waitForIdle(); 762 763 final ArgumentCaptor<Integer> getAddrIdCaptor = ArgumentCaptor.forClass(Integer.class); 764 verify(mMockMDnsM).getServiceAddress(getAddrIdCaptor.capture(), eq(DOMAIN_NAME), 765 eq(IFACE_IDX_ANY)); 766 767 // Fail to get service address. 768 final int getAddrId = getAddrIdCaptor.getValue(); 769 final GetAddressInfo gettingAddrFailedInfo = new GetAddressInfo( 770 getAddrId, 771 IMDnsEventListener.SERVICE_GET_ADDR_FAILED, 772 null /* hostname */, 773 null /* address */, 774 IFACE_IDX_ANY, 775 0 /* netId */); 776 doReturn(TEST_TIME_MS + 10L).when(mClock).elapsedRealtime(); 777 eventListener.onGettingServiceAddressStatus(gettingAddrFailedInfo); 778 verify(resolveListener, timeout(TIMEOUT_MS)) 779 .onResolveFailed(any(), eq(FAILURE_INTERNAL_ERROR)); 780 verify(mMetrics).reportServiceResolutionFailed( 781 true /* isLegacy */, getAddrId, 10L /* durationMs */); 782 } 783 784 @EnableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND) 785 @Test testPerClientListenerLimit()786 public void testPerClientListenerLimit() throws Exception { 787 final NsdManager client1 = connectClient(mService); 788 final NsdManager client2 = connectClient(mService); 789 790 final String testType1 = "_testtype1._tcp"; 791 final NsdServiceInfo testServiceInfo1 = new NsdServiceInfo("MyTestService1", testType1); 792 testServiceInfo1.setPort(12345); 793 final String testType2 = "_testtype2._tcp"; 794 final NsdServiceInfo testServiceInfo2 = new NsdServiceInfo("MyTestService2", testType2); 795 testServiceInfo2.setPort(12345); 796 797 // Each client can register 200 requests (for example 100 discover and 100 register). 798 final int numEachListener = 100; 799 final ArrayList<DiscoveryListener> discListeners = new ArrayList<>(numEachListener); 800 final ArrayList<RegistrationListener> regListeners = new ArrayList<>(numEachListener); 801 for (int i = 0; i < numEachListener; i++) { 802 final DiscoveryListener discListener1 = mock(DiscoveryListener.class); 803 discListeners.add(discListener1); 804 final RegistrationListener regListener1 = mock(RegistrationListener.class); 805 regListeners.add(regListener1); 806 final DiscoveryListener discListener2 = mock(DiscoveryListener.class); 807 discListeners.add(discListener2); 808 final RegistrationListener regListener2 = mock(RegistrationListener.class); 809 regListeners.add(regListener2); 810 client1.discoverServices(testType1, NsdManager.PROTOCOL_DNS_SD, 811 (Network) null, Runnable::run, discListener1); 812 client1.registerService(testServiceInfo1, NsdManager.PROTOCOL_DNS_SD, Runnable::run, 813 regListener1); 814 815 client2.registerService(testServiceInfo2, NsdManager.PROTOCOL_DNS_SD, Runnable::run, 816 regListener2); 817 client2.discoverServices(testType2, NsdManager.PROTOCOL_DNS_SD, 818 (Network) null, Runnable::run, discListener2); 819 } 820 821 // Use a longer timeout than usual for the handler to process all the events. The 822 // registrations take about 1s on a high-end 2013 device. 823 HandlerUtils.waitForIdle(mHandler, 30_000L); 824 for (int i = 0; i < discListeners.size(); i++) { 825 // Callbacks are sent on the manager handler which is different from mHandler, so use 826 // a short timeout (each callback should come quickly after the previous one). 827 verify(discListeners.get(i), timeout(TEST_TIME_MS)) 828 .onDiscoveryStarted(i % 2 == 0 ? testType1 : testType2); 829 830 // registerService does not get a callback before probing finishes (will not happen as 831 // this is mocked) 832 verifyNoMoreInteractions(regListeners.get(i)); 833 } 834 835 // The next registrations should fail 836 final DiscoveryListener failDiscListener1 = mock(DiscoveryListener.class); 837 final RegistrationListener failRegListener1 = mock(RegistrationListener.class); 838 final DiscoveryListener failDiscListener2 = mock(DiscoveryListener.class); 839 final RegistrationListener failRegListener2 = mock(RegistrationListener.class); 840 841 client1.discoverServices(testType1, NsdManager.PROTOCOL_DNS_SD, 842 (Network) null, Runnable::run, failDiscListener1); 843 verify(failDiscListener1, timeout(TEST_TIME_MS)) 844 .onStartDiscoveryFailed(testType1, FAILURE_MAX_LIMIT); 845 846 client1.registerService(testServiceInfo1, NsdManager.PROTOCOL_DNS_SD, Runnable::run, 847 failRegListener1); 848 verify(failRegListener1, timeout(TEST_TIME_MS)).onRegistrationFailed( 849 argThat(a -> testServiceInfo1.getServiceName().equals(a.getServiceName())), 850 eq(FAILURE_MAX_LIMIT)); 851 852 client1.discoverServices(testType2, NsdManager.PROTOCOL_DNS_SD, 853 (Network) null, Runnable::run, failDiscListener2); 854 verify(failDiscListener2, timeout(TEST_TIME_MS)) 855 .onStartDiscoveryFailed(testType2, FAILURE_MAX_LIMIT); 856 857 client1.registerService(testServiceInfo2, NsdManager.PROTOCOL_DNS_SD, Runnable::run, 858 failRegListener2); 859 verify(failRegListener2, timeout(TEST_TIME_MS)).onRegistrationFailed( 860 argThat(a -> testServiceInfo2.getServiceName().equals(a.getServiceName())), 861 eq(FAILURE_MAX_LIMIT)); 862 } 863 864 @Test 865 @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND) 866 @DevSdkIgnoreRule.IgnoreAfter(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) testNoCrashWhenProcessResolutionAfterBinderDied()867 public void testNoCrashWhenProcessResolutionAfterBinderDied() throws Exception { 868 final NsdManager client = connectClient(mService); 869 final INsdManagerCallback cb = getCallback(); 870 final IBinder.DeathRecipient deathRecipient = verifyLinkToDeath(cb); 871 deathRecipient.binderDied(); 872 873 final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE); 874 final ResolveListener resolveListener = mock(ResolveListener.class); 875 client.resolveService(request, resolveListener); 876 waitForIdle(); 877 878 verify(mMockMDnsM, never()).registerEventListener(any()); 879 verify(mMockMDnsM, never()).startDaemon(); 880 verify(mMockMDnsM, never()).resolve(anyInt() /* id */, anyString() /* serviceName */, 881 anyString() /* registrationType */, anyString() /* domain */, 882 anyInt()/* interfaceIdx */); 883 } 884 885 @Test 886 @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND) 887 @DevSdkIgnoreRule.IgnoreAfter(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) testStopServiceResolution()888 public void testStopServiceResolution() { 889 final NsdManager client = connectClient(mService); 890 final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE); 891 final ResolveListener resolveListener = mock(ResolveListener.class); 892 client.resolveService(request, resolveListener); 893 waitForIdle(); 894 895 final ArgumentCaptor<Integer> resolvIdCaptor = ArgumentCaptor.forClass(Integer.class); 896 verify(mMockMDnsM).resolve(resolvIdCaptor.capture(), eq(SERVICE_NAME), eq(SERVICE_TYPE), 897 eq("local.") /* domain */, eq(IFACE_IDX_ANY)); 898 899 final int resolveId = resolvIdCaptor.getValue(); 900 doReturn(TEST_TIME_MS + 10L).when(mClock).elapsedRealtime(); 901 client.stopServiceResolution(resolveListener); 902 waitForIdle(); 903 904 verify(mMockMDnsM).stopOperation(resolveId); 905 verify(resolveListener, timeout(TIMEOUT_MS)).onResolutionStopped(argThat(ns -> 906 request.getServiceName().equals(ns.getServiceName()) 907 && request.getServiceType().equals(ns.getServiceType()))); 908 verify(mMetrics).reportServiceResolutionStop( 909 true /* isLegacy */, resolveId, 10L /* durationMs */, 0 /* sentQueryCount */); 910 } 911 912 @Test 913 @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND) 914 @DevSdkIgnoreRule.IgnoreAfter(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) testStopResolutionFailed()915 public void testStopResolutionFailed() { 916 final NsdManager client = connectClient(mService); 917 final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE); 918 final ResolveListener resolveListener = mock(ResolveListener.class); 919 client.resolveService(request, resolveListener); 920 waitForIdle(); 921 922 final ArgumentCaptor<Integer> resolvIdCaptor = ArgumentCaptor.forClass(Integer.class); 923 verify(mMockMDnsM).resolve(resolvIdCaptor.capture(), eq(SERVICE_NAME), eq(SERVICE_TYPE), 924 eq("local.") /* domain */, eq(IFACE_IDX_ANY)); 925 926 final int resolveId = resolvIdCaptor.getValue(); 927 doReturn(false).when(mMockMDnsM).stopOperation(anyInt()); 928 client.stopServiceResolution(resolveListener); 929 waitForIdle(); 930 931 verify(mMockMDnsM).stopOperation(resolveId); 932 verify(resolveListener, timeout(TIMEOUT_MS)).onStopResolutionFailed(argThat(ns -> 933 request.getServiceName().equals(ns.getServiceName()) 934 && request.getServiceType().equals(ns.getServiceType())), 935 eq(FAILURE_OPERATION_NOT_RUNNING)); 936 } 937 938 @Test @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.TIRAMISU) 939 @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND) 940 @DevSdkIgnoreRule.IgnoreAfter(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) testStopResolutionDuringGettingAddress()941 public void testStopResolutionDuringGettingAddress() throws RemoteException { 942 final NsdManager client = connectClient(mService); 943 final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE); 944 final ResolveListener resolveListener = mock(ResolveListener.class); 945 client.resolveService(request, resolveListener); 946 waitForIdle(); 947 948 final IMDnsEventListener eventListener = getEventListener(); 949 final ArgumentCaptor<Integer> resolvIdCaptor = ArgumentCaptor.forClass(Integer.class); 950 verify(mMockMDnsM).resolve(resolvIdCaptor.capture(), eq(SERVICE_NAME), eq(SERVICE_TYPE), 951 eq("local.") /* domain */, eq(IFACE_IDX_ANY)); 952 953 // Resolve service successfully. 954 final ResolutionInfo resolutionInfo = new ResolutionInfo( 955 resolvIdCaptor.getValue(), 956 IMDnsEventListener.SERVICE_RESOLVED, 957 null /* serviceName */, 958 null /* serviceType */, 959 null /* domain */, 960 SERVICE_FULL_NAME, 961 DOMAIN_NAME, 962 PORT, 963 new byte[0] /* txtRecord */, 964 IFACE_IDX_ANY); 965 doReturn(true).when(mMockMDnsM).getServiceAddress(anyInt(), any(), anyInt()); 966 eventListener.onServiceResolutionStatus(resolutionInfo); 967 waitForIdle(); 968 969 final ArgumentCaptor<Integer> getAddrIdCaptor = ArgumentCaptor.forClass(Integer.class); 970 verify(mMockMDnsM).getServiceAddress(getAddrIdCaptor.capture(), eq(DOMAIN_NAME), 971 eq(IFACE_IDX_ANY)); 972 973 final int getAddrId = getAddrIdCaptor.getValue(); 974 doReturn(TEST_TIME_MS + 10L).when(mClock).elapsedRealtime(); 975 client.stopServiceResolution(resolveListener); 976 waitForIdle(); 977 978 verify(mMockMDnsM).stopOperation(getAddrId); 979 verify(resolveListener, timeout(TIMEOUT_MS)).onResolutionStopped(argThat(ns -> 980 request.getServiceName().equals(ns.getServiceName()) 981 && request.getServiceType().equals(ns.getServiceType()))); 982 verify(mMetrics).reportServiceResolutionStop( 983 true /* isLegacy */, getAddrId, 10L /* durationMs */, 0 /* sentQueryCount */); 984 } 985 verifyUpdatedServiceInfo(NsdServiceInfo info, String serviceName, String serviceType, List<InetAddress> address, int port, int interfaceIndex, Network network)986 private void verifyUpdatedServiceInfo(NsdServiceInfo info, String serviceName, 987 String serviceType, List<InetAddress> address, int port, int interfaceIndex, 988 Network network) { 989 assertEquals(serviceName, info.getServiceName()); 990 assertEquals(serviceType, info.getServiceType()); 991 assertEquals(address, info.getHostAddresses()); 992 assertEquals(port, info.getPort()); 993 assertEquals(network, info.getNetwork()); 994 assertEquals(interfaceIndex, info.getInterfaceIndex()); 995 } 996 997 @Test testRegisterAndUnregisterServiceInfoCallback()998 public void testRegisterAndUnregisterServiceInfoCallback() { 999 final NsdManager client = connectClient(mService); 1000 final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE); 1001 final ServiceInfoCallback serviceInfoCallback = mock( 1002 ServiceInfoCallback.class); 1003 final String serviceTypeWithLocalDomain = SERVICE_TYPE + ".local"; 1004 final Network network = new Network(999); 1005 request.setNetwork(network); 1006 client.registerServiceInfoCallback(request, Runnable::run, serviceInfoCallback); 1007 waitForIdle(); 1008 // Verify the registration callback start. 1009 final ArgumentCaptor<MdnsListener> listenerCaptor = 1010 ArgumentCaptor.forClass(MdnsListener.class); 1011 verify(mSocketProvider).startMonitoringSockets(); 1012 verify(mDiscoveryManager).registerListener(eq(serviceTypeWithLocalDomain), 1013 listenerCaptor.capture(), argThat(options -> network.equals(options.getNetwork()))); 1014 1015 final MdnsListener listener = listenerCaptor.getValue(); 1016 final int servInfoId = listener.mTransactionId; 1017 // Verify the service info callback registered. 1018 verify(mMetrics).reportServiceInfoCallbackRegistered(servInfoId); 1019 1020 final MdnsServiceInfo mdnsServiceInfo = new MdnsServiceInfo( 1021 SERVICE_NAME, 1022 serviceTypeWithLocalDomain.split("\\."), 1023 List.of(), /* subtypes */ 1024 new String[]{"android", "local"}, /* hostName */ 1025 PORT, 1026 List.of(IPV4_ADDRESS), 1027 List.of(IPV6_ADDRESS), 1028 List.of() /* textStrings */, 1029 List.of() /* textEntries */, 1030 1234, 1031 network, 1032 Instant.MAX /* expirationTime */); 1033 1034 // Callbacks for query sent. 1035 listener.onDiscoveryQuerySent(Collections.emptyList(), 1 /* transactionId */); 1036 1037 // Verify onServiceFound callback 1038 listener.onServiceFound(mdnsServiceInfo, true /* isServiceFromCache */); 1039 final ArgumentCaptor<NsdServiceInfo> updateInfoCaptor = 1040 ArgumentCaptor.forClass(NsdServiceInfo.class); 1041 verify(serviceInfoCallback, timeout(TIMEOUT_MS).times(1)) 1042 .onServiceUpdated(updateInfoCaptor.capture()); 1043 verifyUpdatedServiceInfo(updateInfoCaptor.getAllValues().get(0) /* info */, SERVICE_NAME, 1044 SERVICE_TYPE, 1045 List.of(parseNumericAddress(IPV4_ADDRESS), parseNumericAddress(IPV6_ADDRESS)), 1046 PORT, IFACE_IDX_ANY, new Network(999)); 1047 1048 // Service addresses changed. 1049 final String v4Address = "192.0.2.1"; 1050 final String v6Address = "2001:db8::1"; 1051 final MdnsServiceInfo updatedServiceInfo = new MdnsServiceInfo( 1052 SERVICE_NAME, 1053 serviceTypeWithLocalDomain.split("\\."), 1054 List.of(), /* subtypes */ 1055 new String[]{"android", "local"}, /* hostName */ 1056 PORT, 1057 List.of(v4Address), 1058 List.of(v6Address), 1059 List.of() /* textStrings */, 1060 List.of() /* textEntries */, 1061 1234, 1062 network, 1063 Instant.MAX /* expirationTime */); 1064 1065 // Verify onServiceUpdated callback. 1066 listener.onServiceUpdated(updatedServiceInfo); 1067 verify(serviceInfoCallback, timeout(TIMEOUT_MS).times(2)) 1068 .onServiceUpdated(updateInfoCaptor.capture()); 1069 verifyUpdatedServiceInfo(updateInfoCaptor.getAllValues().get(2) /* info */, SERVICE_NAME, 1070 SERVICE_TYPE, 1071 List.of(parseNumericAddress(v4Address), parseNumericAddress(v6Address)), 1072 PORT, IFACE_IDX_ANY, new Network(999)); 1073 1074 // Service lost then recovered. 1075 listener.onServiceRemoved(updatedServiceInfo); 1076 listener.onServiceFound(updatedServiceInfo, false /* isServiceFromCache */); 1077 1078 // Verify service callback unregistration. 1079 doReturn(TEST_TIME_MS + 10L).when(mClock).elapsedRealtime(); 1080 client.unregisterServiceInfoCallback(serviceInfoCallback); 1081 waitForIdle(); 1082 verify(serviceInfoCallback, timeout(TIMEOUT_MS)).onServiceInfoCallbackUnregistered(); 1083 verify(mMetrics).reportServiceInfoCallbackUnregistered(servInfoId, 10L /* durationMs */, 1084 3 /* updateCallbackCount */, 1 /* lostCallbackCount */, 1085 true /* isServiceFromCache */, 1 /* sentQueryCount */); 1086 } 1087 1088 @Test testRegisterServiceCallbackFailed()1089 public void testRegisterServiceCallbackFailed() { 1090 final NsdManager client = connectClient(mService); 1091 final String invalidServiceType = "a_service"; 1092 final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, invalidServiceType); 1093 final ServiceInfoCallback serviceInfoCallback = mock( 1094 ServiceInfoCallback.class); 1095 client.registerServiceInfoCallback(request, Runnable::run, serviceInfoCallback); 1096 waitForIdle(); 1097 1098 // Fail to register service callback. 1099 verify(serviceInfoCallback, timeout(TIMEOUT_MS)) 1100 .onServiceInfoCallbackRegistrationFailed(eq(FAILURE_BAD_PARAMETERS)); 1101 verify(mMetrics).reportServiceInfoCallbackRegistrationFailed(NO_TRANSACTION); 1102 } 1103 1104 @Test testUnregisterNotRegisteredCallback()1105 public void testUnregisterNotRegisteredCallback() { 1106 final NsdManager client = connectClient(mService); 1107 final ServiceInfoCallback serviceInfoCallback = mock( 1108 ServiceInfoCallback.class); 1109 1110 assertThrows(IllegalArgumentException.class, () -> 1111 client.unregisterServiceInfoCallback(serviceInfoCallback)); 1112 } 1113 setMdnsDiscoveryManagerEnabled()1114 private void setMdnsDiscoveryManagerEnabled() { 1115 doReturn(true).when(mDeps).isMdnsDiscoveryManagerEnabled(any(Context.class)); 1116 } 1117 setMdnsAdvertiserEnabled()1118 private void setMdnsAdvertiserEnabled() { 1119 doReturn(true).when(mDeps).isMdnsAdvertiserEnabled(any(Context.class)); 1120 } 1121 1122 @Test 1123 @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND) 1124 @DevSdkIgnoreRule.IgnoreAfter(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) testMdnsDiscoveryManagerFeature()1125 public void testMdnsDiscoveryManagerFeature() { 1126 // Create NsdService w/o feature enabled. 1127 final NsdManager client = connectClient(mService); 1128 final DiscoveryListener discListenerWithoutFeature = mock(DiscoveryListener.class); 1129 client.discoverServices(SERVICE_TYPE, PROTOCOL, discListenerWithoutFeature); 1130 waitForIdle(); 1131 1132 final ArgumentCaptor<Integer> legacyIdCaptor = ArgumentCaptor.forClass(Integer.class); 1133 verify(mMockMDnsM).discover(legacyIdCaptor.capture(), any(), anyInt()); 1134 verifyNoMoreInteractions(mDiscoveryManager); 1135 1136 setMdnsDiscoveryManagerEnabled(); 1137 final DiscoveryListener discListenerWithFeature = mock(DiscoveryListener.class); 1138 client.discoverServices(SERVICE_TYPE, PROTOCOL, discListenerWithFeature); 1139 waitForIdle(); 1140 1141 final String serviceTypeWithLocalDomain = SERVICE_TYPE + ".local"; 1142 final ArgumentCaptor<MdnsServiceBrowserListener> listenerCaptor = 1143 ArgumentCaptor.forClass(MdnsServiceBrowserListener.class); 1144 verify(mDiscoveryManager).registerListener(eq(serviceTypeWithLocalDomain), 1145 listenerCaptor.capture(), any()); 1146 1147 client.stopServiceDiscovery(discListenerWithoutFeature); 1148 waitForIdle(); 1149 verify(mMockMDnsM).stopOperation(legacyIdCaptor.getValue()); 1150 1151 client.stopServiceDiscovery(discListenerWithFeature); 1152 waitForIdle(); 1153 verify(mDiscoveryManager).unregisterListener(serviceTypeWithLocalDomain, 1154 listenerCaptor.getValue()); 1155 } 1156 1157 @Test testDiscoveryWithMdnsDiscoveryManager()1158 public void testDiscoveryWithMdnsDiscoveryManager() { 1159 setMdnsDiscoveryManagerEnabled(); 1160 1161 final NsdManager client = connectClient(mService); 1162 final DiscoveryListener discListener = mock(DiscoveryListener.class); 1163 final Network network = new Network(999); 1164 final String serviceTypeWithLocalDomain = SERVICE_TYPE + ".local"; 1165 // Verify the discovery start / stop. 1166 final ArgumentCaptor<MdnsListener> listenerCaptor = 1167 ArgumentCaptor.forClass(MdnsListener.class); 1168 client.discoverServices(SERVICE_TYPE, PROTOCOL, network, r -> r.run(), discListener); 1169 waitForIdle(); 1170 verify(mSocketProvider).startMonitoringSockets(); 1171 verify(mDiscoveryManager).registerListener(eq(serviceTypeWithLocalDomain), 1172 listenerCaptor.capture(), argThat(options -> network.equals(options.getNetwork()))); 1173 verify(discListener, timeout(TIMEOUT_MS)).onDiscoveryStarted(SERVICE_TYPE); 1174 1175 final MdnsListener listener = listenerCaptor.getValue(); 1176 final int discId = listener.mTransactionId; 1177 verify(mMetrics).reportServiceDiscoveryStarted(false /* isLegacy */, discId); 1178 1179 // Callbacks for query sent. 1180 listener.onDiscoveryQuerySent(Collections.emptyList(), 1 /* transactionId */); 1181 listener.onDiscoveryQuerySent(Collections.emptyList(), 2 /* transactionId */); 1182 listener.onDiscoveryQuerySent(Collections.emptyList(), 3 /* transactionId */); 1183 1184 final MdnsServiceInfo foundInfo = new MdnsServiceInfo( 1185 SERVICE_NAME, /* serviceInstanceName */ 1186 serviceTypeWithLocalDomain.split("\\."), /* serviceType */ 1187 List.of(), /* subtypes */ 1188 new String[] {"android", "local"}, /* hostName */ 1189 12345, /* port */ 1190 List.of(IPV4_ADDRESS), 1191 List.of(IPV6_ADDRESS), 1192 List.of(), /* textStrings */ 1193 List.of(), /* textEntries */ 1194 1234, /* interfaceIndex */ 1195 network, 1196 Instant.MAX /* expirationTime */); 1197 1198 // Verify onServiceNameDiscovered callback 1199 listener.onServiceNameDiscovered(foundInfo, true /* isServiceFromCache */); 1200 verify(discListener, timeout(TIMEOUT_MS)).onServiceFound(argThat(info -> 1201 info.getServiceName().equals(SERVICE_NAME) 1202 // Service type in discovery callbacks has a dot at the end 1203 && info.getServiceType().equals(SERVICE_TYPE + ".") 1204 && info.getNetwork().equals(network))); 1205 1206 final MdnsServiceInfo removedInfo = new MdnsServiceInfo( 1207 SERVICE_NAME, /* serviceInstanceName */ 1208 serviceTypeWithLocalDomain.split("\\."), /* serviceType */ 1209 null, /* subtypes */ 1210 null, /* hostName */ 1211 0, /* port */ 1212 List.of(), /* ipv4Address */ 1213 List.of(), /* ipv6Address */ 1214 null, /* textStrings */ 1215 null, /* textEntries */ 1216 1234, /* interfaceIndex */ 1217 network, 1218 Instant.MAX /* expirationTime */); 1219 // Verify onServiceNameRemoved callback 1220 listener.onServiceNameRemoved(removedInfo); 1221 verify(discListener, timeout(TIMEOUT_MS)).onServiceLost(argThat(info -> 1222 info.getServiceName().equals(SERVICE_NAME) 1223 // Service type in discovery callbacks has a dot at the end 1224 && info.getServiceType().equals(SERVICE_TYPE + ".") 1225 && info.getNetwork().equals(network))); 1226 1227 doReturn(TEST_TIME_MS + 10L).when(mClock).elapsedRealtime(); 1228 client.stopServiceDiscovery(discListener); 1229 waitForIdle(); 1230 verify(mDiscoveryManager).unregisterListener(eq(serviceTypeWithLocalDomain), any()); 1231 verify(discListener, timeout(TIMEOUT_MS)).onDiscoveryStopped(SERVICE_TYPE); 1232 verify(mSocketProvider, timeout(CLEANUP_DELAY_MS + TIMEOUT_MS)).requestStopWhenInactive(); 1233 verify(mMetrics).reportServiceDiscoveryStop(false /* isLegacy */, discId, 1234 10L /* durationMs */, 1 /* foundCallbackCount */, 1 /* lostCallbackCount */, 1235 1 /* servicesCount */, 3 /* sentQueryCount */, true /* isServiceFromCache */); 1236 } 1237 1238 @Test testDiscoveryWithMdnsDiscoveryManager_FailedWithInvalidServiceType()1239 public void testDiscoveryWithMdnsDiscoveryManager_FailedWithInvalidServiceType() { 1240 setMdnsDiscoveryManagerEnabled(); 1241 1242 final NsdManager client = connectClient(mService); 1243 final DiscoveryListener discListener = mock(DiscoveryListener.class); 1244 final Network network = new Network(999); 1245 final String invalidServiceType = "a_service"; 1246 client.discoverServices( 1247 invalidServiceType, PROTOCOL, network, r -> r.run(), discListener); 1248 waitForIdle(); 1249 verify(discListener, timeout(TIMEOUT_MS)) 1250 .onStartDiscoveryFailed(invalidServiceType, FAILURE_INTERNAL_ERROR); 1251 verify(mMetrics, times(1)).reportServiceDiscoveryFailed( 1252 false /* isLegacy */, NO_TRANSACTION, 0L /* durationMs */); 1253 1254 final String serviceTypeWithLocalDomain = SERVICE_TYPE + ".local"; 1255 client.discoverServices( 1256 serviceTypeWithLocalDomain, PROTOCOL, network, r -> r.run(), discListener); 1257 waitForIdle(); 1258 verify(discListener, timeout(TIMEOUT_MS)) 1259 .onStartDiscoveryFailed(serviceTypeWithLocalDomain, FAILURE_INTERNAL_ERROR); 1260 verify(mMetrics, times(2)).reportServiceDiscoveryFailed( 1261 false /* isLegacy */, NO_TRANSACTION, 0L /* durationMs */); 1262 1263 final String serviceTypeWithoutTcpOrUdpEnding = "_test._com"; 1264 client.discoverServices( 1265 serviceTypeWithoutTcpOrUdpEnding, PROTOCOL, network, r -> r.run(), discListener); 1266 waitForIdle(); 1267 verify(discListener, timeout(TIMEOUT_MS)) 1268 .onStartDiscoveryFailed(serviceTypeWithoutTcpOrUdpEnding, FAILURE_INTERNAL_ERROR); 1269 verify(mMetrics, times(3)).reportServiceDiscoveryFailed( 1270 false /* isLegacy */, NO_TRANSACTION, 0L /* durationMs */); 1271 } 1272 1273 @Test 1274 @EnableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND) testDiscoveryWithMdnsDiscoveryManager_UsesSubtypes()1275 public void testDiscoveryWithMdnsDiscoveryManager_UsesSubtypes() { 1276 final String typeWithSubtype = SERVICE_TYPE + ",_subtype"; 1277 final NsdManager client = connectClient(mService); 1278 final NsdServiceInfo regInfo = new NsdServiceInfo("Instance", typeWithSubtype); 1279 final Network network = new Network(999); 1280 regInfo.setHostAddresses(List.of(parseNumericAddress("192.0.2.123"))); 1281 regInfo.setPort(12345); 1282 regInfo.setNetwork(network); 1283 1284 final RegistrationListener regListener = mock(RegistrationListener.class); 1285 client.registerService(regInfo, NsdManager.PROTOCOL_DNS_SD, Runnable::run, regListener); 1286 waitForIdle(); 1287 verify(mAdvertiser).addOrUpdateService(anyInt(), argThat(s -> 1288 "Instance".equals(s.getServiceName()) 1289 && SERVICE_TYPE.equals(s.getServiceType()) 1290 && s.getSubtypes().equals(Set.of("_subtype"))), any(), anyInt()); 1291 1292 final DiscoveryListener discListener = mock(DiscoveryListener.class); 1293 client.discoverServices(typeWithSubtype, PROTOCOL, network, Runnable::run, discListener); 1294 waitForIdle(); 1295 final ArgumentCaptor<MdnsSearchOptions> optionsCaptor = 1296 ArgumentCaptor.forClass(MdnsSearchOptions.class); 1297 verify(mDiscoveryManager).registerListener(eq(SERVICE_TYPE + ".local"), any(), 1298 optionsCaptor.capture()); 1299 assertEquals(Collections.singletonList("subtype"), optionsCaptor.getValue().getSubtypes()); 1300 } 1301 1302 @Test testResolutionWithMdnsDiscoveryManager()1303 public void testResolutionWithMdnsDiscoveryManager() throws UnknownHostException { 1304 setMdnsDiscoveryManagerEnabled(); 1305 1306 final NsdManager client = connectClient(mService); 1307 final ResolveListener resolveListener = mock(ResolveListener.class); 1308 final Network network = new Network(999); 1309 final String serviceType = "_nsd._service._tcp"; 1310 final String constructedServiceType = "_service._tcp.local"; 1311 final ArgumentCaptor<MdnsListener> listenerCaptor = 1312 ArgumentCaptor.forClass(MdnsListener.class); 1313 final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, serviceType); 1314 request.setNetwork(network); 1315 client.resolveService(request, resolveListener); 1316 waitForIdle(); 1317 verify(mSocketProvider).startMonitoringSockets(); 1318 final ArgumentCaptor<MdnsSearchOptions> optionsCaptor = 1319 ArgumentCaptor.forClass(MdnsSearchOptions.class); 1320 verify(mDiscoveryManager).registerListener(eq(constructedServiceType), 1321 listenerCaptor.capture(), 1322 optionsCaptor.capture()); 1323 assertEquals(network, optionsCaptor.getValue().getNetwork()); 1324 // Subtypes are not used for resolution, only for discovery 1325 assertEquals(Collections.emptyList(), optionsCaptor.getValue().getSubtypes()); 1326 1327 final MdnsListener listener = listenerCaptor.getValue(); 1328 final MdnsServiceInfo mdnsServiceInfo = new MdnsServiceInfo( 1329 SERVICE_NAME, 1330 constructedServiceType.split("\\."), 1331 List.of(), /* subtypes */ 1332 new String[]{"android", "local"}, /* hostName */ 1333 PORT, 1334 List.of(IPV4_ADDRESS), 1335 List.of("2001:db8::1", "2001:db8::2"), 1336 List.of() /* textStrings */, 1337 List.of(MdnsServiceInfo.TextEntry.fromBytes(new byte[]{ 1338 'k', 'e', 'y', '=', (byte) 0xFF, (byte) 0xFE})) /* textEntries */, 1339 1234, 1340 network, 1341 Instant.ofEpochSecond(1000_000L) /* expirationTime */); 1342 1343 // Verify onServiceFound callback 1344 doReturn(TEST_TIME_MS + 10L).when(mClock).elapsedRealtime(); 1345 listener.onServiceFound(mdnsServiceInfo, true /* isServiceFromCache */); 1346 final ArgumentCaptor<NsdServiceInfo> infoCaptor = 1347 ArgumentCaptor.forClass(NsdServiceInfo.class); 1348 verify(resolveListener, timeout(TIMEOUT_MS)).onServiceResolved(infoCaptor.capture()); 1349 verify(mMetrics).reportServiceResolved(false /* isLegacy */, listener.mTransactionId, 1350 10 /* durationMs */, true /* isServiceFromCache */, 0 /* sendQueryCount */); 1351 1352 final NsdServiceInfo info = infoCaptor.getValue(); 1353 assertEquals(SERVICE_NAME, info.getServiceName()); 1354 assertEquals("._service._tcp", info.getServiceType()); 1355 assertEquals(PORT, info.getPort()); 1356 assertTrue(info.getAttributes().containsKey("key")); 1357 assertEquals(1, info.getAttributes().size()); 1358 assertArrayEquals(new byte[]{(byte) 0xFF, (byte) 0xFE}, info.getAttributes().get("key")); 1359 assertEquals(parseNumericAddress(IPV4_ADDRESS), info.getHost()); 1360 assertEquals(3, info.getHostAddresses().size()); 1361 assertTrue(info.getHostAddresses().stream().anyMatch( 1362 address -> address.equals(parseNumericAddress("2001:db8::1")))); 1363 assertTrue(info.getHostAddresses().stream().anyMatch( 1364 address -> address.equals(parseNumericAddress("2001:db8::2")))); 1365 assertEquals(network, info.getNetwork()); 1366 assertEquals(Instant.ofEpochSecond(1000_000L), info.getExpirationTime()); 1367 1368 // Verify the listener has been unregistered. 1369 verify(mDiscoveryManager, timeout(TIMEOUT_MS)) 1370 .unregisterListener(eq(constructedServiceType), any()); 1371 verify(mSocketProvider, timeout(CLEANUP_DELAY_MS + TIMEOUT_MS)).requestStopWhenInactive(); 1372 } 1373 1374 @Test 1375 @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND) 1376 @DevSdkIgnoreRule.IgnoreAfter(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) testMdnsAdvertiserFeatureFlagging()1377 public void testMdnsAdvertiserFeatureFlagging() { 1378 // Create NsdService w/o feature enabled. 1379 final NsdManager client = connectClient(mService); 1380 final NsdServiceInfo regInfo = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE); 1381 regInfo.setHost(parseNumericAddress("192.0.2.123")); 1382 regInfo.setPort(12345); 1383 final RegistrationListener regListenerWithoutFeature = mock(RegistrationListener.class); 1384 client.registerService(regInfo, PROTOCOL, regListenerWithoutFeature); 1385 waitForIdle(); 1386 1387 final ArgumentCaptor<Integer> legacyIdCaptor = ArgumentCaptor.forClass(Integer.class); 1388 verify(mMockMDnsM).registerService(legacyIdCaptor.capture(), any(), any(), anyInt(), 1389 any(), anyInt()); 1390 verifyNoMoreInteractions(mAdvertiser); 1391 1392 setMdnsAdvertiserEnabled(); 1393 final RegistrationListener regListenerWithFeature = mock(RegistrationListener.class); 1394 client.registerService(regInfo, PROTOCOL, regListenerWithFeature); 1395 waitForIdle(); 1396 1397 final ArgumentCaptor<Integer> serviceIdCaptor = ArgumentCaptor.forClass(Integer.class); 1398 verify(mAdvertiser).addOrUpdateService(serviceIdCaptor.capture(), 1399 argThat(info -> matches(info, regInfo)), any(), anyInt()); 1400 1401 client.unregisterService(regListenerWithoutFeature); 1402 waitForIdle(); 1403 verify(mMockMDnsM).stopOperation(legacyIdCaptor.getValue()); 1404 verify(mAdvertiser, never()).removeService(anyInt()); 1405 1406 doReturn(mock(MdnsAdvertiser.AdvertiserMetrics.class)) 1407 .when(mAdvertiser).getAdvertiserMetrics(anyInt()); 1408 client.unregisterService(regListenerWithFeature); 1409 waitForIdle(); 1410 verify(mAdvertiser).removeService(serviceIdCaptor.getValue()); 1411 } 1412 1413 @Test 1414 @DisableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND) 1415 @DevSdkIgnoreRule.IgnoreAfter(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) testTypeSpecificFeatureFlagging()1416 public void testTypeSpecificFeatureFlagging() { 1417 doReturn("_type1._tcp:flag1,_type2._tcp:flag2").when(mDeps).getTypeAllowlistFlags(); 1418 doReturn(true).when(mDeps).isFeatureEnabled(any(), 1419 eq("mdns_discovery_manager_allowlist_flag1_version")); 1420 doReturn(true).when(mDeps).isFeatureEnabled(any(), 1421 eq("mdns_advertiser_allowlist_flag2_version")); 1422 1423 final NsdManager client = connectClient(mService); 1424 final NsdServiceInfo service1 = new NsdServiceInfo(SERVICE_NAME, "_type1._tcp"); 1425 service1.setHostAddresses(List.of(parseNumericAddress("2001:db8::123"))); 1426 service1.setPort(1234); 1427 final NsdServiceInfo service2 = new NsdServiceInfo(SERVICE_NAME, "_type2._tcp"); 1428 service1.setHostAddresses(List.of(parseNumericAddress("2001:db8::123"))); 1429 service2.setPort(1234); 1430 1431 client.discoverServices(service1.getServiceType(), 1432 NsdManager.PROTOCOL_DNS_SD, mock(DiscoveryListener.class)); 1433 client.discoverServices(service2.getServiceType(), 1434 NsdManager.PROTOCOL_DNS_SD, mock(DiscoveryListener.class)); 1435 waitForIdle(); 1436 1437 // The DiscoveryManager is enabled for _type1 but not _type2 1438 verify(mDiscoveryManager).registerListener(eq("_type1._tcp.local"), any(), any()); 1439 verify(mDiscoveryManager, never()).registerListener( 1440 eq("_type2._tcp.local"), any(), any()); 1441 1442 client.resolveService(service1, mock(ResolveListener.class)); 1443 client.resolveService(service2, mock(ResolveListener.class)); 1444 waitForIdle(); 1445 1446 // Same behavior for resolve 1447 verify(mDiscoveryManager, times(2)).registerListener( 1448 eq("_type1._tcp.local"), any(), any()); 1449 verify(mDiscoveryManager, never()).registerListener( 1450 eq("_type2._tcp.local"), any(), any()); 1451 1452 client.registerService(service1, NsdManager.PROTOCOL_DNS_SD, 1453 mock(RegistrationListener.class)); 1454 client.registerService(service2, NsdManager.PROTOCOL_DNS_SD, 1455 mock(RegistrationListener.class)); 1456 waitForIdle(); 1457 1458 // The advertiser is enabled for _type2 but not _type1 1459 verify(mAdvertiser, never()).addOrUpdateService(anyInt(), 1460 argThat(info -> matches(info, service1)), any(), anyInt()); 1461 verify(mAdvertiser).addOrUpdateService(anyInt(), argThat(info -> matches(info, service2)), 1462 any(), anyInt()); 1463 } 1464 1465 @Test testAdvertiseWithMdnsAdvertiser()1466 public void testAdvertiseWithMdnsAdvertiser() { 1467 setMdnsAdvertiserEnabled(); 1468 1469 final NsdManager client = connectClient(mService); 1470 final RegistrationListener regListener = mock(RegistrationListener.class); 1471 // final String serviceTypeWithLocalDomain = SERVICE_TYPE + ".local"; 1472 final ArgumentCaptor<MdnsAdvertiser.AdvertiserCallback> cbCaptor = 1473 ArgumentCaptor.forClass(MdnsAdvertiser.AdvertiserCallback.class); 1474 verify(mDeps).makeMdnsAdvertiser(any(), any(), cbCaptor.capture(), any(), any(), any()); 1475 1476 final NsdServiceInfo regInfo = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE); 1477 regInfo.setHost(parseNumericAddress("192.0.2.123")); 1478 regInfo.setPort(12345); 1479 regInfo.setAttribute("testattr", "testvalue"); 1480 regInfo.setNetwork(new Network(999)); 1481 1482 client.registerService(regInfo, NsdManager.PROTOCOL_DNS_SD, Runnable::run, regListener); 1483 waitForIdle(); 1484 verify(mSocketProvider).startMonitoringSockets(); 1485 final ArgumentCaptor<Integer> idCaptor = ArgumentCaptor.forClass(Integer.class); 1486 verify(mAdvertiser).addOrUpdateService(idCaptor.capture(), argThat(info -> 1487 matches(info, regInfo)), any(), anyInt()); 1488 1489 // Verify onServiceRegistered callback 1490 final MdnsAdvertiser.AdvertiserCallback cb = cbCaptor.getValue(); 1491 final int regId = idCaptor.getValue(); 1492 doReturn(TEST_TIME_MS + 10L).when(mClock).elapsedRealtime(); 1493 cb.onRegisterServiceSucceeded(regId, regInfo); 1494 1495 verify(regListener, timeout(TIMEOUT_MS)).onServiceRegistered(argThat(info -> matches(info, 1496 new NsdServiceInfo(regInfo.getServiceName(), null)))); 1497 verify(mMetrics).reportServiceRegistrationSucceeded( 1498 false /* isLegacy */, regId, 10L /* durationMs */); 1499 1500 final MdnsAdvertiser.AdvertiserMetrics metrics = new MdnsAdvertiser.AdvertiserMetrics( 1501 50 /* repliedRequestCount */, 100 /* sentPacketCount */, 1502 3 /* conflictDuringProbingCount */, 2 /* conflictAfterProbingCount */); 1503 doReturn(TEST_TIME_MS + 100L).when(mClock).elapsedRealtime(); 1504 doReturn(metrics).when(mAdvertiser).getAdvertiserMetrics(regId); 1505 client.unregisterService(regListener); 1506 waitForIdle(); 1507 verify(mAdvertiser).removeService(idCaptor.getValue()); 1508 verify(regListener, timeout(TIMEOUT_MS)).onServiceUnregistered( 1509 argThat(info -> matches(info, regInfo))); 1510 verify(mSocketProvider, timeout(TIMEOUT_MS)).requestStopWhenInactive(); 1511 verify(mMetrics).reportServiceUnregistration(false /* isLegacy */, regId, 1512 100L /* durationMs */, 50 /* repliedRequestCount */, 100 /* sentPacketCount */, 1513 3 /* conflictDuringProbingCount */, 2 /* conflictAfterProbingCount */); 1514 } 1515 1516 @Test testAdvertiseWithMdnsAdvertiser_FailedWithInvalidServiceType()1517 public void testAdvertiseWithMdnsAdvertiser_FailedWithInvalidServiceType() { 1518 setMdnsAdvertiserEnabled(); 1519 1520 final NsdManager client = connectClient(mService); 1521 final RegistrationListener regListener = mock(RegistrationListener.class); 1522 // final String serviceTypeWithLocalDomain = SERVICE_TYPE + ".local"; 1523 final ArgumentCaptor<MdnsAdvertiser.AdvertiserCallback> cbCaptor = 1524 ArgumentCaptor.forClass(MdnsAdvertiser.AdvertiserCallback.class); 1525 verify(mDeps).makeMdnsAdvertiser(any(), any(), cbCaptor.capture(), any(), any(), any()); 1526 1527 final NsdServiceInfo regInfo = new NsdServiceInfo(SERVICE_NAME, "invalid_type"); 1528 regInfo.setHost(parseNumericAddress("192.0.2.123")); 1529 regInfo.setPort(12345); 1530 regInfo.setAttribute("testattr", "testvalue"); 1531 regInfo.setNetwork(new Network(999)); 1532 1533 client.registerService(regInfo, NsdManager.PROTOCOL_DNS_SD, Runnable::run, regListener); 1534 waitForIdle(); 1535 verify(mAdvertiser, never()).addOrUpdateService(anyInt(), any(), any(), anyInt()); 1536 1537 verify(regListener, timeout(TIMEOUT_MS)).onRegistrationFailed( 1538 argThat(info -> matches(info, regInfo)), eq(FAILURE_INTERNAL_ERROR)); 1539 verify(mMetrics).reportServiceRegistrationFailed( 1540 false /* isLegacy */, NO_TRANSACTION, 0L /* durationMs */); 1541 } 1542 1543 @Test testAdvertiseWithMdnsAdvertiser_LongServiceName()1544 public void testAdvertiseWithMdnsAdvertiser_LongServiceName() { 1545 setMdnsAdvertiserEnabled(); 1546 1547 final NsdManager client = connectClient(mService); 1548 final RegistrationListener regListener = mock(RegistrationListener.class); 1549 // final String serviceTypeWithLocalDomain = SERVICE_TYPE + ".local"; 1550 final ArgumentCaptor<MdnsAdvertiser.AdvertiserCallback> cbCaptor = 1551 ArgumentCaptor.forClass(MdnsAdvertiser.AdvertiserCallback.class); 1552 verify(mDeps).makeMdnsAdvertiser(any(), any(), cbCaptor.capture(), any(), any(), any()); 1553 1554 final NsdServiceInfo regInfo = new NsdServiceInfo("a".repeat(70), SERVICE_TYPE); 1555 regInfo.setHost(parseNumericAddress("192.0.2.123")); 1556 regInfo.setPort(12345); 1557 regInfo.setAttribute("testattr", "testvalue"); 1558 regInfo.setNetwork(new Network(999)); 1559 1560 client.registerService(regInfo, NsdManager.PROTOCOL_DNS_SD, Runnable::run, regListener); 1561 waitForIdle(); 1562 final ArgumentCaptor<Integer> idCaptor = ArgumentCaptor.forClass(Integer.class); 1563 // Service name is truncated to 63 characters 1564 verify(mAdvertiser) 1565 .addOrUpdateService( 1566 idCaptor.capture(), 1567 argThat(info -> info.getServiceName().equals("a".repeat(63))), 1568 any(), 1569 anyInt()); 1570 1571 // Verify onServiceRegistered callback 1572 final MdnsAdvertiser.AdvertiserCallback cb = cbCaptor.getValue(); 1573 final int regId = idCaptor.getValue(); 1574 doReturn(TEST_TIME_MS + 10L).when(mClock).elapsedRealtime(); 1575 cb.onRegisterServiceSucceeded(regId, regInfo); 1576 1577 verify(regListener, timeout(TIMEOUT_MS)).onServiceRegistered( 1578 argThat(info -> matches(info, new NsdServiceInfo(regInfo.getServiceName(), null)))); 1579 verify(mMetrics).reportServiceRegistrationSucceeded( 1580 false /* isLegacy */, regId, 10L /* durationMs */); 1581 } 1582 1583 @Test testAdvertiseCustomTtl_validTtl_success()1584 public void testAdvertiseCustomTtl_validTtl_success() { 1585 runValidTtlAdvertisingTest(30L); 1586 runValidTtlAdvertisingTest(10 * 3600L); 1587 } 1588 1589 @Test testAdvertiseCustomTtl_ttlSmallerThan30SecondsButClientIsSystemServer_success()1590 public void testAdvertiseCustomTtl_ttlSmallerThan30SecondsButClientIsSystemServer_success() { 1591 when(mDeps.getCallingUid()).thenReturn(Process.SYSTEM_UID); 1592 1593 runValidTtlAdvertisingTest(29L); 1594 } 1595 1596 @Test testAdvertiseCustomTtl_ttlLargerThan10HoursButClientIsSystemServer_success()1597 public void testAdvertiseCustomTtl_ttlLargerThan10HoursButClientIsSystemServer_success() { 1598 when(mDeps.getCallingUid()).thenReturn(Process.SYSTEM_UID); 1599 1600 runValidTtlAdvertisingTest(10 * 3600L + 1); 1601 runValidTtlAdvertisingTest(0xffffffffL); 1602 } 1603 runValidTtlAdvertisingTest(long validTtlSeconds)1604 private void runValidTtlAdvertisingTest(long validTtlSeconds) { 1605 setMdnsAdvertiserEnabled(); 1606 1607 final NsdManager client = connectClient(mService); 1608 final RegistrationListener regListener = mock(RegistrationListener.class); 1609 final ArgumentCaptor<MdnsAdvertiser.AdvertiserCallback> cbCaptor = 1610 ArgumentCaptor.forClass(MdnsAdvertiser.AdvertiserCallback.class); 1611 verify(mDeps).makeMdnsAdvertiser(any(), any(), cbCaptor.capture(), any(), any(), any()); 1612 1613 final NsdServiceInfo regInfo = new NsdServiceInfo("Service custom TTL", SERVICE_TYPE); 1614 regInfo.setPort(1234); 1615 final AdvertisingRequest request = 1616 new AdvertisingRequest.Builder(regInfo, NsdManager.PROTOCOL_DNS_SD) 1617 .setTtl(Duration.ofSeconds(validTtlSeconds)).build(); 1618 1619 client.registerService(request, Runnable::run, regListener); 1620 waitForIdle(); 1621 1622 final ArgumentCaptor<Integer> idCaptor = ArgumentCaptor.forClass(Integer.class); 1623 final MdnsAdvertisingOptions expectedAdverstingOptions = 1624 MdnsAdvertisingOptions.newBuilder().setTtl(request.getTtl()).build(); 1625 verify(mAdvertiser).addOrUpdateService(idCaptor.capture(), any(), 1626 eq(expectedAdverstingOptions), anyInt()); 1627 1628 // Verify onServiceRegistered callback 1629 final MdnsAdvertiser.AdvertiserCallback cb = cbCaptor.getValue(); 1630 final int regId = idCaptor.getValue(); 1631 cb.onRegisterServiceSucceeded(regId, regInfo); 1632 1633 verify(regListener, timeout(TIMEOUT_MS)).onServiceRegistered( 1634 argThat(info -> matches(info, new NsdServiceInfo(regInfo.getServiceName(), null)))); 1635 } 1636 1637 @Test testAdvertiseCustomTtl_invalidTtl_FailsWithBadParameters()1638 public void testAdvertiseCustomTtl_invalidTtl_FailsWithBadParameters() { 1639 setMdnsAdvertiserEnabled(); 1640 final long invalidTtlSeconds = 29L; 1641 final NsdManager client = connectClient(mService); 1642 final RegistrationListener regListener = mock(RegistrationListener.class); 1643 final ArgumentCaptor<MdnsAdvertiser.AdvertiserCallback> cbCaptor = 1644 ArgumentCaptor.forClass(MdnsAdvertiser.AdvertiserCallback.class); 1645 verify(mDeps).makeMdnsAdvertiser(any(), any(), cbCaptor.capture(), any(), any(), any()); 1646 1647 final NsdServiceInfo regInfo = new NsdServiceInfo("Service custom TTL", SERVICE_TYPE); 1648 regInfo.setPort(1234); 1649 final AdvertisingRequest request = 1650 new AdvertisingRequest.Builder(regInfo, NsdManager.PROTOCOL_DNS_SD) 1651 .setTtl(Duration.ofSeconds(invalidTtlSeconds)).build(); 1652 client.registerService(request, Runnable::run, regListener); 1653 waitForIdle(); 1654 1655 verify(regListener, timeout(TIMEOUT_MS)) 1656 .onRegistrationFailed(any(), eq(FAILURE_BAD_PARAMETERS)); 1657 } 1658 1659 @Test testStopServiceResolutionWithMdnsDiscoveryManager()1660 public void testStopServiceResolutionWithMdnsDiscoveryManager() { 1661 setMdnsDiscoveryManagerEnabled(); 1662 1663 final NsdManager client = connectClient(mService); 1664 final ResolveListener resolveListener = mock(ResolveListener.class); 1665 final Network network = new Network(999); 1666 final String serviceType = "_nsd._service._tcp"; 1667 final String constructedServiceType = "_service._tcp.local"; 1668 final ArgumentCaptor<MdnsListener> listenerCaptor = 1669 ArgumentCaptor.forClass(MdnsListener.class); 1670 final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, serviceType); 1671 request.setNetwork(network); 1672 client.resolveService(request, resolveListener); 1673 waitForIdle(); 1674 verify(mSocketProvider).startMonitoringSockets(); 1675 final ArgumentCaptor<MdnsSearchOptions> optionsCaptor = 1676 ArgumentCaptor.forClass(MdnsSearchOptions.class); 1677 verify(mDiscoveryManager).registerListener(eq(constructedServiceType), 1678 listenerCaptor.capture(), 1679 optionsCaptor.capture()); 1680 assertEquals(network, optionsCaptor.getValue().getNetwork()); 1681 // Subtypes are not used for resolution, only for discovery 1682 assertEquals(Collections.emptyList(), optionsCaptor.getValue().getSubtypes()); 1683 1684 final MdnsListener listener = listenerCaptor.getValue(); 1685 // Callbacks for query sent. 1686 listener.onDiscoveryQuerySent(Collections.emptyList(), 1 /* transactionId */); 1687 1688 doReturn(TEST_TIME_MS + 10L).when(mClock).elapsedRealtime(); 1689 client.stopServiceResolution(resolveListener); 1690 waitForIdle(); 1691 1692 // Verify the listener has been unregistered. 1693 verify(mDiscoveryManager, timeout(TIMEOUT_MS)) 1694 .unregisterListener(eq(constructedServiceType), eq(listener)); 1695 verify(resolveListener, timeout(TIMEOUT_MS)).onResolutionStopped(argThat(ns -> 1696 request.getServiceName().equals(ns.getServiceName()) 1697 && request.getServiceType().equals(ns.getServiceType()))); 1698 verify(mSocketProvider, timeout(CLEANUP_DELAY_MS + TIMEOUT_MS)).requestStopWhenInactive(); 1699 verify(mMetrics).reportServiceResolutionStop(false /* isLegacy */, listener.mTransactionId, 1700 10L /* durationMs */, 1 /* sentQueryCount */); 1701 } 1702 1703 @Test testParseTypeAndSubtype()1704 public void testParseTypeAndSubtype() { 1705 final String serviceType1 = "test._tcp"; 1706 final String serviceType2 = "_test._quic"; 1707 final String serviceType3 = "_test._quic,_test1,_test2"; 1708 final String serviceType4 = "_123._udp."; 1709 final String serviceType5 = "_TEST._999._tcp."; 1710 final String serviceType6 = "_998._tcp.,_TEST"; 1711 final String serviceType7 = "_997._tcp,_TEST"; 1712 final String serviceType8 = "_997._tcp,_test1,_test2,_test3"; 1713 final String serviceType9 = "_test4._997._tcp,_test1,_test2,_test3"; 1714 1715 assertNull(parseTypeAndSubtype(serviceType1)); 1716 assertNull(parseTypeAndSubtype(serviceType2)); 1717 assertNull(parseTypeAndSubtype(serviceType3)); 1718 assertEquals(new Pair<>("_123._udp", Collections.emptyList()), 1719 parseTypeAndSubtype(serviceType4)); 1720 assertEquals(new Pair<>("_999._tcp", List.of("_TEST")), parseTypeAndSubtype(serviceType5)); 1721 assertEquals(new Pair<>("_998._tcp", List.of("_TEST")), parseTypeAndSubtype(serviceType6)); 1722 assertEquals(new Pair<>("_997._tcp", List.of("_TEST")), parseTypeAndSubtype(serviceType7)); 1723 1724 assertEquals(new Pair<>("_997._tcp", List.of("_test1", "_test2", "_test3")), 1725 parseTypeAndSubtype(serviceType8)); 1726 assertEquals(new Pair<>("_997._tcp", List.of("_test4")), 1727 parseTypeAndSubtype(serviceType9)); 1728 } 1729 1730 @Test TestCheckHostname()1731 public void TestCheckHostname() { 1732 // Valid cases 1733 assertTrue(checkHostname(null)); 1734 assertTrue(checkHostname("a")); 1735 assertTrue(checkHostname("1")); 1736 assertTrue(checkHostname("a-1234-bbbb-cccc000")); 1737 assertTrue(checkHostname("A-1234-BBbb-CCCC000")); 1738 assertTrue(checkHostname("1234-bbbb-cccc000")); 1739 assertTrue(checkHostname("0123456789abcdef" 1740 + "0123456789abcdef" 1741 + "0123456789abcdef" 1742 + "0123456789abcde" // 63 characters 1743 )); 1744 1745 // Invalid cases 1746 assertFalse(checkHostname("?")); 1747 assertFalse(checkHostname("/")); 1748 assertFalse(checkHostname("a-")); 1749 assertFalse(checkHostname("B-")); 1750 assertFalse(checkHostname("-A")); 1751 assertFalse(checkHostname("-b")); 1752 assertFalse(checkHostname("-1-")); 1753 assertFalse(checkHostname("0123456789abcdef" 1754 + "0123456789abcdef" 1755 + "0123456789abcdef" 1756 + "0123456789abcdef" // 64 characters 1757 )); 1758 } 1759 1760 @Test 1761 @EnableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND) testEnablePlatformMdnsBackend()1762 public void testEnablePlatformMdnsBackend() { 1763 final NsdManager client = connectClient(mService); 1764 final NsdServiceInfo regInfo = new NsdServiceInfo("a".repeat(70), SERVICE_TYPE); 1765 final Network network = new Network(999); 1766 regInfo.setHostAddresses(List.of(parseNumericAddress("192.0.2.123"))); 1767 regInfo.setPort(12345); 1768 regInfo.setAttribute("testattr", "testvalue"); 1769 regInfo.setNetwork(network); 1770 1771 // Verify the registration uses MdnsAdvertiser 1772 final RegistrationListener regListener = mock(RegistrationListener.class); 1773 client.registerService(regInfo, NsdManager.PROTOCOL_DNS_SD, Runnable::run, regListener); 1774 waitForIdle(); 1775 verify(mSocketProvider).startMonitoringSockets(); 1776 verify(mAdvertiser).addOrUpdateService(anyInt(), any(), any(), anyInt()); 1777 1778 // Verify the discovery uses MdnsDiscoveryManager 1779 final DiscoveryListener discListener = mock(DiscoveryListener.class); 1780 client.discoverServices(SERVICE_TYPE, PROTOCOL, network, r -> r.run(), discListener); 1781 waitForIdle(); 1782 verify(mDiscoveryManager).registerListener(anyString(), any(), any()); 1783 1784 // Verify the discovery uses MdnsDiscoveryManager 1785 final ResolveListener resolveListener = mock(ResolveListener.class); 1786 client.resolveService(regInfo, r -> r.run(), resolveListener); 1787 waitForIdle(); 1788 verify(mDiscoveryManager, times(2)).registerListener(anyString(), any(), any()); 1789 } 1790 1791 @Test 1792 @EnableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND) testTakeMulticastLockOnBehalfOfClient_ForWifiNetworksOnly()1793 public void testTakeMulticastLockOnBehalfOfClient_ForWifiNetworksOnly() { 1794 // Test on one client in the foreground 1795 mUidImportanceListener.onUidImportance(123, IMPORTANCE_FOREGROUND); 1796 doReturn(123).when(mDeps).getCallingUid(); 1797 final NsdManager client = connectClient(mService); 1798 1799 final NsdServiceInfo regInfo = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE); 1800 regInfo.setHostAddresses(List.of(parseNumericAddress("192.0.2.123"))); 1801 regInfo.setPort(12345); 1802 // File a request for all networks 1803 regInfo.setNetwork(null); 1804 1805 final RegistrationListener regListener = mock(RegistrationListener.class); 1806 client.registerService(regInfo, NsdManager.PROTOCOL_DNS_SD, Runnable::run, regListener); 1807 waitForIdle(); 1808 verify(mSocketProvider).startMonitoringSockets(); 1809 verify(mAdvertiser).addOrUpdateService(anyInt(), any(), any(), anyInt()); 1810 1811 final Network wifiNetwork1 = new Network(123); 1812 final Network wifiNetwork2 = new Network(124); 1813 final Network ethernetNetwork = new Network(125); 1814 1815 final MdnsInterfaceSocket wifiNetworkSocket1 = mock(MdnsInterfaceSocket.class); 1816 final MdnsInterfaceSocket wifiNetworkSocket2 = mock(MdnsInterfaceSocket.class); 1817 final MdnsInterfaceSocket ethernetNetworkSocket = mock(MdnsInterfaceSocket.class); 1818 1819 // Nothing happens for networks with no transports, no Wi-Fi transport, or VPN transport 1820 mHandler.post(() -> { 1821 mSocketRequestMonitor.onSocketRequestFulfilled( 1822 new Network(125), mock(MdnsInterfaceSocket.class), new int[0]); 1823 mSocketRequestMonitor.onSocketRequestFulfilled( 1824 ethernetNetwork, ethernetNetworkSocket, 1825 new int[] { TRANSPORT_ETHERNET }); 1826 mSocketRequestMonitor.onSocketRequestFulfilled( 1827 new Network(127), mock(MdnsInterfaceSocket.class), 1828 new int[] { TRANSPORT_WIFI, TRANSPORT_VPN }); 1829 }); 1830 waitForIdle(); 1831 verify(mWifiManager, never()).createMulticastLock(any()); 1832 1833 // First Wi-Fi network 1834 mHandler.post(() -> mSocketRequestMonitor.onSocketRequestFulfilled( 1835 wifiNetwork1, wifiNetworkSocket1, new int[] { TRANSPORT_WIFI })); 1836 waitForIdle(); 1837 verify(mWifiManager).createMulticastLock(any()); 1838 verify(mMulticastLock).acquire(); 1839 1840 // Second Wi-Fi network 1841 mHandler.post(() -> mSocketRequestMonitor.onSocketRequestFulfilled( 1842 wifiNetwork2, wifiNetworkSocket2, new int[] { TRANSPORT_WIFI })); 1843 waitForIdle(); 1844 verifyNoMoreInteractions(mMulticastLock); 1845 1846 // One Wi-Fi network becomes unused, nothing happens 1847 mHandler.post(() -> mSocketRequestMonitor.onSocketDestroyed( 1848 wifiNetwork1, wifiNetworkSocket1)); 1849 waitForIdle(); 1850 verifyNoMoreInteractions(mMulticastLock); 1851 1852 // Ethernet network becomes unused, still nothing 1853 mHandler.post(() -> mSocketRequestMonitor.onSocketDestroyed( 1854 ethernetNetwork, ethernetNetworkSocket)); 1855 waitForIdle(); 1856 verifyNoMoreInteractions(mMulticastLock); 1857 1858 // The second Wi-Fi network becomes unused, the lock is released 1859 mHandler.post(() -> mSocketRequestMonitor.onSocketDestroyed( 1860 wifiNetwork2, wifiNetworkSocket2)); 1861 waitForIdle(); 1862 verify(mMulticastLock).release(); 1863 } 1864 1865 @Test 1866 @EnableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND) testTakeMulticastLockOnBehalfOfClient_ForForegroundAppsOnly()1867 public void testTakeMulticastLockOnBehalfOfClient_ForForegroundAppsOnly() { 1868 final int uid1 = 12; 1869 final int uid2 = 34; 1870 final int uid3 = 56; 1871 final int uid4 = 78; 1872 final InOrder lockOrder = inOrder(mMulticastLock); 1873 // Connect one client without any foreground info 1874 doReturn(uid1).when(mDeps).getCallingUid(); 1875 final NsdManager client1 = connectClient(mService); 1876 1877 // Connect client2 as visible, but not foreground 1878 mUidImportanceListener.onUidImportance(uid2, IMPORTANCE_VISIBLE); 1879 waitForIdle(); 1880 doReturn(uid2).when(mDeps).getCallingUid(); 1881 final NsdManager client2 = connectClient(mService); 1882 1883 // Connect client3, client4 as foreground 1884 mUidImportanceListener.onUidImportance(uid3, IMPORTANCE_FOREGROUND); 1885 waitForIdle(); 1886 doReturn(uid3).when(mDeps).getCallingUid(); 1887 final NsdManager client3 = connectClient(mService); 1888 1889 mUidImportanceListener.onUidImportance(uid4, IMPORTANCE_FOREGROUND); 1890 waitForIdle(); 1891 doReturn(uid4).when(mDeps).getCallingUid(); 1892 final NsdManager client4 = connectClient(mService); 1893 1894 // First client advertises on any network 1895 final NsdServiceInfo regInfo = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE); 1896 regInfo.setHostAddresses(List.of(parseNumericAddress("192.0.2.123"))); 1897 regInfo.setPort(12345); 1898 regInfo.setNetwork(null); 1899 final RegistrationListener regListener = mock(RegistrationListener.class); 1900 client1.registerService(regInfo, NsdManager.PROTOCOL_DNS_SD, Runnable::run, regListener); 1901 waitForIdle(); 1902 1903 final MdnsInterfaceSocket wifiSocket = mock(MdnsInterfaceSocket.class); 1904 final Network wifiNetwork = new Network(123); 1905 1906 final MdnsInterfaceSocket ethSocket = mock(MdnsInterfaceSocket.class); 1907 final Network ethNetwork = new Network(234); 1908 1909 mHandler.post(() -> { 1910 mSocketRequestMonitor.onSocketRequestFulfilled( 1911 wifiNetwork, wifiSocket, new int[] { TRANSPORT_WIFI }); 1912 mSocketRequestMonitor.onSocketRequestFulfilled( 1913 ethNetwork, ethSocket, new int[] { TRANSPORT_ETHERNET }); 1914 }); 1915 waitForIdle(); 1916 1917 // No multicast lock since client1 has no foreground info 1918 lockOrder.verifyNoMoreInteractions(); 1919 1920 // Second client discovers specifically on the Wi-Fi network 1921 final DiscoveryListener discListener = mock(DiscoveryListener.class); 1922 client2.discoverServices(SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, wifiNetwork, 1923 Runnable::run, discListener); 1924 waitForIdle(); 1925 mHandler.post(() -> mSocketRequestMonitor.onSocketRequestFulfilled( 1926 wifiNetwork, wifiSocket, new int[] { TRANSPORT_WIFI })); 1927 waitForIdle(); 1928 // No multicast lock since client2 is not visible enough 1929 lockOrder.verifyNoMoreInteractions(); 1930 1931 // Third client registers a callback on all networks 1932 final NsdServiceInfo cbInfo = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE); 1933 cbInfo.setNetwork(null); 1934 final ServiceInfoCallback infoCb = mock(ServiceInfoCallback.class); 1935 client3.registerServiceInfoCallback(cbInfo, Runnable::run, infoCb); 1936 waitForIdle(); 1937 mHandler.post(() -> { 1938 mSocketRequestMonitor.onSocketRequestFulfilled( 1939 wifiNetwork, wifiSocket, new int[] { TRANSPORT_WIFI }); 1940 mSocketRequestMonitor.onSocketRequestFulfilled( 1941 ethNetwork, ethSocket, new int[] { TRANSPORT_ETHERNET }); 1942 }); 1943 waitForIdle(); 1944 1945 // Multicast lock is taken for third client 1946 lockOrder.verify(mMulticastLock).acquire(); 1947 1948 // Client3 goes to the background 1949 mUidImportanceListener.onUidImportance(uid3, IMPORTANCE_CACHED); 1950 waitForIdle(); 1951 lockOrder.verify(mMulticastLock).release(); 1952 1953 // client4 resolves on a different network 1954 final ResolveListener resolveListener = mock(ResolveListener.class); 1955 final NsdServiceInfo resolveInfo = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE); 1956 resolveInfo.setNetwork(ethNetwork); 1957 client4.resolveService(resolveInfo, Runnable::run, resolveListener); 1958 waitForIdle(); 1959 mHandler.post(() -> mSocketRequestMonitor.onSocketRequestFulfilled( 1960 ethNetwork, ethSocket, new int[] { TRANSPORT_ETHERNET })); 1961 waitForIdle(); 1962 1963 // client4 is foreground, but not Wi-Fi 1964 lockOrder.verifyNoMoreInteractions(); 1965 1966 // Second client becomes foreground 1967 mUidImportanceListener.onUidImportance(uid2, IMPORTANCE_FOREGROUND); 1968 waitForIdle(); 1969 1970 lockOrder.verify(mMulticastLock).acquire(); 1971 1972 // Second client is lost 1973 mUidImportanceListener.onUidImportance(uid2, IMPORTANCE_GONE); 1974 waitForIdle(); 1975 1976 lockOrder.verify(mMulticastLock).release(); 1977 } 1978 1979 @Test testNullINsdManagerCallback()1980 public void testNullINsdManagerCallback() { 1981 final NsdService service = new NsdService(mContext, mHandler, CLEANUP_DELAY_MS, mDeps) { 1982 @Override 1983 public INsdServiceConnector connect(INsdManagerCallback baseCb, 1984 boolean runNewMdnsBackend) { 1985 // Pass null INsdManagerCallback 1986 return super.connect(null /* cb */, runNewMdnsBackend); 1987 } 1988 }; 1989 1990 assertThrows(IllegalArgumentException.class, () -> new NsdManager(mContext, service)); 1991 } 1992 1993 @Test 1994 @EnableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND) 1995 @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) testRegisterOffloadEngine_checkPermission_V()1996 public void testRegisterOffloadEngine_checkPermission_V() { 1997 final NsdManager client = connectClient(mService); 1998 final OffloadEngine offloadEngine = mock(OffloadEngine.class); 1999 doReturn(PERMISSION_DENIED).when(mContext).checkCallingOrSelfPermission(NETWORK_STACK); 2000 doReturn(PERMISSION_DENIED).when(mContext).checkCallingOrSelfPermission( 2001 PERMISSION_MAINLINE_NETWORK_STACK); 2002 doReturn(PERMISSION_DENIED).when(mContext).checkCallingOrSelfPermission(NETWORK_SETTINGS); 2003 doReturn(PERMISSION_GRANTED).when(mContext).checkCallingOrSelfPermission( 2004 REGISTER_NSD_OFFLOAD_ENGINE); 2005 2006 doReturn(PERMISSION_DENIED).when(mContext).checkCallingOrSelfPermission( 2007 REGISTER_NSD_OFFLOAD_ENGINE); 2008 doReturn(PERMISSION_GRANTED).when(mContext).checkCallingOrSelfPermission(DEVICE_POWER); 2009 assertThrows(SecurityException.class, 2010 () -> client.registerOffloadEngine("iface1", OffloadEngine.OFFLOAD_TYPE_REPLY, 2011 OffloadEngine.OFFLOAD_CAPABILITY_BYPASS_MULTICAST_LOCK, Runnable::run, 2012 offloadEngine)); 2013 doReturn(PERMISSION_GRANTED).when(mContext).checkCallingOrSelfPermission( 2014 REGISTER_NSD_OFFLOAD_ENGINE); 2015 final OffloadEngine offloadEngine2 = mock(OffloadEngine.class); 2016 client.registerOffloadEngine("iface2", OffloadEngine.OFFLOAD_TYPE_REPLY, 2017 OffloadEngine.OFFLOAD_CAPABILITY_BYPASS_MULTICAST_LOCK, Runnable::run, 2018 offloadEngine2); 2019 client.unregisterOffloadEngine(offloadEngine2); 2020 } 2021 2022 @Test 2023 @EnableCompatChanges(ENABLE_PLATFORM_MDNS_BACKEND) 2024 @DevSdkIgnoreRule.IgnoreAfter(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 2025 @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.TIRAMISU) testRegisterOffloadEngine_checkPermission_U()2026 public void testRegisterOffloadEngine_checkPermission_U() { 2027 final NsdManager client = connectClient(mService); 2028 final OffloadEngine offloadEngine = mock(OffloadEngine.class); 2029 doReturn(PERMISSION_DENIED).when(mContext).checkCallingOrSelfPermission(NETWORK_STACK); 2030 doReturn(PERMISSION_DENIED).when(mContext).checkCallingOrSelfPermission( 2031 PERMISSION_MAINLINE_NETWORK_STACK); 2032 doReturn(PERMISSION_DENIED).when(mContext).checkCallingOrSelfPermission(NETWORK_SETTINGS); 2033 doReturn(PERMISSION_GRANTED).when(mContext).checkCallingOrSelfPermission( 2034 REGISTER_NSD_OFFLOAD_ENGINE); 2035 2036 doReturn(PERMISSION_GRANTED).when(mContext).checkCallingOrSelfPermission(DEVICE_POWER); 2037 client.registerOffloadEngine("iface2", OffloadEngine.OFFLOAD_TYPE_REPLY, 2038 OffloadEngine.OFFLOAD_CAPABILITY_BYPASS_MULTICAST_LOCK, Runnable::run, 2039 offloadEngine); 2040 client.unregisterOffloadEngine(offloadEngine); 2041 } 2042 2043 waitForIdle()2044 private void waitForIdle() { 2045 HandlerUtils.waitForIdle(mHandler, TIMEOUT_MS); 2046 } 2047 makeService()2048 NsdService makeService() { 2049 final NsdService service = new NsdService(mContext, mHandler, CLEANUP_DELAY_MS, mDeps) { 2050 @Override 2051 public INsdServiceConnector connect(INsdManagerCallback baseCb, 2052 boolean runNewMdnsBackend) { 2053 // Wrap the callback in a transparent mock, to mock asBinder returning a 2054 // LinkToDeathRecorder. This will allow recording the binder death recipient 2055 // registered on the callback. Use a transparent mock and not a spy as the actual 2056 // implementation class is not public and cannot be spied on by Mockito. 2057 final INsdManagerCallback cb = mock(INsdManagerCallback.class, 2058 AdditionalAnswers.delegatesTo(baseCb)); 2059 doReturn(new LinkToDeathRecorder()).when(cb).asBinder(); 2060 mCreatedCallbacks.add(cb); 2061 return super.connect(cb, runNewMdnsBackend); 2062 } 2063 }; 2064 return service; 2065 } 2066 getCallback()2067 private INsdManagerCallback getCallback() { 2068 return mCreatedCallbacks.remove(); 2069 } 2070 connectClient(NsdService service)2071 NsdManager connectClient(NsdService service) { 2072 final NsdManager nsdManager = new NsdManager(mContext, service); 2073 // Wait for client registration done. 2074 waitForIdle(); 2075 return nsdManager; 2076 } 2077 verifyDelayMaybeStopDaemon(long cleanupDelayMs)2078 void verifyDelayMaybeStopDaemon(long cleanupDelayMs) throws Exception { 2079 waitForIdle(); 2080 // Stop daemon shouldn't be called immediately. 2081 verify(mMockMDnsM, never()).unregisterEventListener(any()); 2082 verify(mMockMDnsM, never()).stopDaemon(); 2083 2084 // Clean up the daemon after CLEANUP_DELAY_MS. 2085 verify(mMockMDnsM, timeout(cleanupDelayMs + TIMEOUT_MS)).unregisterEventListener(any()); 2086 verify(mMockMDnsM, timeout(cleanupDelayMs + TIMEOUT_MS)).stopDaemon(); 2087 } 2088 2089 /** 2090 * Return true if two service info are the same. 2091 * 2092 * Useful for argument matchers as {@link NsdServiceInfo} does not implement equals. 2093 */ matches(NsdServiceInfo a, NsdServiceInfo b)2094 private boolean matches(NsdServiceInfo a, NsdServiceInfo b) { 2095 return Objects.equals(a.getServiceName(), b.getServiceName()) 2096 && Objects.equals(a.getServiceType(), b.getServiceType()) 2097 && Objects.equals(a.getHost(), b.getHost()) 2098 && Objects.equals(a.getNetwork(), b.getNetwork()) 2099 && Objects.equals(a.getAttributes(), b.getAttributes()); 2100 } 2101 2102 public static class TestHandler extends Handler { 2103 public Message lastMessage; 2104 TestHandler(Looper looper)2105 TestHandler(Looper looper) { 2106 super(looper); 2107 } 2108 2109 @Override handleMessage(Message msg)2110 public void handleMessage(Message msg) { 2111 lastMessage = obtainMessage(); 2112 lastMessage.copyFrom(msg); 2113 } 2114 } 2115 } 2116