In this document
- The Basics
- Working with Profiles
- Health Device Profile
- Setting Up Bluetooth
- Finding Devices
- Connecting Devices
- Managing a Connection
- Key Classes and Interfaces
Related samples
The Android platform includes support for the Bluetooth network stack, which allows a device to wirelessly exchange data with other Bluetooth devices. The application framework provides access to the Bluetooth functionality through the Android Bluetooth APIs. These APIs let applications wirelessly connect to other Bluetooth devices, enabling point-to-point and multipoint wireless features.
Using the Bluetooth APIs, an Android application can perform the following:
- Scan for other Bluetooth devices
- Query the local Bluetooth adapter for paired Bluetooth devices
- Establish RFCOMM channels
- Connect to other devices through service discovery
- Transfer data to and from other devices
- Manage multiple connections
This page focuses on Classic Bluetooth. Classic Bluetooth is the right choice for more battery-intensive operations, which include streaming and communicating between Android devices. For Bluetooth devices with low power requirements, Android 4.3 (API level 18) introduces API support for Bluetooth Low Energy. To learn more, see Bluetooth Low Energy.
This document describes different Bluetooth profiles, including the Health Device Profile. It then explains how to use the Android Bluetooth APIs to accomplish the four major tasks necessary to communicate using Bluetooth: setting up Bluetooth, finding devices that are either paired or available in the local area, connecting devices, and transferring data between devices.
The Basics
In order for Bluetooth-enabled devices to transmit data between each other, they must first form a channel of communication using a pairing process. One device, a discoverable device, makes itself available for incoming connection requests. Another device finds the discoverable device using a service discovery process. After the discoverable device accepts the pairing request, the two devices complete a bonding process where they exchange security keys. The devices cache these keys for later use. After the pairing and bonding processes are complete, the two devices exchange information. When the session is complete, the device that initiated the pairing request releases the channel that had linked it to the discoverable device. The two devices remain bonded, however, so they can reconnect automatically during a future session as long as they're in range of each other and neither device has removed the bond.
Bluetooth Permissions
In order to use Bluetooth features in your application, you must declare
the Bluetooth permission BLUETOOTH.
You need this permission to perform any Bluetooth communication,
such as requesting a connection, accepting a connection, and transferring data.
If you want your app to initiate device discovery or manipulate Bluetooth
settings, you must declare the BLUETOOTH_ADMIN
permission in addition to the BLUETOOTH
permission. Most applications need this permission solely for the
ability to discover local Bluetooth devices. The other abilities granted by this
permission should not be used, unless the application is a "power manager" that
will modify Bluetooth settings upon user request.
Declare the Bluetooth permission(s) in your application manifest file. For example:
<manifest ... > <uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> ... </manifest>
See the <uses-permission> reference for more information about declaring application permissions.
Working with Profiles
Starting in Android 3.0, the Bluetooth API includes support for working with Bluetooth profiles. A Bluetooth profile is a wireless interface specification for Bluetooth-based communication between devices. An example is the Hands-Free profile. For a mobile phone to connect to a wireless headset, both devices must support the Hands-Free profile.
The Android Bluetooth API provides implementations for the following Bluetooth profiles:
- Headset. The Headset profile provides support for
Bluetooth headsets to be used with mobile phones. Android provides the
BluetoothHeadsetclass, which is a proxy for controlling the Bluetooth Headset Service. This includes both Bluetooth Headset and Hands-Free (v1.5) profiles. TheBluetoothHeadsetclass includes support for AT commands. For more discussion of this topic, see Vendor-specific AT commands - A2DP. The Advanced Audio Distribution Profile (A2DP)
profile defines how high quality audio can be streamed from one device to
another over a Bluetooth connection. Android provides the
BluetoothA2dpclass, which is a proxy for controlling the Bluetooth A2DP Service. - Health Device. Android 4.0 (API level 14) introduces support for the Bluetooth Health Device Profile (HDP). This lets you create applications that use Bluetooth to communicate with health devices that support Bluetooth, such as heart-rate monitors, blood meters, thermometers, scales, and so on. For a list of supported devices and their corresponding device data specialization codes, refer to Bluetooth's HDP Device Data Specializations. These values are also referenced in the ISO/IEEE 11073-20601 [7] specification as MDC_DEV_SPEC_PROFILE_* in the Nomenclature Codes Annex. For more discussion of HDP, see Health Device Profile.
Here are the basic steps for working with a profile:
- Get the default adapter, as described in Setting Up Bluetooth.
- Set up a
BluetoothProfile.ServiceListener. This listener notifiesBluetoothProfileclients when they have been connected to or disconnected from the service. - Use
getProfileProxy()to establish a connection to the profile proxy object associated with the profile. In the example below, the profile proxy object is an instance ofBluetoothHeadset. - In
onServiceConnected(), get a handle to the profile proxy object. - Once you have the profile proxy object, you can use it to monitor the state of the connection and perform other operations that are relevant to that profile.
For example, this code snippet shows how to connect to a BluetoothHeadset proxy object so that you can control the
Headset profile:
BluetoothHeadset mBluetoothHeadset;
// Get the default adapter
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
private BluetoothProfile.ServiceListener mProfileListener = new BluetoothProfile.ServiceListener() {
public void onServiceConnected(int profile, BluetoothProfile proxy) {
if (profile == BluetoothProfile.HEADSET) {
mBluetoothHeadset = (BluetoothHeadset) proxy;
}
}
public void onServiceDisconnected(int profile) {
if (profile == BluetoothProfile.HEADSET) {
mBluetoothHeadset = null;
}
}
};
// Establish connection to the proxy.
mBluetoothAdapter.getProfileProxy(context, mProfileListener, BluetoothProfile.HEADSET);
// ... call functions on mBluetoothHeadset
// Close proxy connection after use.
mBluetoothAdapter.closeProfileProxy(mBluetoothHeadset);
Vendor-specific AT commands
Starting in Android 3.0 (API level 11), applications can register to receive system
broadcasts of pre-defined vendor-specific AT commands sent by headsets (such as
a Plantronics +XEVENT command). For example, an application could receive
broadcasts that indicate a connected device's battery level and could notify the
user or take other action as needed. Create a broadcast receiver for the ACTION_VENDOR_SPECIFIC_HEADSET_EVENT intent
to handle vendor-specific AT commands for the headset.
Health Device Profile
Android 4.0 (API level 14) introduces support for the Bluetooth Health Device
Profile (HDP). This lets you create applications that use Bluetooth to
communicate with health devices that support Bluetooth, such as heart-rate
monitors, blood meters, thermometers, and scales. The Bluetooth Health API
includes the classes BluetoothHealth, BluetoothHealthCallback, and BluetoothHealthAppConfiguration, which are described in Key Classes and Interfaces.
In using the Bluetooth Health API, it's helpful to understand these key HDP concepts:
- Source
- A health device—such as a weight scale, glucose meter, or thermometer—that transmits medical data to a smart device, such as an Android phone or tablet.
- Sink
- The smart device that
receives the medical data. In an Android HDP application, the sink is
represented by a
BluetoothHealthAppConfigurationobject. - Registration
- The process used to register a sink for communicating with a particular health device.
- Connection
- The process used to open a channel between a health device (source) and a smart device (sink).
Creating an HDP Application
Here are the basic steps involved in creating an Android HDP application:
- Get a reference to the
BluetoothHealthproxy object.Similar to regular headset and A2DP profile devices, you must call
getProfileProxy()with aBluetoothProfile.ServiceListenerand theHEALTHprofile type to establish a connection with the profile proxy object. - Create a
BluetoothHealthCallbackand register an application configuration (BluetoothHealthAppConfiguration) that acts as a health sink. Establish a connection to a health device.
Note: Some devices will initiate the connection automatically. It is unnecessary to carry out this step for those devices.
- When connected successfully to a health device, read/write to the health device using the file descriptor. The received data need to be interpreted using a health manager which implements the IEEE 11073 specifications.
- When done, close the health channel and unregister the application. The channel also closes when there is extended inactivity.
Setting Up Bluetooth
Before your application can communicate over Bluetooth, you need to verify that Bluetooth is supported on the device, and if so, ensure that it is enabled.
If Bluetooth is not supported, then you should gracefully disable any
Bluetooth features. If Bluetooth is supported, but disabled, then you can request that the
user enable Bluetooth without leaving your application. This setup is
accomplished in two steps, using the BluetoothAdapter:
- Get the
BluetoothAdapter.The
BluetoothAdapteris required for any and all Bluetooth activity. To get theBluetoothAdapter, call the staticgetDefaultAdapter()method. This returns aBluetoothAdapterthat represents the device's own Bluetooth adapter (the Bluetooth radio). There's one Bluetooth adapter for the entire system, and your application can interact with it using this object. IfgetDefaultAdapter()returns null, then the device does not support Bluetooth and your story ends here. For example:BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); if (mBluetoothAdapter == null) { // Device does not support Bluetooth } - Enable Bluetooth.
Next, you need to ensure that Bluetooth is enabled. Call
isEnabled()to check whether Bluetooth is currently enabled. If this method returns false, then Bluetooth is disabled. To request that Bluetooth be enabled, callstartActivityForResult(), passing in anACTION_REQUEST_ENABLEintent action. This call issues a request to enable Bluetooth through the system settings (without stopping your application). For example:if (!mBluetoothAdapter.isEnabled()) { Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT); }A dialog will appear requesting user permission to enable Bluetooth, as shown in Figure 1. If the user responds "Yes," the system will begin to enable Bluetooth, and focus will return to your application once the process completes (or fails).
Figure 1: The enabling Bluetooth dialog.
The
REQUEST_ENABLE_BTconstant passed tostartActivityForResult()is a locally defined integer that must be greater than 0. The system passes this constant back to you in youronActivityResult()implementation as therequestCodeparameter.If enabling Bluetooth succeeds, your activity receives the
RESULT_OKresult code in theonActivityResult()callback. If Bluetooth was not enabled due to an error (or the user responded "No") then the result code isRESULT_CANCELED.
Optionally, your application can also listen for the
ACTION_STATE_CHANGED broadcast intent, which
the system broadcasts whenever the Bluetooth state changes. This broadcast contains
the extra fields EXTRA_STATE and EXTRA_PREVIOUS_STATE, containing the new and old
Bluetooth states, respectively. Possible values for these extra fields are
STATE_TURNING_ON, STATE_ON, STATE_TURNING_OFF, and STATE_OFF. Listening for this
broadcast can be useful if your app needs to detect runtime changes made to the Bluetooth state.
Tip: Enabling discoverability will automatically enable Bluetooth. If you plan to consistently enable device discoverability before performing Bluetooth activity, you can skip step 2 above. For more information, read the enabling discoverability, section on this page.
Finding Devices
Using the BluetoothAdapter, you can find remote Bluetooth
devices either through device discovery or by querying the list of paired
devices.
Device discovery is a scanning procedure that searches the local area for Bluetooth-enabled devices and requests some information about each one. This process is sometimes referred to as discovering, inquiring, or scanning. However, a nearby Bluetooth device responds to a discovery request only if it is currently accepting information requests by being discoverable. If a device is discoverable, it will respond to the discovery request by sharing some information, such as the device's name, its class, and its unique MAC address. Using this information, the device that is performing the discovery process can then choose to initiate a connection to the discovered device.
Once a connection is made with a remote device for the first time, a pairing request is automatically presented to the user. When a device is paired, the basic information about that device—such as the device's name, class, and MAC address—is saved and can be read using the Bluetooth APIs. Using the known MAC address for a remote device, a connection can be initiated with it at any time without performing discovery, assuming the device is still within range.
Note that there is a difference between being paired and being connected:
- To be paired means that two devices are aware of each other's existence, have a shared link-key that can be used for authentication, and are capable of establishing an encrypted connection with each other.
- To be connected means that the devices currently share an RFCOMM channel and are able to transmit data with each other. The current Android Bluetooth API's require devices to be paired before an RFCOMM connection can be established. Pairing is automatically performed when you initiate an encrypted connection with the Bluetooth APIs.
The following sections describe how to find devices that have been paired, or discover new devices using device discovery.
Note: Android-powered devices are not discoverable by default. A user can make the device discoverable for a limited time through the system settings, or an application can request that the user enable discoverability without leaving the application. For more information, see the enable discoverability section on this page.
Querying paired devices
Before performing device discovery, it's worth querying the set
of paired devices to see if the desired device is already known. To do so,
call getBondedDevices(). This
will return a set of BluetoothDevice objects representing
paired devices. For example, you can query all paired devices and
get the name and MAC address of each device, as the following
code snippet demonstrates:
Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
if (pairedDevices.size() > 0) {
// There are paired devices. Get the name and address of each paired device.
for (BluetoothDevice device : pairedDevices) {
String deviceName = device.getName();
String deviceHardwareAddress = device.getAddress(); // MAC address
}
}
To initiate a connection with a Bluetooth device, all that's needed from the
associated BluetoothDevice object is the MAC address,
which you retrieve by calling getAddress(). You can learn more about creating a connection in the section
about Connecting Devices.
Caution: Performing device discovery consumes
a lot of the Bluetooth adapter's resources. After you have found a device to
connect to, be certain that you stop discovery with
cancelDiscovery() before
attempting a connection. Also, you shouldn't perform discovery while connected
to a device because the discovery process significantly reduces the bandwidth
available for any existing connections.
Discovering devices
To start discovering devices, simply call startDiscovery(). The
process is asynchronous and returns a boolean value
indicating whether discovery has successfully started. The discovery process
usually involves an inquiry scan of about 12 seconds, followed by a page scan of
each device found to retrieve its Bluetooth name.
In order to receive information about each
device discovered, your application must register a BroadcastReceiver for the
ACTION_FOUND intent. The system
broadcasts this intent for each device. The intent contains the extra fields
EXTRA_DEVICE and
EXTRA_CLASS, which in turn contain a
BluetoothDevice and a BluetoothClass, respectively.
The following code snippet shows how you can
register to handle the broadcast when devices are discovered:
@Override
protected void onCreate(Bundle savedInstanceState) {
...
// Register for broadcasts when a device is discovered.
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(mReceiver, filter);
}
// Create a BroadcastReceiver for ACTION_FOUND.
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
// Discovery has found a device. Get the BluetoothDevice
// object and its info from the Intent.
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
String deviceName = device.getName();
String deviceHardwareAddress = device.getAddress(); // MAC address
}
}
};
@Override
protected void onDestroy() {
super.onDestroy();
...
// Don't forget to unregister the ACTION_FOUND receiver.
unregisterReceiver(mReceiver);
}
To initiate a connection with a Bluetooth device, all that's needed from the
associated BluetoothDevice object is the MAC address,
which you retrieve by calling
getAddress(). You can learn
more about creating a connection in the section about
Connecting Devices.
Caution: Performing device discovery consumes
a lot of the Bluetooth adapter's resources. After you have found a device to
connect to, be certain that you stop discovery with
cancelDiscovery() before
attempting a connection. Also, you shouldn't perform discovery while connected
to a device because the discovery process significantly reduces the bandwidth
available for any existing connections.
Enabling discoverability
If you would like to make the local device discoverable to other devices,
call startActivityForResult(Intent, int) with the
ACTION_REQUEST_DISCOVERABLE intent.
This will issue a request to enable the system's discoverable mode without having
to navigate to the Settings app, which would stop your own app. By default, the device becomes
discoverable for 120 seconds, or 2 minutes. You can define a different duration,
up to 3600 seconds (1 hour), by adding the
EXTRA_DISCOVERABLE_DURATION extra.
Caution: If you set the
EXTRA_DISCOVERABLE_DURATION extra's
value to 0, the device is always discoverable. This configuration is insecure
and therefore highly discouraged.
The following code snippet sets the device to be discoverable for 5 minutes (300 seconds):
Intent discoverableIntent =
new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
startActivity(discoverableIntent);
Figure 2: The enabling discoverability dialog.
A dialog is displayed, requesting the user's permission to make the device
discoverable, as shown in Figure 2. If the user responds "Yes," then the device
becomes discoverable for the specified amount of time. Your activity then
receives a call to the onActivityResult() callback, with the result code equal to the duration that the device
is discoverable. If the user responded "No," or if an error occurred, the result code will
be RESULT_CANCELED.
Note: If Bluetooth has not been enabled on the device, then enabling device discoverability will automatically enable Bluetooth.
The device silently remains in discoverable mode for the allotted time.
If you would like to be notified when the discoverable mode has changed, you can
register a BroadcastReceiver for the ACTION_SCAN_MODE_CHANGED
intent. This intent contains the extra fields EXTRA_SCAN_MODE and
EXTRA_PREVIOUS_SCAN_MODE, which provide the
new and old scan mode, respectively. Possible values for each extra are as follows:
SCAN_MODE_CONNECTABLE_DISCOVERABLE- The device is in discoverable mode.
SCAN_MODE_CONNECTABLE- The device is not in discoverable mode but can still receive connections.
SCAN_MODE_NONE- The device is not in discoverable mode and cannot receive connections.
If you are initiating the connection to a remote device, you do not need to enable device discoverability. Enabling discoverability is only necessary when you want your application to host a server socket that accepts incoming connections, as remote devices must be able to discover other devices before initiating connections to those other devices.
Connecting Devices
In order to create a connection between two devices, you
must implement both the server-side and client-side mechanisms because one
device must open a server socket, and the other one must initiate the connection
using the server device's MAC address. The server device and the client device
each obtain the required BluetoothSocket
in different ways. The server receives socket information when an incoming
connection is accepted. The client provides socket information when it opens an
RFCOMM channel to the server.
The server and client are considered
connected to each other when they each have a connected
BluetoothSocket on the same RFCOMM channel. At this
point, each device can obtain input and output streams, and and data transfer can
begin, which is discussed in the section about Managing a Connection. This section describes how
to initiate the connection between two devices.
Connection techniques
One implementation technique is to automatically prepare each device as a server so that each device has a server socket open and listening for connections. In this case, either device can initiate a connection with the other and become the client. Alternatively, one device can explicitly host the connection and open a server socket on demand, and the other device initiates the connection.
Figure 3: The Bluetooth pairing dialog.
Note: If the two devices have not been previously paired, then the Android framework automatically shows a pairing request notification or dialog to the user during the connection procedure, as shown in Figure 3. Therefore, when your application attempts to connect devices, it doesn't need to be concerned about whether or not the devices are paired. Your RFCOMM connection attempt will block until the user has successfully paired the two devices, and the attempt fails if the user rejects pairing, or if the pairing process fails or times out.
Connecting as a server
When you want to connect two devices, one must act as a server by holding an
open BluetoothServerSocket. The purpose of the server
socket is to listen for incoming connection requests and
provide a connected BluetoothSocket after a request is
accepted. When the BluetoothSocket is acquired from the BluetoothServerSocket,
the BluetoothServerSocket can—and should—be
discarded, unless you want the device to accept more connections.
About UUID
A Universally Unique Identifier (UUID) is a standardized 128-bit format for a string
ID used to uniquely identify information. The point of a UUID is that it's big
enough that you can select any random ID, and it won't clash with any other ID.
In this case, it's
used to uniquely identify your application's Bluetooth service. To get a UUID to
use with your application, you can use one of the many random UUID generators on
the web, then initialize a UUID with fromString(String).
To set up a server socket and accept a connection, complete the following sequence of steps:
- Get a
BluetoothServerSocketby callinglistenUsingRfcommWithServiceRecord().The string is an identifiable name of your service, which the system automatically writes to a new Service Discovery Protocol (SDP) database entry on the device. The name is arbitrary and can simply be your application name. The UUID is also included in the SDP entry and forms the basis for the connection agreement with the client device. That is, when the client attempts to connect with this device, it will carry a UUID that uniquely identifies the service with which it wants to connect. These UUIDs must match in order for the connection to be accepted.
- Start listening for connection requests by calling
accept().This is a blocking call. It returns when either a connection has been accepted or an exception has occurred. A connection is accepted only when a remote device has sent a connection request containing a UUID that matches the one registered with this listening server socket. When successful,
accept()returns a connectedBluetoothSocket. - Unless you want to accept additional connections, call
close().This method call releases the server socket and all its resources, but doesn't close the connected
BluetoothSocketthat's been returned byaccept(). Unlike TCP/IP, RFCOMM allows only one connected client per channel at a time, so in most cases, it makes sense to callclose()on theBluetoothServerSocketimmediately after accepting a connected socket.
Because the accept() call is
a blocking call, it should not be executed in the main activity UI thread so
that your application can still respond to other user interactions. It usually makes
sense to do all work that involves a BluetoothServerSocket or BluetoothSocket in a new
thread managed by your application. To abort a blocked call such as accept(), call close() on the BluetoothServerSocket or BluetoothSocket from another thread. Note that all methods on a BluetoothServerSocket or BluetoothSocket
are thread-safe.
Example
Here's a simplified thread for the server component that accepts incoming connections:
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) {
Log.e(TAG, "Socket's listen() method failed", 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) {
Log.e(TAG, "Socket's accept() method failed", e);
break;
}
if (socket != null) {
// A connection was accepted. Perform work associated with
// the connection in a separate thread.
manageMyConnectedSocket(socket);
mmServerSocket.close();
break;
}
}
}
// Closes the connect socket and causes the thread to finish.
public void cancel() {
try {
mmServerSocket.close();
} catch (IOException e) {
Log.e(TAG, "Could not close the connect socket", e);
}
}
}
In this example, only one incoming connection is desired, so as soon as a
connection is accepted and the BluetoothSocket is
acquired, the app
passes the acquired BluetoothSocket to a separate
thread, closes the
BluetoothServerSocket, and breaks out of the loop.
Note that when accept()
returns the BluetoothSocket, the socket is already
connected. Therefore, you shouldn't call connect(),
as you do from the client side.
The app-specific manageMyConnectedSocket() method is designed to
initiate the thread for transferring data, which is discussed in the section
about Managing a Connection.
Usually, you should close your BluetoothServerSocket
as soon as you are done listening for incoming connections. In this example, close() is called as soon
as the BluetoothSocket is acquired. You may also want
to provide a public method in your thread that can close the private BluetoothSocket in the event that you need to stop listening on that
server socket.
Connecting as a client
In order to initiate a connection with a remote device that is accepting
connections on an open server socket, you must first obtain a
BluetoothDevice object that represents the remote device.
To learn how to create a BluetoothDevice, see Finding Devices. You must then use the
BluetoothDevice to acquire a BluetoothSocket and initiate the connection.
The basic procedure is as follows:
- Using the
BluetoothDevice, get aBluetoothSocketby callingcreateRfcommSocketToServiceRecord(UUID).This method initializes a
BluetoothSocketobject that allows the client to connect to aBluetoothDevice. The UUID passed here must match the UUID used by the server device when it calledlistenUsingRfcommWithServiceRecord(String, UUID)to open itsBluetoothServerSocket. To use a matching UUID, hard-code the UUID string into your application, and then reference it from both the server and client code. - Initiate the connection by calling
connect(). Note that this method is a blocking call.After a client calls this method, the system performs an SDP lookup to find the remote device with the matching UUID. If the lookup is successful and the remote device accepts the connection, it shares the RFCOMM channel to use during the connection, and the
connect()method returns. If the connection fails, or if theconnect()method times out (after about 12 seconds), then the method throws anIOException.Because
connect()is a blocking call, you should always perform this connection procedure in a thread that is separate from the main activity (UI) thread.Note: You should always call
cancelDiscovery()to ensure that the device is not performing device discovery before you callconnect(). If discovery is in progress, then the connection attempt is significantly slowed, and it's more likely to fail.
Example
Here is a basic example of a client thread that initiates a Bluetooth connection:
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;
try {
// Get a BluetoothSocket to connect with the given BluetoothDevice.
// MY_UUID is the app's UUID string, also used in the server code.
tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
} catch (IOException e) {
Log.e(TAG, "Socket's create() method failed", e);
}
mmSocket = tmp;
}
public void run() {
// Cancel discovery because it otherwise slows down the connection.
mBluetoothAdapter.cancelDiscovery();
try {
// Connect to the remote device through the socket. This call blocks
// until it succeeds or throws an exception.
mmSocket.connect();
} catch (IOException connectException) {
// Unable to connect; close the socket and return.
try {
mmSocket.close();
} catch (IOException closeException) {
Log.e(TAG, "Could not close the client socket", closeException);
}
return;
}
// The connection attempt succeeded. Perform work associated with
// the connection in a separate thread.
manageMyConnectedSocket(mmSocket);
}
// Closes the client socket and causes the thread to finish.
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) {
Log.e(TAG, "Could not close the client socket", e);
}
}
}
Notice that, in this snippet,
cancelDiscovery() is called before
the connection attempt occurs. You should always call
cancelDiscovery() before
connect(), especially because
cancelDiscovery() succeeds regardless of whether device discovery
is currently in progress. If your app needs to determine whether device
discovery is in progress, however, you can check using
isDiscovering().
The app-specific manageMyConnectedSocket() method is designed to
initiate the thread for transferring data, which is discussed in the section
about Managing a Connection.
When you're done with your BluetoothSocket, always
call close(). Doing so will
immediately close the connected socket and release all related internal
resources.
Managing a Connection
After you have successfully connected multiple devices, each one has a
connected BluetoothSocket. This is where the fun
begins because you can share information between devices. Using the
BluetoothSocket, the general procedure to transfer
data is as follows:
- Get the
InputStreamandOutputStreamthat handle transmissions through the socket usinggetInputStream()andgetOutputStream(), respectively. - Read and write data to the streams using
read(byte[])andwrite(byte[]).
There are, of course, implementation details to consider. In particular,
you should use a dedicated thread for reading from the stream and writing to it. This is
important because both the read(byte[]) and
write(byte[]) methods are blocking calls. The
read(byte[]) method blocks until there is something to read
from the stream. The write(byte[]) method doesn't usually
block, but it can block for flow control if the remote device is not calling
read(byte[]) quickly enough and the intermediate buffers become full as a result.
So, your main loop in the thread should be dedicated to reading from the
InputStream. A separate public method in the thread can be used to initiate
writes to the OutputStream.
Example
Here's an example of how you can transfer data between two devices connected over Bluetooth:
public class MyBluetoothService {
private static final String TAG = "MY_APP_DEBUG_TAG";
private Handler mHandler; // handler that gets info from Bluetooth service
// Defines several constants used when transmitting messages between the
// service and the UI.
private interface MessageConstants {
public static final int MESSAGE_READ = 0;
public static final int MESSAGE_WRITE = 1;
public static final int MESSAGE_TOAST = 2;
// ... (Add other message types here as needed.)
}
private class ConnectedThread extends Thread {
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;
private byte[] mmBuffer; // mmBuffer store for the stream
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();
} catch (IOException e) {
Log.e(TAG, "Error occurred when creating input stream", e);
}
try {
tmpOut = socket.getOutputStream();
} catch (IOException e) {
Log.e(TAG, "Error occurred when creating output stream", e);
}
mmInStream = tmpIn;
mmOutStream = tmpOut;
}
public void run() {
mmBuffer = new byte[1024];
int numBytes; // bytes returned from read()
// Keep listening to the InputStream until an exception occurs.
while (true) {
try {
// Read from the InputStream.
numBytes = mmInStream.read(mmBuffer);
// Send the obtained bytes to the UI activity.
Message readMsg = mHandler.obtainMessage(
MessageConstants.MESSAGE_READ, numBytes, -1,
mmBuffer);
readMsg.sendToTarget();
} catch (IOException e) {
Log.d(TAG, "Input stream was disconnected", e);
break;
}
}
}
// Call this from the main activity to send data to the remote device.
public void write(byte[] bytes) {
try {
mmOutStream.write(bytes);
// Share the sent message with the UI activity.
Message writtenMsg = mHandler.obtainMessage(
MessageConstants.MESSAGE_WRITE, -1, -1, mmBuffer);
writtenMsg.sendToTarget();
} catch (IOException e) {
Log.e(TAG, "Error occurred when sending data", e);
// Send a failure message back to the activity.
Message writeErrorMsg =
mHandler.obtainMessage(MessageConstants.MESSAGE_TOAST);
Bundle bundle = new Bundle();
bundle.putString("toast",
"Couldn't send data to the other device");
writeErrorMsg.setData(bundle);
mHandler.sendMessage(writeErrorMsg);
}
}
// Call this method from the main activity to shut down the connection.
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) {
Log.e(TAG, "Could not close the connect socket", e);
}
}
}
}
After the constructor acquires the necessary streams, the thread waits for
data to come through the InputStream. When read(byte[]) returns with
data from the stream, the data is sent to the main activity using a member
Handler from the parent class. The thread then waits for more
bytes to be read from the InputStream.
Sending outgoing data is as simple as calling the thread's
write() method from the main activity and passing in the bytes to
be sent. This method calls write(byte[]) to send
the data to the remote device. If an IOException is thrown when
calling write(byte[]), the thread sends a toast to
the main activity, explaining to the user that the device couldn't send the
given bytes to the other (connected) device.
The thread's cancel() method allows the connection to be
terminated at any time by closing the BluetoothSocket.
This method should always be called when you're done using the Bluetooth
connection.
For a demonstration of using the Bluetooth APIs, see the Bluetooth Chat sample app.
Key Classes and Interfaces
All of the Bluetooth APIs are available in the android.bluetooth
package. Here's a summary of the classes and interfaces you will need to create Bluetooth
connections:
BluetoothAdapter- Represents the local Bluetooth adapter (Bluetooth radio). The
BluetoothAdapteris the entry-point for all Bluetooth interaction. Using this, you can discover other Bluetooth devices, query a list of bonded (paired) devices, instantiate aBluetoothDeviceusing a known MAC address, and create aBluetoothServerSocketto listen for communications from other devices. BluetoothDevice- Represents a remote Bluetooth device. Use this to request a connection
with a remote device through a
BluetoothSocketor query information about the device such as its name, address, class, and bonding state. BluetoothSocket- Represents the interface for a Bluetooth socket (similar to a TCP
Socket). This is the connection point that allows an application to exchange data with another Bluetooth device usingInputStreamandOutputStream. BluetoothServerSocket- Represents an open server socket that listens for incoming requests
(similar to a TCP
ServerSocket). In order to connect two Android devices, one device must open a server socket with this class. When a remote Bluetooth device makes a connection request to this device, the device accepts the connection, then returns a connectedBluetoothSocket. BluetoothClass- Describes the general characteristics and capabilities of a Bluetooth device. This is a read-only set of properties that defines the device's classes and services. Although this information provides a useful hint regarding a device's type, the attributes of this class don't necessarily describe all Bluetooth profiles and services that the device supports.
BluetoothProfile- An interface that represents a Bluetooth profile. A Bluetooth profile is a wireless interface specification for Bluetooth-based communication between devices. An example is the Hands-Free profile. For more discussion of profiles, see Working with Profiles.
BluetoothHeadset- Provides support for Bluetooth headsets to be used with mobile phones. This includes both the Bluetooth Headset profile and the Hands-Free (v1.5) profile.
BluetoothA2dp- Defines how high-quality audio can be streamed from one device to another over a Bluetooth connection using the Advanced Audio Distribution Profile (A2DP).
BluetoothHealth- Represents a Health Device Profile proxy that controls the Bluetooth service.
BluetoothHealthCallback- An abstract class that you use to implement
BluetoothHealthcallbacks. You must extend this class and implement the callback methods to receive updates about changes in the application’s registration state and Bluetooth channel state. BluetoothHealthAppConfiguration- Represents an application configuration that the Bluetooth Health third-party application registers to communicate with a remote Bluetooth health device.
BluetoothProfile.ServiceListener- An interface that notifies
BluetoothProfileinterprocess communication (IPC) clients when they have been connected to or disconnected from the internal service that runs a particular profile.