react-native-zeroconf 0.17.2 → 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 (117) 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 -13
  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/mDNSPosix/PosixDaemon.c +19 -1
  19. package/android/src/main/jni/mdnsresponder/mDNSPosix/mDNSPosix.c +104 -1
  20. package/android/src/main/jni/mdnsresponder/mDNSPosix/mDNSPosix.h +7 -0
  21. package/android/src/main/jni/mdnsresponder/mDNSShared/dnssd_clientshim.c +34 -9
  22. package/cpp/Backend.cpp +16 -0
  23. package/cpp/Backend.h +116 -0
  24. package/cpp/ZeroconfModule.cpp +280 -0
  25. package/cpp/ZeroconfModule.h +144 -0
  26. package/cpp/android/NsdBackend.cpp +462 -0
  27. package/cpp/android/NsdBackend.h +61 -0
  28. package/cpp/android/ZeroconfAndroidModule.cpp +49 -0
  29. package/cpp/android/ZeroconfAndroidModule.h +15 -0
  30. package/cpp/apple/DispatchExecutor.cpp +66 -0
  31. package/cpp/apple/DispatchExecutor.h +37 -0
  32. package/cpp/apple/LocalNetworkAccess.h +19 -0
  33. package/cpp/apple/LocalNetworkAccess.mm +87 -0
  34. package/cpp/dnssd/DnssdBackend.cpp +976 -0
  35. package/cpp/dnssd/DnssdBackend.h +114 -0
  36. package/cpp/dnssd/Executor.h +28 -0
  37. package/cpp/embedded/EmbeddedExecutor.cpp +124 -0
  38. package/cpp/embedded/EmbeddedExecutor.h +57 -0
  39. package/ios/ZeroconfOnLoad.mm +34 -0
  40. package/ios/generated/ReactCodegen/RNZeroconfSpec/RNZeroconfSpec-generated.mm +140 -0
  41. package/ios/generated/ReactCodegen/RNZeroconfSpec/RNZeroconfSpec.h +189 -0
  42. package/ios/generated/ReactCodegen/RNZeroconfSpecJSI.h +798 -0
  43. package/lib/module/NativeZeroconf.js +15 -0
  44. package/lib/module/NativeZeroconf.js.map +1 -0
  45. package/lib/module/bridge.js +124 -0
  46. package/lib/module/bridge.js.map +1 -0
  47. package/{dist → lib/module}/index.js +149 -149
  48. package/lib/module/index.js.map +1 -0
  49. package/lib/module/package.json +1 -0
  50. package/lib/module/types.js +24 -0
  51. package/lib/module/types.js.map +1 -0
  52. package/lib/typescript/package.json +1 -0
  53. package/lib/typescript/src/NativeZeroconf.d.ts +75 -0
  54. package/lib/typescript/src/NativeZeroconf.d.ts.map +1 -0
  55. package/lib/typescript/src/bridge.d.ts +22 -0
  56. package/lib/typescript/src/bridge.d.ts.map +1 -0
  57. package/lib/typescript/src/index.d.ts +111 -0
  58. package/lib/typescript/src/index.d.ts.map +1 -0
  59. package/lib/typescript/src/types.d.ts +175 -0
  60. package/lib/typescript/src/types.d.ts.map +1 -0
  61. package/package.json +65 -29
  62. package/react-native-zeroconf.podspec +2 -1
  63. package/react-native.config.js +17 -0
  64. package/src/NativeZeroconf.ts +120 -0
  65. package/src/bridge.ts +153 -0
  66. package/src/index.ts +607 -0
  67. package/{index.d.ts → src/types.ts} +5 -99
  68. package/windows/RNZeroconf/RNZeroconf.vcxproj +17 -2
  69. package/windows/RNZeroconf/RNZeroconf.vcxproj.filters +0 -6
  70. package/windows/RNZeroconf/ReactPackageProvider.cpp +23 -20
  71. package/windows/RNZeroconf/WindowsBackend.cpp +156 -0
  72. package/windows/RNZeroconf/WindowsBackend.h +13 -0
  73. package/windows/RNZeroconf/ZeroconfCore.cpp +2 -2
  74. package/windows/RNZeroconf/ZeroconfCore.h +2 -2
  75. package/windows/RNZeroconf/ZeroconfWindowsModule.cpp +22 -0
  76. package/windows/RNZeroconf/ZeroconfWindowsModule.h +14 -0
  77. package/android/src/main/java/com/balthazargronon/RCTZeroconf/ZeroConfImplFactory.java +0 -56
  78. package/android/src/main/java/com/balthazargronon/RCTZeroconf/dnssd/DnssdImpl.java +0 -654
  79. package/android/src/main/java/com/github/druk/dnssd/BaseListener.java +0 -33
  80. package/android/src/main/java/com/github/druk/dnssd/BrowseListener.java +0 -67
  81. package/android/src/main/java/com/github/druk/dnssd/DNSRecord.java +0 -47
  82. package/android/src/main/java/com/github/druk/dnssd/DNSSD.java +0 -650
  83. package/android/src/main/java/com/github/druk/dnssd/DNSSDEmbedded.java +0 -135
  84. package/android/src/main/java/com/github/druk/dnssd/DNSSDException.java +0 -66
  85. package/android/src/main/java/com/github/druk/dnssd/DNSSDRecordRegistrar.java +0 -55
  86. package/android/src/main/java/com/github/druk/dnssd/DNSSDRegistration.java +0 -54
  87. package/android/src/main/java/com/github/druk/dnssd/DNSSDService.java +0 -35
  88. package/android/src/main/java/com/github/druk/dnssd/DomainListener.java +0 -57
  89. package/android/src/main/java/com/github/druk/dnssd/InternalBrowseListener.java +0 -76
  90. package/android/src/main/java/com/github/druk/dnssd/InternalDNSSD.java +0 -870
  91. package/android/src/main/java/com/github/druk/dnssd/InternalDNSSDRecordRegistrar.java +0 -30
  92. package/android/src/main/java/com/github/druk/dnssd/InternalDNSSDRegistration.java +0 -34
  93. package/android/src/main/java/com/github/druk/dnssd/InternalDNSSDService.java +0 -29
  94. package/android/src/main/java/com/github/druk/dnssd/InternalDomainListener.java +0 -62
  95. package/android/src/main/java/com/github/druk/dnssd/InternalQueryListener.java +0 -61
  96. package/android/src/main/java/com/github/druk/dnssd/InternalRegisterListener.java +0 -51
  97. package/android/src/main/java/com/github/druk/dnssd/InternalResolveListener.java +0 -58
  98. package/android/src/main/java/com/github/druk/dnssd/NSClass.java +0 -16
  99. package/android/src/main/java/com/github/druk/dnssd/NSType.java +0 -58
  100. package/android/src/main/java/com/github/druk/dnssd/QueryListener.java +0 -54
  101. package/android/src/main/java/com/github/druk/dnssd/RegisterListener.java +0 -46
  102. package/android/src/main/java/com/github/druk/dnssd/RegisterRecordListener.java +0 -35
  103. package/android/src/main/java/com/github/druk/dnssd/ResolveListener.java +0 -55
  104. package/android/src/main/java/com/github/druk/dnssd/TXTRecord.java +0 -293
  105. package/android/src/main/jni/Android.mk +0 -73
  106. package/android/src/main/jni/Application.mk +0 -3
  107. package/android/src/main/jni/DNSSD.java.h +0 -290
  108. package/android/src/main/jni/JNISupport.c +0 -1140
  109. package/ios/RNZeroconf/RNNetServiceSerializer.h +0 -31
  110. package/ios/RNZeroconf/RNNetServiceSerializer.m +0 -84
  111. package/ios/RNZeroconf/RNZeroconf.h +0 -17
  112. package/ios/RNZeroconf/RNZeroconf.m +0 -1025
  113. package/ios/RNZeroconf.xcodeproj/project.pbxproj +0 -261
  114. package/ios/RNZeroconf.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
  115. package/src/index.js +0 -563
  116. package/windows/RNZeroconf/RNZeroconf.cpp +0 -228
  117. package/windows/RNZeroconf/RNZeroconf.h +0 -70
