react-native-zeroconf 0.17.3 → 1.0.0-rc.1

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.
Files changed (114) hide show
  1. package/NOTICE +2 -6
  2. package/README.md +1 -1
  3. package/android/CMakeLists.txt +64 -0
  4. package/android/build.gradle +1 -17
  5. package/android/generated/java/com/balthazargronon/RCTZeroconf/NativeZeroconfSpec.java +96 -0
  6. package/android/generated/jni/CMakeLists.txt +28 -0
  7. package/android/generated/jni/RNZeroconfSpec-generated.cpp +77 -0
  8. package/android/generated/jni/RNZeroconfSpec.h +31 -0
  9. package/android/generated/jni/react/renderer/components/RNZeroconfSpec/RNZeroconfSpecJSI.h +798 -0
  10. package/android/src/main/java/com/balthazargronon/RCTZeroconf/{ZeroconfModule.java → NsdHost.java} +97 -140
  11. package/android/src/main/java/com/balthazargronon/RCTZeroconf/NsdPayload.java +87 -0
  12. package/android/src/main/java/com/balthazargronon/RCTZeroconf/NsdPromise.java +40 -0
  13. package/android/src/main/java/com/balthazargronon/RCTZeroconf/Zeroconf.java +4 -6
  14. package/android/src/main/java/com/balthazargronon/RCTZeroconf/ZeroconfNativeSupport.java +60 -0
  15. package/android/src/main/java/com/balthazargronon/RCTZeroconf/ZeroconfOptions.java +15 -26
  16. package/android/src/main/java/com/balthazargronon/RCTZeroconf/ZeroconfReactPackage.java +5 -15
  17. package/android/src/main/java/com/balthazargronon/RCTZeroconf/nsd/NsdServiceImpl.java +90 -96
  18. package/android/src/main/jni/mdnsresponder/mDNSShared/dnssd_clientshim.c +31 -6
  19. package/cpp/Backend.cpp +16 -0
  20. package/cpp/Backend.h +116 -0
  21. package/cpp/ZeroconfModule.cpp +280 -0
  22. package/cpp/ZeroconfModule.h +144 -0
  23. package/cpp/android/NsdBackend.cpp +462 -0
  24. package/cpp/android/NsdBackend.h +61 -0
  25. package/cpp/android/ZeroconfAndroidModule.cpp +49 -0
  26. package/cpp/android/ZeroconfAndroidModule.h +15 -0
  27. package/cpp/apple/DispatchExecutor.cpp +66 -0
  28. package/cpp/apple/DispatchExecutor.h +37 -0
  29. package/cpp/apple/LocalNetworkAccess.h +19 -0
  30. package/cpp/apple/LocalNetworkAccess.mm +87 -0
  31. package/cpp/dnssd/DnssdBackend.cpp +976 -0
  32. package/cpp/dnssd/DnssdBackend.h +114 -0
  33. package/cpp/dnssd/Executor.h +28 -0
  34. package/cpp/embedded/EmbeddedExecutor.cpp +124 -0
  35. package/cpp/embedded/EmbeddedExecutor.h +57 -0
  36. package/ios/ZeroconfOnLoad.mm +34 -0
  37. package/ios/generated/ReactCodegen/RNZeroconfSpec/RNZeroconfSpec-generated.mm +140 -0
  38. package/ios/generated/ReactCodegen/RNZeroconfSpec/RNZeroconfSpec.h +189 -0
  39. package/ios/generated/ReactCodegen/RNZeroconfSpecJSI.h +798 -0
  40. package/lib/module/NativeZeroconf.js +15 -0
  41. package/lib/module/NativeZeroconf.js.map +1 -0
  42. package/lib/module/bridge.js +124 -0
  43. package/lib/module/bridge.js.map +1 -0
  44. package/{dist → lib/module}/index.js +149 -149
  45. package/lib/module/index.js.map +1 -0
  46. package/lib/module/package.json +1 -0
  47. package/lib/module/types.js +24 -0
  48. package/lib/module/types.js.map +1 -0
  49. package/lib/typescript/package.json +1 -0
  50. package/lib/typescript/src/NativeZeroconf.d.ts +75 -0
  51. package/lib/typescript/src/NativeZeroconf.d.ts.map +1 -0
  52. package/lib/typescript/src/bridge.d.ts +22 -0
  53. package/lib/typescript/src/bridge.d.ts.map +1 -0
  54. package/lib/typescript/src/index.d.ts +111 -0
  55. package/lib/typescript/src/index.d.ts.map +1 -0
  56. package/lib/typescript/src/types.d.ts +175 -0
  57. package/lib/typescript/src/types.d.ts.map +1 -0
  58. package/package.json +64 -29
  59. package/react-native-zeroconf.podspec +2 -1
  60. package/react-native.config.js +17 -0
  61. package/src/NativeZeroconf.ts +120 -0
  62. package/src/bridge.ts +153 -0
  63. package/src/index.ts +607 -0
  64. package/{index.d.ts → src/types.ts} +5 -99
  65. package/windows/RNZeroconf/RNZeroconf.vcxproj +17 -2
  66. package/windows/RNZeroconf/RNZeroconf.vcxproj.filters +0 -6
  67. package/windows/RNZeroconf/ReactPackageProvider.cpp +23 -20
  68. package/windows/RNZeroconf/WindowsBackend.cpp +156 -0
  69. package/windows/RNZeroconf/WindowsBackend.h +13 -0
  70. package/windows/RNZeroconf/ZeroconfCore.cpp +2 -2
  71. package/windows/RNZeroconf/ZeroconfCore.h +2 -2
  72. package/windows/RNZeroconf/ZeroconfWindowsModule.cpp +22 -0
  73. package/windows/RNZeroconf/ZeroconfWindowsModule.h +14 -0
  74. package/android/src/main/java/com/balthazargronon/RCTZeroconf/ZeroConfImplFactory.java +0 -56
  75. package/android/src/main/java/com/balthazargronon/RCTZeroconf/dnssd/DnssdImpl.java +0 -654
  76. package/android/src/main/java/com/github/druk/dnssd/BaseListener.java +0 -33
  77. package/android/src/main/java/com/github/druk/dnssd/BrowseListener.java +0 -67
  78. package/android/src/main/java/com/github/druk/dnssd/DNSRecord.java +0 -47
  79. package/android/src/main/java/com/github/druk/dnssd/DNSSD.java +0 -681
  80. package/android/src/main/java/com/github/druk/dnssd/DNSSDEmbedded.java +0 -198
  81. package/android/src/main/java/com/github/druk/dnssd/DNSSDException.java +0 -66
  82. package/android/src/main/java/com/github/druk/dnssd/DNSSDRecordRegistrar.java +0 -55
  83. package/android/src/main/java/com/github/druk/dnssd/DNSSDRegistration.java +0 -54
  84. package/android/src/main/java/com/github/druk/dnssd/DNSSDService.java +0 -35
  85. package/android/src/main/java/com/github/druk/dnssd/DomainListener.java +0 -57
  86. package/android/src/main/java/com/github/druk/dnssd/InternalBrowseListener.java +0 -76
  87. package/android/src/main/java/com/github/druk/dnssd/InternalDNSSD.java +0 -870
  88. package/android/src/main/java/com/github/druk/dnssd/InternalDNSSDRecordRegistrar.java +0 -30
  89. package/android/src/main/java/com/github/druk/dnssd/InternalDNSSDRegistration.java +0 -34
  90. package/android/src/main/java/com/github/druk/dnssd/InternalDNSSDService.java +0 -29
  91. package/android/src/main/java/com/github/druk/dnssd/InternalDomainListener.java +0 -62
  92. package/android/src/main/java/com/github/druk/dnssd/InternalQueryListener.java +0 -61
  93. package/android/src/main/java/com/github/druk/dnssd/InternalRegisterListener.java +0 -51
  94. package/android/src/main/java/com/github/druk/dnssd/InternalResolveListener.java +0 -58
  95. package/android/src/main/java/com/github/druk/dnssd/NSClass.java +0 -16
  96. package/android/src/main/java/com/github/druk/dnssd/NSType.java +0 -58
  97. package/android/src/main/java/com/github/druk/dnssd/QueryListener.java +0 -54
  98. package/android/src/main/java/com/github/druk/dnssd/RegisterListener.java +0 -46
  99. package/android/src/main/java/com/github/druk/dnssd/RegisterRecordListener.java +0 -35
  100. package/android/src/main/java/com/github/druk/dnssd/ResolveListener.java +0 -55
  101. package/android/src/main/java/com/github/druk/dnssd/TXTRecord.java +0 -293
  102. package/android/src/main/jni/Android.mk +0 -73
  103. package/android/src/main/jni/Application.mk +0 -3
  104. package/android/src/main/jni/DNSSD.java.h +0 -290
  105. package/android/src/main/jni/JNISupport.c +0 -1221
  106. package/ios/RNZeroconf/RNNetServiceSerializer.h +0 -31
  107. package/ios/RNZeroconf/RNNetServiceSerializer.m +0 -84
  108. package/ios/RNZeroconf/RNZeroconf.h +0 -17
  109. package/ios/RNZeroconf/RNZeroconf.m +0 -1025
  110. package/ios/RNZeroconf.xcodeproj/project.pbxproj +0 -261
  111. package/ios/RNZeroconf.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
  112. package/src/index.js +0 -563
  113. package/windows/RNZeroconf/RNZeroconf.cpp +0 -228
  114. package/windows/RNZeroconf/RNZeroconf.h +0 -70
