Two-way communication without internet: Nearby Connections (Part 2 of 3)

Isai Damier
Android Developers
Published in
5 min readJun 30, 2021

--

The Nearby Connections API allows your users to connect with each other even when no internet is available. The API uses Bluetooth and other technologies that allow for close range peer-to-peer communication, usually within 100 meters.

This opens up a lot of possibilities for what users can do or share. For example, users can collaborate within a large group or share videos, files, and text messages.

Of course, there are limitations on how large the group can be. Bluetooth can only maintain approximately 4 connections at once; WiFi caps at approximately 10 to 100 connections at once depending on whether wall-powered routers are available as bridges.

While the Nearby Messages API works across Android and iOS, the Nearby Connections API only works on Android devices. Two key advantages of the Nearby Connections API over Messages are:

  1. It allows unlimited amounts of data to be transferred over Bluetooth and peer-to-peer WiFi, whereas Messages is only for small packets (up to 4kb)
  2. It allows users to communicate with each other even without an internet connection, whereas Messages requires an internet connection

To allow your app to enable the Nearby Connections API for a user, you must first add the latest release of the following Gradle dependency in your app module.

Equally important, Android currently prompts the user to grant ACCESS_FINE_LOCATION permissions to your app on your behalf.

The following additional permissions need to be added to the manifest even though the user is not prompted to grant the permissions:

  • BLUETOOTH
  • BLUETOOTH_ADMIN
  • ACCESS_WIFI_STATE
  • CHANGE_WIFI_STATE
  • ACCESS_FINE_LOCATION

Prior to Android 10 (API level 29), your app only needed to ask for the ACCESS_COARSE_LOCATION permission. However, ACCESS_FINE_LOCATION is required for devices running Android 10 and higher. Because fine location permission implies coarse location permission in the Android framework — and the converse is not true — your app can simply request ACCESS_FINE_LOCATION for all API levels.

It’s important to remember that if a user doesn’t grant the required permissions to your app, the feature will not work and your app should handle that denial gracefully. A user can always go to Settings to revoke granted permissions, at which point the feature will stop working.

For two users (device X and device Y) of your app to connect and share content using the Nearby Connections API, the following must happen:

Both users must choose the same Strategy.

The Nearby Connections API requires that you specify the peer-to-peer Strategy that your app uses for communication.

There are three Strategy options:

  1. P2P_CLUSTER
  2. P2P_STAR
  3. P2P_POINT_TO_POINT.

Although bandwidth restrictions might not be the deciding factor with regard to which strategy you end up choosing, it’s good to know that P2P_CLUSTER tends to result in lower bandwidth connections than P2P_STAR, and that P2P_POINT_TO_POINT tends to be the highest possible throughput among the three.

Once you decide on a Strategy, the advertiser must use it for advertising:

And the discoverer must use the same Strategy during discovery:

The app on both devices must use the same serviceId

The serviceId is just an identifier your app sends to the API so the API can distinguish your app from other apps.

Communication only happens between the same serviceId: for example, serviceId=”YouTube” will never discover a device advertising serviceId=”Gmail”. The identifier should be unique. Therefore, it’s best practice to use your app’s package name as the unique identifier for all API calls that require it.

Call and response

When your app on device X calls startAdvertising(), a Bluetooth signal is transmitted.

When the signal reaches device Y, the Nearby Connections API calls the EndpointDiscoveryCallback callback in your app on device Y.

In response to detecting the advertisement from device X, device Y may choose to send a connection request to device X.

Note: While in discovery mode, Device Y may potentially be discovering endpoints from 100 nearby devices, in which case the onEndpointFound() method would be triggered 100 times. Hence, the user will usually have to select which device to send a connection request to. In our example, the user chooses Device X.

Making a connection

Although the connection request originates from device Y in response to the advertisement from device X, both device X and device Y must accept the connection in order for the actual connection to be established between the two devices.

The actual acceptance takes place within the callback ConnectionLifecycleCallback. As shown in the preceding snippets, the advertiser passes this callback to the API through the startAdvertising() method call, whereas the discoverer passes in the callback through the requestConnection() method call.

Nonetheless, the connection requests are delivered to ConnectionLifecycleCallback#onConnectionInitiated().

The connection shown in the preceding snippet is accepted automatically, but you should take further precautions when sharing sensitive data.

The API provides a unique token per connection through the ConnectionInfo object of onConnectionInitiated(String endpointId

, ConnectionInfo info). You may obtain the token from info.getAuthenticationDigits().

We recommend displaying the tokens to both users, so they can visually confirm they are equal. If visual confirmation isn’t feasible because the users cannot see each other’s screens, you can have one device encrypt the raw token, send it to the other device for decryption and comparison before you start sharing the sensitive content (more on Android encryption).

Communication

While the two devices are connected, they can send content to each other through the sendPayload() method call and the onPayloadReceived() callback.

A Payload can be anything your users want to share: streams, files, music, videos, photos, text, etc.

Each device registered their onPayloadReceived() callback through Nearby.getConnectionsClient(context).acceptConnection(endpointId, payloadCallback). Then, any time they want to share content they simply call:

Summary

The Nearby Connections API is perfect for two-way communication between Android users, especially if they want to share content without requiring cellular data or an internet connection.

For much more information on how to use the Nearby Connections API, see our Nearby Connections API documentation pages. Stay tuned for part 3 in this series, where we’ll cover the Fast Pair Service.

Check out more in Part 3 of this blog series, here.

--

--

Isai Damier
Android Developers

Android Engineer @ Google; founded geekviewpoint.com; Haitian; enjoy classical lit and chess. Twitter: @isaidamier