Scanning for Bluetooth Devices in Android

In this post I’m going to outline 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 bluetooth devices in range.

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
  2. 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.
  3. 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.
  2. 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.
  3. 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 succesfully 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
  2. 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
  3. I then ask the mBluetoothAdapter to give me the set of already bonded devices (mBluetoothAdapter.getBondedDevices())
  4. I check to see if the set contains at least 1 element.
  5. 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
  6. 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 Bluetooth devices in range.

 

Scan for 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 Bluetooth devices that are already set to be discoverable.
  2. Scanning for Bluetooth devices is Asynchronous, which means the OS will do this in the background at its own speed and rate
  3. 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
  4. 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
  2. Create a Broadcast receiver and override the “onReceive” method
  3. 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
  4. 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);”
  5. 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
  6. 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.
The source code for this project can be found here.