@@ -1,6 +1,5 @@
1
1
  package com.balthazargronon.RCTZeroconf;
2
2
 
3
-
4
3
  import android.content.Context;
5
4
  import android.content.pm.PackageManager;
6
5
  import android.net.ConnectivityManager;
@@ -9,25 +8,21 @@ import android.net.Network;
9
8
  import android.os.Build;
10
9
  import android.util.Log;
11
10
 
12
- import com.facebook.react.bridge.Promise;
13
- import com.facebook.react.bridge.ReactApplicationContext;
14
- import com.facebook.react.bridge.ReactContext;
15
- import com.facebook.react.bridge.ReactContextBaseJavaModule;
16
- import com.facebook.react.bridge.ReactMethod;
17
- import com.facebook.react.bridge.ReadableArray;
18
- import com.facebook.react.bridge.ReadableMap;
19
- import com.facebook.react.bridge.WritableMap;
20
- import com.facebook.react.bridge.WritableNativeMap;
21
- import com.facebook.react.modules.core.DeviceEventManagerModule;
11
+ import com.balthazargronon.RCTZeroconf.nsd.NsdServiceImpl;
12
+ import com.facebook.proguard.annotations.DoNotStrip;
22
13
 
23
- import javax.annotation.Nullable;
14
+ import java.util.LinkedHashMap;
15
+ import java.util.Map;
24
16
 
