30
30
31
31
import android .Manifest .permission ;
32
32
import android .content .Context ;
33
+ import android .net .ConnectivityManager ;
34
+ import android .net .Network ;
33
35
import android .net .nsd .NsdManager ;
34
36
import android .net .nsd .NsdServiceInfo ;
35
37
import android .net .wifi .WifiManager ;
36
38
import android .util .Log ;
37
39
import androidx .annotation .Nullable ;
38
40
import androidx .annotation .RequiresPermission ;
41
+ import com .google .common .net .InetAddresses ;
42
+ import java .net .Inet6Address ;
43
+ import java .net .InetAddress ;
39
44
import java .util .Map ;
40
45
import java .util .concurrent .ArrayBlockingQueue ;
41
46
import java .util .concurrent .BlockingQueue ;
@@ -47,35 +52,43 @@ public class BorderAgentDiscoverer implements NsdManager.DiscoveryListener {
47
52
48
53
private static final String TAG = BorderAgentDiscoverer .class .getSimpleName ();
49
54
50
- private static final String SERVICE_TYPE = "_meshcop._udp" ;
55
+ public static final String MESHCOP_SERVICE_TYPE = "_meshcop._udp" ;
56
+ public static final String MESHCOP_E_SERVICE_TYPE = "_meshcop-e._udp" ;
51
57
private static final String KEY_ID = "id" ;
52
58
private static final String KEY_NETWORK_NAME = "nn" ;
53
59
private static final String KEY_EXTENDED_PAN_ID = "xp" ;
60
+ private static final String KEY_VENDOR_NAME = "vn" ;
61
+ private static final String KEY_MODEL_NAME = "mn" ;
54
62
55
- private WifiManager .MulticastLock wifiMulticastLock ;
56
- private NsdManager nsdManager ;
57
- private BorderAgentListener borderAgentListener ;
63
+ private final WifiManager .MulticastLock wifiMulticastLock ;
64
+ private final NsdManager nsdManager ;
65
+ private final ConnectivityManager connManager ;
66
+ private final String serviceType ;
67
+ private final BorderAgentListener borderAgentListener ;
58
68
59
69
private ExecutorService executor = Executors .newSingleThreadExecutor ();
60
- private BlockingQueue <NsdServiceInfo > unresolvedServices = new ArrayBlockingQueue <>(256 );
61
- private AtomicBoolean isResolvingService = new AtomicBoolean (false );
70
+ private final BlockingQueue <NsdServiceInfo > unresolvedServices = new ArrayBlockingQueue <>(256 );
71
+ private final AtomicBoolean isResolvingService = new AtomicBoolean (false );
62
72
63
73
private boolean isScanning = false ;
64
74
65
75
public interface BorderAgentListener {
66
76
67
77
void onBorderAgentFound (BorderAgentInfo borderAgentInfo );
68
78
69
- void onBorderAgentLost (byte [] id );
79
+ default void onBorderAgentLost (boolean isEpskcService , String instanceName ) {}
70
80
}
71
81
72
82
@ RequiresPermission (permission .INTERNET )
73
- public BorderAgentDiscoverer (Context context , BorderAgentListener borderAgentListener ) {
83
+ public BorderAgentDiscoverer (
84
+ Context context , String serviceType , BorderAgentListener borderAgentListener ) {
74
85
WifiManager wifi = (WifiManager ) context .getSystemService (Context .WIFI_SERVICE );
75
86
wifiMulticastLock = wifi .createMulticastLock ("multicastLock" );
76
87
77
- nsdManager = (NsdManager ) context .getSystemService (Context .NSD_SERVICE );
88
+ nsdManager = context .getSystemService (NsdManager .class );
89
+ connManager = context .getSystemService (ConnectivityManager .class );
78
90
91
+ this .serviceType = serviceType ;
79
92
this .borderAgentListener = borderAgentListener ;
80
93
}
81
94
@@ -91,8 +104,8 @@ public void start() {
91
104
wifiMulticastLock .acquire ();
92
105
93
106
startResolver ();
94
- nsdManager . discoverServices (
95
- BorderAgentDiscoverer . SERVICE_TYPE , NsdManager .PROTOCOL_DNS_SD , this );
107
+
108
+ nsdManager . discoverServices ( serviceType , NsdManager .PROTOCOL_DNS_SD , this );
96
109
}
97
110
98
111
private void startResolver () {
@@ -112,7 +125,7 @@ public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
112
125
public void onServiceResolved (NsdServiceInfo serviceInfo ) {
113
126
BorderAgentInfo borderAgent = getBorderAgentInfo (serviceInfo );
114
127
if (borderAgent != null ) {
115
- Log .d (TAG , "successfully resolved service: " + serviceInfo . toString () );
128
+ Log .d (TAG , "successfully resolved service: " + serviceInfo );
116
129
Log .d (
117
130
TAG ,
118
131
"successfully resolved service: " + serviceInfo .getHost ().getCanonicalHostName ());
@@ -167,7 +180,7 @@ public void stop() {
167
180
168
181
@ Override
169
182
public void onDiscoveryStarted (String serviceType ) {
170
- Log .d (TAG , "start discovering Border Agent" );
183
+ Log .d (TAG , "start discovering Border Agent: " + serviceType );
171
184
}
172
185
173
186
@ Override
@@ -184,11 +197,9 @@ public void onServiceFound(NsdServiceInfo nsdServiceInfo) {
184
197
185
198
@ Override
186
199
public void onServiceLost (NsdServiceInfo nsdServiceInfo ) {
187
- byte [] id = getBorderAgentId (nsdServiceInfo );
188
- if (id != null ) {
189
- Log .d (TAG , "a Border Agent service is gone: " + nsdServiceInfo .getServiceName ());
190
- borderAgentListener .onBorderAgentLost (id );
191
- }
200
+ Log .d (TAG , "a Border Agent service is gone: " + nsdServiceInfo .getServiceName ());
201
+ borderAgentListener .onBorderAgentLost (
202
+ serviceType .equals (MESHCOP_E_SERVICE_TYPE ), nsdServiceInfo .getServiceName ());
192
203
}
193
204
194
205
@ Override
@@ -204,26 +215,54 @@ public void onStopDiscoveryFailed(String serviceType, int errorCode) {
204
215
@ Nullable
205
216
private BorderAgentInfo getBorderAgentInfo (NsdServiceInfo serviceInfo ) {
206
217
Map <String , byte []> attrs = serviceInfo .getAttributes ();
207
- byte [] id = getBorderAgentId (serviceInfo );
208
-
209
- if (!attrs .containsKey (KEY_NETWORK_NAME ) || !attrs .containsKey (KEY_EXTENDED_PAN_ID )) {
210
- return null ;
211
- }
212
-
213
218
return new BorderAgentInfo (
214
- id ,
215
- new String (attrs .get (KEY_NETWORK_NAME )),
219
+ serviceType .equals (MESHCOP_E_SERVICE_TYPE ),
220
+ serviceInfo .getServiceName (),
221
+ handleNsdServiceAddress (serviceInfo .getHost ()),
222
+ serviceInfo .getPort (),
223
+ attrs .get (KEY_ID ),
224
+ getStringAttribute (attrs , KEY_NETWORK_NAME ),
216
225
attrs .get (KEY_EXTENDED_PAN_ID ),
217
- serviceInfo . getHost ( ),
218
- serviceInfo . getPort ( ));
226
+ getStringAttribute ( attrs , KEY_VENDOR_NAME ),
227
+ getStringAttribute ( attrs , KEY_MODEL_NAME ));
219
228
}
220
229
221
230
@ Nullable
222
- private byte [] getBorderAgentId (NsdServiceInfo serviceInfo ) {
223
- Map <String , byte []> attrs = serviceInfo .getAttributes ();
224
- if (attrs .containsKey (KEY_ID )) {
225
- return attrs .get (KEY_ID ).clone ();
231
+ private static String getStringAttribute (Map <String , byte []> attributes , String key ) {
232
+ byte [] value = attributes .get (key );
233
+ if (value == null ) {
234
+ return null ;
235
+ }
236
+ return new String (value );
237
+ }
238
+
239
+ /**
240
+ * Properly handles the {@link InetAddress} within a discovered {@link NsdServiceInfo}.
241
+ *
242
+ * <p>For example, adds the scope ID to an IPv6 link-local address to make is usable.
243
+ */
244
+ private InetAddress handleNsdServiceAddress (InetAddress address ) {
245
+ if (!(address instanceof Inet6Address )) {
246
+ return address ;
226
247
}
227
- return null ;
248
+
249
+ Inet6Address address6 = (Inet6Address ) address ;
250
+
251
+ // Sets the scope ID for IPv6 link-local address if it's missing. This can happen before
252
+ // Android U
253
+ if (address6 .isLinkLocalAddress () && address6 .getScopeId () == 0 ) {
254
+ // Assume the mDNS service is discovered on the current active default network
255
+ Network network = connManager .getActiveNetwork ();
256
+ if (network == null ) {
257
+ return address6 ;
258
+ }
259
+ String interfaceName = connManager .getLinkProperties (network ).getInterfaceName ();
260
+ if (interfaceName == null ) {
261
+ return address6 ;
262
+ }
263
+ return InetAddresses .forString (address6 .getHostAddress () + "%" + interfaceName );
264
+ }
265
+
266
+ return address6 ;
228
267
}
229
268
}
0 commit comments