One of the first things users will want to do with a new smart home device is get it on their wireless network. Many IoT devices lack a screen or keyboard, so one way to do this is by allowing users to pair a smartphone to the device so that they can control and configure the device. This is how Nest and Google Home work, among others, and the Nearby Connections 2.0 API makes it possible.
In this article you'll get an introduction to the Nearby Connections 2.0 API and how it can be used to pair an Android smartphone to an Android Things device in order to provide your users with a companion device experience.
What Is the Nearby Connections API?
The Nearby Connections API allows two devices to communicate with each other directly over Bluetooth or wireless without the use of a centralized access point. There are two roles that a device may take on: advertiser, which lets other devices know that it is available to be connected to, and discoverer, which attempts to find advertisers and connect to them. Once a set of devices (also known as "endpoints" at this stage) have connected together, they may send data to any other endpoint on the Nearby Connections network.
There are two strategies that the Nearby Connections API can use for connecting devices together. The first, P2P_STAR, is the simplest to work with. It consists of one advertiser that can support multiple discoverers connecting to it. The second, P2P_CLUSTER, allows any number of devices to connect to, and accept connections from, any other number of devices. This creates a mesh network with a less centralized point of failure, though it also takes up more bandwidth. This strategy is ideal for smaller payloads that do not need to go through a central device, such as for games.
This tutorial will focus on using the simpler star strategy to connect the IoT device as an advertiser and will use the user’s smartphone as a discoverer. However, by the end, you should also have enough information to implement a cluster strategy as well.
Let’s Get Set Up!
There will be two modules for this tutorial: the mobile app and the Android Things app. Once you have created those in Android Studio, you will need to include the Google Play Services dependency for Nearby Connections in the module-level build.gradle file for both apps.
compile ‘com.google.android.gms:play-services-nearby:11.6.2’
After you have run a gradle sync, open the AndroidManifest.xml files for both modules and include the following permissions within the application
nodes.
<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" />
Android Things devices will have these permissions granted to the device after rebooting, though you will need to request the location permission from users on the phone app.
The MainActivity
class in both the things and mobile modules will need to implement the interfaces used for Google Play Services callbacks, like so:
public class MainActivity extends FragmentActivity implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener { @Override public void onConnected(@Nullable Bundle bundle) {} @Override public void onConnectionSuspended(int i) {} @Override public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {} }
Once you have validated that the user has the proper location permissions in onCreate()
, you can begin connecting to Google Play Services to use the Nearby Connections API.
mGoogleApiClient = new GoogleApiClient .Builder(this, this, this) .addApi(Nearby.CONNECTIONS_API) .enableAutoManage(this, this) .build();
When the GoogleApiClient
has finished connecting, the onConnected()
method will be called. This is where you will start the advertising or discovery process for your device. In addition, both applications will need a service id, which is a unique String
identifier.
private static final String SERVICE_ID = "UNIQUE_SERVICE_ID";
Advertising on Nearby Connections
When working with the Nearby Connections API, you will need to create a ConnectionLifecycleCallback
that will, as the name implies, be triggered on various connection lifecycle events. For this demo, we will only use the onConnectionInitiated()
method. It will save a reference to the first endpoint that attempts to connect to it, accept the connection, and then stop advertising. If the connection is not successful, the app can restart advertising.
private final ConnectionLifecycleCallback mConnectionLifecycleCallback = new ConnectionLifecycleCallback() { @Override public void onConnectionInitiated(String endpointId, ConnectionInfo connectionInfo) { endpoint = endpointId; Nearby.Connections.acceptConnection(mGoogleApiClient, endpointId, mPayloadCallback) .setResultCallback(new ResultCallback<com.google.android.gms.common.api.Status>() { @Override public void onResult(@NonNull com.google.android.gms.common.api.Status status) { if( status.isSuccess() ) { //Connection accepted } } }); Nearby.Connections.stopAdvertising(mGoogleApiClient); } @Override public void onConnectionResult(String endpointId, ConnectionResolution result) {} @Override public void onDisconnected(String endpointId) {} };
You may have noticed that the above method also references a PayloadCallback
object. This object has methods that are called when a payload of data is sent from the advertiser to an endpoint, as well as when data is received from an endpoint. The onPayloadReceived()
method is where we would handle any data send to our Android Things device. This method contains the Payload
object that can be turned into an array of bytes, and a String
representing the endpoint address of the sending device.
private PayloadCallback mPayloadCallback = new PayloadCallback() { @Override public void onPayloadReceived(String endpoint, Payload payload) { Log.e("Tuts+", new String(payload.asBytes())); } @Override public void onPayloadTransferUpdate(String endpoint, PayloadTransferUpdate payloadTransferUpdate) {} };
At this point, you can start advertising on your IoT device with the following method:
Nearby.Connections.startAdvertising( mGoogleApiClient, "Device Name", SERVICE_ID, mConnectionLifecycleCallback, new AdvertisingOptions(Strategy.P2P_STAR));
You may notice that this is where we apply the P2P_STAR strategy to our Nearby Connections network.
When you want to send a payload to another device, you can use the Nearby.Connections.sendPayload()
method with the Google API client reference, the name of your endpoint, and a byte array of the data you would like to send.
Nearby.Connections.sendPayload(mGoogleApiClient, endpoint, Payload.fromBytes("Message".getBytes()));
Tip: Enable WiFi on Reboot
One trick that I found useful while working with the Nearby Connections API on an Android Things device is re-enabling WiFi on reboot, as the device can end up with wireless disabled if the device is shut down or loses power while advertising. You can do this by retrieving the WifiManager
system service and calling setWifiEnabled()
.
wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE); wifiManager.setWifiEnabled(true);
Discover Devices With Nearby Connections
Discovering a device follows a mostly similar pattern to advertising. The device will connect to the Google API Client and start discovering. When an advertiser is found, the discoverer will request to connect to the advertiser. If the advertiser approves the request, then the two devices will connect and be able to send payloads back and forth. The discoverer will use a PayloadCallback
just like the advertiser.
private PayloadCallback mPayloadCallback = new PayloadCallback() { @Override public void onPayloadReceived(String s, Payload payload) { Log.e("Tuts+", new String(payload.asBytes())); } @Override public void onPayloadTransferUpdate(String s, PayloadTransferUpdate payloadTransferUpdate) {} };
The discoverer's (the mobile app's) ConnectionLifecycleCallback
will also look similar to the advertiser's:
private final ConnectionLifecycleCallback mConnectionLifecycleCallback = new ConnectionLifecycleCallback() { @Override public void onConnectionInitiated(String endpointId, ConnectionInfo connectionInfo) { Nearby.Connections.acceptConnection(mGoogleApiClient, endpointId, mPayloadCallback); mEndpoint = endpointId; Nearby.Connections.stopDiscovery(mGoogleApiClient); } @Override public void onConnectionResult(String endpointId, ConnectionResolution result) {} @Override public void onDisconnected(String endpointId) {} };
What is different is that discoverers will require an EndpointDiscoveryCallback
that will be used when an advertiser is found but not yet connected to. This object will initiate the request to connect to the advertiser.
private final EndpointDiscoveryCallback mEndpointDiscoveryCallback = new EndpointDiscoveryCallback() { @Override public void onEndpointFound( String endpointId, DiscoveredEndpointInfo discoveredEndpointInfo) { if( discoveredEndpointInfo.getServiceId().equalsIgnoreCase(SERVICE_ID)) { Nearby.Connections.requestConnection( mGoogleApiClient, "Name", endpointId, mConnectionLifecycleCallback); } } @Override public void onEndpointLost(String endpointId) { Log.e("Tuts+", "Disconnected"); } };
Once your discoverer has connected to Google Play Services, you can initiate discovery with the following command:
Nearby.Connections.startDiscovery( mGoogleApiClient, SERVICE_ID, mEndpointDiscoveryCallback, new DiscoveryOptions(Strategy.P2P_STAR));
Finally, when you want to disconnect from an advertiser, you can use the disconnectFromEndpoint()
method from the Nearby Connections API. It's generally a good idea to do this in your Activity
's onDestroy()
callback.
Nearby.Connections.disconnectFromEndpoint(mGoogleApiClient, mEndpoint);
Conclusion
In this article you learned about the Nearby Connections 2.0 API for Android in the context of creating a companion app for an Android Things IoT device.
It's worth noting that this API can be used for any Android devices that you would like to network together, from phones and tablets to Android TV boxes and Android Wear smartwatches. The API provides a simple way to connect and communicate without the use of the Internet or a centralized router, and adds a great utility to your collection of tools for Android development.
While you're here, check out some of our other posts on Android Things IoT development!
- Android SDKAndroid Things: Peripheral Input/Output
- Android ThingsAndroid Things and Machine Learning
- Android ThingsAndroid Things: Creating a Cloud-Connected Doorman
- Android SDKAndroid Things: Understanding and Writing Drivers