From 407ea30a00ea6d57d7d319f923a4a033ce752539 Mon Sep 17 00:00:00 2001 From: Aniket Singh Date: Tue, 17 Feb 2026 16:36:16 +0530 Subject: [PATCH 01/11] BT changes for common --- android/main/AndroidManifest.xml | 1 + common/CMakeLists.txt | 2 + common/main/cpp/MetadataHelper.cc | 80 +++ common/main/cpp/MetadataHelper.h | 12 + ...e_internal_core_impl_NativeBluetoothPeer.h | 66 ++ ..._core_impl_NativeC4PeerDiscoveryProvider.h | 61 ++ common/main/cpp/native_bluetoothpeer.cc | 90 +++ .../main/cpp/native_c4multipeerreplicator.cc | 11 +- .../cpp/native_c4peerdiscoveryprovider.cc | 586 ++++++++++++++++++ common/main/cpp/native_glue.cc | 3 + common/main/cpp/native_glue.hh | 9 + .../lite/internal/core/C4Replicator.java | 1 + 12 files changed, 916 insertions(+), 6 deletions(-) create mode 100644 common/main/cpp/MetadataHelper.cc create mode 100644 common/main/cpp/MetadataHelper.h create mode 100644 common/main/cpp/com_couchbase_lite_internal_core_impl_NativeBluetoothPeer.h create mode 100644 common/main/cpp/com_couchbase_lite_internal_core_impl_NativeC4PeerDiscoveryProvider.h create mode 100644 common/main/cpp/native_bluetoothpeer.cc create mode 100644 common/main/cpp/native_c4peerdiscoveryprovider.cc diff --git a/android/main/AndroidManifest.xml b/android/main/AndroidManifest.xml index cf090d8a7..acece305b 100644 --- a/android/main/AndroidManifest.xml +++ b/android/main/AndroidManifest.xml @@ -20,6 +20,7 @@ limitations under the License. xmlns:tools="http://schemas.android.com/tools" > + diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index d1aa2a0f0..2e727ce3d 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -67,6 +67,8 @@ add_library( LiteCoreJNI SHARED ${JNI_SRC}/native_c4index.cc ${JNI_SRC}/native_c4listener.cc ${JNI_SRC}/native_c4multipeerreplicator.cc + ${JNI_SRC}/native_c4peerdiscoveryprovider.cc + ${JNI_SRC}/native_bluetoothpeer.cc ${JNI_SRC}/native_c4observer.cc ${JNI_SRC}/native_c4prediction.cc ${JNI_SRC}/native_c4query.cc diff --git a/common/main/cpp/MetadataHelper.cc b/common/main/cpp/MetadataHelper.cc new file mode 100644 index 000000000..2b21be76e --- /dev/null +++ b/common/main/cpp/MetadataHelper.cc @@ -0,0 +1,80 @@ +#include +#include "c4PeerDiscovery.hh" +#include "native_glue.hh" + +namespace litecore::jni { +// Helper to convert Java Map to Metadata + static C4Peer::Metadata javaMapToMetadata(JNIEnv* env, jobject map) { + C4Peer::Metadata metadata; + + if (!map) return metadata; + + jclass mapClass = env->GetObjectClass(map); + jmethodID entrySetMethod = env->GetMethodID(mapClass, "entrySet", "()Ljava/util/Set;"); + jobject entrySet = env->CallObjectMethod(map, entrySetMethod); + + jclass setClass = env->GetObjectClass(entrySet); + jmethodID toArrayMethod = env->GetMethodID(setClass, "toArray", "()[Ljava/lang/Object;"); + jobjectArray entries = (jobjectArray)env->CallObjectMethod(entrySet, toArrayMethod); + + jsize entryCount = env->GetArrayLength(entries); + + jclass entryClass = env->FindClass("java/util/Map$Entry"); + jmethodID getKeyMethod = env->GetMethodID(entryClass, "getKey", "()Ljava/lang/Object;"); + jmethodID getValueMethod = env->GetMethodID(entryClass, "getValue", "()Ljava/lang/Object;"); + + for (jsize i = 0; i < entryCount; i++) { + jobject entry = env->GetObjectArrayElement(entries, i); + jstring jKey = (jstring)env->CallObjectMethod(entry, getKeyMethod); + jbyteArray jValue = (jbyteArray)env->CallObjectMethod(entry, getValueMethod); + + std::string key = JstringToUTF8(env, jKey); + jbyteArraySlice value(env, jValue); + + metadata[std::string(key)] = fleece::alloc_slice(value); + + env->DeleteLocalRef(entry); + env->DeleteLocalRef(jKey); + env->DeleteLocalRef(jValue); + } + + env->DeleteLocalRef(entrySet); + env->DeleteLocalRef(entries); + + return metadata; + } + + + jobject metadataToJavaMap(JNIEnv* env, const C4Peer::Metadata& metadata) { + // Create HashMap + jclass hashMapClass = env->FindClass("java/util/HashMap"); + jmethodID hashMapInit = env->GetMethodID(hashMapClass, "", "(I)V"); + jobject hashMap = env->NewObject(hashMapClass, hashMapInit); + + // Put method for HashMap + jmethodID hashMapPut = env->GetMethodID( + hashMapClass, "put", + "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;" + ); + + // Convert each key-value pair + for (const auto& [key, value] : metadata) { + // Convert key to Java String + jstring jKey = UTF8ToJstring(env, key.c_str(), key.size()); + + // Convert value (fleece::alloc_slice) to Java byte[] + jbyteArray jValue = toJByteArray(env, value); + + // Put in map + env->CallObjectMethod(hashMap, hashMapPut, jKey, jValue); + + // Clean up local references + env->DeleteLocalRef(jKey); + env->DeleteLocalRef(jValue); + } + + env->DeleteLocalRef(hashMapClass); + return hashMap; + } + +} \ No newline at end of file diff --git a/common/main/cpp/MetadataHelper.h b/common/main/cpp/MetadataHelper.h new file mode 100644 index 000000000..47dcbfe82 --- /dev/null +++ b/common/main/cpp/MetadataHelper.h @@ -0,0 +1,12 @@ +#include +#include "c4PeerDiscovery.hh" + +#ifndef COUCHBASE_LITE_JAVA_EE_ROOT_METADATAHELPER_H +#define COUCHBASE_LITE_JAVA_EE_ROOT_METADATAHELPER_H + +namespace litecore::jni { + static C4Peer::Metadata javaMapToMetadata(JNIEnv* env, jobject map); + jobject metadataToJavaMap(JNIEnv* env, const C4Peer::Metadata& metadata); +} + +#endif //COUCHBASE_LITE_JAVA_EE_ROOT_METADATAHELPER_H diff --git a/common/main/cpp/com_couchbase_lite_internal_core_impl_NativeBluetoothPeer.h b/common/main/cpp/com_couchbase_lite_internal_core_impl_NativeBluetoothPeer.h new file mode 100644 index 000000000..61daa6ced --- /dev/null +++ b/common/main/cpp/com_couchbase_lite_internal_core_impl_NativeBluetoothPeer.h @@ -0,0 +1,66 @@ +#ifndef COUCHBASE_LITE_JAVA_EE_ROOT_COM_COUCHBASE_LITE_INTERNAL_CORE_IMPL_NATIVEBLUETOOTHPEER_H +#define COUCHBASE_LITE_JAVA_EE_ROOT_COM_COUCHBASE_LITE_INTERNAL_CORE_IMPL_NATIVEBLUETOOTHPEER_H + +#pragma once + +#include + +#ifdef __cplusplus +extern "C++" { +#endif + +/* + * Class: com_couchbase_lite_internal_core_impl_NativeBluetoothPeer + * Method: getId + * Signature: (J)Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL +Java_com_couchbase_lite_internal_core_impl_NativeBluetoothPeer_getId( + JNIEnv *, jclass, jlong); + +/* + * Class: com_couchbase_lite_internal_core_impl_NativeBluetoothPeer + * Method: isOnline + * Signature: (J)Z + */ +JNIEXPORT jboolean JNICALL +Java_com_couchbase_lite_internal_core_impl_NativeBluetoothPeer_isOnline( + JNIEnv *, jclass, jlong); + +/* + * Class: com_couchbase_lite_internal_core_impl_NativeBluetoothPeer + * Method: getAllMetadata + * Signature: (J)Ljava/util/Map; + */ +JNIEXPORT jobject JNICALL +Java_com_couchbase_lite_internal_core_impl_NativeBluetoothPeer_getAllMetadata( + JNIEnv *, jclass, jlong); + + +/* + * Class: com_couchbase_lite_internal_core_impl_NativeBluetoothPeer + * Method: setMetadata + * Signature: (JLjava/util/Map;)V + */ +JNIEXPORT void JNICALL +Java_com_couchbase_lite_internal_core_impl_NativeBluetoothPeer_setMetadata( + JNIEnv *, jclass, jlong, jobject); + +/* + * Class: com_couchbase_lite_internal_core_impl_NativeBluetoothPeer + * Method: monitorMetadata + * Signature: (JZ)V + */ +JNIEXPORT void JNICALL +Java_com_couchbase_lite_internal_core_impl_NativeBluetoothPeer_monitorMetadata( + JNIEnv *, jclass, jlong, jboolean); + +JNIEXPORT void JNICALL +Java_com_couchbase_lite_internal_core_impl_NativeBluetoothPeer_resolvedURL( + JNIEnv *env, jclass clazz, jlong peerPtr, jstring jurl); + +#ifdef __cplusplus +} +#endif + +#endif //COUCHBASE_LITE_JAVA_EE_ROOT_COM_COUCHBASE_LITE_INTERNAL_CORE_IMPL_NATIVEBLUETOOTHPEER_H diff --git a/common/main/cpp/com_couchbase_lite_internal_core_impl_NativeC4PeerDiscoveryProvider.h b/common/main/cpp/com_couchbase_lite_internal_core_impl_NativeC4PeerDiscoveryProvider.h new file mode 100644 index 000000000..257bb8e2c --- /dev/null +++ b/common/main/cpp/com_couchbase_lite_internal_core_impl_NativeC4PeerDiscoveryProvider.h @@ -0,0 +1,61 @@ +#include + +#ifndef COUCHBASE_LITE_JAVA_EE_ROOT_COM_COUCHBASE_LITE_INTERNAL_CORE_IMPL_NATIVEC4PEERDISCOVERYPROVIDER_H +#define COUCHBASE_LITE_JAVA_EE_ROOT_COM_COUCHBASE_LITE_INTERNAL_CORE_IMPL_NATIVEC4PEERDISCOVERYPROVIDER_H + +#ifdef __cplusplus +extern "C++" { +#endif + +JNIEXPORT jlong JNICALL +Java_com_couchbase_lite_internal_core_impl_NativeC4PeerDiscoveryProvider_create( + JNIEnv *, jclass, jlong, jstring); + + +JNIEXPORT void JNICALL +Java_com_couchbase_lite_internal_core_impl_NativeC4PeerDiscoveryProvider_free( + JNIEnv *, jclass, jlong); + +JNIEXPORT void JNICALL +Java_com_couchbase_lite_internal_core_impl_NativeC4PeerDiscoveryProvider_peerDiscovered( + JNIEnv *env, jclass thiz, jlong providerPtr, jstring peerId, jobject metadata); + +JNIEXPORT void JNICALL +Java_com_couchbase_lite_internal_core_impl_NativeC4PeerDiscoveryProvider_peerLost( + JNIEnv *env, jclass thiz, jlong providerPtr, jstring peerId); + + +JNIEXPORT void JNICALL +Java_com_couchbase_lite_internal_core_impl_NativeC4PeerDiscoveryProvider_onIncomingConnection( + JNIEnv *env, jclass thiz, jlong providerPtr, jbyteArray peerId, jlong socketPtr); + +JNIEXPORT void JNICALL +Java_com_couchbase_lite_internal_core_impl_NativeC4PeerDiscoveryProvider_addPeer( + JNIEnv *env, jclass thiz, jlong providerPtr, jstring peerId); + +JNIEXPORT void JNICALL +Java_com_couchbase_lite_internal_core_impl_NativeC4PeerDiscoveryProvider_removePeer( + JNIEnv *env, jclass thiz, jlong providerPtr, jstring peerId); + +JNIEXPORT jobject JNICALL +Java_com_couchbase_lite_internal_core_impl_NativeC4PeerDiscoveryProvider_peerWithID( + JNIEnv *env, jclass thiz, jlong providerPtr, jstring peerId); + +JNIEXPORT void JNICALL +Java_com_couchbase_lite_internal_core_impl_NativeC4PeerDiscoveryProvider_statusChanged( + JNIEnv *env, jclass thiz, jlong providerPtr, jint mode, jboolean online, +jint errorDomain, jint errorCode); + +JNIEXPORT jlongArray JNICALL +Java_com_couchbase_lite_internal_core_impl_NativeC4PeerDiscoveryProvider_peersWithProvider( + JNIEnv *env, jclass thiz, jlong providerPtr); + +JNIEXPORT jstring JNICALL +Java_com_couchbase_lite_internal_core_impl_NativeC4PeerDiscoveryProvider_serviceUuidFromPeerGroup( + JNIEnv* env, jclass, jstring peerGroup); + +#ifdef __cplusplus +} +#endif + +#endif //COUCHBASE_LITE_JAVA_EE_ROOT_COM_COUCHBASE_LITE_INTERNAL_CORE_IMPL_NATIVEC4PEERDISCOVERYPROVIDER_H \ No newline at end of file diff --git a/common/main/cpp/native_bluetoothpeer.cc b/common/main/cpp/native_bluetoothpeer.cc new file mode 100644 index 000000000..8c37cab1d --- /dev/null +++ b/common/main/cpp/native_bluetoothpeer.cc @@ -0,0 +1,90 @@ +#include "com_couchbase_lite_internal_core_impl_NativeBluetoothPeer.h" +#include "native_glue.hh" +#include "c4PeerDiscovery.hh" +#include "c4Error.h" +#include "fleece/FLExpert.h" +#include "MetadataHelper.h" + +using namespace litecore; +using namespace litecore::jni; + +// Helper to convert C4Peer* from jlong +static C4Peer* getPeer(jlong peerPtr) { + return reinterpret_cast(peerPtr); +} + +class BluetoothPeer : public C4Peer { + using C4Peer::C4Peer; + +public: + void resolvingUrl(std::string s, C4Error err) { + resolvedURL(s, err); + } +}; + + +extern "C++" { + JNIEXPORT jstring JNICALL + Java_com_couchbase_lite_internal_core_impl_NativeBluetoothPeer_getId( + JNIEnv *env, jclass clazz, jlong peerPtr) { + + C4Peer* peer = getPeer(peerPtr); + if (!peer) return nullptr; + + return UTF8ToJstring(env, peer->id.c_str()); + } + + JNIEXPORT jboolean JNICALL + Java_com_couchbase_lite_internal_core_impl_NativeBluetoothPeer_isOnline( + JNIEnv *env, jclass clazz, jlong peerPtr) { + + C4Peer* peer = getPeer(peerPtr); + if (!peer) return JNI_FALSE; + + return peer->online() ? JNI_TRUE : JNI_FALSE; + } + + + JNIEXPORT jobject JNICALL + Java_com_couchbase_lite_internal_core_impl_NativeBluetoothPeer_getAllMetadata( + JNIEnv *env, jclass clazz, jlong peerPtr) { + + C4Peer* peer = getPeer(peerPtr); + if (!peer) return nullptr; + + C4Peer::Metadata metadata = peer->getAllMetadata(); + return metadataToJavaMap(env, metadata); + } + + JNIEXPORT void JNICALL + Java_com_couchbase_lite_internal_core_impl_NativeBluetoothPeer_setMetadata( + JNIEnv *env, jclass clazz, jlong peerPtr, jobject metadata) { + + C4Peer* peer = getPeer(peerPtr); + if (!peer) return; + + C4Peer::Metadata nativeMetadata = javaMapToMetadata(env, metadata); + peer->setMetadata(nativeMetadata); + } + + JNIEXPORT void JNICALL + Java_com_couchbase_lite_internal_core_impl_NativeBluetoothPeer_monitorMetadata( + JNIEnv *env, jclass clazz, jlong peerPtr, jboolean enable) { + + C4Peer *peer = getPeer(peerPtr); + if (!peer) return; + + peer->monitorMetadata(enable != JNI_FALSE); + } + + JNIEXPORT void JNICALL + Java_com_couchbase_lite_internal_core_impl_NativeBluetoothPeer_resolvedURL( + JNIEnv *env, jclass clazz, jlong peerPtr, jstring jurl) { + std::string url = JstringToUTF8(env, jurl); + + BluetoothPeer *peer = static_cast(getPeer(peerPtr)); + if (!peer) return; + + peer->resolvingUrl(url, kC4NoError); + } +} \ No newline at end of file diff --git a/common/main/cpp/native_c4multipeerreplicator.cc b/common/main/cpp/native_c4multipeerreplicator.cc index ab64afd19..3d358676f 100644 --- a/common/main/cpp/native_c4multipeerreplicator.cc +++ b/common/main/cpp/native_c4multipeerreplicator.cc @@ -412,8 +412,9 @@ namespace litecore::jni { &statusChangedCallback, &authenticateCallback, &peerDiscoveredCallback, - &replicatorStatusChangedCallback, - &documentEndedCallback, + reinterpret_cast(&replicatorStatusChangedCallback), + nullptr, + reinterpret_cast(documentEndedCallback), nullptr, nullptr, nullptr, @@ -525,9 +526,7 @@ JNICALL Java_com_couchbase_lite_internal_core_impl_NativeC4MultipeerReplicator_c params.peerGroupID = groupId; // Protocols: - C4String protocols[] = {kPeerSyncProtocol_DNS_SD}; - params.protocols = protocols; - params.protocolsCount = sizeof(protocols) / sizeof(protocols[0]); + params.protocols = kPeerSyncProtocol_BluetoothLE; // Identity: bool failed; @@ -688,7 +687,7 @@ JNICALL Java_com_couchbase_lite_internal_core_impl_NativeC4MultipeerReplicator_g m_C4MultipeerReplicator_createPeerInfo, jpeerId, certChain, - info->online ? JNI_TRUE : JNI_FALSE, + JNI_TRUE, neighborIds, replStatus); diff --git a/common/main/cpp/native_c4peerdiscoveryprovider.cc b/common/main/cpp/native_c4peerdiscoveryprovider.cc new file mode 100644 index 000000000..d801b980d --- /dev/null +++ b/common/main/cpp/native_c4peerdiscoveryprovider.cc @@ -0,0 +1,586 @@ +#if defined(COUCHBASE_ENTERPRISE) && defined(__ANDROID__) + +#include "c4PeerDiscovery.hh" +#include "c4PeerSyncTypes.h" +#include "native_glue.hh" +#include "socket_factory.h" +#include "MetadataHelper.h" +#include "com_couchbase_lite_internal_core_impl_NativeC4PeerDiscoveryProvider.h" + +using namespace litecore; +using namespace litecore::jni; +using namespace litecore::p2p; + +namespace litecore::jni { + + + // C4PeerDiscoveryProvider callbacks + static jclass cls_C4PeerDiscoveryProvider; + + static jmethodID m_C4PeerDiscoveryProvider_startBrowsing; + static jmethodID m_C4PeerDiscoveryProvider_stopBrowsing; + static jmethodID m_C4PeerDiscoveryProvider_startPublishing; + static jmethodID m_C4PeerDiscoveryProvider_stopPublishing; + static jmethodID m_C4PeerDiscoveryProvider_resolveURL; + static jmethodID m_C4PeerDiscoveryProvider_updateMetadata; + static jmethodID m_C4PeerDiscoveryProvider_startMetadataMonitoring; + static jmethodID m_C4PeerDiscoveryProvider_stopMetadataMonitoring; + static jmethodID m_C4PeerDiscoveryProvider_initBleProvider; + + bool initC4PeerDiscoveryProvider(JNIEnv *env) { + jclass localClass = env->FindClass("com/couchbase/lite/internal/core/C4PeerDiscoveryProvider"); + if (localClass == nullptr) return false; + + cls_C4PeerDiscoveryProvider = reinterpret_cast(env->NewGlobalRef(localClass)); + if (cls_C4PeerDiscoveryProvider == nullptr) return false; + + m_C4PeerDiscoveryProvider_startBrowsing = env->GetStaticMethodID( + cls_C4PeerDiscoveryProvider, + "startBrowsing", + "(J)V"); + + m_C4PeerDiscoveryProvider_stopBrowsing = env->GetStaticMethodID( + cls_C4PeerDiscoveryProvider, + "stopBrowsing", + "(J)V"); + + m_C4PeerDiscoveryProvider_startPublishing = env->GetStaticMethodID( + cls_C4PeerDiscoveryProvider, + "startPublishing", + "(JLjava/lang/String;ILjava/util/Map;)V"); + + m_C4PeerDiscoveryProvider_stopPublishing = env->GetStaticMethodID( + cls_C4PeerDiscoveryProvider, + "stopPublishing", + "(J)V"); + + m_C4PeerDiscoveryProvider_resolveURL = env->GetStaticMethodID( + cls_C4PeerDiscoveryProvider, + "resolveURL", + "(JJ)V"); + + m_C4PeerDiscoveryProvider_updateMetadata = env->GetStaticMethodID( + cls_C4PeerDiscoveryProvider, + "updateMetadata", + "(JLjava/util/Map;)V"); + + m_C4PeerDiscoveryProvider_startMetadataMonitoring = env->GetStaticMethodID( + cls_C4PeerDiscoveryProvider, + "startMetadataMonitoring", + "(JJ)V"); + + m_C4PeerDiscoveryProvider_stopMetadataMonitoring = env->GetStaticMethodID( + cls_C4PeerDiscoveryProvider, + "stopMetadataMonitoring", + "(JJ)V"); + + m_C4PeerDiscoveryProvider_initBleProvider = env->GetStaticMethodID( + cls_C4PeerDiscoveryProvider, + "initBleProvider", + "(JLjava/lang/String;)J" + ); + + return (m_C4PeerDiscoveryProvider_startBrowsing != nullptr) + && (m_C4PeerDiscoveryProvider_stopBrowsing != nullptr) + && (m_C4PeerDiscoveryProvider_startPublishing != nullptr) + && (m_C4PeerDiscoveryProvider_stopPublishing != nullptr) + && (m_C4PeerDiscoveryProvider_resolveURL != nullptr) + && (m_C4PeerDiscoveryProvider_updateMetadata != nullptr) + && (m_C4PeerDiscoveryProvider_initBleProvider != nullptr); + } + + + + class C4BLEProvider : public C4PeerDiscoveryProvider { + public: + C4BLEProvider(C4PeerDiscovery& discovery, std::string_view peerGroupID) + : C4PeerDiscoveryProvider(discovery, kPeerSyncProtocol_BluetoothLE, peerGroupID) { + std::string pg(peerGroupID); + + JNIEnv* env = nullptr; + jint envState = attachJVM(&env, "initBleProvider"); + if ((envState != JNI_OK) && (envState != JNI_EDETACHED)) return; + + jstring jPeerGroup = UTF8ToJstring(env, pg.data(), pg.size()); + + jlong providerPtr = reinterpret_cast(this); + jlong token = env->CallStaticLongMethod( + cls_C4PeerDiscoveryProvider, + m_C4PeerDiscoveryProvider_initBleProvider, + providerPtr, + jPeerGroup); + + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + token = 0; + } + + _contextToken = token; + + if (envState == JNI_EDETACHED) detachJVM("initBleProvider"); + if (jPeerGroup) env->DeleteLocalRef(jPeerGroup); + } + + virtual void startBrowsing() override { + JNIEnv *env = nullptr; + jint envState = attachJVM(&env, "startBrowsing"); + if ((envState != JNI_OK) && (envState != JNI_EDETACHED)) + return; + + // Call Java BLE service to start scanning + env->CallStaticVoidMethod( + cls_C4PeerDiscoveryProvider, + m_C4PeerDiscoveryProvider_startBrowsing, + _contextToken); + + + if (envState == JNI_EDETACHED) { + detachJVM("startBrowsing"); + } + } + + virtual void stopBrowsing() { + JNIEnv *env = nullptr; + jint envState = attachJVM(&env, "stopBrowsing"); + if ((envState != JNI_OK) && (envState != JNI_EDETACHED)) + return; + + env->CallStaticVoidMethod( + cls_C4PeerDiscoveryProvider, + m_C4PeerDiscoveryProvider_stopBrowsing, + _contextToken); + + if (envState == JNI_EDETACHED) { + detachJVM("stopBrowsing"); + } + } + + virtual void startPublishing(std::string_view displayName, uint16_t port, + C4Peer::Metadata const& metadata) override { + JNIEnv *env = nullptr; + jint envState = attachJVM(&env, "startPublishing"); + if ((envState != JNI_OK) && (envState != JNI_EDETACHED)) + return; + + jstring jDisplayName = UTF8ToJstring(env, displayName.data(), displayName.size()); + jobject jMetadata = metadataToJavaMap(env, metadata); + + env->CallStaticVoidMethod( + cls_C4PeerDiscoveryProvider, + m_C4PeerDiscoveryProvider_startPublishing, + _contextToken, + jDisplayName, + (jint)port, + jMetadata); + if (envState == JNI_EDETACHED) { + detachJVM("startPublishing"); + } else { + if (jDisplayName != nullptr) env->DeleteLocalRef(jDisplayName); + if (jMetadata != nullptr) env->DeleteLocalRef(jMetadata); + } + } + + virtual void stopPublishing() { + JNIEnv *env = nullptr; + jint envState = attachJVM(&env, "stopPublishing"); + if ((envState != JNI_OK) && (envState != JNI_EDETACHED)) + return; + + env->CallStaticVoidMethod( + cls_C4PeerDiscoveryProvider, + m_C4PeerDiscoveryProvider_stopPublishing, + _contextToken); + + if (envState == JNI_EDETACHED) { + detachJVM("stopPublishing"); + } + } + + + + virtual void monitorMetadata(C4Peer* peer, bool start) override { + JNIEnv *env = nullptr; + jint envState = attachJVM(&env, "monitorMetadata"); + if ((envState != JNI_OK) && (envState != JNI_EDETACHED)) + return; + + jbyteArray peerId = toJByteArray(env, (const uint8_t*)peer->id.data(), peer->id.size()); + jlong peerPtr = reinterpret_cast(peer); + + + if (start) { + // Start monitoring metadata characteristic + env->CallStaticVoidMethod( + cls_C4PeerDiscoveryProvider, + m_C4PeerDiscoveryProvider_startMetadataMonitoring, + _contextToken, + peerPtr); + } else { + // Stop monitoring metadata characteristic + env->CallStaticVoidMethod( + cls_C4PeerDiscoveryProvider, + m_C4PeerDiscoveryProvider_stopMetadataMonitoring, + _contextToken, + peerPtr); + } + + if (envState == JNI_EDETACHED) { + detachJVM("monitorMetadata"); + } else { + if (peerId != nullptr) env->DeleteLocalRef(peerId); + } + } + + virtual void resolveURL(C4Peer* peer) override { + JNIEnv *env = nullptr; + jint envState = attachJVM(&env, "resolveURL"); + if ((envState != JNI_OK) && (envState != JNI_EDETACHED)) + return; + + jlong peerPtr = reinterpret_cast(peer); + + env->CallStaticVoidMethod( + cls_C4PeerDiscoveryProvider, + m_C4PeerDiscoveryProvider_resolveURL, + _contextToken, + peerPtr); + + if (envState == JNI_EDETACHED) { + detachJVM("resolveURL"); + } + } + + virtual void updateMetadata(C4Peer::Metadata const& metadata) override { + JNIEnv *env = nullptr; + jint envState = attachJVM(&env, "updateMetadata"); + if ((envState != JNI_OK) && (envState != JNI_EDETACHED)) + return; + + jobject jMetadata = metadataToJavaMap(env, metadata); + + env->CallStaticVoidMethod( + cls_C4PeerDiscoveryProvider, + m_C4PeerDiscoveryProvider_updateMetadata, + _contextToken, + jMetadata); + + if (envState == JNI_EDETACHED) { + detachJVM("updateMetadata"); + } else { + if (jMetadata != nullptr) env->DeleteLocalRef(jMetadata); + } + } + + virtual void shutdown(std::function onComplete) override { + stopBrowsing(); + stopPublishing(); + onComplete(); + } + + virtual void stop(Mode mode) override { + if (mode == C4PeerDiscovery::Mode::browse) { + stopBrowsing(); + } + else if (mode == C4PeerDiscovery::Mode::publish) { + stopPublishing(); + } + } + + void addDiscoveredPeer(C4Peer* peer, bool moreComing = false) { + addPeer(peer, moreComing); + } + + void removeDiscoveredPeer(std::string id, bool moreComing = false) { + removePeer(id, moreComing); + } + + void statusStateChange(Mode m, Status s) { + statusChanged(m, s); + } + + bool notifyIncomConnection(C4Peer* peer, C4Socket* s) { + return notifyIncomingConnection(peer, s); + } + + void setContextToken(jlong token) { + _contextToken = token; + } + + private: + jlong _contextToken{}; + }; + + // Factory function for creating BLE provider + static std::unique_ptr + createBLEProvider(C4PeerDiscovery& discovery, + std::string_view peerGroupID) { + C4BLEProvider* provider = new C4BLEProvider(discovery, peerGroupID); + return C4PeerDiscovery::ProviderRef(provider, [](C4PeerDiscoveryProvider* ptr) { + delete ptr; + }); + } + + // Register the BLE provider + static bool registerBleProvider() { + C4PeerDiscovery::registerProvider(kPeerSyncProtocol_BluetoothLE, &createBLEProvider); + return true; + } + + static bool bleProviderRegistered = registerBleProvider(); +} + +namespace litecore::p2p { + + // BleP2pConstants + static jclass cls_BleP2pConstants; + + static jclass jUuidClass; + static jmethodID uuidFromString; + static jfieldID PORT_CHAR_FIELD, META_CHAR_FIELD, PEER_GROUP_NS_FIELD; + + + void setUuidConstant(JNIEnv* env, jclass cls, const char* uuidStr) { + jstring str = env->NewStringUTF(uuidStr); + jobject uuidObj = env->CallStaticObjectMethod(jUuidClass, uuidFromString, str); + + if (strcmp(uuidStr, litecore::p2p::btle::kPortCharacteristicID) == 0) + env->SetStaticObjectField(cls, PORT_CHAR_FIELD, uuidObj); + else if (strcmp(uuidStr, litecore::p2p::btle::kMetadataCharacteristicID) == 0) + env->SetStaticObjectField(cls, META_CHAR_FIELD, uuidObj); + else if (strcmp(uuidStr, litecore::p2p::btle::kPeerGroupUUIDNamespace) == 0) + env->SetStaticObjectField(cls, PEER_GROUP_NS_FIELD, uuidObj); + } + + bool initBleConstants(JNIEnv* env) { + jUuidClass = env->FindClass("java/util/UUID"); + uuidFromString = env->GetStaticMethodID(jUuidClass, "fromString", + "(Ljava/lang/String;)Ljava/util/UUID;"); + + PORT_CHAR_FIELD = env->GetStaticFieldID(cls_BleP2pConstants, + "PORT_CHARACTERISTIC_ID", "Ljava/util/UUID;"); + META_CHAR_FIELD = env->GetStaticFieldID(cls_BleP2pConstants, + "METADATA_CHARACTERISTIC_ID", "Ljava/util/UUID;"); + + setUuidConstant(env, cls_BleP2pConstants, litecore::p2p::btle::kPortCharacteristicID); + setUuidConstant(env, cls_BleP2pConstants, litecore::p2p::btle::kMetadataCharacteristicID); + setUuidConstant(env, cls_BleP2pConstants, litecore::p2p::btle::kPeerGroupUUIDNamespace); + + return true; + } +} + + +#ifdef __cplusplus +extern "C++" { +#endif + +//------------------------------------------------------------------------- +// com.couchbase.lite.internal.core.impl.NativeC4MultipeerReplicator +//------------------------------------------------------------------------- + + +JNIEXPORT jstring JNICALL +Java_com_couchbase_lite_internal_core_impl_NativeC4PeerDiscoveryProvider_serviceUuidFromPeerGroup( + JNIEnv* env, jclass, jstring peerGroup) { + std::string pg = JstringToUTF8(env, peerGroup); + auto uuid = litecore::p2p::btle::ServiceUUIDFromPeerGroup(pg); + C4Slice s = {&uuid, sizeof(uuid)}; + return toJString(env, s); +} + +JNIEXPORT void JNICALL +Java_com_couchbase_lite_internal_core_impl_NativeC4PeerDiscoveryProvider_addPeer( + JNIEnv *env, jclass thiz, jlong providerPtr, jstring peerId) { + // Get the provider instance from the pointer + auto* provider = (C4BLEProvider*)providerPtr; + if (!provider) return; + + const char* peerIdStr = env->GetStringUTFChars(peerId, nullptr); + if (!peerIdStr) return; + std::string id(peerIdStr); + env->ReleaseStringUTFChars(peerId, peerIdStr); + + + auto* peer = new C4Peer(provider, id); + provider->addDiscoveredPeer(peer); +} + +JNIEXPORT void JNICALL +Java_com_couchbase_lite_internal_core_impl_NativeC4PeerDiscoveryProvider_removePeer( + JNIEnv *env, jclass thiz, jlong providerPtr, jstring peerId) { + // Get the provider instance from the pointer + auto* provider = (C4BLEProvider*)providerPtr; + if (!provider) return; + + // Convert peerId from Java string to C++ string + const char* peerIdStr = env->GetStringUTFChars(peerId, nullptr); + if (!peerIdStr) return; + std::string id(peerIdStr); + env->ReleaseStringUTFChars(peerId, peerIdStr); + + provider->removeDiscoveredPeer(id); +} + +JNIEXPORT jobject JNICALL +Java_com_couchbase_lite_internal_core_impl_NativeC4PeerDiscoveryProvider_peerWithID( + JNIEnv *env, jclass thiz, jlong providerPtr, jstring peerId) { + + // Get provider instance + auto* provider = (C4BLEProvider*)providerPtr; + if (!provider) return nullptr; + + // Convert Java String → C++ string + std::string peerIdStr; + if (peerId) { + const char* peerIdChars = env->GetStringUTFChars(peerId, nullptr); + if (peerIdChars) { + peerIdStr = std::string(peerIdChars); + env->ReleaseStringUTFChars(peerId, peerIdChars); + } + } + + // === CALL LIBRARY API === + C4PeerDiscovery& discovery = provider->discovery(); + fleece::Retained peer = discovery.peerWithID(peerIdStr); + + // Convert C4Peer* → Java C4Peer object + if (peer) { + // Get the native peer pointer + jlong peerPtr = reinterpret_cast(peer.get()); + + // Create Java BluetoothPeer object + jclass bluetoothPeerClass = env->FindClass("com/couchbase/lite/internal/core/BluetoothPeer"); + if (bluetoothPeerClass == nullptr) return nullptr; + + jmethodID constructor = env->GetMethodID(bluetoothPeerClass, "", "(J)V"); + if (constructor == nullptr) return nullptr; + + jobject javaPeer = env->NewObject(bluetoothPeerClass, constructor, peerPtr); + + return env->NewGlobalRef(javaPeer); + } + return nullptr; +} + +JNIEXPORT void JNICALL +Java_com_couchbase_lite_internal_core_impl_NativeC4PeerDiscoveryProvider_peerDiscovered( + JNIEnv *env, jclass thiz, jlong providerPtr, jstring peerId, jobject metadata) { + + auto* provider = (C4BLEProvider*)providerPtr; + if (!provider) return; + + const char* peerIdStr = env->GetStringUTFChars(peerId, nullptr); + if (!peerIdStr) return; + std::string id(peerIdStr); + env->ReleaseStringUTFChars(peerId, peerIdStr); + + // Use the utility function + C4Peer::Metadata peerMetadata = javaMapToMetadata(env, metadata); + + auto* peer = new C4Peer(provider, id, peerMetadata); + provider->addDiscoveredPeer(peer); +} + +JNIEXPORT void JNICALL +Java_com_couchbase_lite_internal_core_impl_NativeC4PeerDiscoveryProvider_peerLost( + JNIEnv *env, jclass thiz, jlong providerPtr, jstring peerId) { + + auto provider = (C4BLEProvider*)providerPtr; + if (!provider) return; + + // Convert peerId from Java string to C++ string + const char* peerIdStr = env->GetStringUTFChars(peerId, nullptr); + if (!peerIdStr) return; + std::string id(peerIdStr); + env->ReleaseStringUTFChars(peerId, peerIdStr); + + // Remove the peer from the discovery system + // This will trigger notifications to observers about the peer going offline + provider->removeDiscoveredPeer(id); +} + +JNIEXPORT void JNICALL +Java_com_couchbase_lite_internal_core_impl_NativeC4PeerDiscoveryProvider_onIncomingConnection( + JNIEnv *env, jclass thiz, jlong providerPtr, jbyteArray peerId, jlong socketPtr) { + + // Get the provider instance from the pointer + auto provider = (C4BLEProvider*)providerPtr; + if (!provider) return; + + // Convert peerId from Java byte array to C4PeerID + C4PeerID peerIdObj = {}; + if (peerId) { + jbyte* bytes = env->GetByteArrayElements(peerId, nullptr); + if (bytes) { + memcpy(peerIdObj.bytes, bytes, 32); + env->ReleaseByteArrayElements(peerId, bytes, JNI_ABORT); + } + } + + // Get the C4Socket from the pointer + C4Socket* socket = (C4Socket*)socketPtr; + if (!socket) return; + + // Find the peer in the discovery system + auto peer = provider->discovery().peerWithID("peerIdObj"); + if (peer) { + // Notify about the incoming connection + bool accepted = provider->notifyIncomConnection(peer.get(), socket); + + // If not accepted, close the socket + if (!accepted) { + // Close the socket - implementation depends on your socket type + // For BLE, you might close the GATT connection + } + } +} + +JNIEXPORT void JNICALL +Java_com_couchbase_lite_internal_core_impl_NativeC4PeerDiscoveryProvider_statusChanged( + JNIEnv *env, jclass thiz, jlong providerPtr, jint mode, jboolean online, + jint errorDomain, jint errorCode) { + + // Get provider from binding + auto* provider = (C4BLEProvider*)providerPtr; + if (!provider) return; + + // Convert parameters + C4PeerDiscoveryProvider::Mode nativeMode = + (mode == 1) ? C4PeerDiscoveryProvider::Mode::publish + : C4PeerDiscoveryProvider::Mode::browse; + + C4PeerDiscoveryProvider::Status status{}; + status.online = (online != JNI_FALSE); + status.error.domain = (C4ErrorDomain) errorDomain; + status.error.code = errorCode; + + provider->statusStateChange(nativeMode, status); +} + +JNIEXPORT jlongArray JNICALL +Java_com_couchbase_lite_internal_core_impl_NativeC4PeerDiscoveryProvider_peersWithProvider( + JNIEnv *env, jclass thiz, jlong providerPtr) { + + // Get provider from binding + auto* provider = (C4BLEProvider*)providerPtr; + if (!provider) return nullptr; + std::vector> peers = provider->discovery().peersWithProvider(provider); + jlongArray arr = env->NewLongArray((jsize) peers.size()); + if (!arr) return nullptr; + + std::vector handles; + handles.reserve(peers.size()); + + for (auto& p : peers) { + handles.push_back(reinterpret_cast(p.get())); + } + + env->SetLongArrayRegion(arr, 0, (jsize)handles.size(), handles.data()); + return arr; +} + +#ifdef __cplusplus +} +#endif +#endif // COUCHBASE_ENTERPRISE && __ANDROID__ \ No newline at end of file diff --git a/common/main/cpp/native_glue.cc b/common/main/cpp/native_glue.cc index 473ae8675..d363be73a 100644 --- a/common/main/cpp/native_glue.cc +++ b/common/main/cpp/native_glue.cc @@ -30,6 +30,7 @@ using namespace std; using namespace litecore; using namespace litecore::jni; +using namespace litecore::p2p; // Global reference to the Java VM static JavaVM *gJVM; @@ -161,6 +162,8 @@ JNI_OnLoad(JavaVM *jvm, void *ignore) { && initC4Prediction(env) #ifdef __ANDROID__ && initC4MultipeerReplicator(env) + && initC4PeerDiscoveryProvider(env) + && initBleConstants(env) #endif #endif && initC4Socket(env)) { diff --git a/common/main/cpp/native_glue.hh b/common/main/cpp/native_glue.hh index 62b5f54b9..20b335b7e 100644 --- a/common/main/cpp/native_glue.hh +++ b/common/main/cpp/native_glue.hh @@ -52,6 +52,7 @@ namespace litecore::jni { #ifdef __ANDROID__ bool initC4MultipeerReplicator(JNIEnv *); // Implemented in native_c4multipeerreplicator.cc + bool initC4PeerDiscoveryProvider(JNIEnv *);// Implemented in native_c4peerdiscoveryprovider.cc #endif #endif @@ -165,4 +166,12 @@ namespace litecore::jni { #endif // COUCHBASE_ENTERPRISE } // namespace litecore::jni +namespace litecore::p2p { +#ifdef COUCHBASE_ENTERPRISE +#ifdef __ANDROID__ + bool initBleConstants(JNIEnv *); +#endif +#endif +} + #endif /* native_glue_hpp */ diff --git a/common/main/java/com/couchbase/lite/internal/core/C4Replicator.java b/common/main/java/com/couchbase/lite/internal/core/C4Replicator.java index 3993e8cc4..760111b66 100644 --- a/common/main/java/com/couchbase/lite/internal/core/C4Replicator.java +++ b/common/main/java/com/couchbase/lite/internal/core/C4Replicator.java @@ -68,6 +68,7 @@ public abstract class C4Replicator extends C4Peer { /// ///// Most of these are defined in c4Replicator.h and must agree with those definitions. + public static final String BT_URL_SCHEME = "l2cap"; public static final String WEBSOCKET_SCHEME = "ws"; public static final String WEBSOCKET_SECURE_CONNECTION_SCHEME = "wss"; public static final String MESSAGE_SCHEME = "x-msg-endpt"; From 1aaf27b29b713a0885e7fe7cbaa8e14d92f7383f Mon Sep 17 00:00:00 2001 From: Aniket Singh Date: Tue, 17 Feb 2026 23:19:50 +0530 Subject: [PATCH 02/11] Update cmakelist and bluetoothpeer enterprise check --- common/CMakeLists.txt | 1 + common/main/cpp/native_bluetoothpeer.cc | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 2e727ce3d..81036c645 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -69,6 +69,7 @@ add_library( LiteCoreJNI SHARED ${JNI_SRC}/native_c4multipeerreplicator.cc ${JNI_SRC}/native_c4peerdiscoveryprovider.cc ${JNI_SRC}/native_bluetoothpeer.cc + ${JNI_SRC}/MetadataHelper.cc ${JNI_SRC}/native_c4observer.cc ${JNI_SRC}/native_c4prediction.cc ${JNI_SRC}/native_c4query.cc diff --git a/common/main/cpp/native_bluetoothpeer.cc b/common/main/cpp/native_bluetoothpeer.cc index 8c37cab1d..e27c12191 100644 --- a/common/main/cpp/native_bluetoothpeer.cc +++ b/common/main/cpp/native_bluetoothpeer.cc @@ -8,6 +8,8 @@ using namespace litecore; using namespace litecore::jni; +#if defined(COUCHBASE_ENTERPRISE) && defined(__ANDROID__) + // Helper to convert C4Peer* from jlong static C4Peer* getPeer(jlong peerPtr) { return reinterpret_cast(peerPtr); @@ -87,4 +89,5 @@ extern "C++" { peer->resolvingUrl(url, kC4NoError); } -} \ No newline at end of file +} +#endif \ No newline at end of file From b370c637366cac79b878141c594f8bb0f8e2da3a Mon Sep 17 00:00:00 2001 From: Aniket Singh Date: Wed, 18 Feb 2026 12:56:54 +0530 Subject: [PATCH 03/11] Updated as build failure due to wrong checks and static declaration --- common/main/cpp/MetadataHelper.cc | 7 ++++--- common/main/cpp/MetadataHelper.h | 5 ++++- common/main/cpp/native_bluetoothpeer.cc | 3 +-- common/main/cpp/native_c4peerdiscoveryprovider.cc | 3 --- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/common/main/cpp/MetadataHelper.cc b/common/main/cpp/MetadataHelper.cc index 2b21be76e..820640846 100644 --- a/common/main/cpp/MetadataHelper.cc +++ b/common/main/cpp/MetadataHelper.cc @@ -1,10 +1,11 @@ +#if defined(COUCHBASE_ENTERPRISE) && defined(__ANDROID__) #include #include "c4PeerDiscovery.hh" #include "native_glue.hh" namespace litecore::jni { // Helper to convert Java Map to Metadata - static C4Peer::Metadata javaMapToMetadata(JNIEnv* env, jobject map) { + C4Peer::Metadata javaMapToMetadata(JNIEnv* env, jobject map) { C4Peer::Metadata metadata; if (!map) return metadata; @@ -76,5 +77,5 @@ namespace litecore::jni { env->DeleteLocalRef(hashMapClass); return hashMap; } - -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/common/main/cpp/MetadataHelper.h b/common/main/cpp/MetadataHelper.h index 47dcbfe82..81e34051d 100644 --- a/common/main/cpp/MetadataHelper.h +++ b/common/main/cpp/MetadataHelper.h @@ -1,12 +1,15 @@ +#if defined(COUCHBASE_ENTERPRISE) && defined(__ANDROID__) #include + #include "c4PeerDiscovery.hh" #ifndef COUCHBASE_LITE_JAVA_EE_ROOT_METADATAHELPER_H #define COUCHBASE_LITE_JAVA_EE_ROOT_METADATAHELPER_H namespace litecore::jni { - static C4Peer::Metadata javaMapToMetadata(JNIEnv* env, jobject map); + C4Peer::Metadata javaMapToMetadata(JNIEnv* env, jobject map); jobject metadataToJavaMap(JNIEnv* env, const C4Peer::Metadata& metadata); } #endif //COUCHBASE_LITE_JAVA_EE_ROOT_METADATAHELPER_H +#endif \ No newline at end of file diff --git a/common/main/cpp/native_bluetoothpeer.cc b/common/main/cpp/native_bluetoothpeer.cc index e27c12191..9e6ba4ec8 100644 --- a/common/main/cpp/native_bluetoothpeer.cc +++ b/common/main/cpp/native_bluetoothpeer.cc @@ -1,3 +1,4 @@ +#if defined(COUCHBASE_ENTERPRISE) && defined(__ANDROID__) #include "com_couchbase_lite_internal_core_impl_NativeBluetoothPeer.h" #include "native_glue.hh" #include "c4PeerDiscovery.hh" @@ -8,8 +9,6 @@ using namespace litecore; using namespace litecore::jni; -#if defined(COUCHBASE_ENTERPRISE) && defined(__ANDROID__) - // Helper to convert C4Peer* from jlong static C4Peer* getPeer(jlong peerPtr) { return reinterpret_cast(peerPtr); diff --git a/common/main/cpp/native_c4peerdiscoveryprovider.cc b/common/main/cpp/native_c4peerdiscoveryprovider.cc index d801b980d..bf0e63332 100644 --- a/common/main/cpp/native_c4peerdiscoveryprovider.cc +++ b/common/main/cpp/native_c4peerdiscoveryprovider.cc @@ -12,9 +12,6 @@ using namespace litecore::jni; using namespace litecore::p2p; namespace litecore::jni { - - - // C4PeerDiscoveryProvider callbacks static jclass cls_C4PeerDiscoveryProvider; static jmethodID m_C4PeerDiscoveryProvider_startBrowsing; From 098dff37801bbef2916eb788d482de06b36d789c Mon Sep 17 00:00:00 2001 From: Aniket Singh Date: Mon, 23 Feb 2026 11:50:13 +0530 Subject: [PATCH 04/11] Updating multipeer replicator argument to take protocol --- common/main/cpp/native_c4multipeerreplicator.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/common/main/cpp/native_c4multipeerreplicator.cc b/common/main/cpp/native_c4multipeerreplicator.cc index 3d358676f..2051b1ac8 100644 --- a/common/main/cpp/native_c4multipeerreplicator.cc +++ b/common/main/cpp/native_c4multipeerreplicator.cc @@ -348,6 +348,7 @@ namespace litecore::jni { static void replicatorStatusChangedCallback( C4PeerSync *ignored, const C4PeerID *peerId, + C4PeerSyncProtocol protocol, bool outbound, const C4ReplicatorStatus *status, void *context) { @@ -377,6 +378,7 @@ namespace litecore::jni { static void documentEndedCallback( C4PeerSync *ignored, const C4PeerID *peerId, + C4PeerSyncProtocol protocol, bool pushing, size_t numDocs, const C4DocumentEnded *documentEnded[], @@ -412,9 +414,9 @@ namespace litecore::jni { &statusChangedCallback, &authenticateCallback, &peerDiscoveredCallback, - reinterpret_cast(&replicatorStatusChangedCallback), nullptr, - reinterpret_cast(documentEndedCallback), + &replicatorStatusChangedCallback, + &documentEndedCallback, nullptr, nullptr, nullptr, From d4215fdc248011fd2cd7e3a7c2d7aa339aa407a1 Mon Sep 17 00:00:00 2001 From: Aniket Singh Date: Fri, 27 Feb 2026 11:45:19 +0530 Subject: [PATCH 05/11] Added release for BT peer and removed onIncommingConnection --- ...e_internal_core_impl_NativeBluetoothPeer.h | 9 ++++ common/main/cpp/native_bluetoothpeer.cc | 7 +++ .../main/cpp/native_c4multipeerreplicator.cc | 4 +- .../cpp/native_c4peerdiscoveryprovider.cc | 54 +++---------------- 4 files changed, 25 insertions(+), 49 deletions(-) diff --git a/common/main/cpp/com_couchbase_lite_internal_core_impl_NativeBluetoothPeer.h b/common/main/cpp/com_couchbase_lite_internal_core_impl_NativeBluetoothPeer.h index 61daa6ced..6d19aabae 100644 --- a/common/main/cpp/com_couchbase_lite_internal_core_impl_NativeBluetoothPeer.h +++ b/common/main/cpp/com_couchbase_lite_internal_core_impl_NativeBluetoothPeer.h @@ -59,6 +59,15 @@ JNIEXPORT void JNICALL Java_com_couchbase_lite_internal_core_impl_NativeBluetoothPeer_resolvedURL( JNIEnv *env, jclass clazz, jlong peerPtr, jstring jurl); +/* + * Class: com_couchbase_lite_internal_core_impl_NativeBluetoothPeer + * Method: release + * Signature: (JZ)V + */ +JNIEXPORT void JNICALL +Java_com_couchbase_lite_internal_core_impl_NativeBluetoothPeer_release( + JNIEnv *env, jclass clazz, jlong peerPtr); + #ifdef __cplusplus } #endif diff --git a/common/main/cpp/native_bluetoothpeer.cc b/common/main/cpp/native_bluetoothpeer.cc index 9e6ba4ec8..ffe75bf8e 100644 --- a/common/main/cpp/native_bluetoothpeer.cc +++ b/common/main/cpp/native_bluetoothpeer.cc @@ -3,6 +3,7 @@ #include "native_glue.hh" #include "c4PeerDiscovery.hh" #include "c4Error.h" +#include "fleece/RefCounted.hh" #include "fleece/FLExpert.h" #include "MetadataHelper.h" @@ -88,5 +89,11 @@ extern "C++" { peer->resolvingUrl(url, kC4NoError); } + + JNIEXPORT void JNICALL + Java_com_couchbase_lite_internal_core_impl_NativeBluetoothPeer_release( + JNIEnv *env, jclass clazz, jlong peerPtr) { + fleece::release(reinterpret_cast(peerPtr)); + } } #endif \ No newline at end of file diff --git a/common/main/cpp/native_c4multipeerreplicator.cc b/common/main/cpp/native_c4multipeerreplicator.cc index 2051b1ac8..09ded0154 100644 --- a/common/main/cpp/native_c4multipeerreplicator.cc +++ b/common/main/cpp/native_c4multipeerreplicator.cc @@ -528,7 +528,7 @@ JNICALL Java_com_couchbase_lite_internal_core_impl_NativeC4MultipeerReplicator_c params.peerGroupID = groupId; // Protocols: - params.protocols = kPeerSyncProtocol_BluetoothLE; + params.protocols = kPeerSyncProtocol_DNS_SD | kPeerSyncProtocol_BluetoothLE; // Identity: bool failed; @@ -689,7 +689,7 @@ JNICALL Java_com_couchbase_lite_internal_core_impl_NativeC4MultipeerReplicator_g m_C4MultipeerReplicator_createPeerInfo, jpeerId, certChain, - JNI_TRUE, + info->onlineProtocols > 0 ? JNI_TRUE : JNI_FALSE, neighborIds, replStatus); diff --git a/common/main/cpp/native_c4peerdiscoveryprovider.cc b/common/main/cpp/native_c4peerdiscoveryprovider.cc index bf0e63332..bcafa1252 100644 --- a/common/main/cpp/native_c4peerdiscoveryprovider.cc +++ b/common/main/cpp/native_c4peerdiscoveryprovider.cc @@ -25,7 +25,7 @@ namespace litecore::jni { static jmethodID m_C4PeerDiscoveryProvider_initBleProvider; bool initC4PeerDiscoveryProvider(JNIEnv *env) { - jclass localClass = env->FindClass("com/couchbase/lite/internal/core/C4PeerDiscoveryProvider"); + jclass localClass = env->FindClass("com/couchbase/lite/internal/core/BluetoothProvider"); if (localClass == nullptr) return false; cls_C4PeerDiscoveryProvider = reinterpret_cast(env->NewGlobalRef(localClass)); @@ -130,8 +130,6 @@ namespace litecore::jni { cls_C4PeerDiscoveryProvider, m_C4PeerDiscoveryProvider_startBrowsing, _contextToken); - - if (envState == JNI_EDETACHED) { detachJVM("startBrowsing"); } @@ -194,8 +192,6 @@ namespace litecore::jni { } } - - virtual void monitorMetadata(C4Peer* peer, bool start) override { JNIEnv *env = nullptr; jint envState = attachJVM(&env, "monitorMetadata"); @@ -296,10 +292,6 @@ namespace litecore::jni { statusChanged(m, s); } - bool notifyIncomConnection(C4Peer* peer, C4Socket* s) { - return notifyIncomingConnection(peer, s); - } - void setContextToken(jlong token) { _contextToken = token; } @@ -350,6 +342,10 @@ namespace litecore::p2p { } bool initBleConstants(JNIEnv* env) { + jclass localClass = env->FindClass("com/couchbase/lite/internal/p2p/ble/BleP2pConstants"); + if (localClass == nullptr) return false; + + cls_BleP2pConstants = reinterpret_cast(env->NewGlobalRef(localClass)); jUuidClass = env->FindClass("java/util/UUID"); uuidFromString = env->GetStaticMethodID(jUuidClass, "fromString", "(Ljava/lang/String;)Ljava/util/UUID;"); @@ -437,14 +433,14 @@ Java_com_couchbase_lite_internal_core_impl_NativeC4PeerDiscoveryProvider_peerWit } } - // === CALL LIBRARY API === C4PeerDiscovery& discovery = provider->discovery(); fleece::Retained peer = discovery.peerWithID(peerIdStr); // Convert C4Peer* → Java C4Peer object if (peer) { // Get the native peer pointer - jlong peerPtr = reinterpret_cast(peer.get()); + C4Peer* rawPeer = std::move(peer).detach(); + jlong peerPtr = reinterpret_cast(rawPeer); // Create Java BluetoothPeer object jclass bluetoothPeerClass = env->FindClass("com/couchbase/lite/internal/core/BluetoothPeer"); @@ -497,42 +493,6 @@ Java_com_couchbase_lite_internal_core_impl_NativeC4PeerDiscoveryProvider_peerLos provider->removeDiscoveredPeer(id); } -JNIEXPORT void JNICALL -Java_com_couchbase_lite_internal_core_impl_NativeC4PeerDiscoveryProvider_onIncomingConnection( - JNIEnv *env, jclass thiz, jlong providerPtr, jbyteArray peerId, jlong socketPtr) { - - // Get the provider instance from the pointer - auto provider = (C4BLEProvider*)providerPtr; - if (!provider) return; - - // Convert peerId from Java byte array to C4PeerID - C4PeerID peerIdObj = {}; - if (peerId) { - jbyte* bytes = env->GetByteArrayElements(peerId, nullptr); - if (bytes) { - memcpy(peerIdObj.bytes, bytes, 32); - env->ReleaseByteArrayElements(peerId, bytes, JNI_ABORT); - } - } - - // Get the C4Socket from the pointer - C4Socket* socket = (C4Socket*)socketPtr; - if (!socket) return; - - // Find the peer in the discovery system - auto peer = provider->discovery().peerWithID("peerIdObj"); - if (peer) { - // Notify about the incoming connection - bool accepted = provider->notifyIncomConnection(peer.get(), socket); - - // If not accepted, close the socket - if (!accepted) { - // Close the socket - implementation depends on your socket type - // For BLE, you might close the GATT connection - } - } -} - JNIEXPORT void JNICALL Java_com_couchbase_lite_internal_core_impl_NativeC4PeerDiscoveryProvider_statusChanged( JNIEnv *env, jclass thiz, jlong providerPtr, jint mode, jboolean online, From 6cf991e6786942401f614111ce954561775224bb Mon Sep 17 00:00:00 2001 From: Aniket Singh Date: Mon, 2 Mar 2026 17:53:21 +0530 Subject: [PATCH 06/11] Added documentation for APIs --- common/main/cpp/P2P_Sync_Documentation.md | 132 ++++++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 common/main/cpp/P2P_Sync_Documentation.md diff --git a/common/main/cpp/P2P_Sync_Documentation.md b/common/main/cpp/P2P_Sync_Documentation.md new file mode 100644 index 000000000..06d4f5f2a --- /dev/null +++ b/common/main/cpp/P2P_Sync_Documentation.md @@ -0,0 +1,132 @@ + +# P2P Bluetooth LE Synchronization Documentation + +## 1. Overview + +This document provides a detailed explanation of the P2P synchronization feature using Bluetooth Low Energy (BLE). The feature allows Couchbase Lite enabled devices to discover each other and synchronize data directly without the need for a central server. + +## 2. Architecture + +The P2P synchronization feature is built on a hybrid architecture that leverages both Java and C++ code. + +* **Java Layer**: The Java layer is responsible for all platform-specific Bluetooth LE operations. This includes scanning for nearby devices, advertising the device's presence, and managing Bluetooth connections. By delegating these tasks to the Java layer, we ensure that the application can take advantage of the latest Android Bluetooth APIs and best practices. + +* **C++ Layer**: The C++ layer contains the core logic for peer discovery and data synchronization. It is responsible for managing the state of discovered peers, handling metadata exchange, and orchestrating the overall synchronization process. The C++ layer communicates with the Java layer through the Java Native Interface (JNI). + +This separation of concerns allows for a clean and maintainable codebase. The C++ layer remains platform-agnostic, while the Java layer handles the intricacies of the Android Bluetooth stack. + +## 3. C++ Class Descriptions + +### 3.1. `C4BLEProvider` + +The `C4BLEProvider` class is the heart of the peer discovery process. It is a C++ class that runs in the native layer and is responsible for the following: + +* **Starting and Stopping Peer Discovery**: The `C4BLEProvider` can be instructed to start or stop scanning for nearby peers. When scanning is active, the provider will notify the application of any discovered peers. +* **Publishing the Device's Presence**: The `C4BLEProvider` can also be used to advertise the device's presence to other peers. This allows other devices to discover and connect to it. +* **Managing Discovered Peers**: The `C4BLEProvider` maintains a list of all discovered peers and their current status (online or offline). +* **Forwarding Calls to the Java Layer**: The `C4BLEProvider` uses JNI to forward calls to the Java layer for all Bluetooth-specific operations. + +### 3.2. `C4Peer` + +The `C4Peer` class represents a discovered peer in the network. It is a C++ class that contains the following information about a peer: + +* **Peer ID**: A unique identifier for the peer. +* **Online Status**: A boolean flag that indicates whether the peer is currently online and reachable. +* **Metadata**: A collection of key-value pairs that can be used to store additional information about the peer. + +The `C4Peer` class provides methods for getting and setting the peer's metadata, as well as for monitoring changes to the metadata. + +### 3.3. `MetadataHelper` + +The `MetadataHelper` is a C++ utility class that provides functions for converting between Java `Map` objects and the `C4Peer::Metadata` type. This is necessary because the metadata is exchanged between the Java and C++ layers as part of the peer discovery and synchronization process. + +## 4. Java Class Descriptions + +### 4.1. `BluetoothProvider` + +The `BluetoothProvider` class is the main entry point for the Java-side of the P2P implementation. It acts as a bridge between the native C++ code and the Java-based `BleService`. It is responsible for: + +* Creating and managing `BleService` instances. +* Forwarding calls from the native layer to the `BleService` for starting/stopping browsing and publishing. +* Handling callbacks from the `BleService` and forwarding them to the native layer. + +### 4.2. `BluetoothPeer` + +The `BluetoothPeer` class is the Java representation of a discovered peer. It mirrors the C++ `C4Peer` class and provides a high-level API for interacting with a peer. It is responsible for: + +* Storing the peer's ID, online status, and metadata. +* Providing methods for monitoring metadata changes and resolving the peer's URL. + +### 4.3. `BleService` + +The `BleService` class is the core of the Java-side implementation. It manages all Bluetooth LE operations, including: + +* **Scanning**: It starts and stops scanning for nearby devices that are advertising the Couchbase Lite P2P service. +* **Device Management**: It maintains a list of discovered devices (`CblBleDevice`) and manages their connection state. +* **Publishing**: It coordinates with the `BlePublisher` to advertise the device's presence. +* **L2CAP Connections**: It initiates L2CAP connections to discovered peers. + +### 4.4. `BlePublisher` + +The `BlePublisher` class is responsible for advertising the device's presence to other peers. It handles the complexities of BLE advertising, including: + +* Starting and stopping advertising with the appropriate settings. +* Managing the advertising lifecycle and handling failures. +* Working in conjunction with the `BleGattServer` to host the Couchbase Lite service. + +### 4.5. `CblBleDevice` + +The `CblBleDevice` class represents a remote Bluetooth LE device that has been discovered by the `BleService`. It is responsible for: + +* Managing the GATT connection to the remote device. +* Discovering the Couchbase Lite P2P service and its characteristics. +* Reading the peer's ID, port, and metadata from the GATT characteristics. +* Initiating an L2CAP connection to the remote device. + +### 4.6. `BleGattServer` + +The `BleGattServer` class is responsible for creating and managing the GATT server that hosts the Couchbase Lite P2P service. Its key responsibilities include: + +* Creating the GATT service with the required characteristics (ID, port, metadata). +* Handling read requests for the characteristics from remote devices. +* Optionally starting an L2CAP server to listen for incoming connections. + +### 4.7. `BleL2CAPConnection` + +The `BleL2CAPConnection` class is a wrapper around a BluetoothSocket that provides a simple interface for sending and receiving data over an L2CAP connection. It handles: + +* Reading data from the socket in a background thread. +* Writing data to the socket asynchronously. +* Notifying a listener of incoming data and connection closure. + +### 4.8. `BleP2pConstants` + +This class defines the UUIDs for the Couchbase Lite P2P service and its characteristics, as well as other constants used by the BLE implementation. + +### 4.9. Listeners and Handles + +* **`BlePublisherListener`**: An interface for receiving lifecycle events from the `BlePublisher`. +* **`BlePublisherHandle`**: A handle that allows for stopping an active advertising session. +* **`BleGattInboundListener`**: An interface for receiving events related to inbound L2CAP connections. + +## 5. Flows + +### 5.1. Peer Discovery + +The peer discovery process is initiated by the application, which calls the `startBrowsing()` method on the `C4BLEProvider` object. The `C4BLEProvider` then forwards this call to the Java layer, which begins scanning for nearby Bluetooth LE devices. + +When a new device is discovered, the Java layer notifies the `C4BLEProvider`, which creates a new `C4Peer` object to represent the device. The `C4BLEProvider` then adds the new `C4Peer` to its list of discovered peers and notifies the application. + +### 5.2. Publishing + +To make itself discoverable to other devices, the application calls the `startPublishing()` method on the `C4BLEProvider` object. The `C4BLEProvider` then forwards this call to the Java layer, which begins advertising the device's presence. + +The advertisement packet contains a service UUID that is specific to the application, as well as the device's display name and other relevant information. + +### 5.3. Metadata Exchange + +The P2P synchronization feature allows for the exchange of metadata between peers. This metadata can be used to store any application-specific information, such as the user's name or the device's capabilities. + +The metadata is exchanged as part of the peer discovery process. When a device discovers a new peer, it can request the peer's metadata. The metadata is then sent over the Bluetooth connection and stored in the `C4Peer` object. + +The application can also monitor changes to a peer's metadata. When the metadata changes, the `C4BLEProvider` will notify the application, which can then take appropriate action. From 3efb72b8e75998a918ea62a056393f84f8798f9a Mon Sep 17 00:00:00 2001 From: Aniket Singh Date: Wed, 4 Mar 2026 01:42:36 +0530 Subject: [PATCH 07/11] Added public transport APIs and removed UUID sync --- ...al_core_impl_NativeC4MultipeerReplicator.h | 1 + .../main/cpp/native_c4multipeerreplicator.cc | 107 ++++++++++++++++-- .../cpp/native_c4peerdiscoveryprovider.cc | 21 ---- common/main/cpp/native_glue.cc | 1 - 4 files changed, 101 insertions(+), 29 deletions(-) diff --git a/common/main/cpp/com_couchbase_lite_internal_core_impl_NativeC4MultipeerReplicator.h b/common/main/cpp/com_couchbase_lite_internal_core_impl_NativeC4MultipeerReplicator.h index 10934b454..2e3725033 100644 --- a/common/main/cpp/com_couchbase_lite_internal_core_impl_NativeC4MultipeerReplicator.h +++ b/common/main/cpp/com_couchbase_lite_internal_core_impl_NativeC4MultipeerReplicator.h @@ -20,6 +20,7 @@ JNICALL Java_com_couchbase_lite_internal_core_impl_NativeC4MultipeerReplicator_c jclass, jlong, jstring, + jobject, jlong, jbyteArray, jlong, diff --git a/common/main/cpp/native_c4multipeerreplicator.cc b/common/main/cpp/native_c4multipeerreplicator.cc index 09ded0154..14ebedde7 100644 --- a/common/main/cpp/native_c4multipeerreplicator.cc +++ b/common/main/cpp/native_c4multipeerreplicator.cc @@ -101,7 +101,7 @@ namespace litecore::jni { m_C4MultipeerReplicator_createPeerInfo = env->GetStaticMethodID( cls_C4MultipeerReplicator, "createPeerInfo", - "([B[BZ[[BLcom/couchbase/lite/internal/core/C4ReplicatorStatus;)Lcom/couchbase/lite/PeerInfo;"); + "([B[BZ[[BLjava/util/Set;Lcom/couchbase/lite/internal/core/C4ReplicatorStatus;)Lcom/couchbase/lite/PeerInfo;"); if (m_C4MultipeerReplicator_createPeerInfo == nullptr) return false; @@ -125,7 +125,7 @@ namespace litecore::jni { m_C4MultipeerReplicator_onPeerDiscovered = env->GetStaticMethodID( cls_C4MultipeerReplicator, "onPeerDiscovered", - "(J[BZ)V"); + "(J[BLcom/couchbase/lite/MultipeerTransport;Z)V"); if (m_C4MultipeerReplicator_onPeerDiscovered == nullptr) return false; @@ -133,7 +133,7 @@ namespace litecore::jni { m_C4MultipeerReplicator_onReplicatorStatusChanged = env->GetStaticMethodID( cls_C4MultipeerReplicator, "onReplicatorStatusChanged", - "(J[BZLcom/couchbase/lite/internal/core/C4ReplicatorStatus;)V"); + "(J[BLcom/couchbase/lite/MultipeerTransport;ZLcom/couchbase/lite/internal/core/C4ReplicatorStatus;)V"); if (m_C4MultipeerReplicator_onReplicatorStatusChanged == nullptr) return false; @@ -141,7 +141,7 @@ namespace litecore::jni { m_C4MultipeerReplicator_onDocumentEnded = env->GetStaticMethodID( cls_C4MultipeerReplicator, "onDocumentEnded", - "(J[BZ[Lcom/couchbase/lite/internal/core/C4DocumentEnded;)V"); + "(J[BLcom/couchbase/lite/MultipeerTransport;Z[Lcom/couchbase/lite/internal/core/C4DocumentEnded;)V"); if (m_C4MultipeerReplicator_onDocumentEnded == nullptr) return false; @@ -218,6 +218,89 @@ namespace litecore::jni { return neighborIds; } + static jobject toJavaMultipeerTransport(JNIEnv* env, C4PeerSyncProtocol p) { + jclass clsTransport = env->FindClass("com/couchbase/lite/MultipeerTransport"); + if (!clsTransport) { return nullptr; } + + const char* fieldName; + switch (p) { + case kPeerSyncProtocol_DNS_SD: + fieldName = "WIFI"; + break; + case kPeerSyncProtocol_BluetoothLE: + fieldName = "BLUETOOTH"; + break; + default: + return nullptr; // or choose a default (e.g., WIFI) if your API requires non-null + } + + jfieldID fid = env->GetStaticFieldID( + clsTransport, + fieldName, + "Lcom/couchbase/lite/MultipeerTransport;"); + if (!fid) { return nullptr; } + + return env->GetStaticObjectField(clsTransport, fid); + } + + static jobject toJavaTransportSet(JNIEnv* env, C4PeerSyncProtocols protos) { + jclass clsEnumSet = env->FindClass("java/util/EnumSet"); + jclass clsTransport = env->FindClass("com/couchbase/lite/MultipeerTransport"); + if (!clsEnumSet || !clsTransport) { return nullptr; } + + jmethodID midNoneOf = env->GetStaticMethodID( + clsEnumSet, "noneOf", "(Ljava/lang/Class;)Ljava/util/EnumSet;"); + if (!midNoneOf) { return nullptr; } + + jobject setObj = env->CallStaticObjectMethod(clsEnumSet, midNoneOf, clsTransport); + if (!setObj) { return nullptr; } + + jmethodID midAdd = env->GetMethodID(clsEnumSet, "add", "(Ljava/lang/Object;)Z"); + if (!midAdd) { return setObj; } // empty set (best effort) + + auto addIfPresent = [&](C4PeerSyncProtocol proto) { + if (!(protos & proto)) { return; } + jobject jTransport = toJavaMultipeerTransport(env, proto); + if (!jTransport) { return; } + env->CallBooleanMethod(setObj, midAdd, jTransport); + env->DeleteLocalRef(jTransport); + }; + + addIfPresent(kPeerSyncProtocol_DNS_SD); + addIfPresent(kPeerSyncProtocol_BluetoothLE); + + return setObj; + } + + static C4PeerSyncProtocols toC4PeerSyncProtocols(JNIEnv* env, jobject enumSetTransports) { + if (!enumSetTransports) { return 0; } + + jclass clsSet = env->FindClass("java/util/Set"); + jmethodID midContains = env->GetMethodID(clsSet, "contains", "(Ljava/lang/Object;)Z"); + + jclass clsTransport = env->FindClass("com/couchbase/lite/MultipeerTransport"); + jfieldID fidWifi = env->GetStaticFieldID( + clsTransport, "WIFI", "Lcom/couchbase/lite/MultipeerTransport;"); + jfieldID fidBt = env->GetStaticFieldID( + clsTransport, "BLUETOOTH", "Lcom/couchbase/lite/MultipeerTransport;"); + + jobject jWifi = env->GetStaticObjectField(clsTransport, fidWifi); + jobject jBt = env->GetStaticObjectField(clsTransport, fidBt); + + C4PeerSyncProtocols protos = 0; + + if (env->CallBooleanMethod(enumSetTransports, midContains, jWifi)) { + protos |= kPeerSyncProtocol_DNS_SD; + } + if (env->CallBooleanMethod(enumSetTransports, midContains, jBt)) { + protos |= kPeerSyncProtocol_BluetoothLE; + } + + env->DeleteLocalRef(jWifi); + env->DeleteLocalRef(jBt); + return protos; + } + // The comment over in native_c4replicator.cc applies here as well. // I'm even sorrier that I have to duplicate this mess. static int fromJavaReplColls( @@ -324,18 +407,21 @@ namespace litecore::jni { return ok != JNI_FALSE; } - static void peerDiscoveredCallback(C4PeerSync *ignored, const C4PeerID *peerId, bool online, void *context) { + static void peerDiscoveredCallback(C4PeerSync *ignored, const C4PeerID *peerId, + C4PeerSyncProtocol protocol, bool online, void *context) { JNIEnv *env = nullptr; jint envState = attachJVM(&env, "p2pPeerDiscovered"); if ((envState != JNI_OK) && (envState != JNI_EDETACHED)) return; jbyteArray _peerId = toJByteArray(env, peerId->bytes, 32); + jobject transport = toJavaMultipeerTransport(env, protocol); env->CallStaticVoidMethod( cls_C4MultipeerReplicator, m_C4MultipeerReplicator_onPeerDiscovered, (jlong) context, _peerId, + transport, online ? JNI_TRUE : JNI_FALSE); if (envState == JNI_EDETACHED) { @@ -359,11 +445,13 @@ namespace litecore::jni { jbyteArray _peerId = toJByteArray(env, peerId->bytes, 32); jobject _status = toJavaReplStatus(env, *status); + jobject _protocol = toJavaMultipeerTransport(env, protocol); env->CallStaticVoidMethod( cls_C4MultipeerReplicator, m_C4MultipeerReplicator_onReplicatorStatusChanged, (jlong) context, _peerId, + _protocol, outbound ? JNI_TRUE : JNI_FALSE, _status); @@ -393,11 +481,13 @@ namespace litecore::jni { jbyteArray _peerId = toJByteArray(env, peerId->bytes, 32); jobjectArray _docs = toJavaDocumentEndedArray(env, nDocs, documentEnded); + jobject _protocol = toJavaMultipeerTransport(env, protocol); env->CallStaticVoidMethod( cls_C4MultipeerReplicator, m_C4MultipeerReplicator_onDocumentEnded, (jlong) context, _peerId, + _protocol, pushing ? JNI_TRUE : JNI_FALSE, _docs); @@ -413,8 +503,8 @@ namespace litecore::jni { return { &statusChangedCallback, &authenticateCallback, - &peerDiscoveredCallback, nullptr, + peerDiscoveredCallback, &replicatorStatusChangedCallback, &documentEndedCallback, nullptr, @@ -514,6 +604,7 @@ JNICALL Java_com_couchbase_lite_internal_core_impl_NativeC4MultipeerReplicator_c jclass ignore, jlong token, jstring jgroupId, + jobject transports, jlong keyPair, jbyteArray cert, jlong c4db, @@ -528,7 +619,7 @@ JNICALL Java_com_couchbase_lite_internal_core_impl_NativeC4MultipeerReplicator_c params.peerGroupID = groupId; // Protocols: - params.protocols = kPeerSyncProtocol_DNS_SD | kPeerSyncProtocol_BluetoothLE; + params.protocols = toC4PeerSyncProtocols(env, transports); // Identity: bool failed; @@ -683,6 +774,7 @@ JNICALL Java_com_couchbase_lite_internal_core_impl_NativeC4MultipeerReplicator_g jobject replStatus = toJavaReplStatus(env, info->replicatorStatus); jobjectArray neighborIds = fromC4PeerID(env, info->neighbors, info->neighborCount); + jobject transports = toJavaTransportSet(env, info->onlineProtocols); jobject peerInfo = env->CallStaticObjectMethod( cls_C4MultipeerReplicator, @@ -690,6 +782,7 @@ JNICALL Java_com_couchbase_lite_internal_core_impl_NativeC4MultipeerReplicator_g jpeerId, certChain, info->onlineProtocols > 0 ? JNI_TRUE : JNI_FALSE, + transports, neighborIds, replStatus); diff --git a/common/main/cpp/native_c4peerdiscoveryprovider.cc b/common/main/cpp/native_c4peerdiscoveryprovider.cc index bcafa1252..a1a6a0256 100644 --- a/common/main/cpp/native_c4peerdiscoveryprovider.cc +++ b/common/main/cpp/native_c4peerdiscoveryprovider.cc @@ -340,27 +340,6 @@ namespace litecore::p2p { else if (strcmp(uuidStr, litecore::p2p::btle::kPeerGroupUUIDNamespace) == 0) env->SetStaticObjectField(cls, PEER_GROUP_NS_FIELD, uuidObj); } - - bool initBleConstants(JNIEnv* env) { - jclass localClass = env->FindClass("com/couchbase/lite/internal/p2p/ble/BleP2pConstants"); - if (localClass == nullptr) return false; - - cls_BleP2pConstants = reinterpret_cast(env->NewGlobalRef(localClass)); - jUuidClass = env->FindClass("java/util/UUID"); - uuidFromString = env->GetStaticMethodID(jUuidClass, "fromString", - "(Ljava/lang/String;)Ljava/util/UUID;"); - - PORT_CHAR_FIELD = env->GetStaticFieldID(cls_BleP2pConstants, - "PORT_CHARACTERISTIC_ID", "Ljava/util/UUID;"); - META_CHAR_FIELD = env->GetStaticFieldID(cls_BleP2pConstants, - "METADATA_CHARACTERISTIC_ID", "Ljava/util/UUID;"); - - setUuidConstant(env, cls_BleP2pConstants, litecore::p2p::btle::kPortCharacteristicID); - setUuidConstant(env, cls_BleP2pConstants, litecore::p2p::btle::kMetadataCharacteristicID); - setUuidConstant(env, cls_BleP2pConstants, litecore::p2p::btle::kPeerGroupUUIDNamespace); - - return true; - } } diff --git a/common/main/cpp/native_glue.cc b/common/main/cpp/native_glue.cc index d363be73a..b3f80756e 100644 --- a/common/main/cpp/native_glue.cc +++ b/common/main/cpp/native_glue.cc @@ -163,7 +163,6 @@ JNI_OnLoad(JavaVM *jvm, void *ignore) { #ifdef __ANDROID__ && initC4MultipeerReplicator(env) && initC4PeerDiscoveryProvider(env) - && initBleConstants(env) #endif #endif && initC4Socket(env)) { From f9ab2631183d754ffb709ab0324bf967bb2c3099 Mon Sep 17 00:00:00 2001 From: Aniket Singh Date: Wed, 4 Mar 2026 19:32:48 +0530 Subject: [PATCH 08/11] Updated the BluetoothPeer object creation in Java instead of native side --- ...e_internal_core_impl_NativeBluetoothPeer.h | 4 ++ ..._core_impl_NativeC4PeerDiscoveryProvider.h | 2 +- common/main/cpp/native_bluetoothpeer.cc | 14 +++++++ .../cpp/native_c4peerdiscoveryprovider.cc | 40 +++++-------------- 4 files changed, 28 insertions(+), 32 deletions(-) diff --git a/common/main/cpp/com_couchbase_lite_internal_core_impl_NativeBluetoothPeer.h b/common/main/cpp/com_couchbase_lite_internal_core_impl_NativeBluetoothPeer.h index 6d19aabae..2853614a9 100644 --- a/common/main/cpp/com_couchbase_lite_internal_core_impl_NativeBluetoothPeer.h +++ b/common/main/cpp/com_couchbase_lite_internal_core_impl_NativeBluetoothPeer.h @@ -68,6 +68,10 @@ JNIEXPORT void JNICALL Java_com_couchbase_lite_internal_core_impl_NativeBluetoothPeer_release( JNIEnv *env, jclass clazz, jlong peerPtr); +JNIEXPORT jlong JNICALL +Java_com_couchbase_lite_internal_core_impl_NativeBluetoothPeer_createC4Peer( + JNIEnv* env, jclass /*clazz*/, jlong providerPtr, jstring jPeerId); + #ifdef __cplusplus } #endif diff --git a/common/main/cpp/com_couchbase_lite_internal_core_impl_NativeC4PeerDiscoveryProvider.h b/common/main/cpp/com_couchbase_lite_internal_core_impl_NativeC4PeerDiscoveryProvider.h index 257bb8e2c..c1202344c 100644 --- a/common/main/cpp/com_couchbase_lite_internal_core_impl_NativeC4PeerDiscoveryProvider.h +++ b/common/main/cpp/com_couchbase_lite_internal_core_impl_NativeC4PeerDiscoveryProvider.h @@ -37,7 +37,7 @@ JNIEXPORT void JNICALL Java_com_couchbase_lite_internal_core_impl_NativeC4PeerDiscoveryProvider_removePeer( JNIEnv *env, jclass thiz, jlong providerPtr, jstring peerId); -JNIEXPORT jobject JNICALL +JNIEXPORT jlong JNICALL Java_com_couchbase_lite_internal_core_impl_NativeC4PeerDiscoveryProvider_peerWithID( JNIEnv *env, jclass thiz, jlong providerPtr, jstring peerId); diff --git a/common/main/cpp/native_bluetoothpeer.cc b/common/main/cpp/native_bluetoothpeer.cc index ffe75bf8e..7d841ca0c 100644 --- a/common/main/cpp/native_bluetoothpeer.cc +++ b/common/main/cpp/native_bluetoothpeer.cc @@ -95,5 +95,19 @@ extern "C++" { JNIEnv *env, jclass clazz, jlong peerPtr) { fleece::release(reinterpret_cast(peerPtr)); } + + JNIEXPORT jlong JNICALL + Java_com_couchbase_lite_internal_core_impl_NativeBluetoothPeer_createC4Peer( + JNIEnv* env, jclass /*clazz*/, jlong providerPtr, jstring jPeerId) { + + auto* provider = reinterpret_cast(providerPtr); + if (!provider || !jPeerId) { return 0; } + + std::string peerId = JstringToUTF8(env, jPeerId); + if (peerId.empty()) { return 0; } + + auto* peer = new C4Peer(provider, peerId); + return (jlong) reinterpret_cast(peer); + } } #endif \ No newline at end of file diff --git a/common/main/cpp/native_c4peerdiscoveryprovider.cc b/common/main/cpp/native_c4peerdiscoveryprovider.cc index a1a6a0256..66a11e4d3 100644 --- a/common/main/cpp/native_c4peerdiscoveryprovider.cc +++ b/common/main/cpp/native_c4peerdiscoveryprovider.cc @@ -394,45 +394,23 @@ Java_com_couchbase_lite_internal_core_impl_NativeC4PeerDiscoveryProvider_removeP provider->removeDiscoveredPeer(id); } -JNIEXPORT jobject JNICALL +JNIEXPORT jlong JNICALL Java_com_couchbase_lite_internal_core_impl_NativeC4PeerDiscoveryProvider_peerWithID( JNIEnv *env, jclass thiz, jlong providerPtr, jstring peerId) { - // Get provider instance - auto* provider = (C4BLEProvider*)providerPtr; - if (!provider) return nullptr; + auto *provider = reinterpret_cast(providerPtr); + if (!provider || !peerId) { return 0; } - // Convert Java String → C++ string - std::string peerIdStr; - if (peerId) { - const char* peerIdChars = env->GetStringUTFChars(peerId, nullptr); - if (peerIdChars) { - peerIdStr = std::string(peerIdChars); - env->ReleaseStringUTFChars(peerId, peerIdChars); - } - } + std::string peerIdStr = JstringToUTF8(env, peerId); + if (peerIdStr.empty()) { return 0; } - C4PeerDiscovery& discovery = provider->discovery(); + C4PeerDiscovery &discovery = provider->discovery(); fleece::Retained peer = discovery.peerWithID(peerIdStr); + if (!peer) { return 0; } - // Convert C4Peer* → Java C4Peer object - if (peer) { - // Get the native peer pointer - C4Peer* rawPeer = std::move(peer).detach(); - jlong peerPtr = reinterpret_cast(rawPeer); + C4Peer *rawPeer = std::move(peer).detach(); - // Create Java BluetoothPeer object - jclass bluetoothPeerClass = env->FindClass("com/couchbase/lite/internal/core/BluetoothPeer"); - if (bluetoothPeerClass == nullptr) return nullptr; - - jmethodID constructor = env->GetMethodID(bluetoothPeerClass, "", "(J)V"); - if (constructor == nullptr) return nullptr; - - jobject javaPeer = env->NewObject(bluetoothPeerClass, constructor, peerPtr); - - return env->NewGlobalRef(javaPeer); - } - return nullptr; + return static_cast(reinterpret_cast(rawPeer)); } JNIEXPORT void JNICALL From f18cb14c3bd63e6ff98614f2687c8568f108e41a Mon Sep 17 00:00:00 2001 From: Aniket Singh Date: Thu, 5 Mar 2026 20:05:43 +0530 Subject: [PATCH 09/11] Updating addpeer and C4Peer creation and protocol usage --- ...e_internal_core_impl_NativeBluetoothPeer.h | 4 --- ...al_core_impl_NativeC4MultipeerReplicator.h | 2 +- ..._core_impl_NativeC4PeerDiscoveryProvider.h | 2 +- common/main/cpp/native_bluetoothpeer.cc | 25 +------------- .../main/cpp/native_bluetoothpeer_internal.h | 16 +++++++++ .../main/cpp/native_c4multipeerreplicator.cc | 33 ++----------------- .../cpp/native_c4peerdiscoveryprovider.cc | 32 ++++++++++-------- .../lite/internal/core/C4Constants.java | 8 +++++ 8 files changed, 47 insertions(+), 75 deletions(-) create mode 100644 common/main/cpp/native_bluetoothpeer_internal.h diff --git a/common/main/cpp/com_couchbase_lite_internal_core_impl_NativeBluetoothPeer.h b/common/main/cpp/com_couchbase_lite_internal_core_impl_NativeBluetoothPeer.h index 2853614a9..6d19aabae 100644 --- a/common/main/cpp/com_couchbase_lite_internal_core_impl_NativeBluetoothPeer.h +++ b/common/main/cpp/com_couchbase_lite_internal_core_impl_NativeBluetoothPeer.h @@ -68,10 +68,6 @@ JNIEXPORT void JNICALL Java_com_couchbase_lite_internal_core_impl_NativeBluetoothPeer_release( JNIEnv *env, jclass clazz, jlong peerPtr); -JNIEXPORT jlong JNICALL -Java_com_couchbase_lite_internal_core_impl_NativeBluetoothPeer_createC4Peer( - JNIEnv* env, jclass /*clazz*/, jlong providerPtr, jstring jPeerId); - #ifdef __cplusplus } #endif diff --git a/common/main/cpp/com_couchbase_lite_internal_core_impl_NativeC4MultipeerReplicator.h b/common/main/cpp/com_couchbase_lite_internal_core_impl_NativeC4MultipeerReplicator.h index 2e3725033..b7d344e88 100644 --- a/common/main/cpp/com_couchbase_lite_internal_core_impl_NativeC4MultipeerReplicator.h +++ b/common/main/cpp/com_couchbase_lite_internal_core_impl_NativeC4MultipeerReplicator.h @@ -20,7 +20,7 @@ JNICALL Java_com_couchbase_lite_internal_core_impl_NativeC4MultipeerReplicator_c jclass, jlong, jstring, - jobject, + jint, jlong, jbyteArray, jlong, diff --git a/common/main/cpp/com_couchbase_lite_internal_core_impl_NativeC4PeerDiscoveryProvider.h b/common/main/cpp/com_couchbase_lite_internal_core_impl_NativeC4PeerDiscoveryProvider.h index c1202344c..187c053e5 100644 --- a/common/main/cpp/com_couchbase_lite_internal_core_impl_NativeC4PeerDiscoveryProvider.h +++ b/common/main/cpp/com_couchbase_lite_internal_core_impl_NativeC4PeerDiscoveryProvider.h @@ -29,7 +29,7 @@ JNIEXPORT void JNICALL Java_com_couchbase_lite_internal_core_impl_NativeC4PeerDiscoveryProvider_onIncomingConnection( JNIEnv *env, jclass thiz, jlong providerPtr, jbyteArray peerId, jlong socketPtr); -JNIEXPORT void JNICALL +JNIEXPORT jlong JNICALL Java_com_couchbase_lite_internal_core_impl_NativeC4PeerDiscoveryProvider_addPeer( JNIEnv *env, jclass thiz, jlong providerPtr, jstring peerId); diff --git a/common/main/cpp/native_bluetoothpeer.cc b/common/main/cpp/native_bluetoothpeer.cc index 7d841ca0c..5dcccef5d 100644 --- a/common/main/cpp/native_bluetoothpeer.cc +++ b/common/main/cpp/native_bluetoothpeer.cc @@ -6,6 +6,7 @@ #include "fleece/RefCounted.hh" #include "fleece/FLExpert.h" #include "MetadataHelper.h" +#include "native_bluetoothpeer_internal.h" using namespace litecore; using namespace litecore::jni; @@ -15,16 +16,6 @@ static C4Peer* getPeer(jlong peerPtr) { return reinterpret_cast(peerPtr); } -class BluetoothPeer : public C4Peer { - using C4Peer::C4Peer; - -public: - void resolvingUrl(std::string s, C4Error err) { - resolvedURL(s, err); - } -}; - - extern "C++" { JNIEXPORT jstring JNICALL Java_com_couchbase_lite_internal_core_impl_NativeBluetoothPeer_getId( @@ -95,19 +86,5 @@ extern "C++" { JNIEnv *env, jclass clazz, jlong peerPtr) { fleece::release(reinterpret_cast(peerPtr)); } - - JNIEXPORT jlong JNICALL - Java_com_couchbase_lite_internal_core_impl_NativeBluetoothPeer_createC4Peer( - JNIEnv* env, jclass /*clazz*/, jlong providerPtr, jstring jPeerId) { - - auto* provider = reinterpret_cast(providerPtr); - if (!provider || !jPeerId) { return 0; } - - std::string peerId = JstringToUTF8(env, jPeerId); - if (peerId.empty()) { return 0; } - - auto* peer = new C4Peer(provider, peerId); - return (jlong) reinterpret_cast(peer); - } } #endif \ No newline at end of file diff --git a/common/main/cpp/native_bluetoothpeer_internal.h b/common/main/cpp/native_bluetoothpeer_internal.h new file mode 100644 index 000000000..9fd6b7254 --- /dev/null +++ b/common/main/cpp/native_bluetoothpeer_internal.h @@ -0,0 +1,16 @@ +#if defined(COUCHBASE_ENTERPRISE) && defined(__ANDROID__) + +#ifndef ANDROID_NATIVE_BLUETOOTHPEER_INTERNAL_H +#define ANDROID_NATIVE_BLUETOOTHPEER_INTERNAL_H + +#include "c4PeerDiscovery.hh" +#include "c4Error.h" + +class BluetoothPeer final : public C4Peer { + using C4Peer::C4Peer; +public: + void resolvingUrl(std::string s, C4Error err) { resolvedURL(std::move(s), err); } +}; + +#endif //ANDROID_NATIVE_BLUETOOTHPEER_INTERNAL_H +#endif diff --git a/common/main/cpp/native_c4multipeerreplicator.cc b/common/main/cpp/native_c4multipeerreplicator.cc index 14ebedde7..e8e15f21f 100644 --- a/common/main/cpp/native_c4multipeerreplicator.cc +++ b/common/main/cpp/native_c4multipeerreplicator.cc @@ -272,35 +272,6 @@ namespace litecore::jni { return setObj; } - static C4PeerSyncProtocols toC4PeerSyncProtocols(JNIEnv* env, jobject enumSetTransports) { - if (!enumSetTransports) { return 0; } - - jclass clsSet = env->FindClass("java/util/Set"); - jmethodID midContains = env->GetMethodID(clsSet, "contains", "(Ljava/lang/Object;)Z"); - - jclass clsTransport = env->FindClass("com/couchbase/lite/MultipeerTransport"); - jfieldID fidWifi = env->GetStaticFieldID( - clsTransport, "WIFI", "Lcom/couchbase/lite/MultipeerTransport;"); - jfieldID fidBt = env->GetStaticFieldID( - clsTransport, "BLUETOOTH", "Lcom/couchbase/lite/MultipeerTransport;"); - - jobject jWifi = env->GetStaticObjectField(clsTransport, fidWifi); - jobject jBt = env->GetStaticObjectField(clsTransport, fidBt); - - C4PeerSyncProtocols protos = 0; - - if (env->CallBooleanMethod(enumSetTransports, midContains, jWifi)) { - protos |= kPeerSyncProtocol_DNS_SD; - } - if (env->CallBooleanMethod(enumSetTransports, midContains, jBt)) { - protos |= kPeerSyncProtocol_BluetoothLE; - } - - env->DeleteLocalRef(jWifi); - env->DeleteLocalRef(jBt); - return protos; - } - // The comment over in native_c4replicator.cc applies here as well. // I'm even sorrier that I have to duplicate this mess. static int fromJavaReplColls( @@ -604,7 +575,7 @@ JNICALL Java_com_couchbase_lite_internal_core_impl_NativeC4MultipeerReplicator_c jclass ignore, jlong token, jstring jgroupId, - jobject transports, + jint protocols, jlong keyPair, jbyteArray cert, jlong c4db, @@ -619,7 +590,7 @@ JNICALL Java_com_couchbase_lite_internal_core_impl_NativeC4MultipeerReplicator_c params.peerGroupID = groupId; // Protocols: - params.protocols = toC4PeerSyncProtocols(env, transports); + params.protocols = (C4PeerSyncProtocols) protocols; // Identity: bool failed; diff --git a/common/main/cpp/native_c4peerdiscoveryprovider.cc b/common/main/cpp/native_c4peerdiscoveryprovider.cc index 66a11e4d3..3c34b4219 100644 --- a/common/main/cpp/native_c4peerdiscoveryprovider.cc +++ b/common/main/cpp/native_c4peerdiscoveryprovider.cc @@ -5,6 +5,7 @@ #include "native_glue.hh" #include "socket_factory.h" #include "MetadataHelper.h" +#include "native_bluetoothpeer_internal.h" #include "com_couchbase_lite_internal_core_impl_NativeC4PeerDiscoveryProvider.h" using namespace litecore; @@ -280,8 +281,8 @@ namespace litecore::jni { } } - void addDiscoveredPeer(C4Peer* peer, bool moreComing = false) { - addPeer(peer, moreComing); + fleece::Ref addDiscoveredPeer(C4Peer* peer, bool moreComing = false) { + return addPeer(peer, moreComing); } void removeDiscoveredPeer(std::string id, bool moreComing = false) { @@ -361,21 +362,19 @@ Java_com_couchbase_lite_internal_core_impl_NativeC4PeerDiscoveryProvider_service return toJString(env, s); } -JNIEXPORT void JNICALL +JNIEXPORT jlong JNICALL Java_com_couchbase_lite_internal_core_impl_NativeC4PeerDiscoveryProvider_addPeer( JNIEnv *env, jclass thiz, jlong providerPtr, jstring peerId) { - // Get the provider instance from the pointer - auto* provider = (C4BLEProvider*)providerPtr; - if (!provider) return; + auto* provider = reinterpret_cast(providerPtr); + if (!provider || !peerId) { return 0; } - const char* peerIdStr = env->GetStringUTFChars(peerId, nullptr); - if (!peerIdStr) return; - std::string id(peerIdStr); - env->ReleaseStringUTFChars(peerId, peerIdStr); + std::string id = JstringToUTF8(env, peerId); + if (id.empty()) { return 0; } + auto created = fleece::make_retained(provider, id); + fleece::Ref peer = provider->addDiscoveredPeer(created.get()); - auto* peer = new C4Peer(provider, id); - provider->addDiscoveredPeer(peer); + return (jlong) reinterpret_cast(std::move(peer).detach()); } JNIEXPORT void JNICALL @@ -428,8 +427,13 @@ Java_com_couchbase_lite_internal_core_impl_NativeC4PeerDiscoveryProvider_peerDis // Use the utility function C4Peer::Metadata peerMetadata = javaMapToMetadata(env, metadata); - auto* peer = new C4Peer(provider, id, peerMetadata); - provider->addDiscoveredPeer(peer); + fleece::Retained peer = provider->discovery().peerWithID(id); + + if (!peer) { + auto created = fleece::make_retained(provider, id); + peer = provider->addDiscoveredPeer(created.get()); + } + peer->setMetadata(std::move(peerMetadata)); } JNIEXPORT void JNICALL diff --git a/common/main/java/com/couchbase/lite/internal/core/C4Constants.java b/common/main/java/com/couchbase/lite/internal/core/C4Constants.java index 93b5307ef..fa57304c8 100644 --- a/common/main/java/com/couchbase/lite/internal/core/C4Constants.java +++ b/common/main/java/com/couchbase/lite/internal/core/C4Constants.java @@ -318,4 +318,12 @@ private WebSocketError() { } public static final int USER_TRANSIENT = 4001; // User-defined transient error public static final int USER_PERMANENT = 4002; // User-defined permanent error } + + //////////////////////////////////// + // c4PeerSyncTypes.h + //////////////////////////////////// + public static final class PeerSyncProtocols { + public static final int DNS_SD = 0x01; + public static final int BLUETOOTH_LE = 0x02; + } } From 569efca769d4c292261a68e6196864d30814209c Mon Sep 17 00:00:00 2001 From: Aniket Singh Date: Fri, 6 Mar 2026 15:16:11 +0530 Subject: [PATCH 10/11] Conversion of MultipeerTransport and Enumset in java rather than in JNI part --- .../main/cpp/native_c4multipeerreplicator.cc | 76 +++---------------- 1 file changed, 9 insertions(+), 67 deletions(-) diff --git a/common/main/cpp/native_c4multipeerreplicator.cc b/common/main/cpp/native_c4multipeerreplicator.cc index e8e15f21f..283b5b011 100644 --- a/common/main/cpp/native_c4multipeerreplicator.cc +++ b/common/main/cpp/native_c4multipeerreplicator.cc @@ -101,7 +101,7 @@ namespace litecore::jni { m_C4MultipeerReplicator_createPeerInfo = env->GetStaticMethodID( cls_C4MultipeerReplicator, "createPeerInfo", - "([B[BZ[[BLjava/util/Set;Lcom/couchbase/lite/internal/core/C4ReplicatorStatus;)Lcom/couchbase/lite/PeerInfo;"); + "([B[BZ[[BILcom/couchbase/lite/internal/core/C4ReplicatorStatus;)Lcom/couchbase/lite/PeerInfo;"); if (m_C4MultipeerReplicator_createPeerInfo == nullptr) return false; @@ -125,7 +125,7 @@ namespace litecore::jni { m_C4MultipeerReplicator_onPeerDiscovered = env->GetStaticMethodID( cls_C4MultipeerReplicator, "onPeerDiscovered", - "(J[BLcom/couchbase/lite/MultipeerTransport;Z)V"); + "(J[BIZ)V"); if (m_C4MultipeerReplicator_onPeerDiscovered == nullptr) return false; @@ -133,7 +133,7 @@ namespace litecore::jni { m_C4MultipeerReplicator_onReplicatorStatusChanged = env->GetStaticMethodID( cls_C4MultipeerReplicator, "onReplicatorStatusChanged", - "(J[BLcom/couchbase/lite/MultipeerTransport;ZLcom/couchbase/lite/internal/core/C4ReplicatorStatus;)V"); + "(J[BIZLcom/couchbase/lite/internal/core/C4ReplicatorStatus;)V"); if (m_C4MultipeerReplicator_onReplicatorStatusChanged == nullptr) return false; @@ -141,7 +141,7 @@ namespace litecore::jni { m_C4MultipeerReplicator_onDocumentEnded = env->GetStaticMethodID( cls_C4MultipeerReplicator, "onDocumentEnded", - "(J[BLcom/couchbase/lite/MultipeerTransport;Z[Lcom/couchbase/lite/internal/core/C4DocumentEnded;)V"); + "(J[BIZ[Lcom/couchbase/lite/internal/core/C4DocumentEnded;)V"); if (m_C4MultipeerReplicator_onDocumentEnded == nullptr) return false; @@ -218,60 +218,6 @@ namespace litecore::jni { return neighborIds; } - static jobject toJavaMultipeerTransport(JNIEnv* env, C4PeerSyncProtocol p) { - jclass clsTransport = env->FindClass("com/couchbase/lite/MultipeerTransport"); - if (!clsTransport) { return nullptr; } - - const char* fieldName; - switch (p) { - case kPeerSyncProtocol_DNS_SD: - fieldName = "WIFI"; - break; - case kPeerSyncProtocol_BluetoothLE: - fieldName = "BLUETOOTH"; - break; - default: - return nullptr; // or choose a default (e.g., WIFI) if your API requires non-null - } - - jfieldID fid = env->GetStaticFieldID( - clsTransport, - fieldName, - "Lcom/couchbase/lite/MultipeerTransport;"); - if (!fid) { return nullptr; } - - return env->GetStaticObjectField(clsTransport, fid); - } - - static jobject toJavaTransportSet(JNIEnv* env, C4PeerSyncProtocols protos) { - jclass clsEnumSet = env->FindClass("java/util/EnumSet"); - jclass clsTransport = env->FindClass("com/couchbase/lite/MultipeerTransport"); - if (!clsEnumSet || !clsTransport) { return nullptr; } - - jmethodID midNoneOf = env->GetStaticMethodID( - clsEnumSet, "noneOf", "(Ljava/lang/Class;)Ljava/util/EnumSet;"); - if (!midNoneOf) { return nullptr; } - - jobject setObj = env->CallStaticObjectMethod(clsEnumSet, midNoneOf, clsTransport); - if (!setObj) { return nullptr; } - - jmethodID midAdd = env->GetMethodID(clsEnumSet, "add", "(Ljava/lang/Object;)Z"); - if (!midAdd) { return setObj; } // empty set (best effort) - - auto addIfPresent = [&](C4PeerSyncProtocol proto) { - if (!(protos & proto)) { return; } - jobject jTransport = toJavaMultipeerTransport(env, proto); - if (!jTransport) { return; } - env->CallBooleanMethod(setObj, midAdd, jTransport); - env->DeleteLocalRef(jTransport); - }; - - addIfPresent(kPeerSyncProtocol_DNS_SD); - addIfPresent(kPeerSyncProtocol_BluetoothLE); - - return setObj; - } - // The comment over in native_c4replicator.cc applies here as well. // I'm even sorrier that I have to duplicate this mess. static int fromJavaReplColls( @@ -386,13 +332,12 @@ namespace litecore::jni { return; jbyteArray _peerId = toJByteArray(env, peerId->bytes, 32); - jobject transport = toJavaMultipeerTransport(env, protocol); env->CallStaticVoidMethod( cls_C4MultipeerReplicator, m_C4MultipeerReplicator_onPeerDiscovered, (jlong) context, _peerId, - transport, + (jint) protocol, online ? JNI_TRUE : JNI_FALSE); if (envState == JNI_EDETACHED) { @@ -416,13 +361,12 @@ namespace litecore::jni { jbyteArray _peerId = toJByteArray(env, peerId->bytes, 32); jobject _status = toJavaReplStatus(env, *status); - jobject _protocol = toJavaMultipeerTransport(env, protocol); env->CallStaticVoidMethod( cls_C4MultipeerReplicator, m_C4MultipeerReplicator_onReplicatorStatusChanged, (jlong) context, _peerId, - _protocol, + (jint) protocol, outbound ? JNI_TRUE : JNI_FALSE, _status); @@ -452,13 +396,12 @@ namespace litecore::jni { jbyteArray _peerId = toJByteArray(env, peerId->bytes, 32); jobjectArray _docs = toJavaDocumentEndedArray(env, nDocs, documentEnded); - jobject _protocol = toJavaMultipeerTransport(env, protocol); env->CallStaticVoidMethod( cls_C4MultipeerReplicator, m_C4MultipeerReplicator_onDocumentEnded, (jlong) context, _peerId, - _protocol, + (jint) protocol, pushing ? JNI_TRUE : JNI_FALSE, _docs); @@ -475,7 +418,7 @@ namespace litecore::jni { &statusChangedCallback, &authenticateCallback, nullptr, - peerDiscoveredCallback, + &peerDiscoveredCallback, &replicatorStatusChangedCallback, &documentEndedCallback, nullptr, @@ -745,7 +688,6 @@ JNICALL Java_com_couchbase_lite_internal_core_impl_NativeC4MultipeerReplicator_g jobject replStatus = toJavaReplStatus(env, info->replicatorStatus); jobjectArray neighborIds = fromC4PeerID(env, info->neighbors, info->neighborCount); - jobject transports = toJavaTransportSet(env, info->onlineProtocols); jobject peerInfo = env->CallStaticObjectMethod( cls_C4MultipeerReplicator, @@ -753,7 +695,7 @@ JNICALL Java_com_couchbase_lite_internal_core_impl_NativeC4MultipeerReplicator_g jpeerId, certChain, info->onlineProtocols > 0 ? JNI_TRUE : JNI_FALSE, - transports, + info->onlineProtocols, neighborIds, replStatus); From 47bd621534f5d0cdcf4be9492d7bd77fa2978b83 Mon Sep 17 00:00:00 2001 From: Aniket Singh Date: Wed, 11 Mar 2026 14:22:10 +0530 Subject: [PATCH 11/11] Adding replicatorTansport API --- common/main/cpp/native_c4multipeerreplicator.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/common/main/cpp/native_c4multipeerreplicator.cc b/common/main/cpp/native_c4multipeerreplicator.cc index 283b5b011..9db2bbd95 100644 --- a/common/main/cpp/native_c4multipeerreplicator.cc +++ b/common/main/cpp/native_c4multipeerreplicator.cc @@ -101,7 +101,7 @@ namespace litecore::jni { m_C4MultipeerReplicator_createPeerInfo = env->GetStaticMethodID( cls_C4MultipeerReplicator, "createPeerInfo", - "([B[BZ[[BILcom/couchbase/lite/internal/core/C4ReplicatorStatus;)Lcom/couchbase/lite/PeerInfo;"); + "([B[BZ[[BIILcom/couchbase/lite/internal/core/C4ReplicatorStatus;)Lcom/couchbase/lite/PeerInfo;"); if (m_C4MultipeerReplicator_createPeerInfo == nullptr) return false; @@ -695,7 +695,8 @@ JNICALL Java_com_couchbase_lite_internal_core_impl_NativeC4MultipeerReplicator_g jpeerId, certChain, info->onlineProtocols > 0 ? JNI_TRUE : JNI_FALSE, - info->onlineProtocols, + (int) info->onlineProtocols, + (int) info->replicatorProtocol, neighborIds, replStatus);