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