-
Notifications
You must be signed in to change notification settings - Fork 6
P2P Bluetooth peer discovery changes #456
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
407ea30
BT changes for common
Aniket392 1aaf27b
Update cmakelist and bluetoothpeer enterprise check
Aniket392 b370c63
Updated as build failure due to wrong checks and static declaration
Aniket392 098dff3
Updating multipeer replicator argument to take protocol
Aniket392 d4215fd
Added release for BT peer and removed onIncommingConnection
Aniket392 6cf991e
Added documentation for APIs
Aniket392 3efb72b
Added public transport APIs and removed UUID sync
Aniket392 f9ab263
Updated the BluetoothPeer object creation in Java instead of native side
Aniket392 f18cb14
Updating addpeer and C4Peer creation and protocol usage
Aniket392 569efca
Conversion of MultipeerTransport and Enumset in java rather than in J…
Aniket392 47bd621
Adding replicatorTansport API
Aniket392 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| #if defined(COUCHBASE_ENTERPRISE) && defined(__ANDROID__) | ||
| #include <jni.h> | ||
| #include "c4PeerDiscovery.hh" | ||
| #include "native_glue.hh" | ||
|
|
||
| namespace litecore::jni { | ||
| // Helper to convert Java Map to Metadata | ||
| 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, "<init>", "(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; | ||
| } | ||
| } | ||
| #endif |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| #if defined(COUCHBASE_ENTERPRISE) && defined(__ANDROID__) | ||
| #include <jni.h> | ||
|
|
||
| #include "c4PeerDiscovery.hh" | ||
|
|
||
| #ifndef COUCHBASE_LITE_JAVA_EE_ROOT_METADATAHELPER_H | ||
| #define COUCHBASE_LITE_JAVA_EE_ROOT_METADATAHELPER_H | ||
|
|
||
| namespace litecore::jni { | ||
| C4Peer::Metadata javaMapToMetadata(JNIEnv* env, jobject map); | ||
| jobject metadataToJavaMap(JNIEnv* env, const C4Peer::Metadata& metadata); | ||
| } | ||
|
|
||
| #endif //COUCHBASE_LITE_JAVA_EE_ROOT_METADATAHELPER_H | ||
| #endif |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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. |
75 changes: 75 additions & 0 deletions
75
common/main/cpp/com_couchbase_lite_internal_core_impl_NativeBluetoothPeer.h
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| #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 <jni.h> | ||
|
|
||
| #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); | ||
|
|
||
| /* | ||
| * 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 | ||
|
|
||
| #endif //COUCHBASE_LITE_JAVA_EE_ROOT_COM_COUCHBASE_LITE_INTERNAL_CORE_IMPL_NATIVEBLUETOOTHPEER_H |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
61 changes: 61 additions & 0 deletions
61
common/main/cpp/com_couchbase_lite_internal_core_impl_NativeC4PeerDiscoveryProvider.h
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| #include <jni.h> | ||
|
|
||
| #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 jlong 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 jlong 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 |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does this mean to users who don't use MultipeerReplicator with Bluetooth?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If anyone do not use MultipeerReplicator with Bluetooth, this permission has no effect.