Scanning for Bluetooth Devices in Android

Our mission is to build quality, efficient and cutting edge software solutions for our clients while building strong, well-rounded careers for our employees.

24 November 2015 alan.monson@stgconsulting.com Comments Off on Scanning for Bluetooth Devices in Android General Software Development

Author: Eduardo Flores
In this post I’m going to outline how to get a list of available nearby bluetooth devices to show when scanned by an Adroid device. I’ll review how you can prompt the user to enable/turn on bluetooth in your Android device, how to show already paired bluetooth devices, and how to scan for new nearby bluetooth devices.

For this tutorial, you’ll need a physical Android device (I haven’t found a way to make the emulator proxy the computer’s bluetooth signal), and I guess at least another bluetooth device in range for you to test things.
The source code for this Android Studio project is linked at the bottom. 

Note: This tutorial DOES NOT explain how to pair, connect and send data to/from another Bluetooth device. I might make another post for those specific subjects.
Also, this tutorial is based on regular Bluetooth and not Bluetooth Low Energy (LE) 

Turn on Bluetooth

While a user can turn Bluetooth on from multiple places or ways in an Android device, one of the issue I had on a client’s project was the need for a unified way to show the user how to turn on Bluetooth in their android device. So instead of making X number of screenshots for “Getting started” guides depending on the OS level and the skin of the device, this approach is mainly universal (sure, the dialog might look slightly different on a different OS level)

So, the plan is to have a button in the app that says something like “Enable Bluetooth” and prompts the user for permission to enable Bluetooth:

Bluetooth button in android devices

In here Bluetooth is disabled

bluetooth permission request on android phone
After selecting the “Enable BT” in the app, the user gets prompt for permission to turn on Bluetooth. 

bluetooth notification in android drawer
Now Bluetooth is enabled on this device
 
This is super easy, so let’s get started.

The first thing you need to do is get the permissions in the Manifest.xml file to access Bluetooth:

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


Then in our activity, which in my case will be the MainActivity.java, we need to create a few variables:

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;

public class MainActivity
{
   private String LOG_TAG; // Just for logging purposes. Could be anything. Set to app_name
   private int REQUEST_ENABLE_BT = 99; // Any positive integer should work.
   private BluetoothAdapter mBluetoothAdapter;

   @Override
   protected void onCreate(Bundle savedInstanceState)
   {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);

       LOG_TAG = getResources().getString(R.string.app_name);
       mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
   }
}


What did I do here?

    1. I create a constant String named LOG_TAG with just the name of the app to use it in LogCat
    1. We will be starting the Bluetooth adapter with an intent and we will need a number for the request in the Intent. Because of this I created a variable named REQUEST_ENABLE_BT with a number of 99. Any positive integer (unsigned int) should work.
  1. Create a new BluetoothAdapter variable, and instantiate it once the app starts to get the defaultAdapter() from the BluetoothAdapter.

With that, my “Enable BT” button calls a local method to enable Bluetooth on the device, like this:

private void enableBluetoothOnDevice()
{
   if (mBluetoothAdapter == null)
   {
       Log.e(LOG_TAG, “This device does not have a bluetooth adapter”);
       finish();
       // If the android device does not have bluetooth, just return and get out.
       // There’s nothing the app can do in this case. Closing app.
   }

   // Check to see if bluetooth is enabled. Prompt to enable it
   if( !mBluetoothAdapter.isEnabled())
   {
       Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
       startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
   }
}


In here:

    1. I check to see if the varible from the BluetoothAdapter.getDefaultAdapter() (so the mBluetoothAdapter variable) is null. If it is, it means this Android device does not have Bluetooth. In my case I’m logging this as an error and I’m closing the app. There’s no point on doing a tutorial on Bluetooth using a device without Bluetooth.
    1. If there is a Bluetooth adapter, I check if it’s not enabled (!mBluetoothAdapter.isEnabled()). If the device is not enabled, we need to turn it on.
  1. Create a new Intent that calles the BluetoothAdapter.ACTION_REQUEST_ENABLE intent, start it with the arbitrary variable of 99 created in the previous step.

 At this point you have successfully prompt the user to enable Bluetooth!

But, umm…..how do we know if the user says no?

