react-native-zeroconf 0.16.1 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NOTICE +0 -3
- package/README.md +1 -1
- package/android/build.gradle +0 -3
- package/android/src/main/java/com/balthazargronon/RCTZeroconf/ZeroConfImplFactory.java +1 -1
- package/android/src/main/java/com/balthazargronon/RCTZeroconf/Zeroconf.java +12 -2
- package/android/src/main/java/com/balthazargronon/RCTZeroconf/ZeroconfModule.java +129 -4
- package/android/src/main/java/com/balthazargronon/RCTZeroconf/ZeroconfOptions.java +72 -0
- package/android/src/main/java/com/balthazargronon/RCTZeroconf/dnssd/DnssdImpl.java +633 -0
- package/android/src/main/java/com/balthazargronon/RCTZeroconf/nsd/NsdServiceImpl.java +331 -34
- package/android/src/main/java/com/balthazargronon/RCTZeroconf/nsd/ServiceTypesQuery.java +296 -0
- package/android/src/main/java/com/github/druk/dnssd/DNSSDEmbedded.java +1 -1
- package/android/src/main/jni/mdnsresponder/mDNSShared/dnssd_clientshim.c +59 -22
- package/dist/index.js +234 -48
- package/index.d.ts +101 -6
- package/ios/RNZeroconf/RNZeroconf.m +285 -22
- package/package.json +28 -4
- package/src/index.js +214 -44
- package/windows/ExperimentalFeatures.props +33 -0
- package/windows/NuGet.config +11 -0
- package/windows/RNZeroconf/RNZeroconf.cpp +227 -0
- package/windows/RNZeroconf/RNZeroconf.def +3 -0
- package/windows/RNZeroconf/RNZeroconf.h +70 -0
- package/windows/RNZeroconf/RNZeroconf.rc +0 -0
- package/windows/RNZeroconf/RNZeroconf.vcxproj +150 -0
- package/windows/RNZeroconf/RNZeroconf.vcxproj.filters +50 -0
- package/windows/RNZeroconf/ReactPackageProvider.cpp +20 -0
- package/windows/RNZeroconf/ReactPackageProvider.h +24 -0
- package/windows/RNZeroconf/ReactPackageProvider.idl +9 -0
- package/windows/RNZeroconf/ZeroconfCore.cpp +1042 -0
- package/windows/RNZeroconf/ZeroconfCore.h +165 -0
- package/windows/RNZeroconf/pch.cpp +1 -0
- package/windows/RNZeroconf/pch.h +30 -0
- package/windows/RNZeroconf/resource.h +5 -0
- package/windows/RNZeroconf/targetver.h +8 -0
- package/windows/RNZeroconf.sln +43 -0
- package/android/src/main/java/com/balthazargronon/RCTZeroconf/rx2dnssd/DnssdImpl.java +0 -305
- package/android/src/main/java/com/github/druk/rx2dnssd/BonjourService.java +0 -400
- package/android/src/main/java/com/github/druk/rx2dnssd/Rx2BrowseListener.java +0 -55
- package/android/src/main/java/com/github/druk/rx2dnssd/Rx2Dnssd.java +0 -127
- package/android/src/main/java/com/github/druk/rx2dnssd/Rx2DnssdCommon.java +0 -280
- package/android/src/main/java/com/github/druk/rx2dnssd/Rx2DnssdEmbedded.java +0 -31
- package/android/src/main/java/com/github/druk/rx2dnssd/Rx2QueryListener.java +0 -70
- package/android/src/main/java/com/github/druk/rx2dnssd/Rx2RegisterListener.java +0 -47
- package/android/src/main/java/com/github/druk/rx2dnssd/Rx2ResolveListener.java +0 -51
|
@@ -2,6 +2,7 @@ package com.balthazargronon.RCTZeroconf.nsd;
|
|
|
2
2
|
|
|
3
3
|
import android.annotation.SuppressLint;
|
|
4
4
|
import android.content.Context;
|
|
5
|
+
import android.net.Network;
|
|
5
6
|
import android.net.nsd.NsdManager;
|
|
6
7
|
import android.net.nsd.NsdServiceInfo;
|
|
7
8
|
import android.net.wifi.WifiManager;
|
|
@@ -12,6 +13,7 @@ import android.util.Log;
|
|
|
12
13
|
|
|
13
14
|
import com.balthazargronon.RCTZeroconf.Zeroconf;
|
|
14
15
|
import com.balthazargronon.RCTZeroconf.ZeroconfModule;
|
|
16
|
+
import com.balthazargronon.RCTZeroconf.ZeroconfOptions;
|
|
15
17
|
import com.facebook.react.bridge.Promise;
|
|
16
18
|
import com.facebook.react.bridge.ReactApplicationContext;
|
|
17
19
|
|
|
@@ -28,6 +30,8 @@ import java.util.ArrayList;
|
|
|
28
30
|
import java.util.Collections;
|
|
29
31
|
import java.util.Iterator;
|
|
30
32
|
import java.util.List;
|
|
33
|
+
import java.util.Set;
|
|
34
|
+
import java.util.HashSet;
|
|
31
35
|
import java.util.Locale;
|
|
32
36
|
import java.util.Map;
|
|
33
37
|
import java.util.concurrent.ConcurrentHashMap;
|
|
@@ -62,6 +66,12 @@ public class NsdServiceImpl implements Zeroconf {
|
|
|
62
66
|
NsdManager.DiscoveryListener discoveryListener;
|
|
63
67
|
// Android 14+: services are followed with registerServiceInfoCallback, null before
|
|
64
68
|
@Nullable ServiceInfoCallbacks infoCallbacks;
|
|
69
|
+
// Service type scans query the network directly, NsdManager can't list types
|
|
70
|
+
@Nullable ServiceTypesQuery typesQuery;
|
|
71
|
+
// Types listed, and those answered by the network: the others are this app's own services,
|
|
72
|
+
// which the system mDNS stack doesn't answer back
|
|
73
|
+
final Set<String> listedTypes = new HashSet<>();
|
|
74
|
+
final Set<String> networkTypes = new HashSet<>();
|
|
65
75
|
|
|
66
76
|
NsdScan(String scanId) {
|
|
67
77
|
this.scanId = scanId;
|
|
@@ -69,12 +79,15 @@ public class NsdServiceImpl implements Zeroconf {
|
|
|
69
79
|
}
|
|
70
80
|
|
|
71
81
|
private static class PendingResolve {
|
|
72
|
-
final String scanId;
|
|
82
|
+
@Nullable final String scanId;
|
|
73
83
|
final NsdServiceInfo serviceInfo;
|
|
84
|
+
// resolveService(): settled instead of emitting events
|
|
85
|
+
@Nullable final Promise promise;
|
|
74
86
|
|
|
75
|
-
PendingResolve(String scanId, NsdServiceInfo serviceInfo) {
|
|
87
|
+
PendingResolve(@Nullable String scanId, NsdServiceInfo serviceInfo, @Nullable Promise promise) {
|
|
76
88
|
this.scanId = scanId;
|
|
77
89
|
this.serviceInfo = serviceInfo;
|
|
90
|
+
this.promise = promise;
|
|
78
91
|
}
|
|
79
92
|
}
|
|
80
93
|
|
|
@@ -85,13 +98,60 @@ public class NsdServiceImpl implements Zeroconf {
|
|
|
85
98
|
}
|
|
86
99
|
|
|
87
100
|
@Override
|
|
88
|
-
public void scan(final String scanId, String type, String protocol, String domain) {
|
|
101
|
+
public void scan(final String scanId, String type, String protocol, String domain, ZeroconfOptions options) {
|
|
89
102
|
final NsdManager nsdManager = getNsdManager();
|
|
90
103
|
|
|
91
104
|
this.stop(scanId);
|
|
92
|
-
acquireMulticastLock();
|
|
93
105
|
|
|
94
106
|
final NsdScan scan = new NsdScan(scanId);
|
|
107
|
+
final boolean typesOnly = ZeroconfModule.isServiceTypesScan(type, protocol);
|
|
108
|
+
|
|
109
|
+
Network network = null;
|
|
110
|
+
if (options.networkInterface != null && !typesOnly) {
|
|
111
|
+
String networkError = checkNetworkSupport(options.networkInterface);
|
|
112
|
+
network = networkError == null ? ZeroconfModule.findNetwork(reactApplicationContext, options.networkInterface) : null;
|
|
113
|
+
if (network == null) {
|
|
114
|
+
sendNetworkError(networkError, options.networkInterface, null, scanId);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
acquireMulticastLock();
|
|
119
|
+
|
|
120
|
+
if (typesOnly) {
|
|
121
|
+
scan.typesQuery = new ServiceTypesQuery(options.networkInterface, new ServiceTypesQuery.Listener() {
|
|
122
|
+
@Override
|
|
123
|
+
public void onTypeFound(String serviceType) {
|
|
124
|
+
synchronized (scan) {
|
|
125
|
+
scan.networkTypes.add(serviceType);
|
|
126
|
+
if (!scan.listedTypes.add(serviceType)) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
sendScanEvent(ZeroconfModule.EVENT_FOUND, nameToMap(serviceType), scanId);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
@Override
|
|
134
|
+
public void onTypeLost(String serviceType) {
|
|
135
|
+
synchronized (scan) {
|
|
136
|
+
scan.networkTypes.remove(serviceType);
|
|
137
|
+
if (localServiceTypes().contains(serviceType) || !scan.listedTypes.remove(serviceType)) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
sendScanEvent(ZeroconfModule.EVENT_REMOVE, nameToMap(serviceType), scanId);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
@Override
|
|
145
|
+
public void onError(String message) {
|
|
146
|
+
zeroconfModule.sendError(ZeroconfModule.ERROR_DOMAIN_LIBRARY, ZeroconfModule.ERROR_CODE_EXCEPTION, "Listing service types failed: " + message, null, scanId);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
mScans.put(scanId, scan);
|
|
150
|
+
scan.typesQuery.start();
|
|
151
|
+
sendScanEvent(ZeroconfModule.EVENT_START, new WritableNativeMap(), scanId);
|
|
152
|
+
syncLocalServiceTypes();
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
95
155
|
|
|
96
156
|
if (Build.VERSION.SDK_INT >= 34) {
|
|
97
157
|
scan.infoCallbacks = new ServiceInfoCallbacks(nsdManager, mCallbackExecutor, new ServiceInfoCallbacks.Listener() {
|
|
@@ -159,8 +219,34 @@ public class NsdServiceImpl implements Zeroconf {
|
|
|
159
219
|
};
|
|
160
220
|
|
|
161
221
|
mScans.put(scanId, scan);
|
|
162
|
-
|
|
163
|
-
|
|
222
|
+
// "_ipp._tcp,_printer" browses the _printer subtype
|
|
223
|
+
String serviceType = options.subtype != null
|
|
224
|
+
? ZeroconfOptions.withSubtypes(String.format("_%s._%s", type, protocol), Collections.singletonList(options.subtype))
|
|
225
|
+
: String.format("_%s._%s.", type, protocol);
|
|
226
|
+
if (network != null && Build.VERSION.SDK_INT >= 33) {
|
|
227
|
+
nsdManager.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD, network, mCallbackExecutor, scan.discoveryListener);
|
|
228
|
+
} else {
|
|
229
|
+
nsdManager.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD, scan.discoveryListener);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Choosing the network of NSD requests needs Android 13, null when supported
|
|
234
|
+
@Nullable
|
|
235
|
+
private static String checkNetworkSupport(String networkInterface) {
|
|
236
|
+
return Build.VERSION.SDK_INT >= 33 ? null : ZeroconfModule.ERROR_CODE_UNSUPPORTED;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
private void sendNetworkError(@Nullable String code, String networkInterface, @Nullable Promise promise, @Nullable String scanId) {
|
|
240
|
+
boolean unsupported = ZeroconfModule.ERROR_CODE_UNSUPPORTED.equals(code);
|
|
241
|
+
String errorCode = unsupported ? ZeroconfModule.ERROR_CODE_UNSUPPORTED : ZeroconfModule.ERROR_CODE_UNKNOWN_INTERFACE;
|
|
242
|
+
String message = unsupported
|
|
243
|
+
? "Choosing the network interface needs Android 13 or later with NSD, use DNSSD"
|
|
244
|
+
: "Unknown network interface " + networkInterface;
|
|
245
|
+
if (promise != null) {
|
|
246
|
+
ZeroconfModule.reject(promise, ZeroconfModule.ERROR_DOMAIN_LIBRARY, errorCode, message, null);
|
|
247
|
+
} else {
|
|
248
|
+
zeroconfModule.sendError(ZeroconfModule.ERROR_DOMAIN_LIBRARY, errorCode, message, null, scanId);
|
|
249
|
+
}
|
|
164
250
|
}
|
|
165
251
|
|
|
166
252
|
@Override
|
|
@@ -183,6 +269,12 @@ public class NsdServiceImpl implements Zeroconf {
|
|
|
183
269
|
scan.infoCallbacks.unregisterAll();
|
|
184
270
|
}
|
|
185
271
|
|
|
272
|
+
if (scan.typesQuery != null) {
|
|
273
|
+
scan.typesQuery.stop();
|
|
274
|
+
// No discovery listener reports it
|
|
275
|
+
sendScanEvent(ZeroconfModule.EVENT_STOP, new WritableNativeMap(), scanId);
|
|
276
|
+
}
|
|
277
|
+
|
|
186
278
|
synchronized (mResolveQueue) {
|
|
187
279
|
Iterator<PendingResolve> iterator = mResolveQueue.iterator();
|
|
188
280
|
while (iterator.hasNext()) {
|
|
@@ -222,6 +314,12 @@ public class NsdServiceImpl implements Zeroconf {
|
|
|
222
314
|
zeroconfModule.sendEvent(getReactApplicationContext(), eventName, body);
|
|
223
315
|
}
|
|
224
316
|
|
|
317
|
+
private static WritableMap nameToMap(String name) {
|
|
318
|
+
WritableMap service = new WritableNativeMap();
|
|
319
|
+
service.putString(ZeroconfModule.KEY_SERVICE_NAME, name);
|
|
320
|
+
return service;
|
|
321
|
+
}
|
|
322
|
+
|
|
225
323
|
private static WritableMap serviceNameToMap(NsdServiceInfo serviceInfo) {
|
|
226
324
|
WritableMap service = new WritableNativeMap();
|
|
227
325
|
service.putString(ZeroconfModule.KEY_SERVICE_NAME, serviceInfo.getServiceName());
|
|
@@ -229,22 +327,119 @@ public class NsdServiceImpl implements Zeroconf {
|
|
|
229
327
|
}
|
|
230
328
|
|
|
231
329
|
@Override
|
|
232
|
-
public void registerService(String type, String protocol, String domain, String name, int port, ReadableArray txt, Promise promise) {
|
|
233
|
-
|
|
330
|
+
public void registerService(String type, String protocol, String domain, String name, int port, ReadableArray txt, ZeroconfOptions options, Promise promise) {
|
|
331
|
+
Network network = null;
|
|
332
|
+
if (options.networkInterface != null) {
|
|
333
|
+
String networkError = checkNetworkSupport(options.networkInterface);
|
|
334
|
+
network = networkError == null ? ZeroconfModule.findNetwork(reactApplicationContext, options.networkInterface) : null;
|
|
335
|
+
if (network == null) {
|
|
336
|
+
sendNetworkError(networkError, options.networkInterface, promise, null);
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
// "_ipp._tcp,_printer,_color" registers the subtypes too
|
|
341
|
+
String serviceType = options.subtypes.isEmpty()
|
|
342
|
+
? String.format("_%s._%s.", type, protocol)
|
|
343
|
+
: ZeroconfOptions.withSubtypes(String.format("_%s._%s", type, protocol), options.subtypes);
|
|
344
|
+
ServiceRegistrationListener listener = new ServiceRegistrationListener(promise, serviceType, port, network);
|
|
345
|
+
getNsdManager().registerService(listener.buildServiceInfo(name, txt), NsdManager.PROTOCOL_DNS_SD, listener);
|
|
346
|
+
}
|
|
234
347
|
|
|
235
|
-
|
|
236
|
-
|
|
348
|
+
/**
|
|
349
|
+
* NsdManager can't update a registration: unregisters and registers again with the new TXT record,
|
|
350
|
+
* without emitting unpublished and published
|
|
351
|
+
*/
|
|
352
|
+
@Override
|
|
353
|
+
public void updateService(String serviceName, final ReadableArray txt, final Promise promise) {
|
|
354
|
+
final ServiceRegistrationListener previous = mPublishedServices.remove(serviceName);
|
|
355
|
+
if (previous == null) {
|
|
356
|
+
ZeroconfModule.reject(promise, ZeroconfModule.ERROR_DOMAIN_LIBRARY, ZeroconfModule.ERROR_CODE_NOT_PUBLISHED, "Service " + serviceName + " is not published", serviceName);
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
final ServiceRegistrationListener next = new ServiceRegistrationListener(promise, previous.serviceType, previous.port, previous.network);
|
|
360
|
+
next.announceRegistered = false;
|
|
361
|
+
final NsdServiceInfo serviceInfo = next.buildServiceInfo(serviceName, txt);
|
|
362
|
+
previous.announceUnregistered = false;
|
|
363
|
+
previous.afterUnregistered = () -> getNsdManager().registerService(serviceInfo, NsdManager.PROTOCOL_DNS_SD, next);
|
|
364
|
+
getNsdManager().unregisterService(previous);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
@Override
|
|
368
|
+
public void resolveService(String name, String type, String protocol, String domain, ZeroconfOptions options, final Promise promise) {
|
|
369
|
+
final NsdServiceInfo serviceInfo = new NsdServiceInfo();
|
|
237
370
|
serviceInfo.setServiceName(name);
|
|
238
|
-
serviceInfo.setServiceType(
|
|
239
|
-
|
|
371
|
+
serviceInfo.setServiceType(String.format("_%s._%s", type, protocol));
|
|
372
|
+
if (options.networkInterface != null) {
|
|
373
|
+
String networkError = checkNetworkSupport(options.networkInterface);
|
|
374
|
+
Network network = networkError == null ? ZeroconfModule.findNetwork(reactApplicationContext, options.networkInterface) : null;
|
|
375
|
+
if (network == null) {
|
|
376
|
+
sendNetworkError(networkError, options.networkInterface, promise, null);
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
if (Build.VERSION.SDK_INT >= 33) {
|
|
380
|
+
serviceInfo.setNetwork(network);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
240
383
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
384
|
+
if (Build.VERSION.SDK_INT < 34) {
|
|
385
|
+
// One resolve at a time before Android 14, shares the queue of the scans
|
|
386
|
+
synchronized (mResolveQueue) {
|
|
387
|
+
mResolveQueue.add(new PendingResolve(null, serviceInfo, promise));
|
|
388
|
+
}
|
|
389
|
+
resolveNext();
|
|
390
|
+
return;
|
|
244
391
|
}
|
|
245
392
|
|
|
246
|
-
|
|
247
|
-
|
|
393
|
+
final boolean[] settled = { false };
|
|
394
|
+
final NsdManager nsdManager = getNsdManager();
|
|
395
|
+
final NsdManager.ServiceInfoCallback[] callback = new NsdManager.ServiceInfoCallback[1];
|
|
396
|
+
final Runnable finish = () -> {
|
|
397
|
+
try {
|
|
398
|
+
nsdManager.unregisterServiceInfoCallback(callback[0]);
|
|
399
|
+
} catch (IllegalArgumentException e) {
|
|
400
|
+
// Already unregistered
|
|
401
|
+
}
|
|
402
|
+
};
|
|
403
|
+
callback[0] = new NsdManager.ServiceInfoCallback() {
|
|
404
|
+
@Override
|
|
405
|
+
public void onServiceInfoCallbackRegistrationFailed(int errorCode) {
|
|
406
|
+
synchronized (settled) {
|
|
407
|
+
if (settled[0]) return;
|
|
408
|
+
settled[0] = true;
|
|
409
|
+
}
|
|
410
|
+
ZeroconfModule.reject(promise, ZeroconfModule.ERROR_DOMAIN_NSD, errorCode, "Resolving service " + name + " failed: " + ZeroconfModule.describeNsdError(errorCode), name);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
@Override
|
|
414
|
+
public void onServiceUpdated(NsdServiceInfo updated) {
|
|
415
|
+
if (getHostAddresses(updated).isEmpty()) {
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
synchronized (settled) {
|
|
419
|
+
if (settled[0]) return;
|
|
420
|
+
settled[0] = true;
|
|
421
|
+
}
|
|
422
|
+
promise.resolve(serviceInfoToMap(updated));
|
|
423
|
+
mHandler.post(finish);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
@Override
|
|
427
|
+
public void onServiceLost() {
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
@Override
|
|
431
|
+
public void onServiceInfoCallbackUnregistered() {
|
|
432
|
+
}
|
|
433
|
+
};
|
|
434
|
+
nsdManager.registerServiceInfoCallback(serviceInfo, mCallbackExecutor, callback[0]);
|
|
435
|
+
mHandler.postDelayed(() -> {
|
|
436
|
+
synchronized (settled) {
|
|
437
|
+
if (settled[0]) return;
|
|
438
|
+
settled[0] = true;
|
|
439
|
+
}
|
|
440
|
+
finish.run();
|
|
441
|
+
ZeroconfModule.reject(promise, ZeroconfModule.ERROR_DOMAIN_LIBRARY, ZeroconfModule.ERROR_CODE_TIMEOUT, "Resolving service " + name + " failed: timed out", name);
|
|
442
|
+
}, (long) (options.timeoutSeconds * 1000));
|
|
248
443
|
}
|
|
249
444
|
|
|
250
445
|
@Override
|
|
@@ -262,6 +457,53 @@ public class NsdServiceImpl implements Zeroconf {
|
|
|
262
457
|
mPublishedServices.remove(serviceName);
|
|
263
458
|
serviceListener.unregisterPromise = promise;
|
|
264
459
|
nsdManager.unregisterService(serviceListener);
|
|
460
|
+
syncLocalServiceTypes();
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// "_ipp._tcp,_printer" or "_http._tcp." -> "_ipp._tcp", "_http._tcp"
|
|
464
|
+
private static String baseServiceType(String serviceType) {
|
|
465
|
+
int comma = serviceType.indexOf(',');
|
|
466
|
+
String type = comma >= 0 ? serviceType.substring(0, comma) : serviceType;
|
|
467
|
+
return type.endsWith(".") ? type.substring(0, type.length() - 1) : type;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
private Set<String> localServiceTypes() {
|
|
471
|
+
Set<String> types = new HashSet<>();
|
|
472
|
+
for (ServiceRegistrationListener listener : mPublishedServices.values()) {
|
|
473
|
+
types.add(baseServiceType(listener.serviceType));
|
|
474
|
+
}
|
|
475
|
+
return types;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// Lists the types this app publishes in service type scans, and removes those it no longer publishes
|
|
479
|
+
private void syncLocalServiceTypes() {
|
|
480
|
+
Set<String> local = localServiceTypes();
|
|
481
|
+
for (NsdScan scan : mScans.values()) {
|
|
482
|
+
if (scan.typesQuery == null) {
|
|
483
|
+
continue;
|
|
484
|
+
}
|
|
485
|
+
List<String> found = new ArrayList<>();
|
|
486
|
+
List<String> removed = new ArrayList<>();
|
|
487
|
+
synchronized (scan) {
|
|
488
|
+
for (String type : local) {
|
|
489
|
+
if (scan.listedTypes.add(type)) {
|
|
490
|
+
found.add(type);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
for (String type : new ArrayList<>(scan.listedTypes)) {
|
|
494
|
+
if (!local.contains(type) && !scan.networkTypes.contains(type)) {
|
|
495
|
+
scan.listedTypes.remove(type);
|
|
496
|
+
removed.add(type);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
for (String type : found) {
|
|
501
|
+
sendScanEvent(ZeroconfModule.EVENT_FOUND, nameToMap(type), scan.scanId);
|
|
502
|
+
}
|
|
503
|
+
for (String type : removed) {
|
|
504
|
+
sendScanEvent(ZeroconfModule.EVENT_REMOVE, nameToMap(type), scan.scanId);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
265
507
|
}
|
|
266
508
|
|
|
267
509
|
@Override
|
|
@@ -284,7 +526,7 @@ public class NsdServiceImpl implements Zeroconf {
|
|
|
284
526
|
|
|
285
527
|
private void enqueueResolve(String scanId, NsdServiceInfo serviceInfo) {
|
|
286
528
|
synchronized (mResolveQueue) {
|
|
287
|
-
mResolveQueue.add(new PendingResolve(scanId, serviceInfo));
|
|
529
|
+
mResolveQueue.add(new PendingResolve(scanId, serviceInfo, null));
|
|
288
530
|
}
|
|
289
531
|
resolveNext();
|
|
290
532
|
}
|
|
@@ -299,7 +541,7 @@ public class NsdServiceImpl implements Zeroconf {
|
|
|
299
541
|
mIsResolving = true;
|
|
300
542
|
}
|
|
301
543
|
try {
|
|
302
|
-
getNsdManager().resolveService(next.serviceInfo, new ZeroResolveListener(next.scanId));
|
|
544
|
+
getNsdManager().resolveService(next.serviceInfo, new ZeroResolveListener(next.scanId, next.promise));
|
|
303
545
|
} catch (Throwable e) {
|
|
304
546
|
Log.e(TAG, "resolveService failed", e);
|
|
305
547
|
String serviceName = next.serviceInfo.getServiceName();
|
|
@@ -316,10 +558,12 @@ public class NsdServiceImpl implements Zeroconf {
|
|
|
316
558
|
}
|
|
317
559
|
|
|
318
560
|
private class ZeroResolveListener implements NsdManager.ResolveListener {
|
|
319
|
-
private final String scanId;
|
|
561
|
+
@Nullable private final String scanId;
|
|
562
|
+
@Nullable private final Promise promise;
|
|
320
563
|
|
|
321
|
-
ZeroResolveListener(String scanId) {
|
|
564
|
+
ZeroResolveListener(@Nullable String scanId, @Nullable Promise promise) {
|
|
322
565
|
this.scanId = scanId;
|
|
566
|
+
this.promise = promise;
|
|
323
567
|
}
|
|
324
568
|
|
|
325
569
|
@Override
|
|
@@ -327,22 +571,27 @@ public class NsdServiceImpl implements Zeroconf {
|
|
|
327
571
|
if (errorCode == NsdManager.FAILURE_ALREADY_ACTIVE) {
|
|
328
572
|
// Another resolve (possibly from another app or listener) is in flight: retry shortly
|
|
329
573
|
synchronized (mResolveQueue) {
|
|
330
|
-
mResolveQueue.addFirst(new PendingResolve(scanId, serviceInfo));
|
|
574
|
+
mResolveQueue.addFirst(new PendingResolve(scanId, serviceInfo, promise));
|
|
331
575
|
mIsResolving = false;
|
|
332
576
|
}
|
|
333
577
|
mHandler.postDelayed(NsdServiceImpl.this::resolveNext, RESOLVE_RETRY_DELAY_MS);
|
|
334
578
|
return;
|
|
335
579
|
}
|
|
336
|
-
|
|
337
|
-
|
|
580
|
+
String message = "Resolving service " + serviceInfo.getServiceName() + " failed: " + ZeroconfModule.describeNsdError(errorCode);
|
|
581
|
+
if (promise != null) {
|
|
582
|
+
ZeroconfModule.reject(promise, ZeroconfModule.ERROR_DOMAIN_NSD, errorCode, message, serviceInfo.getServiceName());
|
|
583
|
+
} else if (mScans.containsKey(scanId)) {
|
|
584
|
+
zeroconfModule.sendError(ZeroconfModule.ERROR_DOMAIN_NSD, errorCode, message, serviceInfo.getServiceName(), scanId);
|
|
338
585
|
}
|
|
339
586
|
onResolveDone();
|
|
340
587
|
}
|
|
341
588
|
|
|
342
589
|
@Override
|
|
343
590
|
public void onServiceResolved(NsdServiceInfo serviceInfo) {
|
|
344
|
-
|
|
345
|
-
|
|
591
|
+
if (promise != null) {
|
|
592
|
+
promise.resolve(serviceInfoToMap(serviceInfo));
|
|
593
|
+
} else if (mScans.containsKey(scanId)) {
|
|
594
|
+
// The scan may have been stopped while resolving
|
|
346
595
|
sendScanEvent(ZeroconfModule.EVENT_RESOLVE, serviceInfoToMap(serviceInfo), scanId);
|
|
347
596
|
}
|
|
348
597
|
onResolveDone();
|
|
@@ -352,9 +601,50 @@ public class NsdServiceImpl implements Zeroconf {
|
|
|
352
601
|
private class ServiceRegistrationListener implements NsdManager.RegistrationListener {
|
|
353
602
|
@Nullable private Promise registerPromise;
|
|
354
603
|
@Nullable private Promise unregisterPromise;
|
|
355
|
-
|
|
356
|
-
|
|
604
|
+
// What it was registered with, to register it again on updates
|
|
605
|
+
final String serviceType;
|
|
606
|
+
final int port;
|
|
607
|
+
@Nullable final Network network;
|
|
608
|
+
// Updates replace the registration without emitting unpublished and published
|
|
609
|
+
boolean announceRegistered = true;
|
|
610
|
+
boolean announceUnregistered = true;
|
|
611
|
+
@Nullable Runnable afterUnregistered;
|
|
612
|
+
// The registration callback doesn't carry the TXT record, report the one registered
|
|
613
|
+
private final Map<String, String> txt = new java.util.LinkedHashMap<>();
|
|
614
|
+
|
|
615
|
+
ServiceRegistrationListener(@Nullable Promise registerPromise, String serviceType, int port, @Nullable Network network) {
|
|
357
616
|
this.registerPromise = registerPromise;
|
|
617
|
+
this.serviceType = serviceType;
|
|
618
|
+
this.port = port;
|
|
619
|
+
this.network = network;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
NsdServiceInfo buildServiceInfo(String name, ReadableArray txt) {
|
|
623
|
+
NsdServiceInfo serviceInfo = new NsdServiceInfo();
|
|
624
|
+
serviceInfo.setServiceName(name);
|
|
625
|
+
serviceInfo.setServiceType(serviceType);
|
|
626
|
+
serviceInfo.setPort(port);
|
|
627
|
+
if (network != null && Build.VERSION.SDK_INT >= 33) {
|
|
628
|
+
serviceInfo.setNetwork(network);
|
|
629
|
+
}
|
|
630
|
+
this.txt.clear();
|
|
631
|
+
for (int i = 0; i < txt.size(); i++) {
|
|
632
|
+
ReadableArray pair = txt.getArray(i);
|
|
633
|
+
serviceInfo.setAttribute(pair.getString(0), pair.getString(1));
|
|
634
|
+
this.txt.put(pair.getString(0), pair.getString(1));
|
|
635
|
+
}
|
|
636
|
+
return serviceInfo;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
WritableMap registeredToMap(NsdServiceInfo serviceInfo) {
|
|
640
|
+
WritableMap service = serviceInfoToMap(serviceInfo);
|
|
641
|
+
WritableMap txtMap = new WritableNativeMap();
|
|
642
|
+
for (Map.Entry<String, String> entry : txt.entrySet()) {
|
|
643
|
+
txtMap.putString(entry.getKey(), entry.getValue());
|
|
644
|
+
}
|
|
645
|
+
service.putMap(ZeroconfModule.KEY_SERVICE_TXT, txtMap);
|
|
646
|
+
service.putInt(ZeroconfModule.KEY_SERVICE_PORT, port);
|
|
647
|
+
return service;
|
|
358
648
|
}
|
|
359
649
|
|
|
360
650
|
@Override
|
|
@@ -365,11 +655,13 @@ public class NsdServiceImpl implements Zeroconf {
|
|
|
365
655
|
|
|
366
656
|
final String serviceName = NsdServiceInfo.getServiceName();
|
|
367
657
|
mPublishedServices.put(serviceName, this);
|
|
658
|
+
syncLocalServiceTypes();
|
|
368
659
|
|
|
369
|
-
|
|
370
|
-
|
|
660
|
+
if (announceRegistered) {
|
|
661
|
+
zeroconfModule.sendEvent(getReactApplicationContext(), ZeroconfModule.EVENT_PUBLISHED, registeredToMap(NsdServiceInfo));
|
|
662
|
+
}
|
|
371
663
|
if (registerPromise != null) {
|
|
372
|
-
registerPromise.resolve(
|
|
664
|
+
registerPromise.resolve(registeredToMap(NsdServiceInfo));
|
|
373
665
|
registerPromise = null;
|
|
374
666
|
}
|
|
375
667
|
}
|
|
@@ -386,10 +678,15 @@ public class NsdServiceImpl implements Zeroconf {
|
|
|
386
678
|
public void onServiceUnregistered(NsdServiceInfo nsdServiceInfo) {
|
|
387
679
|
// Service has been unregistered. This only happens when you call
|
|
388
680
|
// NsdManager.unregisterService() and pass in this listener.
|
|
389
|
-
|
|
390
|
-
|
|
681
|
+
if (announceUnregistered) {
|
|
682
|
+
zeroconfModule.sendEvent(getReactApplicationContext(), ZeroconfModule.EVENT_UNREGISTERED, registeredToMap(nsdServiceInfo));
|
|
683
|
+
}
|
|
684
|
+
if (afterUnregistered != null) {
|
|
685
|
+
afterUnregistered.run();
|
|
686
|
+
afterUnregistered = null;
|
|
687
|
+
}
|
|
391
688
|
if (unregisterPromise != null) {
|
|
392
|
-
unregisterPromise.resolve(
|
|
689
|
+
unregisterPromise.resolve(registeredToMap(nsdServiceInfo));
|
|
393
690
|
unregisterPromise = null;
|
|
394
691
|
}
|
|
395
692
|
}
|