diff --git a/service-t/src/com/android/server/NsdService.java b/service-t/src/com/android/server/NsdService.java index 8f672a1911..2386b4f258 100644 --- a/service-t/src/com/android/server/NsdService.java +++ b/service-t/src/com/android/server/NsdService.java @@ -824,596 +824,615 @@ public class NsdService extends INsdManager.Stub { @Override public boolean processMessage(Message msg) { - final ClientInfo clientInfo; - final int transactionId; final int clientRequestId = msg.arg2; - final OffloadEngineInfo offloadEngineInfo; switch (msg.what) { - case NsdManager.DISCOVER_SERVICES: { - if (DBG) Log.d(TAG, "Discover services"); - final DiscoveryArgs discoveryArgs = (DiscoveryArgs) msg.obj; - clientInfo = mClients.get(discoveryArgs.connector); - // If the binder death notification for a INsdManagerCallback was received - // before any calls are received by NsdService, the clientInfo would be - // cleared and cause NPE. Add a null check here to prevent this corner case. - if (clientInfo == null) { - Log.e(TAG, "Unknown connector in discovery"); - break; - } - - if (requestLimitReached(clientInfo)) { - clientInfo.onDiscoverServicesFailedImmediately(clientRequestId, - NsdManager.FAILURE_MAX_LIMIT, true /* isLegacy */); - break; - } - - final DiscoveryRequest discoveryRequest = discoveryArgs.discoveryRequest; - transactionId = getUniqueId(); - final Pair> typeAndSubtype = - parseTypeAndSubtype(discoveryRequest.getServiceType()); - final String serviceType = typeAndSubtype == null - ? null : typeAndSubtype.first; - if (clientInfo.mUseJavaBackend - || mDeps.isMdnsDiscoveryManagerEnabled(mContext) - || useDiscoveryManagerForType(serviceType)) { - if (serviceType == null || typeAndSubtype.second.size() > 1) { - clientInfo.onDiscoverServicesFailedImmediately(clientRequestId, - NsdManager.FAILURE_INTERNAL_ERROR, false /* isLegacy */); - break; - } - - String subtype = discoveryRequest.getSubtype(); - if (subtype == null && !typeAndSubtype.second.isEmpty()) { - subtype = typeAndSubtype.second.get(0); - } - - if (subtype != null && !checkSubtypeLabel(subtype)) { - clientInfo.onDiscoverServicesFailedImmediately(clientRequestId, - NsdManager.FAILURE_BAD_PARAMETERS, false /* isLegacy */); - break; - } - - final String listenServiceType = serviceType + ".local"; - maybeStartMonitoringSockets(); - final MdnsListener listener = new DiscoveryListener(clientRequestId, - transactionId, listenServiceType); - final MdnsSearchOptions.Builder optionsBuilder = - MdnsSearchOptions.newBuilder() - .setNetwork(discoveryRequest.getNetwork()) - .setRemoveExpiredService(true) - .setQueryMode( - mMdnsFeatureFlags.isAggressiveQueryModeEnabled() - ? AGGRESSIVE_QUERY_MODE - : PASSIVE_QUERY_MODE); - if (subtype != null) { - // checkSubtypeLabels() ensures that subtypes start with '_' but - // MdnsSearchOptions expects the underscore to not be present. - optionsBuilder.addSubtype(subtype.substring(1)); - } - mMdnsDiscoveryManager.registerListener( - listenServiceType, listener, optionsBuilder.build()); - final ClientRequest request = storeDiscoveryManagerRequestMap( - clientRequestId, transactionId, listener, clientInfo, - discoveryRequest.getNetwork()); - clientInfo.onDiscoverServicesStarted( - clientRequestId, discoveryRequest, request); - clientInfo.log("Register a DiscoveryListener " + transactionId - + " for service type:" + listenServiceType); - } else { - maybeStartDaemon(); - if (discoverServices(transactionId, discoveryRequest)) { - if (DBG) { - Log.d(TAG, "Discover " + msg.arg2 + " " + transactionId - + discoveryRequest.getServiceType()); - } - final ClientRequest request = storeLegacyRequestMap(clientRequestId, - transactionId, clientInfo, msg.what, - mClock.elapsedRealtime()); - clientInfo.onDiscoverServicesStarted( - clientRequestId, discoveryRequest, request); - } else { - stopServiceDiscovery(transactionId); - clientInfo.onDiscoverServicesFailedImmediately(clientRequestId, - NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */); - } - } - break; - } - case NsdManager.STOP_DISCOVERY: { - if (DBG) Log.d(TAG, "Stop service discovery"); - final ListenerArgs args = (ListenerArgs) msg.obj; - clientInfo = mClients.get(args.connector); - // If the binder death notification for a INsdManagerCallback was received - // before any calls are received by NsdService, the clientInfo would be - // cleared and cause NPE. Add a null check here to prevent this corner case. - if (clientInfo == null) { - Log.e(TAG, "Unknown connector in stop discovery"); - break; - } - - final ClientRequest request = - clientInfo.mClientRequests.get(clientRequestId); - if (request == null) { - Log.e(TAG, "Unknown client request in STOP_DISCOVERY"); - break; - } - transactionId = request.mTransactionId; - // Note isMdnsDiscoveryManagerEnabled may have changed to false at this - // point, so this needs to check the type of the original request to - // unregister instead of looking at the flag value. - if (request instanceof DiscoveryManagerRequest) { - stopDiscoveryManagerRequest( - request, clientRequestId, transactionId, clientInfo); - clientInfo.onStopDiscoverySucceeded(clientRequestId, request); - clientInfo.log("Unregister the DiscoveryListener " + transactionId); - } else { - removeRequestMap(clientRequestId, transactionId, clientInfo); - if (stopServiceDiscovery(transactionId)) { - clientInfo.onStopDiscoverySucceeded(clientRequestId, request); - } else { - clientInfo.onStopDiscoveryFailed( - clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR); - } - } - break; - } - case NsdManager.REGISTER_SERVICE: { - if (DBG) Log.d(TAG, "Register service"); - final AdvertisingArgs args = (AdvertisingArgs) msg.obj; - clientInfo = mClients.get(args.connector); - // If the binder death notification for a INsdManagerCallback was received - // before any calls are received by NsdService, the clientInfo would be - // cleared and cause NPE. Add a null check here to prevent this corner case. - if (clientInfo == null) { - Log.e(TAG, "Unknown connector in registration"); - break; - } - - if (requestLimitReached(clientInfo)) { - clientInfo.onRegisterServiceFailedImmediately(clientRequestId, - NsdManager.FAILURE_MAX_LIMIT, true /* isLegacy */); - break; - } - final AdvertisingRequest advertisingRequest = args.advertisingRequest; - if (advertisingRequest == null) { - Log.e(TAG, "Unknown advertisingRequest in registration"); - break; - } - final NsdServiceInfo serviceInfo = advertisingRequest.getServiceInfo(); - final String serviceType = serviceInfo.getServiceType(); - final Pair> typeSubtype = parseTypeAndSubtype( - serviceType); - final String registerServiceType = typeSubtype == null - ? null : typeSubtype.first; - final String hostname = serviceInfo.getHostname(); - // Keep compatible with the legacy behavior: It's allowed to set host - // addresses for a service registration although the host addresses - // won't be registered. To register the addresses for a host, the - // hostname must be specified. - if (hostname == null) { - serviceInfo.setHostAddresses(Collections.emptyList()); - } - if (clientInfo.mUseJavaBackend - || mDeps.isMdnsAdvertiserEnabled(mContext) - || useAdvertiserForType(registerServiceType)) { - if (serviceType != null && registerServiceType == null) { - Log.e(TAG, "Invalid service type: " + serviceType); - clientInfo.onRegisterServiceFailedImmediately(clientRequestId, - NsdManager.FAILURE_INTERNAL_ERROR, false /* isLegacy */); - break; - } - boolean isUpdateOnly = (advertisingRequest.getFlags() - & AdvertisingRequest.NSD_ADVERTISING_UPDATE_ONLY) > 0; - // If it is an update request, then reuse the old transactionId - if (isUpdateOnly) { - final ClientRequest existingClientRequest = - clientInfo.mClientRequests.get(clientRequestId); - if (existingClientRequest == null) { - Log.e(TAG, "Invalid update on requestId: " + clientRequestId); - clientInfo.onRegisterServiceFailedImmediately(clientRequestId, - NsdManager.FAILURE_INTERNAL_ERROR, - false /* isLegacy */); - break; - } - transactionId = existingClientRequest.mTransactionId; - } else { - transactionId = getUniqueId(); - } - - if (registerServiceType != null) { - serviceInfo.setServiceType(registerServiceType); - serviceInfo.setServiceName( - truncateServiceName(serviceInfo.getServiceName())); - } - - if (!checkHostname(hostname)) { - clientInfo.onRegisterServiceFailedImmediately(clientRequestId, - NsdManager.FAILURE_BAD_PARAMETERS, false /* isLegacy */); - break; - } - - if (!checkPublicKey(serviceInfo.getPublicKey())) { - Log.e(TAG, - "Invalid public key: " - + Arrays.toString(serviceInfo.getPublicKey())); - clientInfo.onRegisterServiceFailedImmediately( - clientRequestId, - NsdManager.FAILURE_BAD_PARAMETERS, - false /* isLegacy */); - break; - } - - Set subtypes = new ArraySet<>(serviceInfo.getSubtypes()); - if (typeSubtype != null && typeSubtype.second != null) { - for (String subType : typeSubtype.second) { - if (!TextUtils.isEmpty(subType)) { - subtypes.add(subType); - } - } - } - subtypes = dedupSubtypeLabels(subtypes); - - if (!checkSubtypeLabels(subtypes)) { - clientInfo.onRegisterServiceFailedImmediately(clientRequestId, - NsdManager.FAILURE_BAD_PARAMETERS, false /* isLegacy */); - break; - } - - if (!checkTtl(advertisingRequest.getTtl(), clientInfo)) { - clientInfo.onRegisterServiceFailedImmediately(clientRequestId, - NsdManager.FAILURE_BAD_PARAMETERS, false /* isLegacy */); - break; - } - final boolean isOffloadOnly = - (advertisingRequest.getFlags() & FLAG_OFFLOAD_ONLY) != 0; - if (isOffloadOnly && !isOffloadOnlyAllowed()) { - clientInfo.onRegisterServiceFailedImmediately(clientRequestId, - NsdManager.FAILURE_BAD_PARAMETERS, false /* isLegacy */); - break; - } - - serviceInfo.setSubtypes(subtypes); - maybeStartMonitoringSockets(); - final boolean skipProbing = (advertisingRequest.getFlags() - & FLAG_SKIP_PROBING) != 0; - final boolean skipSubtypeAnnouncements = (advertisingRequest.getFlags() - & FLAG_SKIP_SUBTYPE_ANNOUNCEMENTS) != 0; - final MdnsAdvertisingOptions mdnsAdvertisingOptions = - MdnsAdvertisingOptions.newBuilder() - .setIsOnlyUpdate(isUpdateOnly) - .setSkipProbing(skipProbing) - .setTtl(advertisingRequest.getTtl()) - .setSkipSubtypeAnnouncements(skipSubtypeAnnouncements) - .setOffloadOnly(isOffloadOnly) - .build(); - mAdvertiser.addOrUpdateService(transactionId, serviceInfo, - mdnsAdvertisingOptions, clientInfo.mUid); - storeAdvertiserRequestMap(clientRequestId, transactionId, clientInfo, - serviceInfo); - } else { - maybeStartDaemon(); - transactionId = getUniqueId(); - if (registerService(transactionId, serviceInfo)) { - if (DBG) { - Log.d(TAG, "Register " + clientRequestId - + " " + transactionId); - } - storeLegacyRequestMap(clientRequestId, transactionId, clientInfo, - msg.what, mClock.elapsedRealtime()); - // Return success after mDns reports success - } else { - unregisterService(transactionId); - clientInfo.onRegisterServiceFailedImmediately(clientRequestId, - NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */); - } - - } - break; - } - case NsdManager.UNREGISTER_SERVICE: { - if (DBG) Log.d(TAG, "unregister service"); - final ListenerArgs args = (ListenerArgs) msg.obj; - clientInfo = mClients.get(args.connector); - // If the binder death notification for a INsdManagerCallback was received - // before any calls are received by NsdService, the clientInfo would be - // cleared and cause NPE. Add a null check here to prevent this corner case. - if (clientInfo == null) { - Log.e(TAG, "Unknown connector in unregistration"); - break; - } - final ClientRequest request = - clientInfo.mClientRequests.get(clientRequestId); - if (request == null) { - Log.e(TAG, "Unknown client request in UNREGISTER_SERVICE"); - break; - } - transactionId = request.mTransactionId; - removeRequestMap(clientRequestId, transactionId, clientInfo); - - // Note isMdnsAdvertiserEnabled may have changed to false at this point, - // so this needs to check the type of the original request to unregister - // instead of looking at the flag value. - if (request instanceof AdvertiserClientRequest) { - final AdvertiserMetrics metrics = - mAdvertiser.getAdvertiserMetrics(transactionId); - mAdvertiser.removeService(transactionId); - clientInfo.onUnregisterServiceSucceeded( - clientRequestId, request, metrics); - } else { - if (unregisterService(transactionId)) { - clientInfo.onUnregisterServiceSucceeded(clientRequestId, request, - new AdvertiserMetrics(NO_PACKET /* repliedRequestsCount */, - NO_PACKET /* sentPacketCount */, - 0 /* conflictDuringProbingCount */, - 0 /* conflictAfterProbingCount */)); - } else { - clientInfo.onUnregisterServiceFailed( - clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR); - } - } - break; - } - case NsdManager.RESOLVE_SERVICE: { - if (DBG) Log.d(TAG, "Resolve service"); - final ListenerArgs args = (ListenerArgs) msg.obj; - clientInfo = mClients.get(args.connector); - // If the binder death notification for a INsdManagerCallback was received - // before any calls are received by NsdService, the clientInfo would be - // cleared and cause NPE. Add a null check here to prevent this corner case. - if (clientInfo == null) { - Log.e(TAG, "Unknown connector in resolution"); - break; - } - - final NsdServiceInfo info = args.serviceInfo; - transactionId = getUniqueId(); - final Pair> typeSubtype = - parseTypeAndSubtype(info.getServiceType()); - final String serviceType = typeSubtype == null - ? null : typeSubtype.first; - if (clientInfo.mUseJavaBackend - || mDeps.isMdnsDiscoveryManagerEnabled(mContext) - || useDiscoveryManagerForType(serviceType)) { - if (serviceType == null) { - clientInfo.onResolveServiceFailedImmediately(clientRequestId, - NsdManager.FAILURE_INTERNAL_ERROR, false /* isLegacy */); - break; - } - final String resolveServiceType = serviceType + ".local"; - - maybeStartMonitoringSockets(); - final MdnsListener listener = new ResolutionListener(clientRequestId, - transactionId, resolveServiceType, info.getServiceName()); - final int ifaceIdx = info.getNetwork() != null - ? 0 : info.getInterfaceIndex(); - final MdnsSearchOptions options = MdnsSearchOptions.newBuilder() - .setNetwork(info.getNetwork()) - .setInterfaceIndex(ifaceIdx) - .setQueryMode(mMdnsFeatureFlags.isAggressiveQueryModeEnabled() - ? AGGRESSIVE_QUERY_MODE - : PASSIVE_QUERY_MODE) - .setResolveInstanceName(info.getServiceName()) - .setRemoveExpiredService(true) - .build(); - mMdnsDiscoveryManager.registerListener( - resolveServiceType, listener, options); - storeDiscoveryManagerRequestMap(clientRequestId, transactionId, - listener, clientInfo, info.getNetwork()); - clientInfo.log("Register a ResolutionListener " + transactionId - + " for service type:" + resolveServiceType); - } else { - if (clientInfo.mResolvedService != null) { - clientInfo.onResolveServiceFailedImmediately(clientRequestId, - NsdManager.FAILURE_ALREADY_ACTIVE, true /* isLegacy */); - break; - } - - maybeStartDaemon(); - if (resolveService(transactionId, info)) { - clientInfo.mResolvedService = new NsdServiceInfo(); - storeLegacyRequestMap(clientRequestId, transactionId, clientInfo, - msg.what, mClock.elapsedRealtime()); - } else { - clientInfo.onResolveServiceFailedImmediately(clientRequestId, - NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */); - } - } - break; - } - case NsdManager.STOP_RESOLUTION: { - if (DBG) Log.d(TAG, "Stop service resolution"); - final ListenerArgs args = (ListenerArgs) msg.obj; - clientInfo = mClients.get(args.connector); - // If the binder death notification for a INsdManagerCallback was received - // before any calls are received by NsdService, the clientInfo would be - // cleared and cause NPE. Add a null check here to prevent this corner case. - if (clientInfo == null) { - Log.e(TAG, "Unknown connector in stop resolution"); - break; - } - - final ClientRequest request = - clientInfo.mClientRequests.get(clientRequestId); - if (request == null) { - Log.e(TAG, "Unknown client request in STOP_RESOLUTION"); - break; - } - transactionId = request.mTransactionId; - // Note isMdnsDiscoveryManagerEnabled may have changed to false at this - // point, so this needs to check the type of the original request to - // unregister instead of looking at the flag value. - if (request instanceof DiscoveryManagerRequest) { - stopDiscoveryManagerRequest( - request, clientRequestId, transactionId, clientInfo); - clientInfo.onStopResolutionSucceeded(clientRequestId, request); - clientInfo.log("Unregister the ResolutionListener " + transactionId); - } else { - removeRequestMap(clientRequestId, transactionId, clientInfo); - if (stopResolveService(transactionId)) { - clientInfo.onStopResolutionSucceeded(clientRequestId, request); - } else { - clientInfo.onStopResolutionFailed( - clientRequestId, NsdManager.FAILURE_OPERATION_NOT_RUNNING); - } - clientInfo.mResolvedService = null; - } - break; - } - case NsdManager.REGISTER_SERVICE_CALLBACK: { - if (DBG) Log.d(TAG, "Register a service callback"); - final ListenerArgs args = (ListenerArgs) msg.obj; - clientInfo = mClients.get(args.connector); - // If the binder death notification for a INsdManagerCallback was received - // before any calls are received by NsdService, the clientInfo would be - // cleared and cause NPE. Add a null check here to prevent this corner case. - if (clientInfo == null) { - Log.e(TAG, "Unknown connector in callback registration"); - break; - } - - final NsdServiceInfo info = args.serviceInfo; - transactionId = getUniqueId(); - final Pair> typeAndSubtype = - parseTypeAndSubtype(info.getServiceType()); - final String serviceType = typeAndSubtype == null - ? null : typeAndSubtype.first; - if (serviceType == null) { - clientInfo.onServiceInfoCallbackRegistrationFailed(clientRequestId, - NsdManager.FAILURE_BAD_PARAMETERS); - break; - } - final String resolveServiceType = serviceType + ".local"; - - maybeStartMonitoringSockets(); - final MdnsListener listener = new ServiceInfoListener(clientRequestId, - transactionId, resolveServiceType, info.getServiceName()); - final int ifIndex = info.getNetwork() != null - ? 0 : info.getInterfaceIndex(); - final MdnsSearchOptions options = MdnsSearchOptions.newBuilder() - .setNetwork(info.getNetwork()) - .setInterfaceIndex(ifIndex) - .setQueryMode(mMdnsFeatureFlags.isAggressiveQueryModeEnabled() - ? AGGRESSIVE_QUERY_MODE - : PASSIVE_QUERY_MODE) - .setResolveInstanceName(info.getServiceName()) - .setRemoveExpiredService(true) - .build(); - mMdnsDiscoveryManager.registerListener( - resolveServiceType, listener, options); - storeDiscoveryManagerRequestMap(clientRequestId, transactionId, listener, - clientInfo, info.getNetwork()); - clientInfo.onServiceInfoCallbackRegistered(transactionId); - clientInfo.log("Register a ServiceInfoListener " + transactionId - + " for service type:" + resolveServiceType); - break; - } - case NsdManager.UNREGISTER_SERVICE_CALLBACK: { - if (DBG) Log.d(TAG, "Unregister a service callback"); - final ListenerArgs args = (ListenerArgs) msg.obj; - clientInfo = mClients.get(args.connector); - // If the binder death notification for a INsdManagerCallback was received - // before any calls are received by NsdService, the clientInfo would be - // cleared and cause NPE. Add a null check here to prevent this corner case. - if (clientInfo == null) { - Log.e(TAG, "Unknown connector in callback unregistration"); - break; - } - - final ClientRequest request = - clientInfo.mClientRequests.get(clientRequestId); - if (request == null) { - Log.e(TAG, "Unknown client request in UNREGISTER_SERVICE_CALLBACK"); - break; - } - transactionId = request.mTransactionId; - if (request instanceof DiscoveryManagerRequest) { - stopDiscoveryManagerRequest( - request, clientRequestId, transactionId, clientInfo); - clientInfo.onServiceInfoCallbackUnregistered(clientRequestId, request); - clientInfo.log("Unregister the ServiceInfoListener " + transactionId); - } else { - loge("Unregister failed with non-DiscoveryManagerRequest."); - } - break; - } - case MDNS_SERVICE_EVENT: + case NsdManager.DISCOVER_SERVICES -> handleDiscoverServices(clientRequestId, + (DiscoveryArgs) msg.obj); + case NsdManager.STOP_DISCOVERY -> handleStopDiscovery(clientRequestId, + (ListenerArgs) msg.obj); + case NsdManager.REGISTER_SERVICE -> handleRegisterService(clientRequestId, + (AdvertisingArgs) msg.obj); + case NsdManager.UNREGISTER_SERVICE -> handleUnregisterService(clientRequestId, + (ListenerArgs) msg.obj); + case NsdManager.RESOLVE_SERVICE -> handleResolveService(clientRequestId, + (ListenerArgs) msg.obj); + case NsdManager.STOP_RESOLUTION -> handleStopResolution(clientRequestId, + (ListenerArgs) msg.obj); + case NsdManager.REGISTER_SERVICE_CALLBACK -> handleRegisterServiceCallback( + clientRequestId, (ListenerArgs) msg.obj); + case NsdManager.UNREGISTER_SERVICE_CALLBACK -> handleUnregisterServiceCallback( + clientRequestId, (ListenerArgs) msg.obj); + case MDNS_SERVICE_EVENT -> { if (!handleMDnsServiceEvent(msg.arg1, msg.arg2, msg.obj)) { return NOT_HANDLED; } - break; - case MDNS_DISCOVERY_MANAGER_EVENT: + } + case MDNS_DISCOVERY_MANAGER_EVENT -> { if (!handleMdnsDiscoveryManagerEvent(msg.arg1, msg.arg2, msg.obj)) { return NOT_HANDLED; } - break; - case NsdManager.REGISTER_OFFLOAD_ENGINE: - offloadEngineInfo = (OffloadEngineInfo) msg.obj; - clientInfo = mClients.get(offloadEngineInfo.mConnector); - if (clientInfo == null) { - Log.e(TAG, "Unknown connector in calls to register offload engine"); - break; - } - clientInfo.markIsOffloadEngine(); - // TODO: Limits the number of registrations created by a given class. - mOffloadEngines.register(offloadEngineInfo.mOffloadEngine, - offloadEngineInfo); - sendAllOffloadServiceInfos(offloadEngineInfo); - break; - case NsdManager.UNREGISTER_OFFLOAD_ENGINE: - mOffloadEngines.unregister((IOffloadEngine) msg.obj); - break; - case NsdManager.REGISTER_CLIENT: - final ConnectorArgs arg = (ConnectorArgs) msg.obj; - final INsdManagerCallback cb = arg.callback; - try { - cb.asBinder().linkToDeath(arg.connector, 0); - final String tag = "Client" + arg.uid + "-" + mClientNumberId++; - final NetworkNsdReportedMetrics metrics = - mDeps.makeNetworkNsdReportedMetrics( - (int) mClock.elapsedRealtime(), arg.uid); - clientInfo = new ClientInfo(cb, arg.uid, arg.useJavaBackend, - mServiceLogs.forSubComponent(tag), metrics); - mClients.put(arg.connector, clientInfo); - } catch (RemoteException e) { - Log.w(TAG, "Client request id " + clientRequestId - + " has already died"); - } - break; - case NsdManager.UNREGISTER_CLIENT: - final NsdServiceConnector connector = (NsdServiceConnector) msg.obj; - clientInfo = mClients.remove(connector); - if (clientInfo != null) { - clientInfo.expungeAllRequests(); - if (clientInfo.isPreSClient()) { - mLegacyClientCount -= 1; - } - } - maybeStopMonitoringSocketsIfNoActiveRequest(); - maybeScheduleStop(); - break; - case NsdManager.DAEMON_CLEANUP: - maybeStopDaemon(); - break; + } + case NsdManager.REGISTER_OFFLOAD_ENGINE -> handleRegisterOffloadEngine( + (OffloadEngineInfo) msg.obj); + case NsdManager.UNREGISTER_OFFLOAD_ENGINE -> handleUnregisterOffloadEngine( + (IOffloadEngine) msg.obj); + case NsdManager.REGISTER_CLIENT -> handleRegisterClient(clientRequestId, + (ConnectorArgs) msg.obj); + case NsdManager.UNREGISTER_CLIENT -> handleUnregisterClient( + (NsdServiceConnector) msg.obj); + case NsdManager.DAEMON_CLEANUP -> handleDaemonCleanup(); + // This event should be only sent by the legacy (target SDK < S) clients. // Mark the sending client as legacy. - case NsdManager.DAEMON_STARTUP: - clientInfo = getClientInfoForReply(msg); - if (clientInfo != null) { - cancelStop(); - clientInfo.setPreSClient(); - mLegacyClientCount += 1; - maybeStartDaemon(); - } - break; - default: + case NsdManager.DAEMON_STARTUP -> handleDaemonStartup((ListenerArgs) msg.obj); + default -> { Log.wtf(TAG, "Unhandled " + msg); return NOT_HANDLED; + } } return HANDLED; } + private void handleDiscoverServices(int clientRequestId, DiscoveryArgs discoveryArgs) { + if (DBG) Log.d(TAG, "Discover services"); + final ClientInfo clientInfo = mClients.get(discoveryArgs.connector); + // If the binder death notification for a INsdManagerCallback was received + // before any calls are received by NsdService, the clientInfo would be + // cleared and cause NPE. Add a null check here to prevent this corner case. + if (clientInfo == null) { + Log.e(TAG, "Unknown connector in discovery"); + return; + } + + if (requestLimitReached(clientInfo)) { + clientInfo.onDiscoverServicesFailedImmediately(clientRequestId, + NsdManager.FAILURE_MAX_LIMIT, true /* isLegacy */); + return; + } + + final DiscoveryRequest discoveryRequest = discoveryArgs.discoveryRequest; + final int transactionId = getUniqueId(); + final Pair> typeAndSubtype = + parseTypeAndSubtype(discoveryRequest.getServiceType()); + final String serviceType = typeAndSubtype == null + ? null : typeAndSubtype.first; + if (clientInfo.mUseJavaBackend + || mDeps.isMdnsDiscoveryManagerEnabled(mContext) + || useDiscoveryManagerForType(serviceType)) { + if (serviceType == null || typeAndSubtype.second.size() > 1) { + clientInfo.onDiscoverServicesFailedImmediately(clientRequestId, + NsdManager.FAILURE_INTERNAL_ERROR, false /* isLegacy */); + return; + } + + String subtype = discoveryRequest.getSubtype(); + if (subtype == null && !typeAndSubtype.second.isEmpty()) { + subtype = typeAndSubtype.second.get(0); + } + + if (subtype != null && !checkSubtypeLabel(subtype)) { + clientInfo.onDiscoverServicesFailedImmediately(clientRequestId, + NsdManager.FAILURE_BAD_PARAMETERS, false /* isLegacy */); + return; + } + + final String listenServiceType = serviceType + ".local"; + maybeStartMonitoringSockets(); + final MdnsListener listener = new DiscoveryListener(clientRequestId, + transactionId, listenServiceType); + final MdnsSearchOptions.Builder optionsBuilder = + MdnsSearchOptions.newBuilder() + .setNetwork(discoveryRequest.getNetwork()) + .setRemoveExpiredService(true) + .setQueryMode( + mMdnsFeatureFlags.isAggressiveQueryModeEnabled() + ? AGGRESSIVE_QUERY_MODE + : PASSIVE_QUERY_MODE); + if (subtype != null) { + // checkSubtypeLabels() ensures that subtypes start with '_' but + // MdnsSearchOptions expects the underscore to not be present. + optionsBuilder.addSubtype(subtype.substring(1)); + } + mMdnsDiscoveryManager.registerListener( + listenServiceType, listener, optionsBuilder.build()); + final ClientRequest request = storeDiscoveryManagerRequestMap( + clientRequestId, transactionId, listener, clientInfo, + discoveryRequest.getNetwork()); + clientInfo.onDiscoverServicesStarted( + clientRequestId, discoveryRequest, request); + clientInfo.log("Register a DiscoveryListener " + transactionId + + " for service type:" + listenServiceType); + } else { + maybeStartDaemon(); + if (discoverServices(transactionId, discoveryRequest)) { + if (DBG) { + Log.d(TAG, "Discover " + clientRequestId + " " + transactionId + + discoveryRequest.getServiceType()); + } + final ClientRequest request = storeLegacyRequestMap(clientRequestId, + transactionId, clientInfo, NsdManager.DISCOVER_SERVICES, + mClock.elapsedRealtime()); + clientInfo.onDiscoverServicesStarted( + clientRequestId, discoveryRequest, request); + } else { + stopServiceDiscovery(transactionId); + clientInfo.onDiscoverServicesFailedImmediately(clientRequestId, + NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */); + } + } + } + + private void handleStopDiscovery(int clientRequestId, ListenerArgs args) { + if (DBG) Log.d(TAG, "Stop service discovery"); + final ClientInfo clientInfo = mClients.get(args.connector); + // If the binder death notification for a INsdManagerCallback was received + // before any calls are received by NsdService, the clientInfo would be + // cleared and cause NPE. Add a null check here to prevent this corner case. + if (clientInfo == null) { + Log.e(TAG, "Unknown connector in stop discovery"); + return; + } + + final ClientRequest request = + clientInfo.mClientRequests.get(clientRequestId); + if (request == null) { + Log.e(TAG, "Unknown client request in STOP_DISCOVERY"); + return; + } + final int transactionId = request.mTransactionId; + // Note isMdnsDiscoveryManagerEnabled may have changed to false at this + // point, so this needs to check the type of the original request to + // unregister instead of looking at the flag value. + if (request instanceof DiscoveryManagerRequest) { + stopDiscoveryManagerRequest( + request, clientRequestId, transactionId, clientInfo); + clientInfo.onStopDiscoverySucceeded(clientRequestId, request); + clientInfo.log("Unregister the DiscoveryListener " + transactionId); + } else { + removeRequestMap(clientRequestId, transactionId, clientInfo); + if (stopServiceDiscovery(transactionId)) { + clientInfo.onStopDiscoverySucceeded(clientRequestId, request); + } else { + clientInfo.onStopDiscoveryFailed( + clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR); + } + } + } + + private void handleRegisterService(int clientRequestId, AdvertisingArgs args) { + if (DBG) Log.d(TAG, "Register service"); + final ClientInfo clientInfo = mClients.get(args.connector); + // If the binder death notification for a INsdManagerCallback was received + // before any calls are received by NsdService, the clientInfo would be + // cleared and cause NPE. Add a null check here to prevent this corner case. + if (clientInfo == null) { + Log.e(TAG, "Unknown connector in registration"); + return; + } + + if (requestLimitReached(clientInfo)) { + clientInfo.onRegisterServiceFailedImmediately(clientRequestId, + NsdManager.FAILURE_MAX_LIMIT, true /* isLegacy */); + return; + } + final AdvertisingRequest advertisingRequest = args.advertisingRequest; + if (advertisingRequest == null) { + Log.e(TAG, "Unknown advertisingRequest in registration"); + return; + } + final NsdServiceInfo serviceInfo = advertisingRequest.getServiceInfo(); + final String serviceType = serviceInfo.getServiceType(); + final Pair> typeSubtype = parseTypeAndSubtype( + serviceType); + final String registerServiceType = typeSubtype == null + ? null : typeSubtype.first; + final String hostname = serviceInfo.getHostname(); + // Keep compatible with the legacy behavior: It's allowed to set host + // addresses for a service registration although the host addresses + // won't be registered. To register the addresses for a host, the + // hostname must be specified. + if (hostname == null) { + serviceInfo.setHostAddresses(Collections.emptyList()); + } + if (clientInfo.mUseJavaBackend + || mDeps.isMdnsAdvertiserEnabled(mContext) + || useAdvertiserForType(registerServiceType)) { + if (serviceType != null && registerServiceType == null) { + Log.e(TAG, "Invalid service type: " + serviceType); + clientInfo.onRegisterServiceFailedImmediately(clientRequestId, + NsdManager.FAILURE_INTERNAL_ERROR, false /* isLegacy */); + return; + } + final int transactionId; + boolean isUpdateOnly = (advertisingRequest.getFlags() + & AdvertisingRequest.NSD_ADVERTISING_UPDATE_ONLY) > 0; + // If it is an update request, then reuse the old transactionId + if (isUpdateOnly) { + final ClientRequest existingClientRequest = + clientInfo.mClientRequests.get(clientRequestId); + if (existingClientRequest == null) { + Log.e(TAG, "Invalid update on requestId: " + clientRequestId); + clientInfo.onRegisterServiceFailedImmediately(clientRequestId, + NsdManager.FAILURE_INTERNAL_ERROR, + false /* isLegacy */); + return; + } + transactionId = existingClientRequest.mTransactionId; + } else { + transactionId = getUniqueId(); + } + + if (registerServiceType != null) { + serviceInfo.setServiceType(registerServiceType); + serviceInfo.setServiceName( + truncateServiceName(serviceInfo.getServiceName())); + } + + if (!checkHostname(hostname)) { + clientInfo.onRegisterServiceFailedImmediately(clientRequestId, + NsdManager.FAILURE_BAD_PARAMETERS, false /* isLegacy */); + return; + } + + if (!checkPublicKey(serviceInfo.getPublicKey())) { + Log.e(TAG, + "Invalid public key: " + + Arrays.toString(serviceInfo.getPublicKey())); + clientInfo.onRegisterServiceFailedImmediately( + clientRequestId, + NsdManager.FAILURE_BAD_PARAMETERS, + false /* isLegacy */); + return; + } + + Set subtypes = new ArraySet<>(serviceInfo.getSubtypes()); + if (typeSubtype != null && typeSubtype.second != null) { + for (String subType : typeSubtype.second) { + if (!TextUtils.isEmpty(subType)) { + subtypes.add(subType); + } + } + } + subtypes = dedupSubtypeLabels(subtypes); + + if (!checkSubtypeLabels(subtypes)) { + clientInfo.onRegisterServiceFailedImmediately(clientRequestId, + NsdManager.FAILURE_BAD_PARAMETERS, false /* isLegacy */); + return; + } + + if (!checkTtl(advertisingRequest.getTtl(), clientInfo)) { + clientInfo.onRegisterServiceFailedImmediately(clientRequestId, + NsdManager.FAILURE_BAD_PARAMETERS, false /* isLegacy */); + return; + } + final boolean isOffloadOnly = + (advertisingRequest.getFlags() & FLAG_OFFLOAD_ONLY) != 0; + if (isOffloadOnly && !isOffloadOnlyAllowed()) { + clientInfo.onRegisterServiceFailedImmediately(clientRequestId, + NsdManager.FAILURE_BAD_PARAMETERS, false /* isLegacy */); + return; + } + + serviceInfo.setSubtypes(subtypes); + maybeStartMonitoringSockets(); + final boolean skipProbing = (advertisingRequest.getFlags() + & FLAG_SKIP_PROBING) != 0; + final boolean skipSubtypeAnnouncements = (advertisingRequest.getFlags() + & FLAG_SKIP_SUBTYPE_ANNOUNCEMENTS) != 0; + final MdnsAdvertisingOptions mdnsAdvertisingOptions = + MdnsAdvertisingOptions.newBuilder() + .setIsOnlyUpdate(isUpdateOnly) + .setSkipProbing(skipProbing) + .setTtl(advertisingRequest.getTtl()) + .setSkipSubtypeAnnouncements(skipSubtypeAnnouncements) + .setOffloadOnly(isOffloadOnly) + .build(); + mAdvertiser.addOrUpdateService(transactionId, serviceInfo, + mdnsAdvertisingOptions, clientInfo.mUid); + storeAdvertiserRequestMap(clientRequestId, transactionId, clientInfo, + serviceInfo); + } else { + maybeStartDaemon(); + final int transactionId = getUniqueId(); + if (registerService(transactionId, serviceInfo)) { + if (DBG) { + Log.d(TAG, "Register " + clientRequestId + + " " + transactionId); + } + storeLegacyRequestMap(clientRequestId, transactionId, clientInfo, + NsdManager.REGISTER_SERVICE, mClock.elapsedRealtime()); + // Return success after mDns reports success + } else { + unregisterService(transactionId); + clientInfo.onRegisterServiceFailedImmediately(clientRequestId, + NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */); + } + } + } + + private void handleUnregisterService(int clientRequestId, ListenerArgs args) { + if (DBG) Log.d(TAG, "unregister service"); + final ClientInfo clientInfo = mClients.get(args.connector); + // If the binder death notification for a INsdManagerCallback was received + // before any calls are received by NsdService, the clientInfo would be + // cleared and cause NPE. Add a null check here to prevent this corner case. + if (clientInfo == null) { + Log.e(TAG, "Unknown connector in unregistration"); + return; + } + final ClientRequest request = + clientInfo.mClientRequests.get(clientRequestId); + if (request == null) { + Log.e(TAG, "Unknown client request in UNREGISTER_SERVICE"); + return; + } + final int transactionId = request.mTransactionId; + removeRequestMap(clientRequestId, transactionId, clientInfo); + + // Note isMdnsAdvertiserEnabled may have changed to false at this point, + // so this needs to check the type of the original request to unregister + // instead of looking at the flag value. + if (request instanceof AdvertiserClientRequest) { + final AdvertiserMetrics metrics = + mAdvertiser.getAdvertiserMetrics(transactionId); + mAdvertiser.removeService(transactionId); + clientInfo.onUnregisterServiceSucceeded( + clientRequestId, request, metrics); + } else { + if (unregisterService(transactionId)) { + clientInfo.onUnregisterServiceSucceeded(clientRequestId, request, + new AdvertiserMetrics(NO_PACKET /* repliedRequestsCount */, + NO_PACKET /* sentPacketCount */, + 0 /* conflictDuringProbingCount */, + 0 /* conflictAfterProbingCount */)); + } else { + clientInfo.onUnregisterServiceFailed( + clientRequestId, NsdManager.FAILURE_INTERNAL_ERROR); + } + } + } + + private void handleResolveService(int clientRequestId, ListenerArgs args) { + if (DBG) Log.d(TAG, "Resolve service"); + final ClientInfo clientInfo = mClients.get(args.connector); + // If the binder death notification for a INsdManagerCallback was received + // before any calls are received by NsdService, the clientInfo would be + // cleared and cause NPE. Add a null check here to prevent this corner case. + if (clientInfo == null) { + Log.e(TAG, "Unknown connector in resolution"); + return; + } + + final NsdServiceInfo info = args.serviceInfo; + final int transactionId = getUniqueId(); + final Pair> typeSubtype = + parseTypeAndSubtype(info.getServiceType()); + final String serviceType = typeSubtype == null + ? null : typeSubtype.first; + if (clientInfo.mUseJavaBackend + || mDeps.isMdnsDiscoveryManagerEnabled(mContext) + || useDiscoveryManagerForType(serviceType)) { + if (serviceType == null) { + clientInfo.onResolveServiceFailedImmediately(clientRequestId, + NsdManager.FAILURE_INTERNAL_ERROR, false /* isLegacy */); + return; + } + final String resolveServiceType = serviceType + ".local"; + + maybeStartMonitoringSockets(); + final MdnsListener listener = new ResolutionListener(clientRequestId, + transactionId, resolveServiceType, info.getServiceName()); + final int ifaceIdx = info.getNetwork() != null + ? 0 : info.getInterfaceIndex(); + final MdnsSearchOptions options = MdnsSearchOptions.newBuilder() + .setNetwork(info.getNetwork()) + .setInterfaceIndex(ifaceIdx) + .setQueryMode(mMdnsFeatureFlags.isAggressiveQueryModeEnabled() + ? AGGRESSIVE_QUERY_MODE + : PASSIVE_QUERY_MODE) + .setResolveInstanceName(info.getServiceName()) + .setRemoveExpiredService(true) + .build(); + mMdnsDiscoveryManager.registerListener( + resolveServiceType, listener, options); + storeDiscoveryManagerRequestMap(clientRequestId, transactionId, + listener, clientInfo, info.getNetwork()); + clientInfo.log("Register a ResolutionListener " + transactionId + + " for service type:" + resolveServiceType); + } else { + if (clientInfo.mResolvedService != null) { + clientInfo.onResolveServiceFailedImmediately(clientRequestId, + NsdManager.FAILURE_ALREADY_ACTIVE, true /* isLegacy */); + return; + } + + maybeStartDaemon(); + if (resolveService(transactionId, info)) { + clientInfo.mResolvedService = new NsdServiceInfo(); + storeLegacyRequestMap(clientRequestId, transactionId, clientInfo, + NsdManager.RESOLVE_SERVICE, mClock.elapsedRealtime()); + } else { + clientInfo.onResolveServiceFailedImmediately(clientRequestId, + NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */); + } + } + } + + private void handleStopResolution(int clientRequestId, ListenerArgs args) { + if (DBG) Log.d(TAG, "Stop service resolution"); + final ClientInfo clientInfo = mClients.get(args.connector); + // If the binder death notification for a INsdManagerCallback was received + // before any calls are received by NsdService, the clientInfo would be + // cleared and cause NPE. Add a null check here to prevent this corner case. + if (clientInfo == null) { + Log.e(TAG, "Unknown connector in stop resolution"); + return; + } + + final ClientRequest request = + clientInfo.mClientRequests.get(clientRequestId); + if (request == null) { + Log.e(TAG, "Unknown client request in STOP_RESOLUTION"); + return; + } + final int transactionId = request.mTransactionId; + // Note isMdnsDiscoveryManagerEnabled may have changed to false at this + // point, so this needs to check the type of the original request to + // unregister instead of looking at the flag value. + if (request instanceof DiscoveryManagerRequest) { + stopDiscoveryManagerRequest( + request, clientRequestId, transactionId, clientInfo); + clientInfo.onStopResolutionSucceeded(clientRequestId, request); + clientInfo.log("Unregister the ResolutionListener " + transactionId); + } else { + removeRequestMap(clientRequestId, transactionId, clientInfo); + if (stopResolveService(transactionId)) { + clientInfo.onStopResolutionSucceeded(clientRequestId, request); + } else { + clientInfo.onStopResolutionFailed( + clientRequestId, NsdManager.FAILURE_OPERATION_NOT_RUNNING); + } + clientInfo.mResolvedService = null; + } + } + + private void handleRegisterServiceCallback(int clientRequestId, ListenerArgs args) { + if (DBG) Log.d(TAG, "Register a service callback"); + final ClientInfo clientInfo = mClients.get(args.connector); + // If the binder death notification for a INsdManagerCallback was received + // before any calls are received by NsdService, the clientInfo would be + // cleared and cause NPE. Add a null check here to prevent this corner case. + if (clientInfo == null) { + Log.e(TAG, "Unknown connector in callback registration"); + return; + } + + final NsdServiceInfo info = args.serviceInfo; + final int transactionId = getUniqueId(); + final Pair> typeAndSubtype = + parseTypeAndSubtype(info.getServiceType()); + final String serviceType = typeAndSubtype == null + ? null : typeAndSubtype.first; + if (serviceType == null) { + clientInfo.onServiceInfoCallbackRegistrationFailed(clientRequestId, + NsdManager.FAILURE_BAD_PARAMETERS); + return; + } + final String resolveServiceType = serviceType + ".local"; + + maybeStartMonitoringSockets(); + final MdnsListener listener = new ServiceInfoListener(clientRequestId, + transactionId, resolveServiceType, info.getServiceName()); + final int ifIndex = info.getNetwork() != null + ? 0 : info.getInterfaceIndex(); + final MdnsSearchOptions options = MdnsSearchOptions.newBuilder() + .setNetwork(info.getNetwork()) + .setInterfaceIndex(ifIndex) + .setQueryMode(mMdnsFeatureFlags.isAggressiveQueryModeEnabled() + ? AGGRESSIVE_QUERY_MODE + : PASSIVE_QUERY_MODE) + .setResolveInstanceName(info.getServiceName()) + .setRemoveExpiredService(true) + .build(); + mMdnsDiscoveryManager.registerListener( + resolveServiceType, listener, options); + storeDiscoveryManagerRequestMap(clientRequestId, transactionId, listener, + clientInfo, info.getNetwork()); + clientInfo.onServiceInfoCallbackRegistered(transactionId); + clientInfo.log("Register a ServiceInfoListener " + transactionId + + " for service type:" + resolveServiceType); + } + + private void handleUnregisterServiceCallback(int clientRequestId, ListenerArgs args) { + if (DBG) Log.d(TAG, "Unregister a service callback"); + final ClientInfo clientInfo = mClients.get(args.connector); + // If the binder death notification for a INsdManagerCallback was received + // before any calls are received by NsdService, the clientInfo would be + // cleared and cause NPE. Add a null check here to prevent this corner case. + if (clientInfo == null) { + Log.e(TAG, "Unknown connector in callback unregistration"); + return; + } + + final ClientRequest request = + clientInfo.mClientRequests.get(clientRequestId); + if (request == null) { + Log.e(TAG, "Unknown client request in UNREGISTER_SERVICE_CALLBACK"); + return; + } + final int transactionId = request.mTransactionId; + if (request instanceof DiscoveryManagerRequest) { + stopDiscoveryManagerRequest( + request, clientRequestId, transactionId, clientInfo); + clientInfo.onServiceInfoCallbackUnregistered(clientRequestId, request); + clientInfo.log("Unregister the ServiceInfoListener " + transactionId); + } else { + loge("Unregister failed with non-DiscoveryManagerRequest."); + } + } + + private void handleRegisterOffloadEngine(OffloadEngineInfo offloadEngineInfo) { + final ClientInfo clientInfo = mClients.get(offloadEngineInfo.mConnector); + if (clientInfo == null) { + Log.e(TAG, "Unknown connector in calls to register offload engine"); + return; + } + clientInfo.markIsOffloadEngine(); + // TODO: Limits the number of registrations created by a given class. + mOffloadEngines.register(offloadEngineInfo.mOffloadEngine, + offloadEngineInfo); + sendAllOffloadServiceInfos(offloadEngineInfo); + } + + private void handleUnregisterOffloadEngine(IOffloadEngine offloadEngine) { + mOffloadEngines.unregister(offloadEngine); + } + + private void handleRegisterClient(int clientRequestId, ConnectorArgs arg) { + final INsdManagerCallback cb = arg.callback; + try { + cb.asBinder().linkToDeath(arg.connector, 0); + final String tag = "Client" + arg.uid + "-" + mClientNumberId++; + final NetworkNsdReportedMetrics metrics = + mDeps.makeNetworkNsdReportedMetrics( + (int) mClock.elapsedRealtime(), arg.uid); + final ClientInfo clientInfo = new ClientInfo(cb, arg.uid, arg.useJavaBackend, + mServiceLogs.forSubComponent(tag), metrics); + mClients.put(arg.connector, clientInfo); + } catch (RemoteException e) { + Log.w(TAG, "Client request id " + clientRequestId + + " has already died"); + } + } + + private void handleUnregisterClient(NsdServiceConnector connector) { + final ClientInfo clientInfo = mClients.remove(connector); + if (clientInfo != null) { + clientInfo.expungeAllRequests(); + if (clientInfo.isPreSClient()) { + mLegacyClientCount -= 1; + } + } + maybeStopMonitoringSocketsIfNoActiveRequest(); + maybeScheduleStop(); + } + + private void handleDaemonCleanup() { + maybeStopDaemon(); + } + + private void handleDaemonStartup(ListenerArgs args) { + final ClientInfo clientInfo = mClients.get(args.connector); + if (clientInfo != null) { + cancelStop(); + clientInfo.setPreSClient(); + mLegacyClientCount += 1; + maybeStartDaemon(); + } + } + private boolean handleMDnsServiceEvent(int code, int transactionId, Object obj) { - NsdServiceInfo servInfo; ClientInfo clientInfo = mTransactionIdToClientInfoMap.get(transactionId); if (clientInfo == null) { Log.e(TAG, String.format( @@ -1441,162 +1460,200 @@ public class NsdService extends INsdManager.Stub { "MDns service event code:%d transactionId=%d", code, transactionId)); } switch (code) { - case IMDnsEventListener.SERVICE_FOUND: { - final DiscoveryInfo info = (DiscoveryInfo) obj; - final String name = info.serviceName; - final String type = info.registrationType; - servInfo = new NsdServiceInfo(name, type); - final int foundNetId = info.netId; - if (foundNetId == 0L) { - // Ignore services that do not have a Network: they are not usable - // by apps, as they would need privileged permissions to use - // interfaces that do not have an associated Network. - break; - } - if (foundNetId == INetd.DUMMY_NET_ID) { - // Ignore services on the dummy0 interface: they are only seen when - // discovering locally advertised services, and are not reachable - // through that interface. - break; - } - setServiceNetworkForCallback(servInfo, info.netId, info.interfaceIdx); - - clientInfo.onServiceFound(clientRequestId, servInfo, request); - break; - } - case IMDnsEventListener.SERVICE_LOST: { - final DiscoveryInfo info = (DiscoveryInfo) obj; - final String name = info.serviceName; - final String type = info.registrationType; - final int lostNetId = info.netId; - servInfo = new NsdServiceInfo(name, type); - // The network could be set to null (netId 0) if it was torn down when the - // service is lost - // TODO: avoid returning null in that case, possibly by remembering - // found services on the same interface index and their network at the time - setServiceNetworkForCallback(servInfo, lostNetId, info.interfaceIdx); - clientInfo.onServiceLost(clientRequestId, servInfo, request); - break; - } - case IMDnsEventListener.SERVICE_DISCOVERY_FAILED: - clientInfo.onDiscoverServicesFailed(clientRequestId, - NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */, - transactionId, - request.calculateRequestDurationMs(mClock.elapsedRealtime())); - break; - case IMDnsEventListener.SERVICE_REGISTERED: { - final RegistrationInfo info = (RegistrationInfo) obj; - final String name = info.serviceName; - servInfo = new NsdServiceInfo(name, null /* serviceType */); - clientInfo.onRegisterServiceSucceeded(clientRequestId, servInfo, request); - break; - } - case IMDnsEventListener.SERVICE_REGISTRATION_FAILED: - clientInfo.onRegisterServiceFailed(clientRequestId, - NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */, - transactionId, - request.calculateRequestDurationMs(mClock.elapsedRealtime())); - break; - case IMDnsEventListener.SERVICE_RESOLVED: { - final ResolutionInfo info = (ResolutionInfo) obj; - int index = 0; - final String fullName = info.serviceFullName; - while (index < fullName.length() && fullName.charAt(index) != '.') { - if (fullName.charAt(index) == '\\') { - ++index; - } - ++index; - } - if (index >= fullName.length()) { - Log.e(TAG, "Invalid service found " + fullName); - break; - } - - String name = unescape(fullName.substring(0, index)); - String rest = fullName.substring(index); - String type = rest.replace(".local.", ""); - - final NsdServiceInfo serviceInfo = clientInfo.mResolvedService; - serviceInfo.setServiceName(name); - serviceInfo.setServiceType(type); - serviceInfo.setPort(info.port); - serviceInfo.setTxtRecords(info.txtRecord); - // Network will be added after SERVICE_GET_ADDR_SUCCESS - - stopResolveService(transactionId); - removeRequestMap(clientRequestId, transactionId, clientInfo); - - final int transactionId2 = getUniqueId(); - if (getAddrInfo(transactionId2, info.hostname, info.interfaceIdx)) { - storeLegacyRequestMap(clientRequestId, transactionId2, clientInfo, - NsdManager.RESOLVE_SERVICE, request.mStartTimeMs); - } else { - clientInfo.onResolveServiceFailed(clientRequestId, - NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */, - transactionId, - request.calculateRequestDurationMs(mClock.elapsedRealtime())); - clientInfo.mResolvedService = null; - } - break; - } - case IMDnsEventListener.SERVICE_RESOLUTION_FAILED: - /* NNN resolveId errorCode */ - stopResolveService(transactionId); - removeRequestMap(clientRequestId, transactionId, clientInfo); - clientInfo.onResolveServiceFailed(clientRequestId, - NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */, - transactionId, - request.calculateRequestDurationMs(mClock.elapsedRealtime())); - clientInfo.mResolvedService = null; - break; - case IMDnsEventListener.SERVICE_GET_ADDR_FAILED: - /* NNN resolveId errorCode */ - stopGetAddrInfo(transactionId); - removeRequestMap(clientRequestId, transactionId, clientInfo); - clientInfo.onResolveServiceFailed(clientRequestId, - NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */, - transactionId, - request.calculateRequestDurationMs(mClock.elapsedRealtime())); - clientInfo.mResolvedService = null; - break; - case IMDnsEventListener.SERVICE_GET_ADDR_SUCCESS: { - /* NNN resolveId hostname ttl addr interfaceIdx netId */ - final GetAddressInfo info = (GetAddressInfo) obj; - final String address = info.address; - final int netId = info.netId; - InetAddress serviceHost = null; - try { - serviceHost = InetAddress.getByName(address); - } catch (UnknownHostException e) { - Log.wtf(TAG, "Invalid host in GET_ADDR_SUCCESS", e); - } - - // 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 - // privileged permissions. - if (netId != NETID_UNSET && serviceHost != null) { - clientInfo.mResolvedService.setHost(serviceHost); - setServiceNetworkForCallback(clientInfo.mResolvedService, - netId, info.interfaceIdx); - clientInfo.onResolveServiceSucceeded( - clientRequestId, clientInfo.mResolvedService, request); - } else { - clientInfo.onResolveServiceFailed(clientRequestId, - NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */, - transactionId, - request.calculateRequestDurationMs(mClock.elapsedRealtime())); - } - stopGetAddrInfo(transactionId); - removeRequestMap(clientRequestId, transactionId, clientInfo); - clientInfo.mResolvedService = null; - break; - } - default: + case IMDnsEventListener.SERVICE_FOUND -> handleMDnsServiceFound(clientInfo, + clientRequestId, request, obj); + case IMDnsEventListener.SERVICE_LOST -> handleMDnsServiceLost(clientInfo, + clientRequestId, request, obj); + case IMDnsEventListener.SERVICE_DISCOVERY_FAILED -> + handleMDnsServiceDiscoveryFailed(clientInfo, transactionId, + clientRequestId, request); + case IMDnsEventListener.SERVICE_REGISTERED -> handleMDnsServiceRegistered( + clientInfo, clientRequestId, request, obj); + case IMDnsEventListener.SERVICE_REGISTRATION_FAILED -> + handleMDnsServiceRegistrationFailed(clientInfo, transactionId, + clientRequestId, request); + case IMDnsEventListener.SERVICE_RESOLVED -> handleMDnsServiceResolved( + clientInfo, transactionId, + clientRequestId, request, obj); + case IMDnsEventListener.SERVICE_RESOLUTION_FAILED -> + handleMDnsServiceResolutionFailed(clientInfo, transactionId, + clientRequestId, request); + case IMDnsEventListener.SERVICE_GET_ADDR_FAILED -> + handleMDnsServiceGetAddrFailed(clientInfo, transactionId, + clientRequestId, request); + case IMDnsEventListener.SERVICE_GET_ADDR_SUCCESS -> + handleMDnsServiceGetAddrSuccess(clientInfo, transactionId, + clientRequestId, request, obj); + default -> { return false; + } } return true; } + private void handleMDnsServiceFound(ClientInfo clientInfo, int clientRequestId, + ClientRequest request, Object obj) { + final DiscoveryInfo info = (DiscoveryInfo) obj; + final String name = info.serviceName; + final String type = info.registrationType; + final NsdServiceInfo servInfo = new NsdServiceInfo(name, type); + final int foundNetId = info.netId; + if (foundNetId == 0L) { + // Ignore services that do not have a Network: they are not usable + // by apps, as they would need privileged permissions to use + // interfaces that do not have an associated Network. + return; + } + if (foundNetId == INetd.DUMMY_NET_ID) { + // Ignore services on the dummy0 interface: they are only seen when + // discovering locally advertised services, and are not reachable + // through that interface. + return; + } + setServiceNetworkForCallback(servInfo, info.netId, info.interfaceIdx); + + clientInfo.onServiceFound(clientRequestId, servInfo, request); + } + + private void handleMDnsServiceLost(ClientInfo clientInfo, int clientRequestId, + ClientRequest request, Object obj) { + final DiscoveryInfo info = (DiscoveryInfo) obj; + final String name = info.serviceName; + final String type = info.registrationType; + final int lostNetId = info.netId; + final NsdServiceInfo servInfo = new NsdServiceInfo(name, type); + // The network could be set to null (netId 0) if it was torn down when the + // service is lost + // TODO: avoid returning null in that case, possibly by remembering + // found services on the same interface index and their network at the time + setServiceNetworkForCallback(servInfo, lostNetId, info.interfaceIdx); + clientInfo.onServiceLost(clientRequestId, servInfo, request); + } + + private void handleMDnsServiceDiscoveryFailed(ClientInfo clientInfo, int transactionId, + int clientRequestId, ClientRequest request) { + clientInfo.onDiscoverServicesFailed(clientRequestId, + NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */, + transactionId, + request.calculateRequestDurationMs(mClock.elapsedRealtime())); + } + + private void handleMDnsServiceRegistered(ClientInfo clientInfo, int clientRequestId, + ClientRequest request, Object obj) { + final RegistrationInfo info = (RegistrationInfo) obj; + final String name = info.serviceName; + final NsdServiceInfo servInfo = new NsdServiceInfo(name, null /* serviceType */); + clientInfo.onRegisterServiceSucceeded(clientRequestId, servInfo, request); + } + + private void handleMDnsServiceRegistrationFailed(ClientInfo clientInfo, + int transactionId, int clientRequestId, ClientRequest request) { + clientInfo.onRegisterServiceFailed(clientRequestId, + NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */, + transactionId, + request.calculateRequestDurationMs(mClock.elapsedRealtime())); + } + + private void handleMDnsServiceResolved(ClientInfo clientInfo, int transactionId, + int clientRequestId, ClientRequest request, Object obj) { + final ResolutionInfo info = (ResolutionInfo) obj; + int index = 0; + final String fullName = info.serviceFullName; + while (index < fullName.length() && fullName.charAt(index) != '.') { + if (fullName.charAt(index) == '\\') { + ++index; + } + ++index; + } + if (index >= fullName.length()) { + Log.e(TAG, "Invalid service found " + fullName); + return; + } + + String name = unescape(fullName.substring(0, index)); + String rest = fullName.substring(index); + String type = rest.replace(".local.", ""); + + final NsdServiceInfo serviceInfo = clientInfo.mResolvedService; + serviceInfo.setServiceName(name); + serviceInfo.setServiceType(type); + serviceInfo.setPort(info.port); + serviceInfo.setTxtRecords(info.txtRecord); + // Network will be added after SERVICE_GET_ADDR_SUCCESS + + stopResolveService(transactionId); + removeRequestMap(clientRequestId, transactionId, clientInfo); + + final int transactionId2 = getUniqueId(); + if (getAddrInfo(transactionId2, info.hostname, info.interfaceIdx)) { + storeLegacyRequestMap(clientRequestId, transactionId2, clientInfo, + NsdManager.RESOLVE_SERVICE, request.mStartTimeMs); + } else { + clientInfo.onResolveServiceFailed(clientRequestId, + NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */, + transactionId, + request.calculateRequestDurationMs(mClock.elapsedRealtime())); + clientInfo.mResolvedService = null; + } + } + + private void handleMDnsServiceResolutionFailed(ClientInfo clientInfo, int transactionId, + int clientRequestId, ClientRequest request) { + /* NNN resolveId errorCode */ + stopResolveService(transactionId); + removeRequestMap(clientRequestId, transactionId, clientInfo); + clientInfo.onResolveServiceFailed(clientRequestId, + NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */, + transactionId, + request.calculateRequestDurationMs(mClock.elapsedRealtime())); + clientInfo.mResolvedService = null; + } + + private void handleMDnsServiceGetAddrFailed(ClientInfo clientInfo, int transactionId, + int clientRequestId, ClientRequest request) { + /* NNN resolveId errorCode */ + stopGetAddrInfo(transactionId); + removeRequestMap(clientRequestId, transactionId, clientInfo); + clientInfo.onResolveServiceFailed(clientRequestId, + NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */, + transactionId, + request.calculateRequestDurationMs(mClock.elapsedRealtime())); + clientInfo.mResolvedService = null; + } + + private void handleMDnsServiceGetAddrSuccess(ClientInfo clientInfo, int transactionId, + int clientRequestId, ClientRequest request, Object obj) { + /* NNN resolveId hostname ttl addr interfaceIdx netId */ + final GetAddressInfo info = (GetAddressInfo) obj; + final String address = info.address; + final int netId = info.netId; + InetAddress serviceHost = null; + try { + serviceHost = InetAddress.getByName(address); + } catch (UnknownHostException e) { + Log.wtf(TAG, "Invalid host in GET_ADDR_SUCCESS", e); + } + + // 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 + // privileged permissions. + if (netId != NETID_UNSET && serviceHost != null) { + clientInfo.mResolvedService.setHost(serviceHost); + setServiceNetworkForCallback(clientInfo.mResolvedService, + netId, info.interfaceIdx); + clientInfo.onResolveServiceSucceeded( + clientRequestId, clientInfo.mResolvedService, request); + } else { + clientInfo.onResolveServiceFailed(clientRequestId, + NsdManager.FAILURE_INTERNAL_ERROR, true /* isLegacy */, + transactionId, + request.calculateRequestDurationMs(mClock.elapsedRealtime())); + } + stopGetAddrInfo(transactionId); + removeRequestMap(clientRequestId, transactionId, clientInfo); + clientInfo.mResolvedService = null; + } + @Nullable private NsdServiceInfo buildNsdServiceInfoFromMdnsEvent( final MdnsEvent event, int code) { @@ -1675,88 +1732,115 @@ public class NsdService extends INsdManager.Stub { "MdnsDiscoveryManager event code=%s transactionId=%d", NsdManager.nameOf(code), transactionId)); switch (code) { - case NsdManager.SERVICE_FOUND: - // Set the ServiceFromCache flag only if the service is actually being - // retrieved from the cache. This flag should not be overridden by later - // service found event, which may not be cached. - if (event.mIsServiceFromCache) { - request.setServiceFromCache(true); - } - clientInfo.onServiceFound(clientRequestId, info, request); - break; - case NsdManager.SERVICE_LOST: - clientInfo.onServiceLost(clientRequestId, info, request); - break; - case NsdManager.RESOLVE_SERVICE_SUCCEEDED: { - final MdnsServiceInfo serviceInfo = event.mMdnsServiceInfo; - info.setPort(serviceInfo.getPort()); - - Map attrs = serviceInfo.getAttributes(); - for (Map.Entry kv : attrs.entrySet()) { - final String key = kv.getKey(); - try { - info.setAttribute(key, serviceInfo.getAttributeAsBytes(key)); - } catch (IllegalArgumentException e) { - Log.e(TAG, "Invalid attribute", e); - } - } - info.setHostname(getHostname(serviceInfo)); - final List addresses = getInetAddresses(serviceInfo); - if (addresses.size() != 0) { - info.setHostAddresses(addresses); - request.setServiceFromCache(event.mIsServiceFromCache); - clientInfo.onResolveServiceSucceeded(clientRequestId, info, request); - } else { - // No address. Notify resolution failure. - clientInfo.onResolveServiceFailed(clientRequestId, - NsdManager.FAILURE_INTERNAL_ERROR, false /* isLegacy */, - transactionId, - request.calculateRequestDurationMs(mClock.elapsedRealtime())); - } - - // Unregister the listener immediately like IMDnsEventListener design - if (!(request instanceof DiscoveryManagerRequest)) { - Log.wtf(TAG, "non-DiscoveryManager request in DiscoveryManager event"); - break; - } - stopDiscoveryManagerRequest( - request, clientRequestId, transactionId, clientInfo); - break; - } - case NsdManager.SERVICE_UPDATED: { - final MdnsServiceInfo serviceInfo = event.mMdnsServiceInfo; - info.setPort(serviceInfo.getPort()); - - Map attrs = serviceInfo.getAttributes(); - for (Map.Entry kv : attrs.entrySet()) { - final String key = kv.getKey(); - try { - info.setAttribute(key, serviceInfo.getAttributeAsBytes(key)); - } catch (IllegalArgumentException e) { - Log.e(TAG, "Invalid attribute", e); - } - } - - info.setHostname(getHostname(serviceInfo)); - final List addresses = getInetAddresses(serviceInfo); - info.setHostAddresses(addresses); - clientInfo.onServiceUpdated(clientRequestId, info, request); - // Set the ServiceFromCache flag only if the service is actually being - // retrieved from the cache. This flag should not be overridden by later - // service updates, which may not be cached. - if (event.mIsServiceFromCache) { - request.setServiceFromCache(true); - } - break; - } - case NsdManager.SERVICE_UPDATED_LOST: - clientInfo.onServiceUpdatedLost(clientRequestId, request); - break; - default: + case NsdManager.SERVICE_FOUND -> handleDiscoveryManagerServiceFound(clientInfo, + clientRequestId, request, + info, event); + case NsdManager.SERVICE_LOST -> handleDiscoveryManagerServiceLost(clientInfo, + clientRequestId, request, + info); + case NsdManager.RESOLVE_SERVICE_SUCCEEDED -> + handleDiscoveryManagerResolveSucceeded(clientInfo, transactionId, + clientRequestId, request, info, event); + case NsdManager.SERVICE_UPDATED -> handleDiscoveryManagerServiceUpdated( + clientInfo, clientRequestId, request, + info, event); + case NsdManager.SERVICE_UPDATED_LOST -> + handleDiscoveryManagerServiceUpdatedLost(clientInfo, clientRequestId, + request); + default -> { return false; + } } return true; } + + private void handleDiscoveryManagerServiceFound(ClientInfo clientInfo, + int clientRequestId, ClientRequest request, NsdServiceInfo info, + MdnsEvent event) { + // Set the ServiceFromCache flag only if the service is actually being + // retrieved from the cache. This flag should not be overridden by later + // service found event, which may not be cached. + if (event.mIsServiceFromCache) { + request.setServiceFromCache(true); + } + clientInfo.onServiceFound(clientRequestId, info, request); + } + + private void handleDiscoveryManagerServiceLost(ClientInfo clientInfo, + int clientRequestId, ClientRequest request, NsdServiceInfo info) { + clientInfo.onServiceLost(clientRequestId, info, request); + } + + private void handleDiscoveryManagerResolveSucceeded(ClientInfo clientInfo, + int transactionId, int clientRequestId, ClientRequest request, + NsdServiceInfo info, MdnsEvent event) { + final MdnsServiceInfo serviceInfo = event.mMdnsServiceInfo; + info.setPort(serviceInfo.getPort()); + + Map attrs = serviceInfo.getAttributes(); + for (Map.Entry kv : attrs.entrySet()) { + final String key = kv.getKey(); + try { + info.setAttribute(key, serviceInfo.getAttributeAsBytes(key)); + } catch (IllegalArgumentException e) { + Log.e(TAG, "Invalid attribute", e); + } + } + info.setHostname(getHostname(serviceInfo)); + final List addresses = getInetAddresses(serviceInfo); + if (addresses.size() != 0) { + info.setHostAddresses(addresses); + request.setServiceFromCache(event.mIsServiceFromCache); + clientInfo.onResolveServiceSucceeded(clientRequestId, info, request); + } else { + // No address. Notify resolution failure. + clientInfo.onResolveServiceFailed(clientRequestId, + NsdManager.FAILURE_INTERNAL_ERROR, false /* isLegacy */, + transactionId, + request.calculateRequestDurationMs(mClock.elapsedRealtime())); + } + + // Unregister the listener immediately like IMDnsEventListener design + if (!(request instanceof DiscoveryManagerRequest)) { + Log.wtf(TAG, "non-DiscoveryManager request in DiscoveryManager event"); + return; + } + stopDiscoveryManagerRequest( + request, clientRequestId, transactionId, clientInfo); + } + + private void handleDiscoveryManagerServiceUpdated(ClientInfo clientInfo, + int clientRequestId, ClientRequest request, NsdServiceInfo info, + MdnsEvent event) { + final MdnsServiceInfo serviceInfo = event.mMdnsServiceInfo; + info.setPort(serviceInfo.getPort()); + + Map attrs = serviceInfo.getAttributes(); + for (Map.Entry kv : attrs.entrySet()) { + final String key = kv.getKey(); + try { + info.setAttribute(key, serviceInfo.getAttributeAsBytes(key)); + } catch (IllegalArgumentException e) { + Log.e(TAG, "Invalid attribute", e); + } + } + + info.setHostname(getHostname(serviceInfo)); + final List addresses = getInetAddresses(serviceInfo); + info.setHostAddresses(addresses); + clientInfo.onServiceUpdated(clientRequestId, info, request); + // Set the ServiceFromCache flag only if the service is actually being + // retrieved from the cache. This flag should not be overridden by later + // service updates, which may not be cached. + if (event.mIsServiceFromCache) { + request.setServiceFromCache(true); + } + } + + private void handleDiscoveryManagerServiceUpdatedLost(ClientInfo clientInfo, + int clientRequestId, ClientRequest request) { + clientInfo.onServiceUpdatedLost(clientRequestId, request); + } } }