Since we started the Intent to start turn on Bluetooth with the “startActivityForResult”, we can then get a result back using the “onActivityResult” method, like this:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
   super.onActivityResult(requestCode, resultCode, data);

   if (requestCode == REQUEST_ENABLE_BT)
   {
       if (resultCode == 0)
       {
           // If the resultCode is 0, the user selected “No” when prompt to
           // allow the app to enable bluetooth.
           // You may want to display a dialog explaining what would happen if
           // the user doesn’t enable bluetooth.
           Toast.makeText(this, “The user decided to deny bluetooth access”, Toast.LENGTH_LONG).show();
       }
       else
           Log.i(LOG_TAG, “User allowed bluetooth access!”);
   }
}


Since this is a default Android method I’m overriding it. I then check if the requestCode that comes back is the same 99 I created, and if it is I know this answer is coming from the user prompt for Bluetooth I just created.
If the resultCode of this request is 0, it means the user selected no. At this point its up to you what to do but in my case I’m giving a Toast dialog blaming the user for pushing the wrong button.
Any other resultCode means the user accepted the promp.

And…we’re done with the prompt to turn on Bluetooth!
Moving on to the next portion of this tutorial.

Show list of already paired (bonded) Bluetooth devices

Pairing with a device is usually a one time thing, but connecting with already paired devices is a recurring thing.
Luckily for us the OS keeps a list (a Set technically…) of all of the Bluetooth devices this Android device has already paired with, and some information out of them. In this portion we’re going to show a list of already paired devices and what information the OS is giving us:
stgphone4
Note: different devices will show different information, so don’t worry much about null values at this point.

What you see above is what my Note 4 device has been paired with. The first is a Nexus 7 tablet, and the second one is a music player. The information displayed here is whatever the OS already knows of the devices, which is not a lot.

In my app I have a second button for displaying already paired devices, which calls a local method named “getArrayOfAlreadyPairedBluetoothDevices()”, which has this block of code:

private ArrayList getArrayOfAlreadyPairedBluetoothDevices()
{
   ArrayList <BluetoothObject> arrayOfAlreadyPairedBTDevices = null;

   // Query paired devices
   Set <BluetoothObject> pairedDevices = mBluetoothAdapter.getBondedDevices();
   // If there are any paired devices
   if (pairedDevices.size() > 0)
   {
       arrayOfAlreadyPairedBTDevices = new ArrayList<BluetoothObject>();

       // Loop through paired devices
       for (BluetoothDevice device : pairedDevices)
       {
           // Create the device object and add it to the arrayList of devices
           BluetoothObject bluetoothObject = new BluetoothObject();
           bluetoothObject.setBluetooth_name(device.getName());
           bluetoothObject.setBluetooth_address(device.getAddress());
           bluetoothObject.setBluetooth_state(device.getBondState());
           bluetoothObject.setBluetooth_type(device.getType());    // requires API 18 or higher
           bluetoothObject.setBluetooth_uuids(device.getUuids());

           arrayOfAlreadyPairedBTDevices.add(bluetoothObject);
       }
   }

   return arrayOfAlreadyPairedBTDevices;
}


Let’s break this down:

    1. I created a new BluetoothObject class to create an object that I can pass around with data
    1. I also know my final product is going to be a list of BluetoothObjects, so I need to make this method return an ArrayList of BluetoothObjects
    1. I then ask the mBluetoothAdapter to give me the set of already bonded devices (mBluetoothAdapter.getBondedDevices())
    1. I check to see if the set contains at least 1 element.
    1. I then iterate the set of elements, extract each BluetoothDevice (OS provided type), and then set the values of my custom BluetoothObject with the values of the BluetoothDevice
  1. Finally I add my custom BluetoothDevice to a new ArrayList I created, and I return the ArrayList

And that is also all we need to do to get the list of already bonded/paired devices.
Again, this is super easy.

Finally we will scan for new  nearby Bluetooth devices in range.

Scan for nearby Bluetooth devices in range

