This article will talke about how to use Bluetooth 2.0 on Android? The source code is based on Android L.

Get the BluetoothAdapter, search for a Bluetooth device, initiate a socket connection, and get data from a peripheral Bluetooth device. SPP is rarely used now, and the mainstream is BLE.

The common class

BluetoothAdapter

First call the static method getDefaultAdapter() to get the Bluetooth adapter bluetoothadapter.
If the return is empty, it means that the device does not support Bluetooth.

Represents a local Bluetooth adapter. The BluetoothAdapter allows you to perform basic Bluetooth operations. Such as initializing a search device, retrieving a paired device, instantiating a BluetoothDevice based on the MAC address , establishing a BluetoothServerSocket that listens for connection requests from other devices, and initiating a search for the Bluetooth LE device.

This is the starting point for using Bluetooth. Once the local adapter is obtained, you can call getBondedDevices()the BluetoothDevice object that represents the paired device to startDiscovery()start searching for Bluetooth devices. 

Or create a BluetoothServerSocket called listenUsingRfcommWithServiceRecord(String, UUID)to listen for access requests.
startLeScan(LeScanCallback)Search for Bluetooth LE devices.

The corresponding source BluetoothAdapter.java (frameworks/base/core/java/android/bluetooth)



/**
 * Get a handle to the default local Bluetooth adapter.
 * <p>Currently Android only supports one Bluetooth adapter, but the API
 * could be extended to support more. This will always return the default
 * adapter.
 * @return the default local adapter, or null if Bluetooth is not supported
 *         on this hardware platform
 */
public static synchronized BluetoothAdapter getDefaultAdapter() {
    if (sAdapter == null) {
        IBinder b = ServiceManager.getService(BLUETOOTH_MANAGER_SERVICE);
        if (b != null) {
            IBluetoothManager managerService = IBluetoothManager.Stub.asInterface(b);
            sAdapter = new BluetoothAdapter(managerService);
        } else {
            Log.e(TAG, "Bluetooth binder is null");
        }
    }
    return sAdapter;
}

BluetoothSocket

A socket connected or connected uses a BluetoothServerSocket to create a listening service socket.
For the server, when the BluetoothServerSocket receives a connection, it will return a new BluetoothSocket to manage the connection.
For the client, use a separate BluetoothSocket to initiate a send connection and manage the connection.
The most common mode for Bluetooth sockets is RFCOMM, which is the mode supported by the Android API.
RFCOMM is connection oriented and uses streaming. Also known as Serial Port Profile (SPP)

Create a BluetoothSocket to a known Bluetooth device and use BluetoothDevice.createRfcommSocketToServiceRecord()
it to call connect()to connect to the remote device. Calling this method will block the program until a connection is established or the connection fails.

Once the socket is connected, whether initialized as a client or server, the IO stream is called getInputStream()and getOutputStream()opened to receive the input stream and the output stream object, respectively. Stream object is automatically connected to the socket.

BluetoothSocket is thread safe. In particular, the close()ongoing operation is immediately closed and the socket is closed.

Requires BLUETOOTH related permissions.

BluetoothServerSocket

A listening Bluetooth socket is on the server side, using BluetoothServerSocket to create a listening service socket.

Used BluetoothAdapter.listenUsingRfcommWithServiceRecord()to establish a BluetoothServerSocket that accept()listens for incoming connections and then calls the listen for connection request. 

This call will block until a connection is established, and a BluetoothSocket that manages the connection gets a BluetoothSocket and no longer needs to be connected. It can be called close()to close the BluetoothServerSocket and close the BluetoothServerSocket without closing the returned BluetoothSocket.

BluetoothServerSocket is thread-safe, in particular, close()it immediately shuts down the ongoing operation and closes the service socket.

BluetoothDevice

Represents a remote Bluetooth device. BluetoothDevice allows you to connect to other devices or query device information such as name, address, category and connection status.

This is a simple wrapper class for Bluetooth hardware addresses. Finding objects of a class is immutable. The operation of this class will be reflected in the remote Bluetooth hardware address.

