1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.thread; 18 19 import static android.net.nsd.NsdManager.PROTOCOL_DNS_SD; 20 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.content.Context; 24 import android.net.DnsResolver; 25 import android.net.InetAddresses; 26 import android.net.LinkProperties; 27 import android.net.Network; 28 import android.net.nsd.DiscoveryRequest; 29 import android.net.nsd.NsdManager; 30 import android.net.nsd.NsdServiceInfo; 31 import android.os.CancellationSignal; 32 import android.os.Handler; 33 import android.os.RemoteException; 34 import android.system.Os; 35 import android.text.TextUtils; 36 import android.util.SparseArray; 37 38 import com.android.internal.annotations.VisibleForTesting; 39 import com.android.net.module.util.SharedLog; 40 import com.android.server.thread.openthread.DnsTxtAttribute; 41 import com.android.server.thread.openthread.INsdDiscoverServiceCallback; 42 import com.android.server.thread.openthread.INsdPublisher; 43 import com.android.server.thread.openthread.INsdResolveHostCallback; 44 import com.android.server.thread.openthread.INsdResolveServiceCallback; 45 import com.android.server.thread.openthread.INsdStatusReceiver; 46 47 import java.net.Inet6Address; 48 import java.net.InetAddress; 49 import java.util.ArrayList; 50 import java.util.Collections; 51 import java.util.HashSet; 52 import java.util.List; 53 import java.util.Map; 54 import java.util.concurrent.Executor; 55 56 /** 57 * Implementation of {@link INsdPublisher}. 58 * 59 * <p>This class provides API for service registration and discovery over mDNS. This class is a 60 * proxy between ot-daemon and NsdManager. 61 * 62 * <p>All the data members of this class MUST be accessed in the {@code mHandler}'s Thread except 63 * {@code mHandler} itself. 64 */ 65 public final class NsdPublisher extends INsdPublisher.Stub { 66 private static final String TAG = NsdPublisher.class.getSimpleName(); 67 private static final SharedLog LOG = ThreadNetworkLogger.forSubComponent(TAG); 68 69 // TODO: b/321883491 - specify network for mDNS operations 70 @Nullable private Network mNetwork; 71 private final Map<Network, LinkProperties> mNetworkToLinkProperties; 72 private final NsdManager mNsdManager; 73 private final DnsResolver mDnsResolver; 74 private final Handler mHandler; 75 private final Executor mExecutor; 76 private final SparseArray<RegistrationListener> mRegistrationListeners = new SparseArray<>(0); 77 private final SparseArray<DiscoveryListener> mDiscoveryListeners = new SparseArray<>(0); 78 private final SparseArray<ServiceInfoListener> mServiceInfoListeners = new SparseArray<>(0); 79 private final SparseArray<HostInfoListener> mHostInfoListeners = new SparseArray<>(0); 80 81 @VisibleForTesting NsdPublisher( NsdManager nsdManager, DnsResolver dnsResolver, Handler handler, Map<Network, LinkProperties> networkToLinkProperties)82 public NsdPublisher( 83 NsdManager nsdManager, 84 DnsResolver dnsResolver, 85 Handler handler, 86 Map<Network, LinkProperties> networkToLinkProperties) { 87 mNetwork = null; 88 mNsdManager = nsdManager; 89 mDnsResolver = dnsResolver; 90 mHandler = handler; 91 mExecutor = runnable -> mHandler.post(runnable); 92 mNetworkToLinkProperties = networkToLinkProperties; 93 } 94 newInstance( Context context, Handler handler, Map<Network, LinkProperties> networkToLinkProperties)95 public static NsdPublisher newInstance( 96 Context context, 97 Handler handler, 98 Map<Network, LinkProperties> networkToLinkProperties) { 99 return new NsdPublisher( 100 context.getSystemService(NsdManager.class), 101 DnsResolver.getInstance(), 102 handler, 103 networkToLinkProperties); 104 } 105 106 // TODO: b/321883491 - NsdPublisher should be disabled when mNetwork is null setNetworkForHostResolution(@ullable Network network)107 public void setNetworkForHostResolution(@Nullable Network network) { 108 mNetwork = network; 109 } 110 111 @Override registerService( String hostname, String name, String type, List<String> subTypeList, int port, List<DnsTxtAttribute> txt, INsdStatusReceiver receiver, int listenerId)112 public void registerService( 113 String hostname, 114 String name, 115 String type, 116 List<String> subTypeList, 117 int port, 118 List<DnsTxtAttribute> txt, 119 INsdStatusReceiver receiver, 120 int listenerId) { 121 NsdServiceInfo serviceInfo = 122 buildServiceInfoForService(hostname, name, type, subTypeList, port, txt); 123 mHandler.post(() -> registerInternal(serviceInfo, receiver, listenerId, "service")); 124 } 125 buildServiceInfoForService( String hostname, String name, String type, List<String> subTypeList, int port, List<DnsTxtAttribute> txt)126 private static NsdServiceInfo buildServiceInfoForService( 127 String hostname, 128 String name, 129 String type, 130 List<String> subTypeList, 131 int port, 132 List<DnsTxtAttribute> txt) { 133 NsdServiceInfo serviceInfo = new NsdServiceInfo(); 134 135 serviceInfo.setServiceName(name); 136 if (!TextUtils.isEmpty(hostname)) { 137 serviceInfo.setHostname(hostname); 138 } 139 serviceInfo.setServiceType(type); 140 serviceInfo.setPort(port); 141 serviceInfo.setSubtypes(new HashSet<>(subTypeList)); 142 for (DnsTxtAttribute attribute : txt) { 143 serviceInfo.setAttribute(attribute.name, attribute.value); 144 } 145 146 return serviceInfo; 147 } 148 149 @Override registerHost( String name, List<String> addresses, INsdStatusReceiver receiver, int listenerId)150 public void registerHost( 151 String name, List<String> addresses, INsdStatusReceiver receiver, int listenerId) { 152 NsdServiceInfo serviceInfo = buildServiceInfoForHost(name, addresses); 153 mHandler.post(() -> registerInternal(serviceInfo, receiver, listenerId, "host")); 154 } 155 buildServiceInfoForHost( String name, List<String> addressStrings)156 private static NsdServiceInfo buildServiceInfoForHost( 157 String name, List<String> addressStrings) { 158 NsdServiceInfo serviceInfo = new NsdServiceInfo(); 159 160 serviceInfo.setHostname(name); 161 ArrayList<InetAddress> addresses = new ArrayList<>(addressStrings.size()); 162 for (String addressString : addressStrings) { 163 addresses.add(InetAddresses.parseNumericAddress(addressString)); 164 } 165 serviceInfo.setHostAddresses(addresses); 166 167 return serviceInfo; 168 } 169 registerInternal( NsdServiceInfo serviceInfo, INsdStatusReceiver receiver, int listenerId, String registrationType)170 private void registerInternal( 171 NsdServiceInfo serviceInfo, 172 INsdStatusReceiver receiver, 173 int listenerId, 174 String registrationType) { 175 checkOnHandlerThread(); 176 LOG.i( 177 "Registering " 178 + registrationType 179 + ". Listener ID: " 180 + listenerId 181 + ", serviceInfo: " 182 + serviceInfo); 183 RegistrationListener listener = new RegistrationListener(serviceInfo, listenerId, receiver); 184 mRegistrationListeners.append(listenerId, listener); 185 try { 186 mNsdManager.registerService(serviceInfo, PROTOCOL_DNS_SD, mExecutor, listener); 187 } catch (IllegalArgumentException e) { 188 LOG.e("Failed to register service. serviceInfo: " + serviceInfo, e); 189 listener.onRegistrationFailed(serviceInfo, NsdManager.FAILURE_INTERNAL_ERROR); 190 } 191 } 192 unregister(INsdStatusReceiver receiver, int listenerId)193 public void unregister(INsdStatusReceiver receiver, int listenerId) { 194 mHandler.post(() -> unregisterInternal(receiver, listenerId)); 195 } 196 unregisterInternal(INsdStatusReceiver receiver, int listenerId)197 public void unregisterInternal(INsdStatusReceiver receiver, int listenerId) { 198 checkOnHandlerThread(); 199 RegistrationListener registrationListener = mRegistrationListeners.get(listenerId); 200 if (registrationListener == null) { 201 LOG.w( 202 "Failed to unregister service." 203 + " Listener ID: " 204 + listenerId 205 + " The registrationListener is empty."); 206 207 return; 208 } 209 LOG.i( 210 "Unregistering service." 211 + " Listener ID: " 212 + listenerId 213 + " serviceInfo: " 214 + registrationListener.mServiceInfo); 215 registrationListener.addUnregistrationReceiver(receiver); 216 mNsdManager.unregisterService(registrationListener); 217 } 218 219 @Override discoverService(String type, INsdDiscoverServiceCallback callback, int listenerId)220 public void discoverService(String type, INsdDiscoverServiceCallback callback, int listenerId) { 221 mHandler.post(() -> discoverServiceInternal(type, callback, listenerId)); 222 } 223 discoverServiceInternal( String type, INsdDiscoverServiceCallback callback, int listenerId)224 private void discoverServiceInternal( 225 String type, INsdDiscoverServiceCallback callback, int listenerId) { 226 checkOnHandlerThread(); 227 LOG.i("Discovering services." + " Listener ID: " + listenerId + ", service type: " + type); 228 229 DiscoveryListener listener = new DiscoveryListener(listenerId, type, callback); 230 mDiscoveryListeners.append(listenerId, listener); 231 DiscoveryRequest discoveryRequest = 232 new DiscoveryRequest.Builder(type).setNetwork(null).build(); 233 mNsdManager.discoverServices(discoveryRequest, mExecutor, listener); 234 } 235 236 @Override stopServiceDiscovery(int listenerId)237 public void stopServiceDiscovery(int listenerId) { 238 mHandler.post(() -> stopServiceDiscoveryInternal(listenerId)); 239 } 240 stopServiceDiscoveryInternal(int listenerId)241 private void stopServiceDiscoveryInternal(int listenerId) { 242 checkOnHandlerThread(); 243 244 DiscoveryListener listener = mDiscoveryListeners.get(listenerId); 245 if (listener == null) { 246 LOG.w( 247 "Failed to stop service discovery. Listener ID " 248 + listenerId 249 + ". The listener is null."); 250 return; 251 } 252 253 LOG.i("Stopping service discovery. Listener: " + listener); 254 mNsdManager.stopServiceDiscovery(listener); 255 } 256 257 @Override resolveService( String name, String type, INsdResolveServiceCallback callback, int listenerId)258 public void resolveService( 259 String name, String type, INsdResolveServiceCallback callback, int listenerId) { 260 mHandler.post(() -> resolveServiceInternal(name, type, callback, listenerId)); 261 } 262 resolveServiceInternal( String name, String type, INsdResolveServiceCallback callback, int listenerId)263 private void resolveServiceInternal( 264 String name, String type, INsdResolveServiceCallback callback, int listenerId) { 265 checkOnHandlerThread(); 266 267 NsdServiceInfo serviceInfo = new NsdServiceInfo(); 268 serviceInfo.setServiceName(name); 269 serviceInfo.setServiceType(type); 270 serviceInfo.setNetwork(null); 271 LOG.i( 272 "Resolving service." 273 + " Listener ID: " 274 + listenerId 275 + ", service name: " 276 + name 277 + ", service type: " 278 + type); 279 280 ServiceInfoListener listener = new ServiceInfoListener(serviceInfo, listenerId, callback); 281 mServiceInfoListeners.append(listenerId, listener); 282 mNsdManager.registerServiceInfoCallback(serviceInfo, mExecutor, listener); 283 } 284 285 @Override stopServiceResolution(int listenerId)286 public void stopServiceResolution(int listenerId) { 287 mHandler.post(() -> stopServiceResolutionInternal(listenerId)); 288 } 289 stopServiceResolutionInternal(int listenerId)290 private void stopServiceResolutionInternal(int listenerId) { 291 checkOnHandlerThread(); 292 293 ServiceInfoListener listener = mServiceInfoListeners.get(listenerId); 294 if (listener == null) { 295 LOG.w( 296 "Failed to stop service resolution. Listener ID: " 297 + listenerId 298 + ". The listener is null."); 299 return; 300 } 301 302 LOG.i("Stopping service resolution. Listener: " + listener); 303 304 try { 305 mNsdManager.unregisterServiceInfoCallback(listener); 306 } catch (IllegalArgumentException e) { 307 LOG.w( 308 "Failed to stop the service resolution because it's already stopped. Listener: " 309 + listener); 310 } 311 } 312 313 @Override resolveHost(String name, INsdResolveHostCallback callback, int listenerId)314 public void resolveHost(String name, INsdResolveHostCallback callback, int listenerId) { 315 mHandler.post(() -> resolveHostInternal(name, callback, listenerId)); 316 } 317 resolveHostInternal( String name, INsdResolveHostCallback callback, int listenerId)318 private void resolveHostInternal( 319 String name, INsdResolveHostCallback callback, int listenerId) { 320 checkOnHandlerThread(); 321 322 String fullHostname = name + ".local"; 323 CancellationSignal cancellationSignal = new CancellationSignal(); 324 HostInfoListener listener = 325 new HostInfoListener(name, callback, cancellationSignal, listenerId); 326 mDnsResolver.query( 327 mNetwork, 328 fullHostname, 329 DnsResolver.FLAG_NO_CACHE_LOOKUP, 330 mExecutor, 331 cancellationSignal, 332 listener); 333 mHostInfoListeners.append(listenerId, listener); 334 335 LOG.i("Resolving host." + " Listener ID: " + listenerId + ", hostname: " + name); 336 } 337 338 @Override stopHostResolution(int listenerId)339 public void stopHostResolution(int listenerId) { 340 mHandler.post(() -> stopHostResolutionInternal(listenerId)); 341 } 342 stopHostResolutionInternal(int listenerId)343 private void stopHostResolutionInternal(int listenerId) { 344 checkOnHandlerThread(); 345 346 HostInfoListener listener = mHostInfoListeners.get(listenerId); 347 if (listener == null) { 348 LOG.w( 349 "Failed to stop host resolution. Listener ID: " 350 + listenerId 351 + ". The listener is null."); 352 return; 353 } 354 LOG.i("Stopping host resolution. Listener: " + listener); 355 listener.cancel(); 356 mHostInfoListeners.remove(listenerId); 357 } 358 checkOnHandlerThread()359 private void checkOnHandlerThread() { 360 if (mHandler.getLooper().getThread() != Thread.currentThread()) { 361 throw new IllegalStateException( 362 "Not running on handler Thread: " + Thread.currentThread().getName()); 363 } 364 } 365 366 @Override reset()367 public void reset() { 368 mHandler.post(this::resetInternal); 369 } 370 resetInternal()371 private void resetInternal() { 372 checkOnHandlerThread(); 373 for (int i = 0; i < mRegistrationListeners.size(); ++i) { 374 try { 375 mNsdManager.unregisterService(mRegistrationListeners.valueAt(i)); 376 } catch (IllegalArgumentException e) { 377 LOG.i( 378 "Failed to unregister." 379 + " Listener ID: " 380 + mRegistrationListeners.keyAt(i) 381 + " serviceInfo: " 382 + mRegistrationListeners.valueAt(i).mServiceInfo 383 + ", error: " 384 + e.getMessage()); 385 } 386 } 387 mRegistrationListeners.clear(); 388 } 389 390 /** On ot-daemon died, reset. */ onOtDaemonDied()391 public void onOtDaemonDied() { 392 reset(); 393 } 394 395 private final class RegistrationListener implements NsdManager.RegistrationListener { 396 private final NsdServiceInfo mServiceInfo; 397 private final int mListenerId; 398 private final INsdStatusReceiver mRegistrationReceiver; 399 private final List<INsdStatusReceiver> mUnregistrationReceivers; 400 RegistrationListener( @onNull NsdServiceInfo serviceInfo, int listenerId, @NonNull INsdStatusReceiver registrationReceiver)401 RegistrationListener( 402 @NonNull NsdServiceInfo serviceInfo, 403 int listenerId, 404 @NonNull INsdStatusReceiver registrationReceiver) { 405 mServiceInfo = serviceInfo; 406 mListenerId = listenerId; 407 mRegistrationReceiver = registrationReceiver; 408 mUnregistrationReceivers = new ArrayList<>(); 409 } 410 addUnregistrationReceiver(@onNull INsdStatusReceiver unregistrationReceiver)411 void addUnregistrationReceiver(@NonNull INsdStatusReceiver unregistrationReceiver) { 412 mUnregistrationReceivers.add(unregistrationReceiver); 413 } 414 415 @Override onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode)416 public void onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { 417 checkOnHandlerThread(); 418 mRegistrationListeners.remove(mListenerId); 419 LOG.i( 420 "Failed to register listener ID: " 421 + mListenerId 422 + " error code: " 423 + errorCode 424 + " serviceInfo: " 425 + serviceInfo); 426 try { 427 mRegistrationReceiver.onError(errorCode); 428 } catch (RemoteException ignored) { 429 // do nothing if the client is dead 430 } 431 } 432 433 @Override onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode)434 public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { 435 checkOnHandlerThread(); 436 for (INsdStatusReceiver receiver : mUnregistrationReceivers) { 437 LOG.i( 438 "Failed to unregister." 439 + "Listener ID: " 440 + mListenerId 441 + ", error code: " 442 + errorCode 443 + ", serviceInfo: " 444 + serviceInfo); 445 try { 446 receiver.onError(errorCode); 447 } catch (RemoteException ignored) { 448 // do nothing if the client is dead 449 } 450 } 451 } 452 453 @Override onServiceRegistered(NsdServiceInfo serviceInfo)454 public void onServiceRegistered(NsdServiceInfo serviceInfo) { 455 checkOnHandlerThread(); 456 LOG.i( 457 "Registered successfully. " 458 + "Listener ID: " 459 + mListenerId 460 + ", serviceInfo: " 461 + serviceInfo); 462 try { 463 mRegistrationReceiver.onSuccess(); 464 } catch (RemoteException ignored) { 465 // do nothing if the client is dead 466 } 467 } 468 469 @Override onServiceUnregistered(NsdServiceInfo serviceInfo)470 public void onServiceUnregistered(NsdServiceInfo serviceInfo) { 471 checkOnHandlerThread(); 472 for (INsdStatusReceiver receiver : mUnregistrationReceivers) { 473 LOG.i( 474 "Unregistered successfully. " 475 + "Listener ID: " 476 + mListenerId 477 + ", serviceInfo: " 478 + serviceInfo); 479 try { 480 receiver.onSuccess(); 481 } catch (RemoteException ignored) { 482 // do nothing if the client is dead 483 } 484 } 485 mRegistrationListeners.remove(mListenerId); 486 } 487 } 488 489 private final class DiscoveryListener implements NsdManager.DiscoveryListener { 490 private final int mListenerId; 491 private final String mType; 492 private final INsdDiscoverServiceCallback mDiscoverServiceCallback; 493 DiscoveryListener( int listenerId, @NonNull String type, @NonNull INsdDiscoverServiceCallback discoverServiceCallback)494 DiscoveryListener( 495 int listenerId, 496 @NonNull String type, 497 @NonNull INsdDiscoverServiceCallback discoverServiceCallback) { 498 mListenerId = listenerId; 499 mType = type; 500 mDiscoverServiceCallback = discoverServiceCallback; 501 } 502 503 @Override onStartDiscoveryFailed(String serviceType, int errorCode)504 public void onStartDiscoveryFailed(String serviceType, int errorCode) { 505 LOG.e( 506 "Failed to start service discovery." 507 + " Error code: " 508 + errorCode 509 + ", listener: " 510 + this); 511 mDiscoveryListeners.remove(mListenerId); 512 } 513 514 @Override onStopDiscoveryFailed(String serviceType, int errorCode)515 public void onStopDiscoveryFailed(String serviceType, int errorCode) { 516 LOG.e( 517 "Failed to stop service discovery." 518 + " Error code: " 519 + errorCode 520 + ", listener: " 521 + this); 522 mDiscoveryListeners.remove(mListenerId); 523 } 524 525 @Override onDiscoveryStarted(String serviceType)526 public void onDiscoveryStarted(String serviceType) { 527 LOG.i("Started service discovery. Listener: " + this); 528 } 529 530 @Override onDiscoveryStopped(String serviceType)531 public void onDiscoveryStopped(String serviceType) { 532 LOG.i("Stopped service discovery. Listener: " + this); 533 mDiscoveryListeners.remove(mListenerId); 534 } 535 536 @Override onServiceFound(NsdServiceInfo serviceInfo)537 public void onServiceFound(NsdServiceInfo serviceInfo) { 538 LOG.i("Found service: " + serviceInfo); 539 try { 540 mDiscoverServiceCallback.onServiceDiscovered( 541 serviceInfo.getServiceName(), mType, true); 542 } catch (RemoteException e) { 543 // do nothing if the client is dead 544 } 545 } 546 547 @Override onServiceLost(NsdServiceInfo serviceInfo)548 public void onServiceLost(NsdServiceInfo serviceInfo) { 549 LOG.i("Lost service: " + serviceInfo); 550 try { 551 mDiscoverServiceCallback.onServiceDiscovered( 552 serviceInfo.getServiceName(), mType, false); 553 } catch (RemoteException e) { 554 // do nothing if the client is dead 555 } 556 } 557 558 @Override toString()559 public String toString() { 560 return "ID: " + mListenerId + ", type: " + mType; 561 } 562 } 563 564 private final class ServiceInfoListener implements NsdManager.ServiceInfoCallback { 565 private final String mName; 566 private final String mType; 567 private final INsdResolveServiceCallback mResolveServiceCallback; 568 private final int mListenerId; 569 ServiceInfoListener( @onNull NsdServiceInfo serviceInfo, int listenerId, @NonNull INsdResolveServiceCallback resolveServiceCallback)570 ServiceInfoListener( 571 @NonNull NsdServiceInfo serviceInfo, 572 int listenerId, 573 @NonNull INsdResolveServiceCallback resolveServiceCallback) { 574 mName = serviceInfo.getServiceName(); 575 mType = serviceInfo.getServiceType(); 576 mListenerId = listenerId; 577 mResolveServiceCallback = resolveServiceCallback; 578 } 579 580 @Override onServiceInfoCallbackRegistrationFailed(int errorCode)581 public void onServiceInfoCallbackRegistrationFailed(int errorCode) { 582 LOG.e( 583 "Failed to register service info callback." 584 + " Listener ID: " 585 + mListenerId 586 + ", error: " 587 + errorCode 588 + ", service name: " 589 + mName 590 + ", service type: " 591 + mType); 592 } 593 594 @Override onServiceUpdated(@onNull NsdServiceInfo serviceInfo)595 public void onServiceUpdated(@NonNull NsdServiceInfo serviceInfo) { 596 LOG.i( 597 "Service is resolved. " 598 + " Listener ID: " 599 + mListenerId 600 + ", serviceInfo: " 601 + serviceInfo); 602 List<String> addresses = new ArrayList<>(); 603 int interfaceIndex = 0; 604 if (mNetworkToLinkProperties.containsKey(serviceInfo.getNetwork())) { 605 interfaceIndex = 606 Os.if_nametoindex( 607 mNetworkToLinkProperties 608 .get(serviceInfo.getNetwork()) 609 .getInterfaceName()); 610 } 611 for (InetAddress address : serviceInfo.getHostAddresses()) { 612 if (address instanceof Inet6Address) { 613 addresses.add(address.getHostAddress()); 614 } 615 } 616 List<DnsTxtAttribute> txtList = new ArrayList<>(); 617 for (Map.Entry<String, byte[]> entry : serviceInfo.getAttributes().entrySet()) { 618 DnsTxtAttribute attribute = 619 new DnsTxtAttribute(entry.getKey(), entry.getValue().clone()); 620 txtList.add(attribute); 621 } 622 // TODO: b/329018320 - Use the serviceInfo.getExpirationTime to derive TTL. 623 int ttlSeconds = 10; 624 try { 625 mResolveServiceCallback.onServiceResolved( 626 serviceInfo.getHostname(), 627 interfaceIndex, 628 serviceInfo.getServiceName(), 629 serviceInfo.getServiceType(), 630 serviceInfo.getPort(), 631 addresses, 632 txtList, 633 ttlSeconds); 634 635 } catch (RemoteException e) { 636 // do nothing if the client is dead 637 } 638 } 639 640 @Override onServiceLost()641 public void onServiceLost() {} 642 643 @Override onServiceInfoCallbackUnregistered()644 public void onServiceInfoCallbackUnregistered() { 645 LOG.i("The service info callback is unregistered. Listener: " + this); 646 mServiceInfoListeners.remove(mListenerId); 647 } 648 649 @Override toString()650 public String toString() { 651 return "ID: " + mListenerId + ", service name: " + mName + ", service type: " + mType; 652 } 653 } 654 655 class HostInfoListener implements DnsResolver.Callback<List<InetAddress>> { 656 private final String mHostname; 657 private final INsdResolveHostCallback mResolveHostCallback; 658 private final CancellationSignal mCancellationSignal; 659 private final int mListenerId; 660 HostInfoListener( @onNull String hostname, INsdResolveHostCallback resolveHostCallback, CancellationSignal cancellationSignal, int listenerId)661 HostInfoListener( 662 @NonNull String hostname, 663 INsdResolveHostCallback resolveHostCallback, 664 CancellationSignal cancellationSignal, 665 int listenerId) { 666 this.mHostname = hostname; 667 this.mResolveHostCallback = resolveHostCallback; 668 this.mCancellationSignal = cancellationSignal; 669 this.mListenerId = listenerId; 670 } 671 672 @Override onAnswer(@onNull List<InetAddress> answerList, int rcode)673 public void onAnswer(@NonNull List<InetAddress> answerList, int rcode) { 674 checkOnHandlerThread(); 675 676 LOG.i( 677 "Host is resolved." 678 + " Listener ID: " 679 + mListenerId 680 + ", hostname: " 681 + mHostname 682 + ", addresses: " 683 + answerList 684 + ", return code: " 685 + rcode); 686 List<String> addressStrings = new ArrayList<>(); 687 for (InetAddress address : answerList) { 688 addressStrings.add(address.getHostAddress()); 689 } 690 try { 691 mResolveHostCallback.onHostResolved(mHostname, addressStrings); 692 } catch (RemoteException e) { 693 // do nothing if the client is dead 694 } 695 mHostInfoListeners.remove(mListenerId); 696 } 697 698 @Override onError(@onNull DnsResolver.DnsException error)699 public void onError(@NonNull DnsResolver.DnsException error) { 700 checkOnHandlerThread(); 701 702 LOG.i( 703 "Failed to resolve host." 704 + " Listener ID: " 705 + mListenerId 706 + ", hostname: " 707 + mHostname 708 + ", error: " 709 + error.getMessage()); 710 try { 711 mResolveHostCallback.onHostResolved(mHostname, Collections.emptyList()); 712 } catch (RemoteException e) { 713 // do nothing if the client is dead 714 } 715 mHostInfoListeners.remove(mListenerId); 716 } 717 toString()718 public String toString() { 719 return "ID: " + mListenerId + ", hostname: " + mHostname; 720 } 721 cancel()722 void cancel() { 723 mCancellationSignal.cancel(); 724 mHostInfoListeners.remove(mListenerId); 725 } 726 } 727 } 728