Advertisement
  1. Code
  2. Coding Fundamentals

Connect Android Things to a Smartphone With Nearby Connections 2.0

Scroll to top

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.

Google Home companion app connection screenGoogle Home companion app connection screenGoogle Home companion app connection screen

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.

1
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.

1
<uses-permission 
2
   android:name="android.permission.BLUETOOTH"/>    
3
<uses-permission 
4
   android:name="android.permission.BLUETOOTH_ADMIN"/>    
5
<uses-permission 
6
   android:name="android.permission.ACCESS_WIFI_STATE"  />
7
<uses-permission 
8
   android:name="android.permission.CHANGE_WIFI_STATE" />    
9
<uses-permission   
10
   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:

1
public class MainActivity extends FragmentActivity implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
2
    @Override    
3
    public void onConnected(@Nullable Bundle bundle) {}
4
    
5
    @Override
6
    public void onConnectionSuspended(int i) {}
7
    
8
    @Override
9
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {}
10
11
}

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.

1
mGoogleApiClient = new GoogleApiClient
2
   .Builder(this, this, this)                
3
   .addApi(Nearby.CONNECTIONS_API)                
4
   .enableAutoManage(this, this)                
5
   .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.

1
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.

1
private final ConnectionLifecycleCallback mConnectionLifecycleCallback =
2
    new ConnectionLifecycleCallback() {
3
	    @Override
4
	    public void onConnectionInitiated(String endpointId, ConnectionInfo connectionInfo) {
5
	        endpoint = endpointId;
6
7
	        Nearby.Connections.acceptConnection(mGoogleApiClient, endpointId, mPayloadCallback)
8
	                .setResultCallback(new ResultCallback<com.google.android.gms.common.api.Status>() {
9
	                    @Override
10
	                    public void onResult(@NonNull com.google.android.gms.common.api.Status status) {
11
	                        if( status.isSuccess() ) {
12
	                            //Connection accepted

13
	                        }
14
	                    }
15
	                });
16
17
	        Nearby.Connections.stopAdvertising(mGoogleApiClient);
18
	    }
19
20
	    @Override
21
	    public void onConnectionResult(String endpointId, ConnectionResolution result) {}
22
23
	    @Override
24
	    public void onDisconnected(String endpointId) {}
25
	};

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.

1
private PayloadCallback mPayloadCallback = new PayloadCallback() {
2
    @Override
3
    public void onPayloadReceived(String endpoint, Payload payload) {
4
        Log.e("Tuts+", new String(payload.asBytes()));
5
    }
6
7
    @Override
8
    public void onPayloadTransferUpdate(String endpoint, PayloadTransferUpdate payloadTransferUpdate) {}
9
};

At this point, you can start advertising on your IoT device with the following method:

1
Nearby.Connections.startAdvertising(
2
    mGoogleApiClient,
3
    "Device Name",
4
    SERVICE_ID,
5
    mConnectionLifecycleCallback,
6
    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.

1
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().

1
wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
2
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.

1
private PayloadCallback mPayloadCallback = new PayloadCallback() {
2
    @Override
3
    public void onPayloadReceived(String s, Payload payload) {
4
        Log.e("Tuts+", new String(payload.asBytes()));
5
    }
6
7
    @Override
8
    public void onPayloadTransferUpdate(String s, PayloadTransferUpdate payloadTransferUpdate) {}
9
};

The discoverer's (the mobile app's) ConnectionLifecycleCallback will also look similar to the advertiser's:

1
private final ConnectionLifecycleCallback mConnectionLifecycleCallback =
2
        new ConnectionLifecycleCallback() {
3
            @Override
4
            public void onConnectionInitiated(String endpointId, ConnectionInfo connectionInfo) {
5
                Nearby.Connections.acceptConnection(mGoogleApiClient, endpointId, mPayloadCallback);
6
                mEndpoint = endpointId;
7
                Nearby.Connections.stopDiscovery(mGoogleApiClient);
8
            }
9
10
            @Override
11
            public void onConnectionResult(String endpointId, ConnectionResolution result) {}
12
13
            @Override
14
            public void onDisconnected(String endpointId) {}
15
        };

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.

1
private final EndpointDiscoveryCallback mEndpointDiscoveryCallback =
2
    new EndpointDiscoveryCallback() {
3
        @Override
4
        public void onEndpointFound(
5
                String endpointId, DiscoveredEndpointInfo discoveredEndpointInfo) {
6
            if( discoveredEndpointInfo.getServiceId().equalsIgnoreCase(SERVICE_ID)) {
7
                Nearby.Connections.requestConnection(
8
                        mGoogleApiClient,
9
                        "Name",
10
                        endpointId,
11
                        mConnectionLifecycleCallback);
12
            }
13
        }
14
15
        @Override
16
        public void onEndpointLost(String endpointId) {
17
            Log.e("Tuts+", "Disconnected");
18
        }
19
    };

Once your discoverer has connected to Google Play Services, you can initiate discovery with the following command:

1
Nearby.Connections.startDiscovery(
2
        mGoogleApiClient,
3
        SERVICE_ID,
4
        mEndpointDiscoveryCallback,
5
        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.

1
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!

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.