Obtain a BluetoothDevice via a known MAC address (discovered by BluetoothDevice) or via a BluetoothAdapter.getBondedDevices()returned connected device call BluetoothAdapter.getRemoteDevice(String).
Then you can open the BluetoothSocket to establish a connection with the remote device, callcreateRfcommSocketToServiceRecord(UUID).

API description

  • Search for other Bluetooth devices
  • Retrieve matching Bluetooth devices
  • Establish RFCOMM channel
  • Connect to other devices by discovering services
  • Manage multiple connections

Setting Up Bluetooth Setting up Bluetooth

Before using Bluetooth communication, make sure the device supports Bluetooth and turn Bluetooth on.

1. Obtain the BluetoothAdapter to obtain the Bluetooth adapter of the machine using the static method BluetoothAdapter.getDefaultAdapter(); if it returns null, it means the machine does not support Bluetooth.

BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter == null) {
    // Device does not support Bluetooth
}

2. Activate Bluetooth to check if Bluetooth is turned on; if it is not turned on, you can use the following method to turn on Bluetooth.

if (!mBluetoothAdapter.isEnabled()) {
    Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}

A dialog box will pop up asking the user to turn on Bluetooth. If it is successfully opened, the activity’s onActivityResult() will receive RESULT_OK, if it is not open,
it will receive RESULT_CANCELED.

Allows the app to listen to ACTION_STATE_CHANGED and emit this broadcast when the Bluetooth state changes. This broadcast includes the original Bluetooth status and the current status, which are installed in
EXTRA_PREVIOUS_STATE and EXTRA_STATE respectively .
Status may be STATE_TURNING_ON, STATE_ON, STATE_TURNING_OFF, and STATE_OFF.

Finding Devices Looking for equipment

Use the BluetoothAdapter to find a remote Bluetooth device or retrieve a matching device.

The search device is a Bluetooth device program that is searched for in the local area. Bluetooth devices have a visible mode and an invisible mode. If a device is discoverable, it will search for the request and return some information, such as device name, category, and independent MAC address. With this information, the device can initiate a connection to the discovered device.

The first time you connect to another device, a pairing request will automatically pop up to the user. After successful pairing, the basic information of the device will be saved and can be called by the Bluetooth API.

Using the MAC address of a known remote device, you can establish a connection without searching for the device.

Pairing and connection are different. Pairing means that two devices know the existence of the other party, have mutually authenticated keys, and can establish an encrypted connection with the other party.

Connection means that the device currently shares an RFCOMM channel and can send data to each other. Currently, the Android Bluetooth API requires devices to be paired first and then connected.

Note: Android devices are not visible by default Bluetooth. The user can have the device Bluetooth searchable in the system settings.

Querying paired devices Retrieving paired devices

Before searching for a device, you can call to getBondedDevices()retrieve the paired device. This will return the BluetoothDevices collection of the paired device.

For example, you can retrieve paired devices and store each device information in an ArrayAdapter:

Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
// If there are paired devices
if (pairedDevices.size() > 0) {
    // Loop through paired devices
    for (BluetoothDevice device : pairedDevices) {
        // Add the name and address to an array adapter to show in a ListView
        mArrayAdapter.add(device.getName() + "\n" + device.getAddress());
    }
}

Only the MAC address is required to establish a connection with the BluetoothDevice object.

Discovering devices

Call the startDiscovery()search device. This asynchronous process will immediately return whether to start a successful boolean value. The search process is usually an inquiry scan for 12 seconds, followed by a page scan of each discovered device.

Your app must register a broadcast receiver to listen to ACTION_FOUND and receive information about each device it finds. Each time a device is discovered, the system sends an ACTION_FOUND.

Intent with EXTRA_DEVICE and EXTRA_CLASS, which contains a BluetoothDevice and a BluetoothClass, respectively. For example, register a broadcast receiver to listen to the discovered device:

// Create a BroadcastReceiver for ACTION_FOUND
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
    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);
            // Add the name and address to an array adapter to show in a ListView
            mArrayAdapter.add(device.getName() + "\n" + device.getAddress());
        }
    }
};
// Register the BroadcastReceiver
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(mReceiver, filter); // Don't forget to unregister during onDestroy

Warning: Searching for a device is a resource-intensive process for a Bluetooth adapter. After discovering the target device, be sure to cancelDiscovery()stop searching before connecting . Do not initiate a search when connected to a device. Searching for devices reduces the bandwidth of the connection.

