Nearby connections is a peer-to-peer networking API that allows apps to easily connect, discover and exchange data with the nearby device in real-time, regardless of network connectivity. In other words, we can say that Nearby connections enable advertising, discovery and connection between nearby devices in a fully offline peer-to-peer manner. The connection between devices is high-bandwidth, low-latency and fully encrypted to enable fast, secure data transfers. Nearby connections API uses a combination of Bluetooth, BLE and wifi hotspots.
Nearby Connection API implementations
We implement nearby connections API in two phases:
- Pre-Connection
- Post-Connection
In the pre-connection phase, the Advertiser device advertises itself, while discovers devices search the nearby advertiser devices and send connection requests. A connection request from a Discoverer to an Advertiser initiates a symmetric authentication flow that results in both sides independently accepting (or rejecting) the connection request.
After a connection request is accepted by both sides, the connection is considered to be established and the devices enter the post-connection phase, during which both sides can exchange data.
How to advertise the device?
Advertisers begin by invoking startAdvertising(), passing in a ConnectionLifecycleCallback which will be notified whenever a Discoverer wants to connect via the onConnectionInitiated() callback.
How to discover devices?
Discoverers begin by invoking startDiscovery(), passing in an EndpointDiscoveryCallback which will be notified whenever a nearby Advertiser is found via the onEndpointFound() callback.
Steps for implementing Nearby Connection API:
There are following steps for implementing nearby connection API as follows:
1. Add dependency for nearby connections
1 2 3 4 5 |
dependencies { implementation 'com.google.android.gms:play-services-nearby:17.0.0' } |
2. Add permission in Manifest.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<!-- Required for Nearby Connections --> <uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <!-- Optional: only required for FILE payloads --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> |
3. Advertise the advertiser devices
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
Task<Void> startAdvertisingResultPendingResult = Nearby.getConnectionsClient(context) .startAdvertising(userName, AppConstant.SERVICE_ID, connectionLifeCycleCallback, new AdvertisingOptions.Builder().setStrategy(Strategy.P2P_STAR).build()); startAdvertisingResultPendingResult.addOnSuccessListener(new OnSuccessListener<Void>() { @Override public void onSuccess(Void aVoid) { } }); startAdvertisingResultPendingResult.addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { } }); |
4. Discover the advertiser devices
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
Task<Void> statusPendingResult = Nearby.getConnectionsClient(context). startDiscovery(AppConstant.SERVICE_ID, new endPointDiscoverCallback, new DiscoveryOptions.Builder().setStrategy(Strategy.P2P_STAR).build()); statusPendingResult.addOnSuccessListener(new OnSuccessListener<Void>() { @Override public void onSuccess(Void aVoid) { } }); statusPendingResult.addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { } }); |
5. Create a connectionLifecycleCallback class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
private final ConnectionLifecycleCallback connectionLifecycleCallback = new ConnectionLifecycleCallback () { @Override public void onConnectionInitiated(final String endpointId, final ConnectionInfo connectionInfo) { } @Override public void onConnectionResult(String endpointId, ConnectionResolution connectionResolution) { switch (connectionResolution.getStatus().getStatusCode()) { case ConnectionsStatusCodes.STATUS_OK: // We're connected! Can now start sending and receiving data. break; case ConnectionsStatusCodes.STATUS_CONNECTION_REJECTED: // The connection was rejected by one or both sides. break; case ConnectionsStatusCodes.STATUS_ERROR: // The connection broke before it was able to be accepted. break; default: // Unknown status code } } @Override public void onDisconnected(String endPointId) { } } |
6. Create a EndPointDiscoveryCallback
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
private final EndpointDiscoveryCallbacks endPointDiscoverCallback = new EndpointDiscoveryCallback() { @Override public void onEndpointFound(final String endpointId, final DiscoveredEndpointInfo discoveredEndpointInfo) { // An endpoint was found! Task<Void> statusPendingResult = Nearby.getConnectionsClient(context). requestConnection(userName, endpointId , connectionLifecycleCallback); } @Override public void onEndpointLost(String endpointId) { } } |
7. Requesting to create a connection
1 2 3 4 5 6 7 |
Task<Void> statusPendingResult = Nearby.getConnectionsClient(context). requestConnection(userName, endpointId , connectionLifecycleCallback); |
8. Accepting a connection request
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
Nearby.getConnectionsClient(CachingManager.getAppContext()) .acceptConnection(endpointId, genericPayloadCallback) .addOnSuccessListener(new OnSuccessListener<Void>() { @Override public void onSuccess(Void aVoid) { } }) .addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { } }); |
9. For rejecting the connection request
1 |
Nearby.getConnectionsClient(context).rejectConnection(endpointId)) |
10. For sending the data to the endpoint
1 2 3 4 5 6 7 |
Nearby.getConnectionsClient(context) .sendPayload(endPoints,payload) .addOnSuccessListener(resultCallback) .addOnFailureListener(failureListener); |
11. For receiving the data
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
protected class ReceiveBytesPayloadListener extends PayloadCallback { @Override public void onPayloadReceived(String endpointId, Payload payload) { // This always gets the full data of the payload. Will be null if it's not a BYTES // payload. You can check the payload type with payload.getType(). byte[] receivedBytes = payload.asBytes(); } @Override public void onPayloadTransferUpdate(String endpointId, PayloadTransferUpdate update) { // Bytes payloads are sent as a single chunk, so you'll receive a SUCCESS update immediately // after the call to onPayloadReceived(). } } |
12. To stop all endpoints
1 |
Nearby.getConnectionsClient(context).stopAllEndpoints(); |
13. To disconnect from the endpoint
1 |
Nearby.getConnectionsClient(context).disconnectFromEndpoint(endpoint); |
14. For stop advertising
1 |
Nearby.getConnectionsClient(context).stopAdvertising(); |
15. To stop discovering
1 |
Nearby.getConnectionsClient(context).stopDiscovery(); |