2 * Copyright (c) 2020 Project CHIP Authors
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
19 package com.google.chip.chiptool.commissioner.thread.internal;
21 import android.Manifest.permission;
22 import android.content.Context;
23 import android.net.nsd.NsdManager;
24 import android.net.nsd.NsdServiceInfo;
25 import android.net.wifi.WifiManager;
26 import android.util.Log;
27 import androidx.annotation.RequiresPermission;
28 import com.google.chip.chiptool.commissioner.thread.BorderAgentInfo;
30 import java.util.concurrent.ArrayBlockingQueue;
31 import java.util.concurrent.BlockingQueue;
32 import java.util.concurrent.ExecutorService;
33 import java.util.concurrent.Executors;
34 import java.util.concurrent.atomic.AtomicBoolean;
36 class BorderAgentDiscoverer implements NsdManager.DiscoveryListener {
38 private static final String TAG = BorderAgentDiscoverer.class.getSimpleName();
40 private static final String SERVICE_TYPE = "_meshcop._udp";
41 private static final String KEY_DISCRIMINATOR = "discriminator";
42 private static final String KEY_NETWORK_NAME = "nn";
43 private static final String KEY_EXTENDED_PAN_ID = "xp";
45 private WifiManager.MulticastLock wifiMulticastLock;
46 private NsdManager nsdManager;
47 private BorderAgentListener borderAgentListener;
49 private ExecutorService executor = Executors.newSingleThreadExecutor();
50 private BlockingQueue<NsdServiceInfo> unresolvedServices = new ArrayBlockingQueue<>(256);
51 private AtomicBoolean isResolvingService = new AtomicBoolean(false);
53 private boolean isScanning = false;
55 public interface BorderAgentListener {
56 void onBorderAgentFound(BorderAgentInfo borderAgentInfo);
58 void onBorderAgentLost(String discriminator);
61 @RequiresPermission(permission.INTERNET)
62 public BorderAgentDiscoverer(Context context, BorderAgentListener borderAgentListener) {
63 WifiManager wifi = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
64 wifiMulticastLock = wifi.createMulticastLock("multicastLock");
66 nsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE);
68 this.borderAgentListener = borderAgentListener;
73 Log.w(TAG, "the Border Agent discoverer is already running!");
79 wifiMulticastLock.setReferenceCounted(true);
80 wifiMulticastLock.acquire();
83 nsdManager.discoverServices(
84 BorderAgentDiscoverer.SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, this);
87 private void startResolver() {
88 NsdManager.ResolveListener listener =
89 new NsdManager.ResolveListener() {
91 public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
95 "failed to resolve service %s, error: %d, this=%s",
96 serviceInfo.toString(), errorCode, this));
97 isResolvingService.set(false);
101 public void onServiceResolved(NsdServiceInfo serviceInfo) {
102 BorderAgentInfo borderAgent = getBorderAgentInfo(serviceInfo);
103 if (borderAgent != null) {
104 Log.d(TAG, "successfully resolved service: " + serviceInfo.toString());
105 borderAgentListener.onBorderAgentFound(borderAgent);
107 isResolvingService.set(false);
111 Log.d(TAG, "mDNS resolve listener is " + listener);
113 if (executor.isTerminated()) {
114 executor = Executors.newSingleThreadExecutor();
120 if (!isResolvingService.get()) {
121 NsdServiceInfo serviceInfo = unresolvedServices.take();
123 isResolvingService.set(true);
124 nsdManager.resolveService(serviceInfo, listener);
130 private void stopResolver() {
131 if (!executor.isTerminated()) {
132 executor.shutdownNow();
134 isResolvingService.set(false);
135 unresolvedServices.clear();
140 Log.w(TAG, "the Border Agent discoverer has already been stopped!");
144 nsdManager.stopServiceDiscovery(this);
147 if (wifiMulticastLock != null) {
148 wifiMulticastLock.release();
155 public void onDiscoveryStarted(String serviceType) {
156 Log.d(TAG, "start discovering Border Agent");
160 public void onDiscoveryStopped(String serviceType) {
161 Log.d(TAG, "stop discovering Border Agent");
165 public void onServiceFound(NsdServiceInfo nsdServiceInfo) {
166 Log.d(TAG, "a Border Agent service found");
168 unresolvedServices.offer(nsdServiceInfo);
172 public void onServiceLost(NsdServiceInfo nsdServiceInfo) {
173 String discriminator = getBorderAgentDiscriminator(nsdServiceInfo);
174 if (discriminator != null) {
175 Log.d(TAG, "a Border Agent service is gone");
176 borderAgentListener.onBorderAgentLost(discriminator);
181 public void onStartDiscoveryFailed(String serviceType, int errorCode) {
182 Log.d(TAG, "start discovering Border Agent failed: " + errorCode);
186 public void onStopDiscoveryFailed(String serviceType, int errorCode) {
187 Log.d(TAG, "stop discovering Border Agent failed: " + errorCode);
190 private BorderAgentInfo getBorderAgentInfo(NsdServiceInfo serviceInfo) {
191 Map<String, byte[]> attrs = serviceInfo.getAttributes();
193 // Use the host address as default discriminator.
194 String discriminator = serviceInfo.getHost().getHostAddress();
196 if (attrs.containsKey(KEY_DISCRIMINATOR)) {
197 discriminator = new String(attrs.get(KEY_DISCRIMINATOR));
200 if (!attrs.containsKey(KEY_NETWORK_NAME) || !attrs.containsKey(KEY_EXTENDED_PAN_ID)) {
204 return new BorderAgentInfo(
206 new String(attrs.get(KEY_NETWORK_NAME)),
207 attrs.get(KEY_EXTENDED_PAN_ID),
208 serviceInfo.getHost(),
209 serviceInfo.getPort());
212 private String getBorderAgentDiscriminator(NsdServiceInfo serviceInfo) {
213 Map<String, byte[]> attrs = serviceInfo.getAttributes();
215 if (attrs.containsKey(KEY_DISCRIMINATOR)) {
216 return new String(attrs.get(KEY_DISCRIMINATOR));
219 if (serviceInfo.getHost() != null) {
220 // Use the host address as default discriminator.
221 return serviceInfo.getHost().getHostAddress();