Enabling discoverability activates device Bluetooth visible

If you want the local device to be visible to other devices Bluetooth, call startActivityForResult(Intent, int)ACTION_REQUEST_DISCOVERABLE to request activation of the system settings. The default is 120 seconds. 

You can request another time with EXTRA_DISCOVERABLE_DURATION. The maximum time the application can be set is 3600 seconds. 0 means the device is always visible. The number outside 0~3600 will be set to 120 seconds. For example, set the time to 300 seconds:

Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
startActivity(discoverableIntent);

A dialog will be displayed to request the user. If the user clicks “Yes”, the device will become visible to Bluetooth. The activity will receive a onActivityResult()callback and return a value that sets the visible time of the Bluetooth. If the user clicks “No” or an error occurs, the return code will be RESULT_CANCELED.

Note: If the device is not turned on Bluetooth, setting the device Bluetooth to visible will automatically turn on Bluetooth.

The device will remain silent for the allowed time. The broadcast receiver can be registered to listen to ACTION_SCAN_MODE_CHANGED. This Intent will take the EXTRA_SCAN_MODE and EXTRA_PREVIOUS_SCAN_MODE, which are the new and old states. Possible status values ​​would be:
SCAN_MODE_CONNECTABLE_DISCOVERABLE, CAN_MODE_CONNECTABLE, or SCAN_MODE_NONE

If you want to connect to a remote device, you can leave the device visible without violating it. When the application wants to establish a service socket to gain access, the device must be opened to be visible. Because the remote device must discover the local device to establish a connection.

Connecting Devices

The server device and the client device acquire the BluetoothSocket in different ways. When the connection is established, the server obtains a BluetoothSocket.
When the client opens an RFCOMM, it will get a BluetoothSocket.

Connecting as a server as a server in the connection

When you want to connect 2 devices, one of them must act as a server and hold an open BluetoothServerSocket. The purpose of the service socket is to obtain an access request and give the connected device a BluetoothSocket. When you get a BluetoothSocket from a BluetoothServerSocket, the BluetoothServerSocket can be turned off unless you want to access more connections.

  1. Call listenUsingRfcommWithServiceRecord(String, UUID)to get oneBluetoothServerSocket
  2. Call accept()to listen for access requests
  3. If you do not need to access more connections, callclose()

accept()It should not be called in the UI thread because it is blocking. Usually a new thread is started in the application to operate the BluetoothServerSocket or BluetoothSocket.
In another thread, the BluetoothServerSocket (or BluetoothSocket) close()can be called to bounce out and return immediately.

For example:

private class AcceptThread extends Thread {
   private final BluetoothServerSocket mmServerSocket;
   public AcceptThread() {
       // Use a temporary object that is later assigned to mmServerSocket,
       // because mmServerSocket is final
       BluetoothServerSocket tmp = null;
       try {
           // MY_UUID is the app's UUID string, also used by the client code
           tmp = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
       } catch (IOException e) { }
       mmServerSocket = tmp;
   }
   public void run() {
       BluetoothSocket socket = null;
       // Keep listening until exception occurs or a socket is returned
       while (true) {
           try {
               socket = mmServerSocket.accept();
           } catch (IOException e) {
               break;
           }
           // If a connection was accepted
           if (socket != null) {
               // Do work to manage the connection (in a separate thread)
               manageConnectedSocket(socket);
               mmServerSocket.close();
               break;
           }
       }
   }
   /** Will cancel the listening socket, and cause the thread to finish */
   public void cancel() {
       try {
           mmServerSocket.close();
       } catch (IOException e) { }
   }
}

The above example only requires one access connection. After the connection is established and the BluetoothSocket is obtained, the application sends the BluetoothSocket to a separate thread to process
and close the BluetoothServerSocket end loop.

When the accept()BluetoothSocket is returned, the socket is already connected. So don’t have to call connect()(the client is the same).

manageConnectedSocket()Is a fictitious method used to initialize the thread that transfers data, to discuss it in connection management.

It is usually necessary to immediately turn off the BluetoothServerSocket after the listening access connection ends. In this example, it is called immediately after getting the BluetoothSocket close().
You can write a public method in the thread to close the private BluetoothSocket. This method can be used when you need to stop listening to the service socket.

Connecting as a client as a client in the connection

To establish a connection with a remote device (server), first obtain a BluetoothDevice object that represents the remote device. You must use BluetoothDevice to get the BluetoothSocket and initialize the connection.

Basic process:

  1. Call BluetoothDevice to createRfcommSocketToServiceRecord(UUID)get BluetoothSocket
  2. Call connect()to initialize the connection

Note: You connect()must ensure that the device is not in the process of searching when you call it. When you search for a device, the connection attempt is slow and can easily fail.

private class ConnectThread extends Thread {
   private final BluetoothSocket mmSocket;
   private final BluetoothDevice mmDevice;
   public ConnectThread(BluetoothDevice device) {
       // Use a temporary object that is later assigned to mmSocket,
       // because mmSocket is final
       BluetoothSocket tmp = null;
       mmDevice = device;
       // Get a BluetoothSocket to connect with the given BluetoothDevice
       try {
           // MY_UUID is the app's UUID string, also used by the server code
           tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
       } catch (IOException e) { }
       mmSocket = tmp;
   }
   public void run() {
       // Cancel discovery because it will slow down the connection
       mBluetoothAdapter.cancelDiscovery();
       try {
           // Connect the device through the socket. This will block
           // until it succeeds or throws an exception
           mmSocket.connect();
       } catch (IOException connectException) {
           // Unable to connect; close the socket and get out
           try {
               mmSocket.close();
           } catch (IOException closeException) { }
           return;
       }
       // Do work to manage the connection (in a separate thread)
       manageConnectedSocket(mmSocket);
   }
   /** Will cancel an in-progress connection, and close the socket */
   public void cancel() {
       try {
           mmSocket.close();
       } catch (IOException e) { }
   }
}

Called before establishing a connection cancelDiscovery().

manageConnectedSocket()It’s a fictitious way to discuss it in connection management.
After use BluetoothSocket, be sure to call close()to end.

Managing a Connection

After successfully connecting two or more devices, each device has a BluetoothSocket. Devices can share data directly. Transfer any data using BluetoothSocket.

Get the InputStream and OutputStream processing and transmission, respectively, calls getInputStream()and getOutputStream()
calls read(byte[])and write(byte[])to read and write data.

The main loop of the thread should be used to read data specifically from the InputStream. There must be a special public method in the thread to write data to the OutputStream.

private class ConnectedThread extends Thread {
    private final BluetoothSocket mmSocket;
    private final InputStream mmInStream;
    private final OutputStream mmOutStream;
    public ConnectedThread(BluetoothSocket socket) {
        mmSocket = socket;
        InputStream tmpIn = null;
        OutputStream tmpOut = null;
        // Get the input and output streams, using temp objects because
        // member streams are final
        try {
            tmpIn = socket.getInputStream();
            tmpOut = socket.getOutputStream();
        } catch (IOException e) { }
        mmInStream = tmpIn;
        mmOutStream = tmpOut;
    }
    public void run() {
        byte[] buffer = new byte[1024];  // buffer store for the stream
        int bytes; // bytes returned from read()
        // Keep listening to the InputStream until an exception occurs
        while (true) {
            try {
                // Read from the InputStream
                bytes = mmInStream.read(buffer);
                // Send the obtained bytes to the UI activity
                mHandler.obtainMessage(MESSAGE_READ, bytes, -1, buffer)
                        .sendToTarget();
            } catch (IOException e) {
                break;
            }
        }
    }
    /* Call this from the main activity to send data to the remote device */
    public void write(byte[] bytes) {
        try {
            mmOutStream.write(bytes);
        } catch (IOException e) { }
    }
    /* Call this from the main activity to shutdown the connection */
    public void cancel() {
        try {
            mmSocket.close();
        } catch (IOException e) { }
    }
}

The constructor requires a stream of data, and once executed, the thread will wait for the data from the InputStream. When the read(byte[])data stream is returned, the data is sent to the main activity by the handler . Then wait for more bytes in the data stream.

Send data call outwrite().

The threading cancel()method is very important. After completing all operations of the Bluetooth connection, it should be canceled.