17
+ import javax.annotation.Nullable;
25
18
 
26
19
  /**
27
- * Created by Jeremy White on 8/1/2016.
28
- * Copyright © 2016 Balthazar Gronon MIT
20
+ * Android NSD for the C++ module (cpp/android/NsdBackend): it calls these methods through JNI, events,
21
+ * errors and results come back through native methods. Also the constants and helpers of the NSD code.
29
22
  */
30
- public class ZeroconfModule extends ReactContextBaseJavaModule {
23
+ @DoNotStrip
24
+ public final class NsdHost {
25
+ private static final String TAG = "NsdHost";
31
26
 
32
27
  public static final String EVENT_START = "RNZeroconfStart";
33
28
  public static final String EVENT_STOP = "RNZeroconfStop";
@@ -50,7 +45,6 @@ public class ZeroconfModule extends ReactContextBaseJavaModule {
50
45
 
51
46
  // Where an error code comes from
52
47
  public static final String ERROR_DOMAIN_NSD = "NsdManager";
53
- public static final String ERROR_DOMAIN_DNSSD = "DNSSD";
54
48
  public static final String ERROR_DOMAIN_LIBRARY = "RNZeroconf";
55
49
  public static final String ERROR_CODE_EXCEPTION = "EXCEPTION";
56
50
  public static final String ERROR_CODE_TIMEOUT = "TIMEOUT";
@@ -60,121 +54,137 @@ public class ZeroconfModule extends ReactContextBaseJavaModule {
60
54
  public static final String PERMISSION_ACCESS_LOCAL_NETWORK = "android.permission.ACCESS_LOCAL_NETWORK";
61
55
  public static final String ERROR_CODE_NOT_PUBLISHED = "NOT_PUBLISHED";
62
56
 
63
- private ZeroConfImplFactory zeroConfFactory;
57
+ // The C++ backend's id, 0 once it is gone
58
+ private volatile long handle;
59
+ private final Context context;
60
+ private final NsdServiceImpl nsd;
64
61
 
65
- public ZeroconfModule(ReactApplicationContext reactContext) {
66
- super(reactContext);
67
- zeroConfFactory = new ZeroConfImplFactory(this, getReactApplicationContext());
62
+ @DoNotStrip
63
+ public NsdHost(long handle) {
64
+ this.handle = handle;
65
+ this.context = ZeroconfNativeSupport.application();
66
+ this.nsd = new NsdServiceImpl(this, context);
68
67
  }
69
68
 
70
- @Override
71
- public String getName() {
72
- return "RNZeroconf";
69
+ public Context getContext() {
70
+ return context;
73
71
  }
74
72
 
75
- @ReactMethod
76
- public void scan(String scanId, String type, String protocol, String domain, String implType, @Nullable ReadableMap options) {
73
+ @DoNotStrip
74
+ public void scan(String scanId, String type, String protocol, String domain, String subtype, String networkInterface, double resolveTimeout) {
77
75
  try {
78
- getZeroconfImpl(implType).scan(scanId, type, protocol, domain, ZeroconfOptions.from(options));
76
+ nsd.scan(scanId, type, protocol, domain, new ZeroconfOptions(subtype, null, networkInterface, 5, resolveTimeout));
79
77
  } catch (Throwable e) {
80
- Log.e(getClass().getName(), e.getMessage(), e);
78
+ Log.e(TAG, e.getMessage(), e);
81
79
  sendError(ERROR_DOMAIN_LIBRARY, ERROR_CODE_EXCEPTION, "Exception during scan: " + e.getMessage(), null, scanId);
82
80
  }
83
81
  }
84
82
 
85
- /**
86
- * Stops the scan with this id, or every scan of the implementation without one
87
- */
88
- @ReactMethod
89
- public void stop(@Nullable String scanId, String implType) {
83
+ // Stops the scan with this id, or every scan without one
84
+ @DoNotStrip
85
+ public void stop(String scanId) {
90
86
  try {
91
- Zeroconf impl = getZeroconfImpl(implType);
92
87
  if (scanId == null || scanId.isEmpty()) {
93
- impl.stopAll();
88
+ nsd.stopAll();
94
89
  } else {
95
- impl.stop(scanId);
90
+ nsd.stop(scanId);
96
91
  }
97
92
  } catch (Throwable e) {
98
- Log.e(getClass().getName(), e.getMessage(), e);
93
+ Log.e(TAG, e.getMessage(), e);
99
94
  sendError(ERROR_DOMAIN_LIBRARY, ERROR_CODE_EXCEPTION, "Exception during stop: " + e.getMessage(), null, scanId);
100
95
  }
101
96
  }
102
97
 
103
- private Zeroconf getZeroconfImpl(String implType) {
104
- return zeroConfFactory.getZeroconf(implType);
105
- }
106
-
107
- @ReactMethod
108
- public void registerService(String type, String protocol, String domain, String name, int port, ReadableArray txt, String implType, @Nullable ReadableMap options, Promise promise) {
98
+ @DoNotStrip
99
+ public void registerService(String type, String protocol, String domain, String name, int port, String[] txtKeys, String[] txtValues,
100
+ String[] subtypes, String networkInterface, NsdPromise promise) {
109
101
  try {
110
- getZeroconfImpl(implType).registerService(type, protocol, domain, name, port, txt, ZeroconfOptions.from(options), promise);
102
+ nsd.registerService(type, protocol, domain, name, port, txt(txtKeys, txtValues),
103
+ new ZeroconfOptions(null, subtypes, networkInterface, 5, 5), promise);
111
104
  } catch (Throwable e) {
112
- Log.e(getClass().getName(), e.getMessage(), e);
105
+ Log.e(TAG, e.getMessage(), e);
113
106
  String message = "Exception while registering service: " + e.getMessage();
114
107
  sendError(ERROR_DOMAIN_LIBRARY, ERROR_CODE_EXCEPTION, message, name);
115
108
  reject(promise, ERROR_DOMAIN_LIBRARY, ERROR_CODE_EXCEPTION, message, name);
116
109
  }
117
110
  }
118
111
 
119
- @ReactMethod
120
- public void updateService(String serviceName, ReadableArray txt, String implType, Promise promise) {
112
+ @DoNotStrip
113
+ public void updateService(String name, String[] txtKeys, String[] txtValues, NsdPromise promise) {
114
+ try {
115
+ nsd.updateService(name, txt(txtKeys, txtValues), promise);
116
+ } catch (Throwable e) {
117
+ Log.e(TAG, e.getMessage(), e);
118
+ reject(promise, ERROR_DOMAIN_LIBRARY, ERROR_CODE_EXCEPTION, "Exception while updating service: " + e.getMessage(), name);
119
+ }
120
+ }
121
+
122
+ @DoNotStrip
123
+ public void unregisterService(String name, NsdPromise promise) {
121
124
  try {
122
- getZeroconfImpl(implType).updateService(serviceName, txt, promise);
125
+ nsd.unregisterService(name, promise);
123
126
  } catch (Throwable e) {
124
- Log.e(getClass().getName(), e.getMessage(), e);
125
- reject(promise, ERROR_DOMAIN_LIBRARY, ERROR_CODE_EXCEPTION, "Exception while updating service: " + e.getMessage(), serviceName);
127
+ Log.e(TAG, e.getMessage(), e);
128
+ String message = "Exception while unregistering service: " + e.getMessage();
129
+ sendError(ERROR_DOMAIN_LIBRARY, ERROR_CODE_EXCEPTION, message, name);
130
+ reject(promise, ERROR_DOMAIN_LIBRARY, ERROR_CODE_EXCEPTION, message, name);
126
131
  }
127
132
  }
128
133
 
129
- @ReactMethod
130
- public void resolveService(String name, String type, String protocol, String domain, String implType, @Nullable ReadableMap options, Promise promise) {
134
+ @DoNotStrip
135
+ public void resolveService(String name, String type, String protocol, String domain, String networkInterface, double timeout, NsdPromise promise) {
131
136
  try {
132
- getZeroconfImpl(implType).resolveService(name, type, protocol, domain, ZeroconfOptions.from(options), promise);
137
+ nsd.resolveService(name, type, protocol, domain, new ZeroconfOptions(null, null, networkInterface, timeout, 5), promise);
133
138
  } catch (Throwable e) {
134
- Log.e(getClass().getName(), e.getMessage(), e);
139
+ Log.e(TAG, e.getMessage(), e);
135
140
  reject(promise, ERROR_DOMAIN_LIBRARY, ERROR_CODE_EXCEPTION, "Exception while resolving service: " + e.getMessage(), name);
136
141
  }
137
142
  }
138
143
 
144
+ // Stops the scans and unpublishes the services, when the module goes away
145
+ @DoNotStrip
146
+ public void shutdown() {
147
+ try {
148
+ nsd.stopAll();
149
+ nsd.unregisterAllServices();
150
+ } catch (Throwable e) {
151
+ Log.e(TAG, e.getMessage(), e);
152
+ }
153
+ handle = 0;
154
+ }
155
+
139
156
  /**
140
- * Resolves "granted" or "denied". On Android 17 (API 37) devices, the ACCESS_LOCAL_NETWORK runtime permission
157
+ * "granted" or "denied". On Android 17 (API 37) devices, the ACCESS_LOCAL_NETWORK runtime permission
141
158
  * is enforced for apps targeting API 37 and for apps declaring it in their manifest. Otherwise there is nothing to grant.
142
159
  */
143
- @ReactMethod
144
- public void checkLocalNetworkAccess(Promise promise) {
145
- Context context = getReactApplicationContext();
160
+ @DoNotStrip
161
+ public static String checkLocalNetworkAccess() {
162
+ Context context = ZeroconfNativeSupport.application();
163
+ if (context == null) {
164
+ return "unknown";
165
+ }
146
166
  boolean required = Build.VERSION.SDK_INT >= 37
147
167
  && (context.getApplicationInfo().targetSdkVersion >= 37 || declaresPermission(context, PERMISSION_ACCESS_LOCAL_NETWORK));
148
168
  if (!required) {
149
- promise.resolve("granted");
150
- return;
169
+ return "granted";
151
170
  }
152
171
  boolean granted = context.checkSelfPermission(PERMISSION_ACCESS_LOCAL_NETWORK) == PackageManager.PERMISSION_GRANTED;
153
- promise.resolve(granted ? "granted" : "denied");
172
+ return granted ? "granted" : "denied";
154
173
  }
155
174
 
156
- @ReactMethod
157
- public void unregisterService(String serviceName, String implType, Promise promise) {
158
- try {
159
- getZeroconfImpl(implType).unregisterService(serviceName, promise);
160
- } catch (Throwable e) {
161
- Log.e(getClass().getName(), e.getMessage(), e);
162
- String message = "Exception while unregistering service: " + e.getMessage();
163
- sendError(ERROR_DOMAIN_LIBRARY, ERROR_CODE_EXCEPTION, message, serviceName);
164
- reject(promise, ERROR_DOMAIN_LIBRARY, ERROR_CODE_EXCEPTION, message, serviceName);
175
+ private static Map<String, String> txt(String[] keys, String[] values) {
176
+ Map<String, String> txt = new LinkedHashMap<>();
177
+ for (int i = 0; i < keys.length && i < values.length; i++) {
178
+ txt.put(keys[i], values[i]);
165
179
  }
180
+ return txt;
166
181
  }
167
182
 
168
- public void sendEvent(ReactContext reactContext,
169
- String eventName,
170
- @Nullable Object params) {
171
- try {
172
- reactContext
173
- .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
174
- .emit(eventName, params);
175
- } catch (Throwable e) {
176
- // The JS side may already be gone, e.g. callbacks arriving during teardown
177
- Log.w(getClass().getName(), "Could not send " + eventName + ": " + e.getMessage());
183
+ // Events of the NSD code, with the scan id in the payload for scan events
184
+ public void sendEvent(String eventName, NsdPayload body) {
185
+ long current = handle;
186
+ if (current != 0) {
187
+ nativeEvent(current, eventName, body);
178
188
  }
179
189
  }
180
190
 
@@ -191,35 +201,17 @@ public class ZeroconfModule extends ReactContextBaseJavaModule {
191
201
  * @param scanId the scan the error belongs to, null for errors not related to a scan
192
202
  */
193
203
  public void sendError(String domain, Object code, String message, @Nullable String serviceName, @Nullable String scanId) {
194
- WritableMap error = buildError(domain, code, message, serviceName);
195
- if (scanId != null) {
196
- error.putString(KEY_SCAN_ID, scanId);
204
+ long current = handle;
205
+ if (current != 0) {
206
+ nativeError(current, scanId == null ? "" : scanId, domain, String.valueOf(code), message, serviceName == null ? "" : serviceName);
197
207
  }
198
- sendEvent(getReactApplicationContext(), EVENT_ERROR, error);
199
208
  }
200
209
 
201
- /**
202
- * Rejects a promise with the same { message, code, domain, serviceName } shape as error events, in userInfo
203
- */
204
- public static void reject(@Nullable Promise promise, String domain, Object code, String message, @Nullable String serviceName) {
210
+ // Rejects a call with the same { message, code, domain, serviceName } as error events
211
+ public static void reject(@Nullable NsdPromise promise, String domain, Object code, String message, @Nullable String serviceName) {
205
212
  if (promise != null) {
206
- promise.reject(String.valueOf(code), message, buildError(domain, code, message, serviceName));
207
- }
208
- }
209
-
210
- private static WritableMap buildError(String domain, Object code, String message, @Nullable String serviceName) {
211
- WritableMap error = new WritableNativeMap();
212
- error.putString("message", message);
213
- error.putString("domain", domain);
214
- if (code instanceof Integer) {
215
- error.putInt("code", (Integer) code);
216
- } else {
217
- error.putString("code", String.valueOf(code));
218
- }
219
- if (serviceName != null) {
220
- error.putString("serviceName", serviceName);
213
+ promise.reject(domain, String.valueOf(code), message, serviceName);
221
214
  }
222
- return error;
223
215
  }
224
216
 
225
217
  public static final String SERVICE_TYPES_TYPE = "services._dns-sd";
@@ -296,42 +288,7 @@ public class ZeroconfModule extends ReactContextBaseJavaModule {
296
288
  }
297
289
  }
298
290
 
299
- /**
300
- * Readable description of a DNSServiceErrorType code, as on iOS
301
- */
302
- public static String describeDnssdError(int errorCode) {
303
- switch (errorCode) {
304
- case -65548: return "name already in use"; // kDNSServiceErr_NameConflict
305
- case -65540: return "bad parameter"; // kDNSServiceErr_BadParam
306
- case -65538: return "no such name"; // kDNSServiceErr_NoSuchName
307
- case -65554: return "no such record"; // kDNSServiceErr_NoSuchRecord
308
- case -65563: return "mDNSResponder is not running"; // kDNSServiceErr_ServiceNotRunning
309
- case -65539: return "out of memory"; // kDNSServiceErr_NoMemory
310
- case -65544: return "unsupported"; // kDNSServiceErr_Unsupported
311
- default: return "error " + errorCode;
312
- }
313
- }
314
-
315
- // Called on teardown by current React Native versions.
316
- // No @Override so it still compiles against versions that don't declare it.
317
- public void invalidate() {
318
- teardown();
319
- }
320
-
321
- // Called on teardown by older React Native versions, deprecated in favor of invalidate().
322
- // No @Override so it still compiles once it is removed.
323
- public void onCatalystInstanceDestroy() {
324
- teardown();
325
- }
291
+ private static native void nativeEvent(long handle, String eventName, NsdPayload body);
326
292
 
327
- private void teardown() {
328
- try {
329
- for (Zeroconf impl : zeroConfFactory.getCreatedImpls()) {
330
- impl.stopAll();
331
- impl.unregisterAllServices();
332
- }
333
- } catch (Throwable e) {
334
- Log.e(getClass().getName(), e.getMessage(), e);
335
- }
336
- }
293
+ private static native void nativeError(long handle, String scanId, String domain, String code, String message, String serviceName);
337
294
  }
@@ -0,0 +1,87 @@
1
+ package com.balthazargronon.RCTZeroconf;
2
+
3
+ import com.facebook.proguard.annotations.DoNotStrip;
4
+
5
+ import java.util.ArrayList;
6
+ import java.util.LinkedHashMap;
7
+ import java.util.List;
8
+ import java.util.Map;
9
+
10
+ import javax.annotation.Nullable;
11
+
12
+ /**
13
+ * An event or result of the NSD implementation, read by the C++ module (cpp/android/NsdBackend) through JNI:
14
+ * a service's name, full name, host, port, addresses and TXT record.
15
+ */
16
+ @DoNotStrip
17
+ public final class NsdPayload {
18
+ private final Map<String, Object> values = new LinkedHashMap<>();
19
+
20
+ public void putString(String key, @Nullable String value) {
21
+ values.put(key, value);
22
+ }
23
+
24
+ public void putInt(String key, int value) {
25
+ values.put(key, value);
26
+ }
27
+
28
+ public void putArray(String key, List<String> value) {
29
+ values.put(key, new ArrayList<>(value));
30
+ }
31
+
32
+ public void putMap(String key, NsdPayload value) {
33
+ values.put(key, value);
34
+ }
35
+
36
+ @DoNotStrip
37
+ @Nullable
38
+ public String getString(String key) {
39
+ Object value = values.get(key);
40
+ return value instanceof String ? (String) value : null;
41
+ }
42
+
43
+ @DoNotStrip
44
+ public int getInt(String key) {
45
+ Object value = values.get(key);
46
+ return value instanceof Integer ? (Integer) value : 0;
47
+ }
48
+
49
+ @DoNotStrip
50
+ public String[] getStrings(String key) {
51
+ Object value = values.get(key);
52
+ if (!(value instanceof List)) {
53
+ return new String[0];
54
+ }
55
+ List<?> list = (List<?>) value;
56
+ String[] strings = new String[list.size()];
57
+ for (int i = 0; i < strings.length; i++) {
58
+ strings[i] = String.valueOf(list.get(i));
59
+ }
60
+ return strings;
61
+ }
62
+
63
+ // The keys, then the values, of a nested map of strings (the TXT record), in order
64
+ @DoNotStrip
65
+ public String[] getMapKeys(String key) {
66
+ Object value = values.get(key);
67
+ if (!(value instanceof NsdPayload)) {
68
+ return new String[0];
69
+ }
70
+ return ((NsdPayload) value).values.keySet().toArray(new String[0]);
71
+ }
72
+
73
+ @DoNotStrip
74
+ public String[] getMapValues(String key) {
75
+ Object value = values.get(key);
76
+ if (!(value instanceof NsdPayload)) {
77
+ return new String[0];
78
+ }
79
+ Map<String, Object> map = ((NsdPayload) value).values;
80
+ String[] strings = new String[map.size()];
81
+ int i = 0;
82
+ for (Object entry : map.values()) {
83
+ strings[i++] = entry == null ? "" : String.valueOf(entry);
84
+ }
85
+ return strings;
86
+ }
87
+ }
@@ -0,0 +1,40 @@
1
+ package com.balthazargronon.RCTZeroconf;
2
+
3
+ import com.facebook.proguard.annotations.DoNotStrip;
4
+
5
+ import javax.annotation.Nullable;
6
+
7
+ /**
8
+ * Settles a call of the C++ module (cpp/android/NsdBackend) once: with a service, or with
9
+ * { domain, code, message, serviceName }.
10
+ */
11
+ @DoNotStrip
12
+ public final class NsdPromise {
13
+ private final long id;
14
+ private boolean settled = false;
15
+
16
+ @DoNotStrip
17
+ public NsdPromise(long id) {
18
+ this.id = id;
19
+ }
20
+
21
+ public synchronized void resolve(@Nullable NsdPayload service) {
22
+ if (settled) {
23
+ return;
24
+ }
25
+ settled = true;
26
+ nativeResolve(id, service);
27
+ }
28
+
29
+ public synchronized void reject(String domain, String code, String message, @Nullable String serviceName) {
30
+ if (settled) {
31
+ return;
32
+ }
33
+ settled = true;
34
+ nativeReject(id, domain, code, message, serviceName == null ? "" : serviceName);
35
+ }
36
+
37
+ private static native void nativeResolve(long id, @Nullable NsdPayload service);
38
+
39
+ private static native void nativeReject(long id, String domain, String code, String message, String serviceName);
40
+ }
@@ -1,7 +1,5 @@
1
1
  package com.balthazargronon.RCTZeroconf;
2
2
 
3
- import com.facebook.react.bridge.Promise;
4
- import com.facebook.react.bridge.ReadableArray;
5
3
 
6
4
  import javax.annotation.Nullable;
7
5
 
@@ -19,23 +17,23 @@ public interface Zeroconf {
19
17
  /**
20
18
  * @param promise resolved with the service once unregistered, null when nobody waits for it
21
19
  */
22
- public void unregisterService(String serviceName, @Nullable Promise promise);
20
+ public void unregisterService(String serviceName, @Nullable NsdPromise promise);
23
21
 
24
22
  /**
25
23
  * @param txt TXT records as ordered [key, value] pairs
26
24
  * @param promise resolved with the service once registered
27
25
  */
28
- public void registerService(String type, String protocol, String domain, String name, int port, ReadableArray txt, ZeroconfOptions options, Promise promise);
26
+ public void registerService(String type, String protocol, String domain, String name, int port, java.util.Map<String, String> txt, ZeroconfOptions options, NsdPromise promise);
29
27
 
30
28
  /**
31
29
  * Replaces the TXT record of a published service
32
30
  */
33
- void updateService(String serviceName, ReadableArray txt, Promise promise);
31
+ void updateService(String serviceName, java.util.Map<String, String> txt, NsdPromise promise);
34
32
 
35
33
  /**
36
34
  * Resolves one service by name, without scanning
37
35
  */
38
- void resolveService(String name, String type, String protocol, String domain, ZeroconfOptions options, Promise promise);
36
+ void resolveService(String name, String type, String protocol, String domain, ZeroconfOptions options, NsdPromise promise);
39
37
 
40
38
  /**
41
39
  * Unregister every service published through this implementation
@@ -0,0 +1,60 @@
1
+ package com.balthazargronon.RCTZeroconf;
2
+
3
+ import android.annotation.SuppressLint;
4
+ import android.app.Application;
5
+ import android.content.Context;
6
+ import android.net.wifi.WifiManager;
7
+ import android.util.Log;
8
+
9
+ import com.facebook.proguard.annotations.DoNotStrip;
10
+
11
+ /**
12
+ * Android services for the C++ module (cpp/android): it calls these through JNI.
13
+ */
14
+ @DoNotStrip
15
+ public final class ZeroconfNativeSupport {
16
+ private static final String TAG = "ZeroconfNativeSupport";
17
+
18
+ private static WifiManager.MulticastLock multicastLock;
19
+
20
+ private ZeroconfNativeSupport() {}
21
+
22
+ /**
23
+ * Android drops multicast packets unless an app holds a multicast lock: held while scans, resolves
24
+ * or published services are active, released when there are none.
25
+ */
26
+ @DoNotStrip
27
+ public static synchronized void setMulticastLock(boolean held) {
28
+ if (held) {
29
+ if (multicastLock == null) {
30
+ Context context = application();
31
+ if (context == null) {
32
+ Log.w(TAG, "No application context, multicast stays filtered");
33
+ return;
34
+ }
35
+ @SuppressLint("WifiManagerLeak")
36
+ WifiManager wifi = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
37
+ if (wifi == null) {
38
+ return;
39
+ }
40
+ multicastLock = wifi.createMulticastLock("react-native-zeroconf");
41
+ multicastLock.setReferenceCounted(false);
42
+ }
43
+ multicastLock.acquire();
44
+ } else if (multicastLock != null && multicastLock.isHeld()) {
45
+ multicastLock.release();
46
+ }
47
+ }
48
+
49
+ // The application, without a ReactContext: the C++ module has none
50
+ static Context application() {
51
+ try {
52
+ @SuppressLint("PrivateApi")
53
+ Class<?> activityThread = Class.forName("android.app.ActivityThread");
54
+ return (Application) activityThread.getMethod("currentApplication").invoke(null);
55
+ } catch (Exception e) {
56
+ Log.w(TAG, "Cannot get the application", e);
57
+ return null;
58
+ }
59
+ }
60
+ }
@@ -1,8 +1,5 @@
1
1
  package com.balthazargronon.RCTZeroconf;
2
2
 
3
- import com.facebook.react.bridge.ReadableArray;
4
- import com.facebook.react.bridge.ReadableMap;
5
- import com.facebook.react.bridge.ReadableType;
6
3
 
7
4
  import java.util.ArrayList;
8
5
  import java.util.List;
@@ -24,30 +21,27 @@ public class ZeroconfOptions {
24
21
  /** Scan: seconds to resolve each service, retried once before a TIMEOUT error */
25
22
  public double resolveTimeoutSeconds = 5;
26
23
 
27
- public static ZeroconfOptions from(@Nullable ReadableMap map) {
28
- ZeroconfOptions options = new ZeroconfOptions();
29
- if (map == null) {
30
- return options;
31
- }
32
- options.subtype = subtypeLabel(getString(map, "subtype"));
33
- if (map.hasKey("subtypes") && map.getType("subtypes") == ReadableType.Array) {
34
- ReadableArray subtypes = map.getArray("subtypes");
35
- for (int i = 0; i < subtypes.size(); i++) {
36
- String label = subtypes.getType(i) == ReadableType.String ? subtypeLabel(subtypes.getString(i)) : null;
24
+ /**
25
+ * @param timeout resolveService's timeout in seconds, ignored unless positive
26
+ * @param resolveTimeout scans' resolve timeout in seconds, ignored unless positive
27
+ */
28
+ public ZeroconfOptions(@Nullable String subtype, @Nullable String[] subtypes, @Nullable String networkInterface, double timeout, double resolveTimeout) {
29
+ this.subtype = subtypeLabel(subtype);
30
+ if (subtypes != null) {
31
+ for (String candidate : subtypes) {
32
+ String label = subtypeLabel(candidate);
37
33
  if (label != null) {
38
- options.subtypes.add(label);
34
+ this.subtypes.add(label);
39
35
  }
40
36
  }
41
37
  }
42
- String networkInterface = getString(map, "networkInterface");
43
- options.networkInterface = networkInterface == null || networkInterface.isEmpty() ? null : networkInterface;
44
- if (map.hasKey("timeout") && map.getType("timeout") == ReadableType.Number && map.getDouble("timeout") > 0) {
45
- options.timeoutSeconds = map.getDouble("timeout");
38
+ this.networkInterface = networkInterface == null || networkInterface.isEmpty() ? null : networkInterface;
39
+ if (timeout > 0) {
40
+ this.timeoutSeconds = timeout;
46
41
  }
47
- if (map.hasKey("resolveTimeout") && map.getType("resolveTimeout") == ReadableType.Number && map.getDouble("resolveTimeout") > 0) {
48
- options.resolveTimeoutSeconds = map.getDouble("resolveTimeout");
42
+ if (resolveTimeout > 0) {
43
+ this.resolveTimeoutSeconds = resolveTimeout;
49
44
  }
50
- return options;
51
45
  }
52
46
 
53
47
  /**
@@ -61,11 +55,6 @@ public class ZeroconfOptions {
61
55
  return type.toString();
62
56
  }
63
57
 
64
- @Nullable
65
- private static String getString(ReadableMap map, String key) {
66
- return map.hasKey(key) && map.getType(key) == ReadableType.String ? map.getString(key) : null;
67
- }
68
-
69
58
  // "printer" or "_printer" -> "_printer"
70
59
  @Nullable
71
60
  private static String subtypeLabel(@Nullable String subtype) {