From f14ff1c9ce37299c2fcef258a854acf79c026ccf Mon Sep 17 00:00:00 2001
From: kmccormick Provides classes that manage Bluetooth functionality, such as scanning for
-devices, connecting with devices, and managing data transfer between devices.
For more information, see the -Bluetooth guide.
+For more information about Classic Bluetooth, see the +Bluetooth guide. +For more information about Bluetooth Low Energy, see the + +Bluetooth Low Energy guide.
{@more}The Bluetooth APIs let applications:
+Android 4.3 (API Level 18) introduces built-in platform support for Bluetooth Low +Energy in the central role and provides APIs that apps can use to discover +devices, query for services, and read/write characteristics. +In contrast to +Classic Bluetooth, +Bluetooth Low Energy (BLE) is designed to provide significantly lower power consumption. +This allows Android apps to communicate with BLE devices that have low power requirements, +such as proximity sensors, heart rate monitors, fitness devices, and so on.
+ +Here is a summary of key BLE terms and concepts:
+Here are the roles and responsibilities that apply when +an Android device interacts with a BLE device:
+ +To understand the distinction, imagine that you have an Android phone and +an activity tracker that is a BLE device. The phone supports the +central role; the activity tracker supports the peripheral role (to +establish a BLE connection you need one of each—two things that only support +peripheral couldn't talk to each other, nor could two things that only support +central).
+ +Once the phone and the activity tracker have established a connection, they +start transferring GATT metadata to one another. Depending on the kind of data they transfer, +one or the other might act as the server. For example, if the activity tracker +wants to report sensor data to the phone, it might make sense for the activity +tracker to act as the server. If the activity tracker wants to receive updates +from the phone, then it might make sense for the phone to act +as the server.
+ ++In the example used in this document, the Android app (running on an Android +device) is the GATT client. The app gets data from the GATT server, which is a +BLE heart rate monitor that supports the +Heart +Rate Profile. But you could alternatively design +your Android app to play the GATT server +role. See {@link android.bluetooth.BluetoothGattServer} for more +information.
+ +In order to use Bluetooth features in your application, you must declare +the Bluetooth permission {@link android.Manifest.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 also declare the {@link android.Manifest.permission#BLUETOOTH_ADMIN} +permission. Note: If you use the +{@link android.Manifest.permission#BLUETOOTH_ADMIN} permission, then you must +also have the {@link android.Manifest.permission#BLUETOOTH} permission.
+ +Declare the Bluetooth permission(s) in your application manifest file. For +example:
+ ++<uses-permission android:name="android.permission.BLUETOOTH"/> +<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>+ +
If you want to declare that your app is available to BLE-capable devices only, +include the following in your app's manifest:
+ +<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/> ++ +
However, if you want to make your app available to devices that don't support BLE, +you should still include this element in your app's manifest, but set {@code required="false"}. +Then at run-time you can determine BLE availability by using +{@link android.content.pm.PackageManager#hasSystemFeature PackageManager.hasSystemFeature()}: + +
// Use this check to determine whether BLE is supported on the device. Then
+// you can selectively disable BLE-related features.
+if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
+ Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
+ finish();
+}
+
+Before your application can communicate over BLE, you need +to verify that BLE is supported on the device, and if so, ensure that it is enabled. +Note that this check is only necessary if {@code <uses-feature.../>} +is set to false.
+ +If BLE is not supported, then you should gracefully disable any +BLE features. If BLE 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 {@link android.bluetooth.BluetoothAdapter}. +
+ + +The {@link android.bluetooth.BluetoothAdapter} is required for any and all +Bluetooth activity. The {@link android.bluetooth.BluetoothAdapter} 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. The snippet below shows how to get the adapter. Note that this approach +uses {@link android.content.Context#getSystemService getSystemService()} to return +an instance of {@link android.bluetooth.BluetoothManager}, which is then +used to get the adapter. Android 4.3 (API Level 18) introduces +{@link android.bluetooth.BluetoothManager}:
+ +// Initializes Bluetooth adapter. +final BluetoothManager bluetoothManager = + (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); +mBluetoothAdapter = bluetoothManager.getAdapter(); ++
Next, you need to ensure that Bluetooth is enabled. Call {@link +android.bluetooth.BluetoothAdapter#isEnabled()} to check whether Bluetooth is +currently enabled. If this method returns false, then Bluetooth is disabled. +The following snippet checks whether Bluetooth is enabled. If it isn't, the +snippet displays an error prompting the user to go to Settings to enable +Bluetooth:
+private BluetoothAdapter mBluetoothAdapter;
+...
+// Ensures Bluetooth is available on the device and it is enabled. If not,
+// displays a dialog requesting user permission to enable Bluetooth.
+if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
+ Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
+ startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
+}
+To find BLE devices, you use the +{@link android.bluetooth.BluetoothAdapter#startLeScan startLeScan()} method. +This method takes a {@link android.bluetooth.BluetoothAdapter.LeScanCallback} +as a parameter. You must implement this callback, because that is how scan +results are returned. Because scanning is battery-intensive, you should observe +the following guidelines:
+The following snippet shows how to start and stop a scan:
+ +/**
+ * Activity for scanning and displaying available BLE devices.
+ */
+public class DeviceScanActivity extends ListActivity {
+
+ private BluetoothAdapter mBluetoothAdapter;
+ private boolean mScanning;
+ private Handler mHandler;
+
+ // Stops scanning after 10 seconds.
+ private static final long SCAN_PERIOD = 10000;
+ ...
+ private void scanLeDevice(final boolean enable) {
+ if (enable) {
+ // Stops scanning after a pre-defined scan period.
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ mScanning = false;
+ mBluetoothAdapter.stopLeScan(mLeScanCallback);
+ }
+ }, SCAN_PERIOD);
+
+ mScanning = true;
+ mBluetoothAdapter.startLeScan(mLeScanCallback);
+ } else {
+ mScanning = false;
+ mBluetoothAdapter.stopLeScan(mLeScanCallback);
+ }
+ ...
+ }
+...
+}
+
+
+If you want to scan for only specific types of peripherals, you can instead +call {@link android.bluetooth.BluetoothAdapter#startLeScan startLeScan(UUID[], BluetoothAdapter.LeScanCallback)}, +providing an array of {@link java.util.UUID} objects that specify the GATT +services your app supports.
+ +Here is an implementation of the +{@link android.bluetooth.BluetoothAdapter.LeScanCallback}, +which is the interface used to deliver BLE scan results:
+ +
+private LeDeviceListAdapter mLeDeviceListAdapter;
+...
+// Device scan callback.
+private BluetoothAdapter.LeScanCallback mLeScanCallback =
+ new BluetoothAdapter.LeScanCallback() {
+ @Override
+ public void onLeScan(final BluetoothDevice device, int rssi,
+ byte[] scanRecord) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mLeDeviceListAdapter.addDevice(device);
+ mLeDeviceListAdapter.notifyDataSetChanged();
+ }
+ });
+ }
+};
+
+
+Note: You can only scan for Bluetooth LE devices +or scan for Classic Bluetooth devices, as described in +Bluetooth. You cannot +scan for both Bluetooth LE and classic devices at the same time.
+ +The first step in interacting with a BLE device is connecting to it—
+more specifically, connecting to the GATT server on the device. To
+connect to a GATT server on a BLE device, you use the
+{@link android.bluetooth.BluetoothDevice#connectGatt connectGatt()} method.
+This method takes three parameters: a {@link android.content.Context} object,
+autoConnect (boolean indicating whether to automatically connect to
+the BLE device as soon as it becomes available), and a reference to a
+{@link android.bluetooth.BluetoothGattCallback}:
mBluetoothGatt = device.connectGatt(this, false, mGattCallback);+ +
This connects to the GATT server hosted by the BLE device, and returns a +{@link android.bluetooth.BluetoothGatt} instance, which you can then use to +conduct GATT client operations. The caller (the Android app) is the GATT client. +The {@link android.bluetooth.BluetoothGattCallback} is used to deliver results +to the client, such as connection status, as well as any further GATT client +operations.
+ +In this example, the BLE app provides an activity
+(DeviceControlActivity) to connect,
+display data, and display GATT services and characteristics
+supported by the device. Based on user input, this activity communicates with a
+{@link android.app.Service} called {@code BluetoothLeService},
+which interacts with the BLE device via the Android BLE API:
+// A service that interacts with the BLE device via the Android BLE API.
+public class BluetoothLeService extends Service {
+ private final static String TAG = BluetoothLeService.class.getSimpleName();
+
+ private BluetoothManager mBluetoothManager;
+ private BluetoothAdapter mBluetoothAdapter;
+ private String mBluetoothDeviceAddress;
+ private BluetoothGatt mBluetoothGatt;
+ private int mConnectionState = STATE_DISCONNECTED;
+
+ private static final int STATE_DISCONNECTED = 0;
+ private static final int STATE_CONNECTING = 1;
+ private static final int STATE_CONNECTED = 2;
+
+ public final static String ACTION_GATT_CONNECTED =
+ "com.example.bluetooth.le.ACTION_GATT_CONNECTED";
+ public final static String ACTION_GATT_DISCONNECTED =
+ "com.example.bluetooth.le.ACTION_GATT_DISCONNECTED";
+ public final static String ACTION_GATT_SERVICES_DISCOVERED =
+ "com.example.bluetooth.le.ACTION_GATT_SERVICES_DISCOVERED";
+ public final static String ACTION_DATA_AVAILABLE =
+ "com.example.bluetooth.le.ACTION_DATA_AVAILABLE";
+ public final static String EXTRA_DATA =
+ "com.example.bluetooth.le.EXTRA_DATA";
+
+ public final static UUID UUID_HEART_RATE_MEASUREMENT =
+ UUID.fromString(SampleGattAttributes.HEART_RATE_MEASUREMENT);
+
+ // Various callback methods defined by the BLE API.
+ private final BluetoothGattCallback mGattCallback =
+ new BluetoothGattCallback() {
+ @Override
+ public void onConnectionStateChange(BluetoothGatt gatt, int status,
+ int newState) {
+ String intentAction;
+ if (newState == BluetoothProfile.STATE_CONNECTED) {
+ intentAction = ACTION_GATT_CONNECTED;
+ mConnectionState = STATE_CONNECTED;
+ broadcastUpdate(intentAction);
+ Log.i(TAG, "Connected to GATT server.");
+ Log.i(TAG, "Attempting to start service discovery:" +
+ mBluetoothGatt.discoverServices());
+
+ } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
+ intentAction = ACTION_GATT_DISCONNECTED;
+ mConnectionState = STATE_DISCONNECTED;
+ Log.i(TAG, "Disconnected from GATT server.");
+ broadcastUpdate(intentAction);
+ }
+ }
+
+ @Override
+ // New services discovered
+ public void onServicesDiscovered(BluetoothGatt gatt, int status) {
+ if (status == BluetoothGatt.GATT_SUCCESS) {
+ broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
+ } else {
+ Log.w(TAG, "onServicesDiscovered received: " + status);
+ }
+ }
+
+ @Override
+ // Result of a characteristic read operation
+ public void onCharacteristicRead(BluetoothGatt gatt,
+ BluetoothGattCharacteristic characteristic,
+ int status) {
+ if (status == BluetoothGatt.GATT_SUCCESS) {
+ broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
+ }
+ }
+ ...
+ };
+...
+}
+
+When a particular callback is triggered, it calls the appropriate +{@code broadcastUpdate()} helper method and passes it an action. Note that the data +parsing in this section is performed in accordance with the Bluetooth Heart Rate +Measurement + +profile specifications:
+ +private void broadcastUpdate(final String action) {
+ final Intent intent = new Intent(action);
+ sendBroadcast(intent);
+}
+
+private void broadcastUpdate(final String action,
+ final BluetoothGattCharacteristic characteristic) {
+ final Intent intent = new Intent(action);
+
+ // This is special handling for the Heart Rate Measurement profile. Data
+ // parsing is carried out as per profile specifications.
+ if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {
+ int flag = characteristic.getProperties();
+ int format = -1;
+ if ((flag & 0x01) != 0) {
+ format = BluetoothGattCharacteristic.FORMAT_UINT16;
+ Log.d(TAG, "Heart rate format UINT16.");
+ } else {
+ format = BluetoothGattCharacteristic.FORMAT_UINT8;
+ Log.d(TAG, "Heart rate format UINT8.");
+ }
+ final int heartRate = characteristic.getIntValue(format, 1);
+ Log.d(TAG, String.format("Received heart rate: %d", heartRate));
+ intent.putExtra(EXTRA_DATA, String.valueOf(heartRate));
+ } else {
+ // For all other profiles, writes the data formatted in HEX.
+ final byte[] data = characteristic.getValue();
+ if (data != null && data.length > 0) {
+ final StringBuilder stringBuilder = new StringBuilder(data.length);
+ for(byte byteChar : data)
+ stringBuilder.append(String.format("%02X ", byteChar));
+ intent.putExtra(EXTRA_DATA, new String(data) + "\n" +
+ stringBuilder.toString());
+ }
+ }
+ sendBroadcast(intent);
+}
+
+
+
+Back in DeviceControlActivity, these events are handled by a
+{@link android.content.BroadcastReceiver}:
// Handles various events fired by the Service.
+// ACTION_GATT_CONNECTED: connected to a GATT server.
+// ACTION_GATT_DISCONNECTED: disconnected from a GATT server.
+// ACTION_GATT_SERVICES_DISCOVERED: discovered GATT services.
+// ACTION_DATA_AVAILABLE: received data from the device. This can be a
+// result of read or notification operations.
+private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) {
+ mConnected = true;
+ updateConnectionState(R.string.connected);
+ invalidateOptionsMenu();
+ } else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) {
+ mConnected = false;
+ updateConnectionState(R.string.disconnected);
+ invalidateOptionsMenu();
+ clearUI();
+ } else if (BluetoothLeService.
+ ACTION_GATT_SERVICES_DISCOVERED.equals(action)) {
+ // Show all the supported services and characteristics on the
+ // user interface.
+ displayGattServices(mBluetoothLeService.getSupportedGattServices());
+ } else if (BluetoothLeService.ACTION_DATA_AVAILABLE.equals(action)) {
+ displayData(intent.getStringExtra(BluetoothLeService.EXTRA_DATA));
+ }
+ }
+};
+
+
+Once your Android app has connected to a GATT server and discovered services, +it can read and write attributes, where supported. For example, this snippet iterates +through the server's services and characteristics and displays them in the UI:
+ +
+public class DeviceControlActivity extends Activity {
+ ...
+ // Demonstrates how to iterate through the supported GATT
+ // Services/Characteristics.
+ // In this sample, we populate the data structure that is bound to the
+ // ExpandableListView on the UI.
+ private void displayGattServices(List<BluetoothGattService> gattServices) {
+ if (gattServices == null) return;
+ String uuid = null;
+ String unknownServiceString = getResources().
+ getString(R.string.unknown_service);
+ String unknownCharaString = getResources().
+ getString(R.string.unknown_characteristic);
+ ArrayList<HashMap<String, String>> gattServiceData =
+ new ArrayList<HashMap<String, String>>();
+ ArrayList<ArrayList<HashMap<String, String>>> gattCharacteristicData
+ = new ArrayList<ArrayList<HashMap<String, String>>>();
+ mGattCharacteristics =
+ new ArrayList<ArrayList<BluetoothGattCharacteristic>>();
+
+ // Loops through available GATT Services.
+ for (BluetoothGattService gattService : gattServices) {
+ HashMap<String, String> currentServiceData =
+ new HashMap<String, String>();
+ uuid = gattService.getUuid().toString();
+ currentServiceData.put(
+ LIST_NAME, SampleGattAttributes.
+ lookup(uuid, unknownServiceString));
+ currentServiceData.put(LIST_UUID, uuid);
+ gattServiceData.add(currentServiceData);
+
+ ArrayList<HashMap<String, String>> gattCharacteristicGroupData =
+ new ArrayList<HashMap<String, String>>();
+ List<BluetoothGattCharacteristic> gattCharacteristics =
+ gattService.getCharacteristics();
+ ArrayList<BluetoothGattCharacteristic> charas =
+ new ArrayList<BluetoothGattCharacteristic>();
+ // Loops through available Characteristics.
+ for (BluetoothGattCharacteristic gattCharacteristic :
+ gattCharacteristics) {
+ charas.add(gattCharacteristic);
+ HashMap<String, String> currentCharaData =
+ new HashMap<String, String>();
+ uuid = gattCharacteristic.getUuid().toString();
+ currentCharaData.put(
+ LIST_NAME, SampleGattAttributes.lookup(uuid,
+ unknownCharaString));
+ currentCharaData.put(LIST_UUID, uuid);
+ gattCharacteristicGroupData.add(currentCharaData);
+ }
+ mGattCharacteristics.add(charas);
+ gattCharacteristicData.add(gattCharacteristicGroupData);
+ }
+ ...
+ }
+...
+}
+
+It's common for BLE apps to ask to be notified when a particular +characteristic changes on the device. This snippet shows how to set a notification +for a characteristic, using the +{@link android.bluetooth.BluetoothGatt#setCharacteristicNotification setCharacteristicNotification()} +method:
+ ++private BluetoothGatt mBluetoothGatt; +BluetoothGattCharacteristic characteristic; +boolean enabled; +... +mBluetoothGatt.setCharacteristicNotification(characteristic, enabled); +... +BluetoothGattDescriptor descriptor = characteristic.getDescriptor( + UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG)); +descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); +mBluetoothGatt.writeDescriptor(descriptor);+ +
Once notifications are enabled for a characteristic, +an {@link android.bluetooth.BluetoothGattCallback#onCharacteristicChanged onCharacteristicChanged()} +callback is triggered if the characteristic changes on the remote device:
+ +@Override
+// Characteristic notification
+public void onCharacteristicChanged(BluetoothGatt gatt,
+ BluetoothGattCharacteristic characteristic) {
+ broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
+}
+
+
+Once your app has finished using a BLE device, it should call +{@link android.bluetooth.BluetoothGatt#close close()} +so the system can release resources appropriately:
+ +public void close() {
+ if (mBluetoothGatt == null) {
+ return;
+ }
+ mBluetoothGatt.close();
+ mBluetoothGatt = null;
+}
diff --git a/docs/html/guide/topics/connectivity/bluetooth.jd b/docs/html/guide/topics/connectivity/bluetooth.jd
index 1c55d8b6760f2..b57f3e236b717 100644
--- a/docs/html/guide/topics/connectivity/bluetooth.jd
+++ b/docs/html/guide/topics/connectivity/bluetooth.jd
@@ -2,19 +2,13 @@ page.title=Bluetooth
page.tags="wireless","bluetoothadapter","bluetoothdevice"
@jd:body
-This document describes how to use Classic Bluetooth. Classic +Bluetooth is the right choice for more battery-intensive operations such as 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.
In order to use Bluetooth features in your application, you need to declare -at least one of two Bluetooth permissions: {@link -android.Manifest.permission#BLUETOOTH} and {@link -android.Manifest.permission#BLUETOOTH_ADMIN}.
- -You must request the {@link android.Manifest.permission#BLUETOOTH} permission -in order to perform any Bluetooth communication, such as requesting a -connection, accepting a connection, and transferring data.
- -You must request the {@link android.Manifest.permission#BLUETOOTH_ADMIN} -permission in order to initiate device discovery or manipulate Bluetooth -settings. Most applications need this permission solely for the +
In order to use Bluetooth features in your application, you must declare +the Bluetooth permission {@link android.Manifest.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 also declare the {@link android.Manifest.permission#BLUETOOTH_ADMIN} +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. Note: If you -use {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission, then must +use {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission, then you must also have the {@link android.Manifest.permission#BLUETOOTH} permission.
Declare the Bluetooth permission(s) in your application manifest file. For