Discovery of Nearby Bluetooth Devices in Android

For some time now, it is common to see how Bluetooth technology is present in many of the gadgets we use daily. With it, we can share files with our friends, integrate our phone into the audio system of our car or even connect gamepads to the video game console.

There are less known uses for this technology, such as for example working as a trigger of certain events in an application. The simple detection of a Bluetooth emitter, which is placed in a well known position, allow us to state we are close to that point, so that we are able to enrich the user’s experience with contents and features which are only appropriate for that location.

One of the main problems with Bluetooth communications is that a continuous use of thm may lead to a complete drain of the battery in our device. For the last years, the invention of the Bluetooth Low Energy, BLE, and its implementation in many of the latest devices launched in the market, have opened up more possibilities as it implies less power consumption than the classic Bluetooth.

Android provide us with two different APIs, one for the classic Bluetooth and the other one for BLE. In this entry, we will have a look into the discovery or nearby devices by using them.

Classic Bluetooth

Let’s start with the classic Bluetooth API. To use it, we’ll need to add two permissions in the manifest file of our application. One is to use the application and the other one is for being able to discover the devices which are within reach. In particular, they are BLUETOOTH and BLUETOOTH_ADMIN.

<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

To be able to use the Bluetooth sensor we’ll need to get its Bluetooth adapter. The call of the static method getDefaultAdapter on the class BluetoothAdapter retrieves an instance of it. If that instance is null, it means the device doesn’t support Bluetooth.

// Get an instance of the BluetoothAdapter class
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter == null) {
    // If the adapter is null it means that the device does not support Bluetooth
}

Once we know the device has Bluetooth capabilities, we must make sure the sensor is on. If it is off we have to ask the user to enable it. The following lines will launch a dialog requesting to turn on the Bluetooth only when it is off.

if (!mBluetoothAdapter.isEnabled()) {
    // We need to enable the Bluetooth, so we ask the user
    Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    // REQUEST_ENABLE_BT es un valor entero que vale 1
    startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}

The scheme of working involves the use of a broadcast receiver which allows to declare the actions to take when some events happen in the system. To use the broadcast receiver we’ll have to register it with the function registerReceiver which is available in the context. The arguments we need will be two. The former is the broadcast receiver. The latter is the intent filter, which points out the events (broadcast intents) which will launch the actions of the receiver. We will create it with the option BluetoothDevice.ACTION_FOUND which is the one needed for the discovery of Bluetooth devices.

// Register the broadcast receiver
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(mReceiver, filter);

In these lines of code mReceiver is the broadcast receiver. The broadcast receiver is just an implementation of the abstract class BroadcastReceiver of Android. In its method onReceive we will be able to indicate the actions to take when the new device is discovered. In that function, we can have access to some information by reading the extras of the intent received. For instance:

  • In BluetoothDevice.EXTRA_DEVICE we can find the object of type BluetoothDevice which contains some basic information about the device, such as its name or MAC address, and which let us establish connection with it.
  • In BluetoothDevice.EXTRA_CLASS we can get an instance of BluetoothClass which describes roughly the characteristics of the device.
  • In BluetoothDevice.EXTRA_RSSI we may retrieve the Received Signal Strength Indication (RSSI) which is a measure of the strength of the Bluetooth signal. Not all devices support RSSI, so this value is not always present.
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (BluetoothDevice.ACTION_FOUND.equals(action)) {
            // A Bluetooth device was found
            // Getting device information from the intent
            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            Log.i(TAG, "Device found: " + device.getName() + "; MAC " + device.getAddress());
        }
    }
};

Finally, we just need to launch the scanning process for discovering Bluetooth devices. The adapter has a method for it called startDiscovery. It may happen that when we register our broadcast receiver and launch the startDiscovery the Bluetooth was already in discovery status. To force to restart the process and get the whole list of devices we can cancel it with cancelDiscovey and restart it again.

if (mBluetoothAdapter.isDiscovering()) {
   // Bluetooth is already in modo discovery mode, we cancel to restart it again
    mBluetoothAdapter.cancelDiscovery();
}
mBluetoothAdapter.startDiscovery();

Bluetooth Low Energy (BLE)

The support for BLE was included in Android in the API level 18 (Android 4.3), so we need to set the minimum API level of the application to that version to use it. We have to take into account that many of the devices which are currently being used don’t support this technology, so we must control if this feature is available before calling any of the BLE functions. If we add the line below to the manifest file of the application we will avoid that any device which doesn’t have the necessary hardware to support BLE may install it.

<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>

It is possible that the BLE part of the application we are developing is not as important as to avoid installing it if it is not supported. In that case we can control programmatically, in execution time, if BLE is supported and we can even use an alternative if it isn’t.

if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
    // BLE is supported, we can use BLE API
} else {
    // BLE is not supported, Don’t use BLE capabilities here
}

The needed permissions in the manifest file are the same as in the classic Bluetooth API and we’ll also need an instance of the BluetoothAdapter. However the scheme for working is different. In this case we won’t use a broadcast receiver but a scanning method which will call a callback function when it finishes scanning. The method startLeScan of the adapter let us pass the callback function as a parameter where we’ll be able to get the information of the discovered devices. The stopLeScan method will finish the process of scanning. It is important to stop it and scan for small intervals just when needed, because a continuous scanning may lead to drain the battery. In the callback function we will have direct access to the information needed as in the previous API, though now we don’t have to read an intent as the information is in the parameters of the function. It is placed in three different parameters: an instance of BluetoothDevice class, the value of RSSI and the content of the advertisement sent by the device we’ve found (scanRecord).

private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
    @Override
     public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
        // BLE device was found, we can get its information now
       Log.i(TAG, "BLE device found: " + device.getName() + "; MAC " + device.getAddress());
    }
};
// This callback is added to the start scan method as a parameter in this way 
// bleAdapter.startLeScan(mLeScanCallback);

It is important to know that when we are scanning for BLE devices we will only get BLE devices and not the ones which only support classic Bluetooth, so we may need to use the other API in case we needed the complete list. Also, we cannot search for both types of Bluetooth devices at the same time so we need use one after the other has finished.

Conclusions

As you may have found out, both APIs let us discover nearby Bluetooth devices in a quite easy way. The classic Bluetooth API is more mature as it has been working in many of our gadgets for a long time. On the other hand, the BLE API opens up new possibilities due to its lower power consumption, but we can’t just use this one because we will need the classic Bluetooth API to connect to the devices which don’t support BLE.

In any way, the scanning process of nearby devices may result in an intensive process, so we must use it carefully and just when needed to avoid quick-drain battery problems.

Share on LinkedInTweet about this on TwitterShare on FacebookShare on Google+Buffer this page

Leave a Comment

By completing the form you agree to the Privacy Policy