Manage connections

Initiate a connection

When nearby devices are found, the discoverer can initiate connections. The following example requests a connection with a device as soon as it is discovered.

private final EndpointDiscoveryCallback endpointDiscoveryCallback =
    new EndpointDiscoveryCallback() {
      @Override
      public void onEndpointFound(String endpointId, DiscoveredEndpointInfo info) {
        // An endpoint was found. We request a connection to it.
        Nearby.getConnectionsClient(context)
            .requestConnection(getLocalUserName(), endpointId, connectionLifecycleCallback)
            .addOnSuccessListener(
                (Void unused) -> {
                  // We successfully requested a connection. Now both sides
                  // must accept before the connection is established.
                })
            .addOnFailureListener(
                (Exception e) -> {
                  // Nearby Connections failed to request the connection.
                });
      }

      @Override
      public void onEndpointLost(String endpointId) {
        // A previously discovered endpoint has gone away.
      }
    };

Depending on your use case, you may wish to instead display a list of discovered devices to the user, allowing them to choose which devices to connect to.

Accept or reject a connection

After the discoverer has requested a connection to an advertiser, both sides are notified of the connection initiation process via the onConnectionInitiated() method of the ConnectionLifecycleCallback callback. Note that this callback is passed in to startAdvertising() on the advertiser and requestConnection() on the discoverer, but from this point forward, the API is symmetric.

Now both sides must choose whether to accept or reject the connection via a call to acceptConnection() or rejectConnection(), respectively. The connection is fully established only when both sides have accepted. If one or both reject, the connection is discarded. Either way, the result is delivered to onConnectionResult().

The following code snippet shows how to implement this callback:

private final ConnectionLifecycleCallback connectionLifecycleCallback =
    new ConnectionLifecycleCallback() {
      @Override
      public void onConnectionInitiated(String endpointId, ConnectionInfo connectionInfo) {
        // Automatically accept the connection on both sides.
        Nearby.getConnectionsClient(context).acceptConnection(endpointId, payloadCallback);
      }

      @Override
      public void onConnectionResult(String endpointId, ConnectionResolution result) {
        switch (result.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) {
        // We've been disconnected from this endpoint. No more data can be
        // sent or received.
      }
    };

Again, this example shows the connection being automatically accepted by both sides, but depending on your use case you may wish to present this choice to the user in some way.

Authenticate a connection

When a connection is requested, your app can authenticate the connection by using the authentication token provided to onConnectionInitiated(). This provides a way to let users confirm that they are connecting to the intended device. Both devices are given the same token, which is a short random string; it's up to you to decide how to verify it. Typically this involves showing the token on both devices and asking the users to manually compare and confirm, similar to a bluetooth pairing dialog.

This sample code demonstrates one way to authenticate by displaying the authentication token to the user in an AlertDialog:

@Override
public void onConnectionInitiated(String endpointId, ConnectionInfo info) {
  new AlertDialog.Builder(context)
      .setTitle("Accept connection to " + info.getEndpointName())
      .setMessage("Confirm the code matches on both devices: " + info.getAuthenticationDigits())
      .setPositiveButton(
          "Accept",
          (DialogInterface dialog, int which) ->
              // The user confirmed, so we can accept the connection.
              Nearby.getConnectionsClient(context)
                  .acceptConnection(endpointId, payloadCallback))
      .setNegativeButton(
          android.R.string.cancel,
          (DialogInterface dialog, int which) ->
              // The user canceled, so we should reject the connection.
              Nearby.getConnectionsClient(context).rejectConnection(endpointId))
      .setIcon(android.R.drawable.ic_dialog_alert)
      .show();
}