Out of the whole project this is the most complicated section, but even then its super easy as well.
There are a few things you need to know before we start this section:

    1. You can only scan for other nearby Bluetooth devices that are already set to be discoverable.
    1. Scanning for nearby Bluetooth devices is Asynchronous, which means the OS will do this in the background at its own speed and rate
    1. We can only scan for X amount of time (currently 12 seconds) and then the discovery ends. Restarting the discovery usually works but if you restart it many times in a row without a break, the OS returns weird Bluetooth devices. This is the same on the OS’ settings app
  1. You’re on charge of stopping/canceling the discovery of Bluetooth devices

 So, when it’s all said and done, the app will display this:
stgphone5
We click on the “Scan for Bluetooth devices” and in this case I decided to start a new Activity to do this process.

We click on the “Scan for Bluetooth devices” and in this case I decided to start a new Activity to do this process.
Here’s the code of this new “FoundBTDevices.java” class:

private void displayListOfFoundDevices()
{
   arrayOfFoundBTDevices = new ArrayList<BluetoothObject>();

   // start looking for bluetooth devices
   mBluetoothAdapter.startDiscovery();

   // Discover new devices
   // Create a BroadcastReceiver for ACTION_FOUND
   final BroadcastReceiver mReceiver = new BroadcastReceiver()
   {
       @Override
       public void onReceive(Context context, Intent intent)
       {
           String action = intent.getAction();
           // When discovery finds a device
           if (BluetoothDevice.ACTION_FOUND.equals(action))
           {
               // Get the bluetoothDevice object from the Intent
               BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);

               // Get the “RSSI” to get the signal strength as integer,
               // but should be displayed in “dBm” units
               int rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI,Short.MIN_VALUE);

               // Create the device object and add it to the arrayList of devices
               BluetoothObject bluetoothObject = new BluetoothObject();
               bluetoothObject.setBluetooth_name(device.getName());
               bluetoothObject.setBluetooth_address(device.getAddress());
               bluetoothObject.setBluetooth_state(device.getBondState());
               bluetoothObject.setBluetooth_type(device.getType());    // requires API 18 or higher
               bluetoothObject.setBluetooth_uuids(device.getUuids());
               bluetoothObject.setBluetooth_rssi(rssi);

               arrayOfFoundBTDevices.add(bluetoothObject);

               // 1. Pass context and data to the custom adapter
               FoundBTDevicesAdapter adapter = new FoundBTDevicesAdapter(getApplicationContext(), arrayOfFoundBTDevices);

               // 2. setListAdapter
               setListAdapter(adapter);
           }
       }
   };
   // Register the BroadcastReceiver
   IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
   registerReceiver(mReceiver, filter);
}


This works by receiving data in a BroadcastReceiver, so let’s break it down:

    1. Tell the BluetoothAdapter to start the discovery of new devices
    1. Create a Broadcast receiver and override the “onReceive” method
    1. The oddest part on this is the line of “BluetoothDevice.ACTION_FOUND.equals(Intent.getAction())” simply because this part you have to take it on faith as is “just do that and it’ll work”. Those kind of suck
    1. Just like in the Set of already paired devices, we need to get a BluetoothDevice object, and in this case we’re getting it from the intent: “BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);”
    1. Create a new object, put it on an ArrayList and start the list from here by setting the list adapter to the adapter just created
  1. Finally, register the BroadcastReceiver

One thing you might notice different here is that I’m not returning an ArrayList and then passing that to the adapter.
The reason for this is because this is an Asynchronous task. This means the discovery of the devices might take all 12 seconds (or more) so returning an ArrayList immediately won’t really work.
Also, by passing this data directly this way the list can be updated automatically with new devices coming in.
Alternately you could display a “wait while we look for devices” message and then return an ArrayList after X amount of seconds…it’s up to you.

And finally, we’re on charge of canceling the discovery. This doesn’t get done automatically so if you start it and 2 seconds later you decide to close your app, the OS will continue searching for devices and eating up battery.
So:

@Override
protected void onPause()
{
   super.onPause();

   mBluetoothAdapter.cancelDiscovery();
}


I hope this has helped. For more information regarding bluetooth permissions, mobile development, and other Android tutorials, check out our other blog posts.

To learn how you can work with a STG Consultant for your software technology needs, contact us— we’d love to hear from you.


The source code for this project can be found here.

Author: Eduardo Flores

Tags: