Merge "Migrate reigster service callback backend"

This commit is contained in:
Paul Hu
2023-03-08 10:27:57 +00:00
committed by Gerrit Code Review
4 changed files with 179 additions and 188 deletions

View File

@@ -1312,7 +1312,7 @@ public final class NsdManager {
* before registering other callbacks. Upon failure to register a callback for example if * before registering other callbacks. Upon failure to register a callback for example if
* it's a duplicated registration, the application is notified through * it's a duplicated registration, the application is notified through
* {@link ServiceInfoCallback#onServiceInfoCallbackRegistrationFailed} with * {@link ServiceInfoCallback#onServiceInfoCallbackRegistrationFailed} with
* {@link #FAILURE_BAD_PARAMETERS} or {@link #FAILURE_ALREADY_ACTIVE}. * {@link #FAILURE_BAD_PARAMETERS}.
* *
* @param serviceInfo the service to receive updates for * @param serviceInfo the service to receive updates for
* @param executor Executor to run callbacks with * @param executor Executor to run callbacks with

View File

@@ -84,7 +84,6 @@ import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@@ -265,6 +264,35 @@ public class NsdService extends INsdManager.Stub {
} }
} }
private class ServiceInfoListener extends MdnsListener {
ServiceInfoListener(int clientId, int transactionId, @NonNull NsdServiceInfo reqServiceInfo,
@NonNull String listenServiceType) {
super(clientId, transactionId, reqServiceInfo, listenServiceType);
}
@Override
public void onServiceFound(@NonNull MdnsServiceInfo serviceInfo) {
mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
NsdManager.SERVICE_UPDATED,
new MdnsEvent(mClientId, mReqServiceInfo.getServiceType(), serviceInfo));
}
@Override
public void onServiceUpdated(@NonNull MdnsServiceInfo serviceInfo) {
mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
NsdManager.SERVICE_UPDATED,
new MdnsEvent(mClientId, mReqServiceInfo.getServiceType(), serviceInfo));
}
@Override
public void onServiceRemoved(@NonNull MdnsServiceInfo serviceInfo) {
mNsdStateMachine.sendMessage(MDNS_DISCOVERY_MANAGER_EVENT, mTransactionId,
NsdManager.SERVICE_UPDATED_LOST,
new MdnsEvent(mClientId, mReqServiceInfo.getServiceType(), serviceInfo));
}
}
/** /**
* Data class of mdns service callback information. * Data class of mdns service callback information.
*/ */
@@ -521,12 +549,6 @@ public class NsdService extends INsdManager.Stub {
mIdToClientInfoMap.put(globalId, clientInfo); mIdToClientInfoMap.put(globalId, clientInfo);
} }
private void clearRegisteredServiceInfo(ClientInfo clientInfo) {
clientInfo.mRegisteredService = null;
clientInfo.mClientIdForServiceUpdates = 0;
}
/** /**
* Truncate a service name to up to 63 UTF-8 bytes. * Truncate a service name to up to 63 UTF-8 bytes.
* *
@@ -832,7 +854,7 @@ public class NsdService extends INsdManager.Stub {
} }
break; break;
} }
case NsdManager.REGISTER_SERVICE_CALLBACK: case NsdManager.REGISTER_SERVICE_CALLBACK: {
if (DBG) Log.d(TAG, "Register a service callback"); if (DBG) Log.d(TAG, "Register a service callback");
args = (ListenerArgs) msg.obj; args = (ListenerArgs) msg.obj;
clientInfo = mClients.get(args.connector); clientInfo = mClients.get(args.connector);
@@ -844,23 +866,29 @@ public class NsdService extends INsdManager.Stub {
break; break;
} }
if (clientInfo.mRegisteredService != null) { final NsdServiceInfo info = args.serviceInfo;
clientInfo.onServiceInfoCallbackRegistrationFailed( id = getUniqueId();
clientId, NsdManager.FAILURE_ALREADY_ACTIVE); final String serviceType = constructServiceType(info.getServiceType());
if (serviceType == null) {
clientInfo.onServiceInfoCallbackRegistrationFailed(clientId,
NsdManager.FAILURE_BAD_PARAMETERS);
break; break;
} }
final String resolveServiceType = serviceType + ".local";
maybeStartDaemon(); maybeStartMonitoringSockets();
id = getUniqueId(); final MdnsListener listener =
if (resolveService(id, args.serviceInfo)) { new ServiceInfoListener(clientId, id, info, resolveServiceType);
clientInfo.mRegisteredService = new NsdServiceInfo(); final MdnsSearchOptions options = MdnsSearchOptions.newBuilder()
clientInfo.mClientIdForServiceUpdates = clientId; .setNetwork(info.getNetwork())
storeLegacyRequestMap(clientId, id, clientInfo, msg.what); .setIsPassiveMode(true)
} else { .setResolveInstanceName(info.getServiceName())
clientInfo.onServiceInfoCallbackRegistrationFailed( .build();
clientId, NsdManager.FAILURE_BAD_PARAMETERS); mMdnsDiscoveryManager.registerListener(
} resolveServiceType, listener, options);
storeDiscoveryManagerRequestMap(clientId, id, listener, clientInfo);
break; break;
}
case NsdManager.UNREGISTER_SERVICE_CALLBACK: { case NsdManager.UNREGISTER_SERVICE_CALLBACK: {
if (DBG) Log.d(TAG, "Unregister a service callback"); if (DBG) Log.d(TAG, "Unregister a service callback");
args = (ListenerArgs) msg.obj; args = (ListenerArgs) msg.obj;
@@ -875,17 +903,16 @@ public class NsdService extends INsdManager.Stub {
final ClientRequest request = clientInfo.mClientRequests.get(clientId); final ClientRequest request = clientInfo.mClientRequests.get(clientId);
if (request == null) { if (request == null) {
Log.e(TAG, "Unknown client request in STOP_RESOLUTION"); Log.e(TAG, "Unknown client request in UNREGISTER_SERVICE_CALLBACK");
break; break;
} }
id = request.mGlobalId; id = request.mGlobalId;
removeRequestMap(clientId, id, clientInfo); if (request instanceof DiscoveryManagerRequest) {
if (stopResolveService(id)) { stopDiscoveryManagerRequest(request, clientId, id, clientInfo);
clientInfo.onServiceInfoCallbackUnregistered(clientId); clientInfo.onServiceInfoCallbackUnregistered(clientId);
} else { } else {
Log.e(TAG, "Failed to unregister service info callback"); loge("Unregister failed with non-DiscoveryManagerRequest.");
} }
clearRegisteredServiceInfo(clientInfo);
break; break;
} }
case MDNS_SERVICE_EVENT: case MDNS_SERVICE_EVENT:
@@ -904,19 +931,6 @@ public class NsdService extends INsdManager.Stub {
return HANDLED; return HANDLED;
} }
private void notifyResolveFailedResult(boolean isListenedToUpdates, int clientId,
ClientInfo clientInfo, int error) {
if (isListenedToUpdates) {
clientInfo.onServiceInfoCallbackRegistrationFailed(clientId, error);
clearRegisteredServiceInfo(clientInfo);
} else {
// The resolve API always returned FAILURE_INTERNAL_ERROR on error; keep it
// for backwards compatibility.
clientInfo.onResolveServiceFailed(clientId, NsdManager.FAILURE_INTERNAL_ERROR);
clientInfo.mResolvedService = null;
}
}
private boolean handleMDnsServiceEvent(int code, int id, Object obj) { private boolean handleMDnsServiceEvent(int code, int id, Object obj) {
NsdServiceInfo servInfo; NsdServiceInfo servInfo;
ClientInfo clientInfo = mIdToClientInfoMap.get(id); ClientInfo clientInfo = mIdToClientInfoMap.get(id);
@@ -973,8 +987,6 @@ public class NsdService extends INsdManager.Stub {
// found services on the same interface index and their network at the time // found services on the same interface index and their network at the time
setServiceNetworkForCallback(servInfo, lostNetId, info.interfaceIdx); setServiceNetworkForCallback(servInfo, lostNetId, info.interfaceIdx);
clientInfo.onServiceLost(clientId, servInfo); clientInfo.onServiceLost(clientId, servInfo);
// TODO: also support registered service lost when not discovering
clientInfo.maybeNotifyRegisteredServiceLost(servInfo);
break; break;
} }
case IMDnsEventListener.SERVICE_DISCOVERY_FAILED: case IMDnsEventListener.SERVICE_DISCOVERY_FAILED:
@@ -1011,11 +1023,7 @@ public class NsdService extends INsdManager.Stub {
String rest = fullName.substring(index); String rest = fullName.substring(index);
String type = rest.replace(".local.", ""); String type = rest.replace(".local.", "");
final boolean isListenedToUpdates = final NsdServiceInfo serviceInfo = clientInfo.mResolvedService;
clientId == clientInfo.mClientIdForServiceUpdates;
final NsdServiceInfo serviceInfo = isListenedToUpdates
? clientInfo.mRegisteredService : clientInfo.mResolvedService;
serviceInfo.setServiceName(name); serviceInfo.setServiceName(name);
serviceInfo.setServiceType(type); serviceInfo.setServiceType(type);
serviceInfo.setPort(info.port); serviceInfo.setPort(info.port);
@@ -1030,8 +1038,9 @@ public class NsdService extends INsdManager.Stub {
storeLegacyRequestMap(clientId, id2, clientInfo, storeLegacyRequestMap(clientId, id2, clientInfo,
NsdManager.RESOLVE_SERVICE); NsdManager.RESOLVE_SERVICE);
} else { } else {
notifyResolveFailedResult(isListenedToUpdates, clientId, clientInfo, clientInfo.onResolveServiceFailed(
NsdManager.FAILURE_BAD_PARAMETERS); clientId, NsdManager.FAILURE_INTERNAL_ERROR);
clientInfo.mResolvedService = null;
} }
break; break;
} }
@@ -1039,17 +1048,17 @@ public class NsdService extends INsdManager.Stub {
/* NNN resolveId errorCode */ /* NNN resolveId errorCode */
stopResolveService(id); stopResolveService(id);
removeRequestMap(clientId, id, clientInfo); removeRequestMap(clientId, id, clientInfo);
notifyResolveFailedResult( clientInfo.onResolveServiceFailed(
clientId == clientInfo.mClientIdForServiceUpdates, clientId, NsdManager.FAILURE_INTERNAL_ERROR);
clientId, clientInfo, NsdManager.FAILURE_BAD_PARAMETERS); clientInfo.mResolvedService = null;
break; break;
case IMDnsEventListener.SERVICE_GET_ADDR_FAILED: case IMDnsEventListener.SERVICE_GET_ADDR_FAILED:
/* NNN resolveId errorCode */ /* NNN resolveId errorCode */
stopGetAddrInfo(id); stopGetAddrInfo(id);
removeRequestMap(clientId, id, clientInfo); removeRequestMap(clientId, id, clientInfo);
notifyResolveFailedResult( clientInfo.onResolveServiceFailed(
clientId == clientInfo.mClientIdForServiceUpdates, clientId, NsdManager.FAILURE_INTERNAL_ERROR);
clientId, clientInfo, NsdManager.FAILURE_BAD_PARAMETERS); clientInfo.mResolvedService = null;
break; break;
case IMDnsEventListener.SERVICE_GET_ADDR_SUCCESS: { case IMDnsEventListener.SERVICE_GET_ADDR_SUCCESS: {
/* NNN resolveId hostname ttl addr interfaceIdx netId */ /* NNN resolveId hostname ttl addr interfaceIdx netId */
@@ -1066,38 +1075,19 @@ public class NsdService extends INsdManager.Stub {
// If the resolved service is on an interface without a network, consider it // If the resolved service is on an interface without a network, consider it
// as a failure: it would not be usable by apps as they would need // as a failure: it would not be usable by apps as they would need
// privileged permissions. // privileged permissions.
if (clientId == clientInfo.mClientIdForServiceUpdates) { if (netId != NETID_UNSET && serviceHost != null) {
if (netId != NETID_UNSET && serviceHost != null) { clientInfo.mResolvedService.setHost(serviceHost);
setServiceNetworkForCallback(clientInfo.mRegisteredService, setServiceNetworkForCallback(clientInfo.mResolvedService,
netId, info.interfaceIdx); netId, info.interfaceIdx);
final List<InetAddress> addresses = clientInfo.onResolveServiceSucceeded(
clientInfo.mRegisteredService.getHostAddresses(); clientId, clientInfo.mResolvedService);
addresses.add(serviceHost);
clientInfo.mRegisteredService.setHostAddresses(addresses);
clientInfo.onServiceUpdated(
clientId, clientInfo.mRegisteredService);
} else {
stopGetAddrInfo(id);
removeRequestMap(clientId, id, clientInfo);
clearRegisteredServiceInfo(clientInfo);
clientInfo.onServiceInfoCallbackRegistrationFailed(
clientId, NsdManager.FAILURE_BAD_PARAMETERS);
}
} else { } else {
if (netId != NETID_UNSET && serviceHost != null) { clientInfo.onResolveServiceFailed(
clientInfo.mResolvedService.setHost(serviceHost); clientId, NsdManager.FAILURE_INTERNAL_ERROR);
setServiceNetworkForCallback(clientInfo.mResolvedService,
netId, info.interfaceIdx);
clientInfo.onResolveServiceSucceeded(
clientId, clientInfo.mResolvedService);
} else {
clientInfo.onResolveServiceFailed(
clientId, NsdManager.FAILURE_INTERNAL_ERROR);
}
stopGetAddrInfo(id);
removeRequestMap(clientId, id, clientInfo);
clientInfo.mResolvedService = null;
} }
stopGetAddrInfo(id);
removeRequestMap(clientId, id, clientInfo);
clientInfo.mResolvedService = null;
break; break;
} }
default: default:
@@ -1195,6 +1185,42 @@ public class NsdService extends INsdManager.Stub {
stopDiscoveryManagerRequest(request, clientId, transactionId, clientInfo); stopDiscoveryManagerRequest(request, clientId, transactionId, clientInfo);
break; break;
} }
case NsdManager.SERVICE_UPDATED: {
final MdnsServiceInfo serviceInfo = event.mMdnsServiceInfo;
info.setPort(serviceInfo.getPort());
Map<String, String> attrs = serviceInfo.getAttributes();
for (Map.Entry<String, String> kv : attrs.entrySet()) {
final String key = kv.getKey();
try {
info.setAttribute(key, serviceInfo.getAttributeAsBytes(key));
} catch (IllegalArgumentException e) {
Log.e(TAG, "Invalid attribute", e);
}
}
final List<InetAddress> addresses = new ArrayList<>();
for (String ipv4Address : serviceInfo.getIpv4Addresses()) {
try {
addresses.add(InetAddresses.parseNumericAddress(ipv4Address));
} catch (IllegalArgumentException e) {
Log.wtf(TAG, "Invalid ipv4 address", e);
}
}
for (String ipv6Address : serviceInfo.getIpv6Addresses()) {
try {
addresses.add(InetAddresses.parseNumericAddress(ipv6Address));
} catch (IllegalArgumentException e) {
Log.wtf(TAG, "Invalid ipv6 address", e);
}
}
info.setHostAddresses(addresses);
clientInfo.onServiceUpdated(clientId, info);
break;
}
case NsdManager.SERVICE_UPDATED_LOST:
clientInfo.onServiceUpdatedLost(clientId);
break;
default: default:
return false; return false;
} }
@@ -1765,11 +1791,6 @@ public class NsdService extends INsdManager.Stub {
// The target SDK of this client < Build.VERSION_CODES.S // The target SDK of this client < Build.VERSION_CODES.S
private boolean mIsPreSClient = false; private boolean mIsPreSClient = false;
/*** The service that is registered to listen to its updates */
private NsdServiceInfo mRegisteredService;
/*** The client id that listen to updates */
private int mClientIdForServiceUpdates;
private ClientInfo(INsdManagerCallback cb) { private ClientInfo(INsdManagerCallback cb) {
mCb = cb; mCb = cb;
if (DBG) Log.d(TAG, "New client"); if (DBG) Log.d(TAG, "New client");
@@ -1864,18 +1885,6 @@ public class NsdService extends INsdManager.Stub {
return -1; return -1;
} }
private void maybeNotifyRegisteredServiceLost(@NonNull NsdServiceInfo info) {
if (mRegisteredService == null) return;
if (!Objects.equals(mRegisteredService.getServiceName(), info.getServiceName())) return;
// Resolved services have a leading dot appended at the beginning of their type, but in
// discovered info it's at the end
if (!Objects.equals(
mRegisteredService.getServiceType() + ".", "." + info.getServiceType())) {
return;
}
onServiceUpdatedLost(mClientIdForServiceUpdates);
}
void onDiscoverServicesStarted(int listenerKey, NsdServiceInfo info) { void onDiscoverServicesStarted(int listenerKey, NsdServiceInfo info) {
try { try {
mCb.onDiscoverServicesStarted(listenerKey, info); mCb.onDiscoverServicesStarted(listenerKey, info);

View File

@@ -879,7 +879,7 @@ class NsdManagerTest {
@Test @Test
fun testRegisterServiceInfoCallback() { fun testRegisterServiceInfoCallback() {
// This test requires shims supporting U+ APIs (NsdManager.subscribeService) // This test requires shims supporting U+ APIs (NsdManager.registerServiceInfoCallback)
assumeTrue(TestUtils.shouldTestUApis()) assumeTrue(TestUtils.shouldTestUApis())
// Ensure Wi-Fi network connected and get addresses // Ensure Wi-Fi network connected and get addresses
@@ -909,16 +909,14 @@ class NsdManagerTest {
val foundInfo = discoveryRecord.waitForServiceDiscovered( val foundInfo = discoveryRecord.waitForServiceDiscovered(
serviceName, wifiNetwork) serviceName, wifiNetwork)
// Subscribe to service and check the addresses are the same as Wi-Fi addresses // Register service callback and check the addresses are the same as Wi-Fi addresses
nsdShim.registerServiceInfoCallback(nsdManager, foundInfo, { it.run() }, cbRecord) nsdShim.registerServiceInfoCallback(nsdManager, foundInfo, { it.run() }, cbRecord)
for (i in addresses.indices) { val serviceInfoCb = cbRecord.expectCallback<ServiceUpdated>()
val subscribeCb = cbRecord.expectCallback<ServiceUpdated>() assertEquals(foundInfo.serviceName, serviceInfoCb.serviceInfo.serviceName)
assertEquals(foundInfo.serviceName, subscribeCb.serviceInfo.serviceName) val hostAddresses = serviceInfoCb.serviceInfo.hostAddresses
val hostAddresses = subscribeCb.serviceInfo.hostAddresses assertEquals(addresses.size, hostAddresses.size)
assertEquals(i + 1, hostAddresses.size) for (hostAddress in hostAddresses) {
for (hostAddress in hostAddresses) { assertTrue(addresses.contains(hostAddress))
assertTrue(addresses.contains(hostAddress))
}
} }
} cleanupStep { } cleanupStep {
nsdManager.unregisterService(registrationRecord) nsdManager.unregisterService(registrationRecord)

View File

@@ -101,6 +101,7 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations;
import java.net.InetAddress;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@@ -704,119 +705,102 @@ public class NsdServiceTest {
} }
private void verifyUpdatedServiceInfo(NsdServiceInfo info, String serviceName, private void verifyUpdatedServiceInfo(NsdServiceInfo info, String serviceName,
String serviceType, String address, int port, int interfaceIndex, Network network) { String serviceType, List<InetAddress> address, int port, int interfaceIndex,
Network network) {
assertEquals(serviceName, info.getServiceName()); assertEquals(serviceName, info.getServiceName());
assertEquals(serviceType, info.getServiceType()); assertEquals(serviceType, info.getServiceType());
assertTrue(info.getHostAddresses().contains(parseNumericAddress(address))); assertEquals(address, info.getHostAddresses());
assertEquals(port, info.getPort()); assertEquals(port, info.getPort());
assertEquals(network, info.getNetwork()); assertEquals(network, info.getNetwork());
assertEquals(interfaceIndex, info.getInterfaceIndex()); assertEquals(interfaceIndex, info.getInterfaceIndex());
} }
@Test @Test
public void testRegisterAndUnregisterServiceInfoCallback() throws RemoteException { public void testRegisterAndUnregisterServiceInfoCallback() {
final NsdManager client = connectClient(mService); final NsdManager client = connectClient(mService);
final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE); final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE);
final NsdManager.ServiceInfoCallback serviceInfoCallback = mock( final NsdManager.ServiceInfoCallback serviceInfoCallback = mock(
NsdManager.ServiceInfoCallback.class); NsdManager.ServiceInfoCallback.class);
final String serviceTypeWithLocalDomain = SERVICE_TYPE + ".local";
final Network network = new Network(999);
request.setNetwork(network);
client.registerServiceInfoCallback(request, Runnable::run, serviceInfoCallback); client.registerServiceInfoCallback(request, Runnable::run, serviceInfoCallback);
waitForIdle(); waitForIdle();
// Verify the registration callback start.
final ArgumentCaptor<MdnsServiceBrowserListener> listenerCaptor =
ArgumentCaptor.forClass(MdnsServiceBrowserListener.class);
verify(mSocketProvider).startMonitoringSockets();
verify(mDiscoveryManager).registerListener(eq(serviceTypeWithLocalDomain),
listenerCaptor.capture(), argThat(options -> network.equals(options.getNetwork())));
final IMDnsEventListener eventListener = getEventListener(); final MdnsServiceBrowserListener listener = listenerCaptor.getValue();
final ArgumentCaptor<Integer> resolvIdCaptor = ArgumentCaptor.forClass(Integer.class); final MdnsServiceInfo mdnsServiceInfo = new MdnsServiceInfo(
verify(mMockMDnsM).resolve(resolvIdCaptor.capture(), eq(SERVICE_NAME), eq(SERVICE_TYPE), SERVICE_NAME,
eq("local.") /* domain */, eq(IFACE_IDX_ANY)); serviceTypeWithLocalDomain.split("\\."),
List.of(), /* subtypes */
// Resolve service successfully. new String[]{"android", "local"}, /* hostName */
final ResolutionInfo resolutionInfo = new ResolutionInfo(
resolvIdCaptor.getValue(),
IMDnsEventListener.SERVICE_RESOLVED,
null /* serviceName */,
null /* serviceType */,
null /* domain */,
SERVICE_FULL_NAME,
DOMAIN_NAME,
PORT, PORT,
new byte[0] /* txtRecord */, List.of(IPV4_ADDRESS),
IFACE_IDX_ANY); List.of(IPV6_ADDRESS),
doReturn(true).when(mMockMDnsM).getServiceAddress(anyInt(), any(), anyInt()); List.of() /* textStrings */,
eventListener.onServiceResolutionStatus(resolutionInfo); List.of() /* textEntries */,
waitForIdle(); 1234,
network);
final ArgumentCaptor<Integer> getAddrIdCaptor = ArgumentCaptor.forClass(Integer.class);
verify(mMockMDnsM).getServiceAddress(getAddrIdCaptor.capture(), eq(DOMAIN_NAME),
eq(IFACE_IDX_ANY));
// First address info
final String v4Address = "192.0.2.1";
final String v6Address = "2001:db8::";
final GetAddressInfo addressInfo1 = new GetAddressInfo(
getAddrIdCaptor.getValue(),
IMDnsEventListener.SERVICE_GET_ADDR_SUCCESS,
SERVICE_FULL_NAME,
v4Address,
IFACE_IDX_ANY,
999 /* netId */);
eventListener.onGettingServiceAddressStatus(addressInfo1);
waitForIdle();
// Verify onServiceFound callback
listener.onServiceFound(mdnsServiceInfo);
final ArgumentCaptor<NsdServiceInfo> updateInfoCaptor = final ArgumentCaptor<NsdServiceInfo> updateInfoCaptor =
ArgumentCaptor.forClass(NsdServiceInfo.class); ArgumentCaptor.forClass(NsdServiceInfo.class);
verify(serviceInfoCallback, timeout(TIMEOUT_MS).times(1)) verify(serviceInfoCallback, timeout(TIMEOUT_MS).times(1))
.onServiceUpdated(updateInfoCaptor.capture()); .onServiceUpdated(updateInfoCaptor.capture());
verifyUpdatedServiceInfo(updateInfoCaptor.getAllValues().get(0) /* info */, SERVICE_NAME, verifyUpdatedServiceInfo(updateInfoCaptor.getAllValues().get(0) /* info */, SERVICE_NAME,
"." + SERVICE_TYPE, v4Address, PORT, IFACE_IDX_ANY, new Network(999)); SERVICE_TYPE,
List.of(parseNumericAddress(IPV4_ADDRESS), parseNumericAddress(IPV6_ADDRESS)),
PORT, IFACE_IDX_ANY, new Network(999));
// Second address info // Service addresses changed.
final GetAddressInfo addressInfo2 = new GetAddressInfo( final String v4Address = "192.0.2.1";
getAddrIdCaptor.getValue(), final String v6Address = "2001:db8::1";
IMDnsEventListener.SERVICE_GET_ADDR_SUCCESS, final MdnsServiceInfo updatedServiceInfo = new MdnsServiceInfo(
SERVICE_FULL_NAME, SERVICE_NAME,
v6Address, serviceTypeWithLocalDomain.split("\\."),
IFACE_IDX_ANY, List.of(), /* subtypes */
999 /* netId */); new String[]{"android", "local"}, /* hostName */
eventListener.onGettingServiceAddressStatus(addressInfo2); PORT,
waitForIdle(); List.of(v4Address),
List.of(v6Address),
List.of() /* textStrings */,
List.of() /* textEntries */,
1234,
network);
// Verify onServiceUpdated callback.
listener.onServiceUpdated(updatedServiceInfo);
verify(serviceInfoCallback, timeout(TIMEOUT_MS).times(2)) verify(serviceInfoCallback, timeout(TIMEOUT_MS).times(2))
.onServiceUpdated(updateInfoCaptor.capture()); .onServiceUpdated(updateInfoCaptor.capture());
verifyUpdatedServiceInfo(updateInfoCaptor.getAllValues().get(1) /* info */, SERVICE_NAME, verifyUpdatedServiceInfo(updateInfoCaptor.getAllValues().get(2) /* info */, SERVICE_NAME,
"." + SERVICE_TYPE, v6Address, PORT, IFACE_IDX_ANY, new Network(999)); SERVICE_TYPE,
List.of(parseNumericAddress(v4Address), parseNumericAddress(v6Address)),
PORT, IFACE_IDX_ANY, new Network(999));
// Verify service callback unregistration.
client.unregisterServiceInfoCallback(serviceInfoCallback); client.unregisterServiceInfoCallback(serviceInfoCallback);
waitForIdle(); waitForIdle();
verify(serviceInfoCallback, timeout(TIMEOUT_MS)).onServiceInfoCallbackUnregistered(); verify(serviceInfoCallback, timeout(TIMEOUT_MS)).onServiceInfoCallbackUnregistered();
} }
@Test @Test
public void testRegisterServiceCallbackFailed() throws Exception { public void testRegisterServiceCallbackFailed() {
final NsdManager client = connectClient(mService); final NsdManager client = connectClient(mService);
final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE); final String invalidServiceType = "a_service";
final NsdManager.ServiceInfoCallback subscribeListener = mock( final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, invalidServiceType);
final NsdManager.ServiceInfoCallback serviceInfoCallback = mock(
NsdManager.ServiceInfoCallback.class); NsdManager.ServiceInfoCallback.class);
client.registerServiceInfoCallback(request, Runnable::run, subscribeListener); client.registerServiceInfoCallback(request, Runnable::run, serviceInfoCallback);
waitForIdle(); waitForIdle();
final IMDnsEventListener eventListener = getEventListener(); // Fail to register service callback.
final ArgumentCaptor<Integer> resolvIdCaptor = ArgumentCaptor.forClass(Integer.class); verify(serviceInfoCallback, timeout(TIMEOUT_MS))
verify(mMockMDnsM).resolve(resolvIdCaptor.capture(), eq(SERVICE_NAME), eq(SERVICE_TYPE),
eq("local.") /* domain */, eq(IFACE_IDX_ANY));
// Fail to resolve service.
final ResolutionInfo resolutionFailedInfo = new ResolutionInfo(
resolvIdCaptor.getValue(),
IMDnsEventListener.SERVICE_RESOLUTION_FAILED,
null /* serviceName */,
null /* serviceType */,
null /* domain */,
null /* serviceFullName */,
null /* domainName */,
0 /* port */,
new byte[0] /* txtRecord */,
IFACE_IDX_ANY);
eventListener.onServiceResolutionStatus(resolutionFailedInfo);
verify(subscribeListener, timeout(TIMEOUT_MS))
.onServiceInfoCallbackRegistrationFailed(eq(FAILURE_BAD_PARAMETERS)); .onServiceInfoCallbackRegistrationFailed(eq(FAILURE_BAD_PARAMETERS));
} }