@@ -0,0 +1,144 @@
1
+ // The native module, one C++ TurboModule for every platform. It bridges a platform's backends
2
+ // (cpp/Backend.h) to JavaScript: calls go to the backend for the call's implType, backend callbacks
3
+ // become events and settled promises.
4
+ #pragma once
5
+
6
+ #include <RNZeroconfSpecJSI.h>
7
+
8
+ #include "Backend.h"
9
+
10
+ #include <functional>
11
+ #include <map>
12
+ #include <memory>
13
+ #include <mutex>
14
+ #include <optional>
15
+ #include <set>
16
+ #include <string>
17
+ #include <vector>
18
+
19
+ namespace facebook::react {
20
+
21
+ using ZeroconfTxtEntry = NativeZeroconfTxtEntry<std::string, std::string>;
22
+ template <>
23
+ struct Bridging<ZeroconfTxtEntry> : NativeZeroconfTxtEntryBridging<ZeroconfTxtEntry> {};
24
+
25
+ using ZeroconfService = NativeZeroconfNativeService<std::string, std::string, std::string, double, std::vector<std::string>, std::vector<ZeroconfTxtEntry>>;
26
+ template <>
27
+ struct Bridging<ZeroconfService> : NativeZeroconfNativeServiceBridging<ZeroconfService> {};
28
+
29
+ using ZeroconfError = NativeZeroconfNativeError<std::string, std::string, std::string, std::string>;
30
+ template <>
31
+ struct Bridging<ZeroconfError> : NativeZeroconfNativeErrorBridging<ZeroconfError> {};
32
+
33
+ using ZeroconfResult = NativeZeroconfNativeResult<std::optional<ZeroconfService>, std::optional<ZeroconfError>>;
34
+ template <>
35
+ struct Bridging<ZeroconfResult> : NativeZeroconfNativeResultBridging<ZeroconfResult> {};
36
+
37
+ using ZeroconfAccessResult = NativeZeroconfNativeAccessResult<std::optional<std::string>, std::optional<ZeroconfError>>;
38
+ template <>
39
+ struct Bridging<ZeroconfAccessResult> : NativeZeroconfNativeAccessResultBridging<ZeroconfAccessResult> {};
40
+
41
+ using ZeroconfScanEvent = NativeZeroconfScanEvent<std::string>;
42
+ template <>
43
+ struct Bridging<ZeroconfScanEvent> : NativeZeroconfScanEventBridging<ZeroconfScanEvent> {};
44
+
45
+ using ZeroconfNameEvent = NativeZeroconfNameEvent<std::string, std::string>;
46
+ template <>
47
+ struct Bridging<ZeroconfNameEvent> : NativeZeroconfNameEventBridging<ZeroconfNameEvent> {};
48
+
49
+ using ZeroconfResolvedEvent = NativeZeroconfResolvedEvent<std::string, ZeroconfService>;
50
+ template <>
51
+ struct Bridging<ZeroconfResolvedEvent> : NativeZeroconfResolvedEventBridging<ZeroconfResolvedEvent> {};
52
+
53
+ using ZeroconfErrorEvent = NativeZeroconfErrorEvent<std::string, ZeroconfError>;
54
+ template <>
55
+ struct Bridging<ZeroconfErrorEvent> : NativeZeroconfErrorEventBridging<ZeroconfErrorEvent> {};
56
+
57
+ using ZeroconfScanOptions = NativeZeroconfNativeScanOptions<std::optional<double>, std::optional<std::string>, std::optional<std::string>>;
58
+ template <>
59
+ struct Bridging<ZeroconfScanOptions> : NativeZeroconfNativeScanOptionsBridging<ZeroconfScanOptions> {};
60
+
61
+ using ZeroconfPublishOptions = NativeZeroconfNativePublishOptions<std::optional<std::vector<std::string>>, std::optional<std::string>>;
62
+ template <>
63
+ struct Bridging<ZeroconfPublishOptions> : NativeZeroconfNativePublishOptionsBridging<ZeroconfPublishOptions> {};
64
+
65
+ using ZeroconfResolveOptions = NativeZeroconfNativeResolveOptions<std::optional<double>, std::optional<std::string>>;
66
+ template <>
67
+ struct Bridging<ZeroconfResolveOptions> : NativeZeroconfNativeResolveOptionsBridging<ZeroconfResolveOptions> {};
68
+
69
+ // What a platform provides to the module, set before the module is created
70
+ struct ZeroconfPlatform {
71
+ // The backend for a key; the platform maps implTypes to keys, so platforms with one backend ignore implType
72
+ std::function<std::string(const std::string &implType)> backendKey;
73
+ std::function<std::shared_ptr<rnzeroconf::Backend>(const std::string &key, rnzeroconf::Events events)> createBackend;
74
+ // Settles with 'granted', 'denied' or 'unknown', or an error
75
+ std::function<void(
76
+ const std::string &type,
77
+ double timeoutSeconds,
78
+ std::function<void(const std::string &status)> resolve,
79
+ std::function<void(const rnzeroconf::Error &error)> reject)>
80
+ checkLocalNetworkAccess;
81
+ // Called when the module starts or stops having anything running: scans, published services,
82
+ // publishes and resolves in flight. Android holds its multicast lock in between
83
+ std::function<void(bool active)> setActive;
84
+ };
85
+
86
+ class ZeroconfModule : public NativeZeroconfCxxSpec<ZeroconfModule> {
87
+ public:
88
+ ZeroconfModule(std::shared_ptr<CallInvoker> jsInvoker, ZeroconfPlatform platform);
89
+ ~ZeroconfModule() override;
90
+
91
+ void scan(jsi::Runtime &rt, std::string scanId, std::string type, std::string protocol, std::string domain, std::string implType, ZeroconfScanOptions options);
92
+ void stop(jsi::Runtime &rt, std::string scanId, std::string implType);
93
+ AsyncPromise<ZeroconfResult> registerService(
94
+ jsi::Runtime &rt,
95
+ std::string type,
96
+ std::string protocol,
97
+ std::string domain,
98
+ std::string name,
99
+ double port,
100
+ std::vector<ZeroconfTxtEntry> txt,
101
+ std::string implType,
102
+ ZeroconfPublishOptions options);
103
+ AsyncPromise<ZeroconfResult> updateService(jsi::Runtime &rt, std::string name, std::vector<ZeroconfTxtEntry> txt, std::string implType);
104
+ AsyncPromise<ZeroconfResult> unregisterService(jsi::Runtime &rt, std::string name, std::string implType);
105
+ AsyncPromise<ZeroconfResult> resolveService(
106
+ jsi::Runtime &rt,
107
+ std::string name,
108
+ std::string type,
109
+ std::string protocol,
110
+ std::string domain,
111
+ std::string implType,
112
+ ZeroconfResolveOptions options);
113
+ AsyncPromise<ZeroconfAccessResult> checkLocalNetworkAccess(jsi::Runtime &rt, std::string type, double timeout);
114
+
115
+ private:
116
+ // Backend callbacks can outlive the module, they go through this
117
+ struct Emitter {
118
+ std::mutex mutex;
119
+ ZeroconfModule *module = nullptr;
120
+ };
121
+
122
+ std::shared_ptr<rnzeroconf::Backend> Backend(const std::string &implType);
123
+ rnzeroconf::Events MakeEvents();
124
+ AsyncPromise<ZeroconfResult> Settle(
125
+ jsi::Runtime &rt,
126
+ const std::function<void(rnzeroconf::ServiceCallback, rnzeroconf::ErrorCallback)> &call,
127
+ bool active = false);
128
+
129
+ // What is running, for ZeroconfPlatform::setActive
130
+ enum class Activity { ScanStarted, ScanStopped, AllScansStopped, Published, Unpublished, CallStarted, CallSettled };
131
+ void Track(Activity activity, const std::string &key = "");
132
+
133
+ ZeroconfPlatform platform_;
134
+ std::shared_ptr<Emitter> emitter_;
135
+ std::mutex backendsMutex_;
136
+ std::map<std::string, std::shared_ptr<rnzeroconf::Backend>> backends_;
137
+ std::mutex activityMutex_;
138
+ std::set<std::string> runningScans_;
139
+ std::set<std::string> publishedServices_;
140
+ int callsInFlight_ = 0;
141
+ bool active_ = false;
142
+ };
143
+
144
+ } // namespace facebook::react
@@ -0,0 +1,462 @@
1
+ #include "NsdBackend.h"
2
+
3
+ #include <android/log.h>
4
+
5
+ #include <map>
6
+ #include <mutex>
7
+ #include <optional>
8
+ #include <utility>
9
+
10
+ using namespace facebook;
11
+
12
+ namespace rnzeroconf {
13
+
14
+ namespace {
15
+
16
+ constexpr const char *kHostClass = "com/balthazargronon/RCTZeroconf/NsdHost";
17
+ constexpr const char *kPromiseClass = "com/balthazargronon/RCTZeroconf/NsdPromise";
18
+ constexpr const char *kPayloadClass = "com/balthazargronon/RCTZeroconf/NsdPayload";
19
+
20
+ // What the Java side's handles point to: integers looked up here, so a late callback finds nothing
21
+ // instead of a freed object
22
+ struct Registry {
23
+ std::mutex mutex;
24
+ int64_t next = 0;
25
+ std::map<int64_t, Events> hosts;
26
+ std::map<int64_t, std::pair<ServiceCallback, ErrorCallback>> promises;
27
+ };
28
+
29
+ Registry &registry() {
30
+ static Registry instance;
31
+ return instance;
32
+ }
33
+
34
+ // Runs with the app's class loader, which finds the library's classes from any thread
35
+ template <typename F>
36
+ void WithEnv(F &&f) {
37
+ jni::ThreadScope::WithClassLoader([&] { f(jni::Environment::current()); });
38
+ }
39
+
40
+ bool ClearException(JNIEnv *env, const char *what) {
41
+ if (!env->ExceptionCheck()) {
42
+ return false;
43
+ }
44
+ __android_log_print(ANDROID_LOG_ERROR, "RNZeroconf", "Java exception in %s", what);
45
+ env->ExceptionDescribe();
46
+ env->ExceptionClear();
47
+ return true;
48
+ }
49
+
50
+ jclass GlobalClass(const char *name) {
51
+ return static_cast<jclass>(jni::Environment::current()->NewGlobalRef(jni::findClassLocal(name).get()));
52
+ }
53
+
54
+ jclass HostClass() {
55
+ static jclass cls = GlobalClass(kHostClass);
56
+ return cls;
57
+ }
58
+
59
+ jclass PromiseClass() {
60
+ static jclass cls = GlobalClass(kPromiseClass);
61
+ return cls;
62
+ }
63
+
64
+ jclass StringClass() {
65
+ static jclass cls = GlobalClass("java/lang/String");
66
+ return cls;
67
+ }
68
+
69
+ std::string ToString(jstring value) {
70
+ return value ? jni::wrap_alias(value)->toStdString() : std::string();
71
+ }
72
+
73
+ std::vector<std::string> ToStrings(JNIEnv *env, jobjectArray array) {
74
+ std::vector<std::string> strings;
75
+ if (!array) {
76
+ return strings;
77
+ }
78
+ jsize length = env->GetArrayLength(array);
79
+ for (jsize i = 0; i < length; i++) {
80
+ auto element = static_cast<jstring>(env->GetObjectArrayElement(array, i));
81
+ strings.push_back(ToString(element));
82
+ env->DeleteLocalRef(element);
83
+ }
84
+ return strings;
85
+ }
86
+
87
+ jobjectArray ToArray(JNIEnv *env, const std::vector<std::string> &strings) {
88
+ jobjectArray array = env->NewObjectArray(static_cast<jsize>(strings.size()), StringClass(), nullptr);
89
+ for (size_t i = 0; i < strings.size(); i++) {
90
+ auto element = jni::make_jstring(strings[i]);
91
+ env->SetObjectArrayElement(array, static_cast<jsize>(i), element.get());
92
+ }
93
+ return array;
94
+ }
95
+
96
+ // NsdPayload -> Service
97
+ Service ToService(JNIEnv *env, jobject payload) {
98
+ Service service;
99
+ if (!payload) {
100
+ return service;
101
+ }
102
+ static jclass payloadClass = GlobalClass(kPayloadClass);
103
+ static jmethodID getString = env->GetMethodID(payloadClass, "getString", "(Ljava/lang/String;)Ljava/lang/String;");
104
+ static jmethodID getInt = env->GetMethodID(payloadClass, "getInt", "(Ljava/lang/String;)I");
105
+ static jmethodID getStrings = env->GetMethodID(payloadClass, "getStrings", "(Ljava/lang/String;)[Ljava/lang/String;");
106
+ static jmethodID getMapKeys = env->GetMethodID(payloadClass, "getMapKeys", "(Ljava/lang/String;)[Ljava/lang/String;");
107
+ static jmethodID getMapValues = env->GetMethodID(payloadClass, "getMapValues", "(Ljava/lang/String;)[Ljava/lang/String;");
108
+
109
+ auto string = [&](const char *key) {
110
+ auto name = jni::make_jstring(key);
111
+ auto value = static_cast<jstring>(env->CallObjectMethod(payload, getString, name.get()));
112
+ std::string result = ToString(value);
113
+ env->DeleteLocalRef(value);
114
+ return result;
115
+ };
116
+ auto strings = [&](jmethodID method, const char *key) {
117
+ auto name = jni::make_jstring(key);
118
+ auto value = static_cast<jobjectArray>(env->CallObjectMethod(payload, method, name.get()));
119
+ auto result = ToStrings(env, value);
120
+ env->DeleteLocalRef(value);
121
+ return result;
122
+ };
123
+
124
+ service.name = string("name");
125
+ service.fullName = string("fullName");
126
+ service.host = string("host");
127
+ {
128
+ auto key = jni::make_jstring("port");
129
+ service.port = static_cast<uint16_t>(env->CallIntMethod(payload, getInt, key.get()));
130
+ }
131
+ service.addresses = strings(getStrings, "addresses");
132
+ auto keys = strings(getMapKeys, "txt");
133
+ auto values = strings(getMapValues, "txt");
134
+ for (size_t i = 0; i < keys.size() && i < values.size(); i++) {
135
+ service.txt.emplace_back(keys[i], values[i]);
136
+ }
137
+ ClearException(env, "NsdPayload");
138
+ return service;
139
+ }
140
+
141
+ std::optional<Events> HostEvents(jlong handle) {
142
+ std::lock_guard<std::mutex> lock(registry().mutex);
143
+ auto found = registry().hosts.find(handle);
144
+ if (found == registry().hosts.end()) {
145
+ return std::nullopt;
146
+ }
147
+ return found->second;
148
+ }
149
+
150
+ std::optional<std::pair<ServiceCallback, ErrorCallback>> TakePromise(jlong id) {
151
+ std::lock_guard<std::mutex> lock(registry().mutex);
152
+ auto found = registry().promises.find(id);
153
+ if (found == registry().promises.end()) {
154
+ return std::nullopt;
155
+ }
156
+ auto callbacks = std::move(found->second);
157
+ registry().promises.erase(found);
158
+ return callbacks;
159
+ }
160
+
161
+ // NsdHost.nativeEvent: the event names of the Java side
162
+ void NativeEvent(JNIEnv *env, jclass, jlong handle, jstring eventName, jobject payload) {
163
+ auto events = HostEvents(handle);
164
+ if (!events) {
165
+ return;
166
+ }
167
+ std::string event = ToString(eventName);
168
+ Service service = ToService(env, payload);
169
+ std::string scanId;
170
+ if (payload) {
171
+ static jclass payloadClass = GlobalClass(kPayloadClass);
172
+ static jmethodID getString = env->GetMethodID(payloadClass, "getString", "(Ljava/lang/String;)Ljava/lang/String;");
173
+ auto key = jni::make_jstring("scanId");
174
+ auto value = static_cast<jstring>(env->CallObjectMethod(payload, getString, key.get()));
175
+ scanId = ToString(value);
176
+ env->DeleteLocalRef(value);
177
+ }
178
+ if (event == "RNZeroconfStart" && events->start) {
179
+ events->start(scanId);
180
+ } else if (event == "RNZeroconfStop" && events->stop) {
181
+ events->stop(scanId);
182
+ } else if (event == "RNZeroconfFound" && events->found) {
183
+ events->found(scanId, service.name);
184
+ } else if (event == "RNZeroconfRemove" && events->remove) {
185
+ events->remove(scanId, service.name);
186
+ } else if (event == "RNZeroconfResolved" && events->resolved) {
187
+ events->resolved(scanId, service);
188
+ } else if (event == "RNZeroconfServiceRegistered" && events->published) {
189
+ events->published(service);
190
+ } else if (event == "RNZeroconfServiceUnregistered" && events->unpublished) {
191
+ events->unpublished(service);
192
+ }
193
+ }
194
+
195
+ void NativeError(JNIEnv *, jclass, jlong handle, jstring scanId, jstring domain, jstring code, jstring message, jstring serviceName) {
196
+ auto events = HostEvents(handle);
197
+ if (events && events->error) {
198
+ events->error(ToString(scanId), Error{ToString(domain), ToString(code), ToString(message), ToString(serviceName)});
199
+ }
200
+ }
201
+
202
+ void NativeResolve(JNIEnv *env, jclass, jlong id, jobject payload) {
203
+ auto callbacks = TakePromise(id);
204
+ if (callbacks && callbacks->first) {
205
+ callbacks->first(ToService(env, payload));
206
+ }
207
+ }
208
+
209
+ void NativeReject(JNIEnv *, jclass, jlong id, jstring domain, jstring code, jstring message, jstring serviceName) {
210
+ auto callbacks = TakePromise(id);
211
+ if (callbacks && callbacks->second) {
212
+ callbacks->second(Error{ToString(domain), ToString(code), ToString(message), ToString(serviceName)});
213
+ }
214
+ }
215
+
216
+ void RegisterNatives() {
217
+ static std::once_flag once;
218
+ std::call_once(once, [] {
219
+ WithEnv([](JNIEnv *env) {
220
+ JNINativeMethod hostMethods[] = {
221
+ {const_cast<char *>("nativeEvent"), const_cast<char *>("(JLjava/lang/String;Lcom/balthazargronon/RCTZeroconf/NsdPayload;)V"),
222
+ reinterpret_cast<void *>(NativeEvent)},
223
+ {const_cast<char *>("nativeError"),
224
+ const_cast<char *>("(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"),
225
+ reinterpret_cast<void *>(NativeError)},
226
+ };
227
+ env->RegisterNatives(HostClass(), hostMethods, 2);
228
+ ClearException(env, "registering NsdHost");
229
+ JNINativeMethod promiseMethods[] = {
230
+ {const_cast<char *>("nativeResolve"), const_cast<char *>("(JLcom/balthazargronon/RCTZeroconf/NsdPayload;)V"),
231
+ reinterpret_cast<void *>(NativeResolve)},
232
+ {const_cast<char *>("nativeReject"),
233
+ const_cast<char *>("(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"),
234
+ reinterpret_cast<void *>(NativeReject)},
235
+ };
236
+ env->RegisterNatives(PromiseClass(), promiseMethods, 2);
237
+ ClearException(env, "registering NsdPromise");
238
+ });
239
+ });
240
+ }
241
+
242
+ // A new NsdPromise settling these callbacks
243
+ jobject NewPromise(JNIEnv *env, ServiceCallback resolve, ErrorCallback reject) {
244
+ int64_t id;
245
+ {
246
+ std::lock_guard<std::mutex> lock(registry().mutex);
247
+ id = ++registry().next;
248
+ registry().promises[id] = {std::move(resolve), std::move(reject)};
249
+ }
250
+ static jmethodID constructor = env->GetMethodID(PromiseClass(), "<init>", "(J)V");
251
+ return env->NewObject(PromiseClass(), constructor, static_cast<jlong>(id));
252
+ }
253
+
254
+ // Rejects a call whose promise never reached Java
255
+ void RejectLocally(jobject promise, JNIEnv *env, const std::string &name) {
256
+ static jfieldID idField = env->GetFieldID(PromiseClass(), "id", "J");
257
+ if (!promise) {
258
+ return;
259
+ }
260
+ auto callbacks = TakePromise(env->GetLongField(promise, idField));
261
+ if (callbacks && callbacks->second) {
262
+ callbacks->second(LibraryError("EXCEPTION", "The NSD implementation failed", name));
263
+ }
264
+ }
265
+
266
+ std::pair<std::vector<std::string>, std::vector<std::string>> Split(const TxtPairs &txt) {
267
+ std::vector<std::string> keys;
268
+ std::vector<std::string> values;
269
+ for (const auto &[key, value] : txt) {
270
+ keys.push_back(key);
271
+ values.push_back(value);
272
+ }
273
+ return {keys, values};
274
+ }
275
+
276
+ } // namespace
277
+
278
+ std::shared_ptr<NsdBackend> NsdBackend::Create(Events events) {
279
+ RegisterNatives();
280
+ int64_t handle;
281
+ {
282
+ std::lock_guard<std::mutex> lock(registry().mutex);
283
+ handle = ++registry().next;
284
+ registry().hosts[handle] = std::move(events);
285
+ }
286
+ jni::global_ref<jobject> host;
287
+ WithEnv([&](JNIEnv *env) {
288
+ static jmethodID constructor = env->GetMethodID(HostClass(), "<init>", "(J)V");
289
+ jobject local = env->NewObject(HostClass(), constructor, static_cast<jlong>(handle));
290
+ if (!ClearException(env, "NsdHost()") && local) {
291
+ host = jni::make_global(jni::wrap_alias(local));
292
+ env->DeleteLocalRef(local);
293
+ }
294
+ });
295
+ return std::shared_ptr<NsdBackend>(new NsdBackend(handle, std::move(host)));
296
+ }
297
+
298
+ NsdBackend::NsdBackend(int64_t handle, jni::global_ref<jobject> host) : handle_(handle), host_(std::move(host)) {}
299
+
300
+ NsdBackend::~NsdBackend() {
301
+ std::lock_guard<std::mutex> lock(registry().mutex);
302
+ registry().hosts.erase(handle_);
303
+ }
304
+
305
+ void NsdBackend::Scan(
306
+ const std::string &scanId,
307
+ const std::string &type,
308
+ const std::string &protocol,
309
+ const std::string &domain,
310
+ const ScanOptions &options) {
311
+ if (!host_) {
312
+ return;
313
+ }
314
+ WithEnv([&](JNIEnv *env) {
315
+ static jmethodID scan = env->GetMethodID(
316
+ HostClass(), "scan", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;D)V");
317
+ env->CallVoidMethod(host_.get(), scan, jni::make_jstring(scanId).get(), jni::make_jstring(type).get(), jni::make_jstring(protocol).get(),
318
+ jni::make_jstring(domain).get(), jni::make_jstring(options.subtype).get(), jni::make_jstring(options.networkInterface).get(),
319
+ options.resolveTimeoutSeconds);
320
+ ClearException(env, "NsdHost.scan");
321
+ });
322
+ }
323
+
324
+ void NsdBackend::Stop(const std::string &scanId) {
325
+ if (!host_) {
326
+ return;
327
+ }
328
+ WithEnv([&](JNIEnv *env) {
329
+ static jmethodID stop = env->GetMethodID(HostClass(), "stop", "(Ljava/lang/String;)V");
330
+ env->CallVoidMethod(host_.get(), stop, jni::make_jstring(scanId).get());
331
+ ClearException(env, "NsdHost.stop");
332
+ });
333
+ }
334
+
335
+ void NsdBackend::Publish(
336
+ const std::string &type,
337
+ const std::string &protocol,
338
+ const std::string &domain,
339
+ const std::string &name,
340
+ uint16_t port,
341
+ const TxtPairs &txt,
342
+ const PublishOptions &options,
343
+ ServiceCallback resolve,
344
+ ErrorCallback reject) {
345
+ if (!host_) {
346
+ reject(LibraryError("EXCEPTION", "The NSD implementation is not available", name));
347
+ return;
348
+ }
349
+ WithEnv([&](JNIEnv *env) {
350
+ static jmethodID registerService = env->GetMethodID(
351
+ HostClass(), "registerService",
352
+ "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I[Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;Lcom/balthazargronon/RCTZeroconf/NsdPromise;)V");
353
+ auto [keys, values] = Split(txt);
354
+ jobjectArray keyArray = ToArray(env, keys);
355
+ jobjectArray valueArray = ToArray(env, values);
356
+ jobjectArray subtypeArray = ToArray(env, options.subtypes);
357
+ jobject promise = NewPromise(env, std::move(resolve), std::move(reject));
358
+ env->CallVoidMethod(host_.get(), registerService, jni::make_jstring(type).get(), jni::make_jstring(protocol).get(), jni::make_jstring(domain).get(),
359
+ jni::make_jstring(name).get(), static_cast<jint>(port), keyArray, valueArray, subtypeArray,
360
+ jni::make_jstring(options.networkInterface).get(), promise);
361
+ if (ClearException(env, "NsdHost.registerService")) {
362
+ RejectLocally(promise, env, name);
363
+ }
364
+ env->DeleteLocalRef(keyArray);
365
+ env->DeleteLocalRef(valueArray);
366
+ env->DeleteLocalRef(subtypeArray);
367
+ env->DeleteLocalRef(promise);
368
+ });
369
+ }
370
+
371
+ void NsdBackend::Unpublish(const std::string &name, ServiceCallback resolve, ErrorCallback reject) {
372
+ if (!host_) {
373
+ reject(LibraryError("EXCEPTION", "The NSD implementation is not available", name));
374
+ return;
375
+ }
376
+ WithEnv([&](JNIEnv *env) {
377
+ static jmethodID unregisterService =
378
+ env->GetMethodID(HostClass(), "unregisterService", "(Ljava/lang/String;Lcom/balthazargronon/RCTZeroconf/NsdPromise;)V");
379
+ jobject promise = NewPromise(env, std::move(resolve), std::move(reject));
380
+ env->CallVoidMethod(host_.get(), unregisterService, jni::make_jstring(name).get(), promise);
381
+ if (ClearException(env, "NsdHost.unregisterService")) {
382
+ RejectLocally(promise, env, name);
383
+ }
384
+ env->DeleteLocalRef(promise);
385
+ });
386
+ }
387
+
388
+ void NsdBackend::Update(const std::string &name, const TxtPairs &txt, ServiceCallback resolve, ErrorCallback reject) {
389
+ if (!host_) {
390
+ reject(LibraryError("EXCEPTION", "The NSD implementation is not available", name));
391
+ return;
392
+ }
393
+ WithEnv([&](JNIEnv *env) {
394
+ static jmethodID updateService = env->GetMethodID(
395
+ HostClass(), "updateService", "(Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;Lcom/balthazargronon/RCTZeroconf/NsdPromise;)V");
396
+ auto [keys, values] = Split(txt);
397
+ jobjectArray keyArray = ToArray(env, keys);
398
+ jobjectArray valueArray = ToArray(env, values);
399
+ jobject promise = NewPromise(env, std::move(resolve), std::move(reject));
400
+ env->CallVoidMethod(host_.get(), updateService, jni::make_jstring(name).get(), keyArray, valueArray, promise);
401
+ if (ClearException(env, "NsdHost.updateService")) {
402
+ RejectLocally(promise, env, name);
403
+ }
404
+ env->DeleteLocalRef(keyArray);
405
+ env->DeleteLocalRef(valueArray);
406
+ env->DeleteLocalRef(promise);
407
+ });
408
+ }
409
+
410
+ void NsdBackend::ResolveService(
411
+ const std::string &name,
412
+ const std::string &type,
413
+ const std::string &protocol,
414
+ const std::string &domain,
415
+ const ResolveOptions &options,
416
+ ServiceCallback resolve,
417
+ ErrorCallback reject) {
418
+ if (!host_) {
419
+ reject(LibraryError("EXCEPTION", "The NSD implementation is not available", name));
420
+ return;
421
+ }
422
+ WithEnv([&](JNIEnv *env) {
423
+ static jmethodID resolveService = env->GetMethodID(
424
+ HostClass(), "resolveService",
425
+ "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;DLcom/balthazargronon/RCTZeroconf/NsdPromise;)V");
426
+ jobject promise = NewPromise(env, std::move(resolve), std::move(reject));
427
+ env->CallVoidMethod(host_.get(), resolveService, jni::make_jstring(name).get(), jni::make_jstring(type).get(), jni::make_jstring(protocol).get(),
428
+ jni::make_jstring(domain).get(), jni::make_jstring(options.networkInterface).get(), options.timeoutSeconds, promise);
429
+ if (ClearException(env, "NsdHost.resolveService")) {
430
+ RejectLocally(promise, env, name);
431
+ }
432
+ env->DeleteLocalRef(promise);
433
+ });
434
+ }
435
+
436
+ void NsdBackend::Shutdown() {
437
+ if (!host_) {
438
+ return;
439
+ }
440
+ WithEnv([&](JNIEnv *env) {
441
+ static jmethodID shutdown = env->GetMethodID(HostClass(), "shutdown", "()V");
442
+ env->CallVoidMethod(host_.get(), shutdown);
443
+ ClearException(env, "NsdHost.shutdown");
444
+ });
445
+ std::lock_guard<std::mutex> lock(registry().mutex);
446
+ registry().hosts.erase(handle_);
447
+ }
448
+
449
+ std::string NsdBackend::CheckLocalNetworkAccess() {
450
+ std::string status = "unknown";
451
+ WithEnv([&](JNIEnv *env) {
452
+ static jmethodID check = env->GetStaticMethodID(HostClass(), "checkLocalNetworkAccess", "()Ljava/lang/String;");
453
+ auto result = static_cast<jstring>(env->CallStaticObjectMethod(HostClass(), check));
454
+ if (!ClearException(env, "NsdHost.checkLocalNetworkAccess")) {
455
+ status = ToString(result);
456
+ }
457
+ env->DeleteLocalRef(result);
458
+ });
459
+ return status;
460
+ }
461
+
462
+ } // namespace rnzeroconf
@@ -0,0 +1,61 @@
1
+ // The backend for Android's NsdManager (NSD), which is a Java API: it drives the NSD code of the library's
2
+ // Java side (NsdHost) through JNI, events and results come back through native methods.
3
+ #pragma once
4
+
5
+ #include "../Backend.h"
6
+
7
+ #include <fbjni/fbjni.h>
8
+
9
+ #include <memory>
10
+ #include <string>
11
+
12
+ namespace rnzeroconf {
13
+
14
+ class NsdBackend : public Backend {
15
+ public:
16
+ static std::shared_ptr<NsdBackend> Create(Events events);
17
+ ~NsdBackend() override;
18
+
19
+ void Scan(
20
+ const std::string &scanId,
21
+ const std::string &type,
22
+ const std::string &protocol,
23
+ const std::string &domain,
24
+ const ScanOptions &options) override;
25
+ void Stop(const std::string &scanId) override;
26
+
27
+ void Publish(
28
+ const std::string &type,
29
+ const std::string &protocol,
30
+ const std::string &domain,
31
+ const std::string &name,
32
+ uint16_t port,
33
+ const TxtPairs &txt,
34
+ const PublishOptions &options,
35
+ ServiceCallback resolve,
36
+ ErrorCallback reject) override;
37
+ void Unpublish(const std::string &name, ServiceCallback resolve, ErrorCallback reject) override;
38
+ void Update(const std::string &name, const TxtPairs &txt, ServiceCallback resolve, ErrorCallback reject) override;
39
+
40
+ void ResolveService(
41
+ const std::string &name,
42
+ const std::string &type,
43
+ const std::string &protocol,
44
+ const std::string &domain,
45
+ const ResolveOptions &options,
46
+ ServiceCallback resolve,
47
+ ErrorCallback reject) override;
48
+
49
+ void Shutdown() override;
50
+
51
+ // "granted", "denied" or "unknown": Android 17's ACCESS_LOCAL_NETWORK permission
52
+ static std::string CheckLocalNetworkAccess();
53
+
54
+ private:
55
+ NsdBackend(int64_t handle, facebook::jni::global_ref<jobject> host);
56
+
57
+ int64_t handle_;
58
+ facebook::jni::global_ref<jobject> host_;
59
+ };
60
+
61
+ } // namespace rnzeroconf