1page.title=Bluetooth Low Energy 2page.tags=wireless,bluetoothadapter,bluetoothdevice,BLE,BTLE 3@jd:body 4 5<div id="qv-wrapper"> 6<div id="qv"> 7 8 <h2>In this document</h2> 9 <ol> 10 <li><a href="#terms">Key Terms and Concepts</a> 11 <ol> 12 <li><a href="#roles">Roles and Responsibilities</a></li> 13 </ol> 14 </li> 15 <li><a href="#permissions">BLE Permissions</a></li> 16 <li><a href="#setup">Setting Up BLE</a></li> 17 <li><a href="#find">Finding BLE Devices</a></li> 18 <li><a href="#connect">Connecting to a GATT Server</a></li> 19 <li><a href="#read">Reading BLE Attributes</a></li> 20 <li><a href="#notification">Receiving GATT Notifications</a></li> 21 <li><a href="#close">Closing the Client App</a></li> 22 </ol> 23 24 <h2>Key classes</h2> 25 <ol> 26 <li>{@link android.bluetooth.BluetoothGatt}</li> 27 <li>{@link android.bluetooth.BluetoothGattCallback}</li> 28 <li>{@link android.bluetooth.BluetoothGattCharacteristic}</li> 29 <li>{@link android.bluetooth.BluetoothGattService}</li> 30 </ol> 31 32 <h2>Related samples</h2> 33 <ol> 34 <li><a href="{@docRoot}tools/samples/index.html">Bluetooth LE sample</a></li> 35 </ol> 36 37 <h2>See Also</h2> 38 <ol> 39 <li><a href="http://developers.google.com/events/io/sessions/326240948"> 40 Best Practices for Bluetooth Development</a> (video)</li> 41 42 </ol> 43 44</div> 45</div> 46 47<a class="notice-developers-video" href="http://www.youtube.com/watch?v=vUbFB1Qypg8"> 48<div> 49 <h3>Video</h3> 50 <p>DevBytes: Bluetooth Low Energy API</p> 51</div> 52</a> 53 54 55 56 57<p> 58Android 4.3 (API Level 18) introduces built-in platform support for Bluetooth Low 59Energy in the <em>central role</em> and provides APIs that apps can use to discover 60devices, query for services, and read/write characteristics. 61In contrast to 62<a href="{@docRoot}guide/topics/connectivity/bluetooth.html">Classic Bluetooth</a>, 63Bluetooth Low Energy (BLE) is designed to provide significantly lower power consumption. 64This allows Android apps to communicate with BLE devices that have low power requirements, 65such as proximity sensors, heart rate monitors, fitness devices, and so on.</p> 66 67<h2 id="terms">Key Terms and Concepts</h2> 68<p>Here is a summary of key BLE terms and concepts:</p> 69<ul> 70 <li><strong>Generic Attribute Profile (GATT)</strong>—The GATT profile 71is a general specification for sending and receiving short pieces of data known 72as "attributes" over a BLE link. All current Low Energy application profiles are 73based on GATT. 74 <ul> 75 <li>The Bluetooth SIG defines many 76<a href="https://www.bluetooth.org/en-us/specification/adopted-specifications">profiles</a> 77for Low Energy devices. A profile is a specification for how a device works in a 78particular application. Note that a device can implement more than one profile. 79For example, a device could contain a heart rate monitor and a battery level 80detector.</li> 81 </ul> 82 </li> 83 <li><strong>Attribute Protocol (ATT)</strong>—GATT is built on top of 84the Attribute Protocol (ATT). This is also referred to as GATT/ATT. ATT is 85optimized to run on BLE devices. To this end, it uses as few bytes as possible. 86Each attribute is uniquely identified by a Universally Unique Identifier (UUID), 87which is a standardized 128-bit format for a string ID used to uniquely 88identify information. The <em>attributes</em> transported by ATT are formatted 89as <em>characteristics</em> and <em>services</em>. </li> 90 91 <li><strong>Characteristic</strong>—A characteristic contains a single 92value and 0-n descriptors that describe the characteristic's value. A 93characteristic can be thought of as a type, analogous to a class. </li> 94 <li><strong>Descriptor</strong>—Descriptors are defined attributes that 95describe a characteristic value. For example, a descriptor might specify a 96human-readable description, an acceptable range for a characteristic's value, or 97a unit of measure that is specific to a characteristic's value.</li> 98 99 <li><strong>Service</strong>—A service is a collection of 100characteristics. For example, you could have a service called 101"Heart Rate Monitor" that includes characteristics such as 102"heart rate measurement." You can find a list of existing GATT-based 103profiles and services on 104<a href="https://www.bluetooth.org/en-us/specification/adopted-specifications"> 105bluetooth.org</a>.</li> 106 107</ul> 108 109<h3 id="roles">Roles and Responsibilities</h3> 110 111<p>Here are the roles and responsibilities that apply when 112an Android device interacts with a BLE device:</p> 113 114<ul> 115 <li>Central vs. peripheral. This applies to the BLE connection itself. The 116 device in the central role scans, looking for advertisement, and the device in 117 the peripheral role makes the advertisement.</li> 118 <li>GATT server vs. GATT client. This determines how two devices talk to each 119other once they've established the connection.</li> 120</ul> 121 122<p>To understand the distinction, imagine that you have an Android phone and 123an activity tracker that is a BLE device. The phone supports the 124central role; the activity tracker supports the peripheral role (to 125establish a BLE connection you need one of each—two things that only support 126peripheral couldn't talk to each other, nor could two things that only support 127central).</p> 128 129<p>Once the phone and the activity tracker have established a connection, they 130start transferring GATT metadata to one another. Depending on the kind of data they transfer, 131one or the other might act as the server. For example, if the activity tracker 132wants to report sensor data to the phone, it might make sense for the activity 133tracker to act as the server. If the activity tracker wants to receive updates 134from the phone, then it might make sense for the phone to act 135as the server.</p> 136 137<p> 138In the example used in this document, the Android app (running on an Android 139device) is the GATT client. The app gets data from the GATT server, which is a 140BLE heart rate monitor that supports the 141<a href="http://developer.bluetooth.org/TechnologyOverview/Pages/HRP.aspx">Heart 142Rate Profile</a>. But you could alternatively design 143your Android app to play the GATT server 144role. See {@link android.bluetooth.BluetoothGattServer} for more 145information.</p> 146 147<h2 id="permissions">BLE Permissions</h2> 148 149<p>In order to use Bluetooth features in your application, you must declare 150the Bluetooth permission {@link android.Manifest.permission#BLUETOOTH}. 151You need this permission to perform any Bluetooth communication, 152such as requesting a connection, accepting a connection, and transferring data.</p> 153 154<p>If you want your app to initiate device discovery or manipulate Bluetooth 155settings, you must also declare the {@link android.Manifest.permission#BLUETOOTH_ADMIN} 156permission. <strong>Note:</strong> If you use the 157{@link android.Manifest.permission#BLUETOOTH_ADMIN} permission, then you must 158also have the {@link android.Manifest.permission#BLUETOOTH} permission.</p> 159 160<p>Declare the Bluetooth permission(s) in your application manifest file. For 161example:</p> 162 163<pre> 164<uses-permission android:name="android.permission.BLUETOOTH"/> 165<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/></pre> 166 167<p>If you want to declare that your app is available to BLE-capable devices only, 168include the following in your app's manifest:</p> 169 170<pre><uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/> 171</pre> 172 173<p>However, if you want to make your app available to devices that don't support BLE, 174you should still include this element in your app's manifest, but set {@code required="false"}. 175Then at run-time you can determine BLE availability by using 176{@link android.content.pm.PackageManager#hasSystemFeature PackageManager.hasSystemFeature()}: 177 178<pre>// Use this check to determine whether BLE is supported on the device. Then 179// you can selectively disable BLE-related features. 180if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { 181 Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show(); 182 finish(); 183}</pre> 184 185<h2 id="setup">Setting Up BLE</h2> 186 187<p>Before your application can communicate over BLE, you need 188to verify that BLE is supported on the device, and if so, ensure that it is enabled. 189Note that this check is only necessary if {@code <uses-feature.../>} 190is set to false.</p> 191 192<p>If BLE is not supported, then you should gracefully disable any 193BLE features. If BLE is supported, but disabled, then you can request that the 194user enable Bluetooth without leaving your application. This setup is 195accomplished in two steps, using the {@link android.bluetooth.BluetoothAdapter}. 196</p> 197 198 199<ol> 200<li>Get the {@link android.bluetooth.BluetoothAdapter} 201<p>The {@link android.bluetooth.BluetoothAdapter} is required for any and all 202Bluetooth activity. The {@link android.bluetooth.BluetoothAdapter} represents 203the device's own Bluetooth adapter (the Bluetooth radio). There's one Bluetooth 204adapter for the entire system, and your application can interact with it using 205this object. The snippet below shows how to get the adapter. Note that this approach 206uses {@link android.content.Context#getSystemService getSystemService()} to return 207an instance of {@link android.bluetooth.BluetoothManager}, which is then 208used to get the adapter. Android 4.3 (API Level 18) introduces 209{@link android.bluetooth.BluetoothManager}:</p> 210 211<pre>// Initializes Bluetooth adapter. 212final BluetoothManager bluetoothManager = 213 (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); 214mBluetoothAdapter = bluetoothManager.getAdapter(); 215</pre> 216</li> 217 218<li>Enable Bluetooth 219<p>Next, you need to ensure that Bluetooth is enabled. Call {@link 220android.bluetooth.BluetoothAdapter#isEnabled()} to check whether Bluetooth is 221currently enabled. If this method returns false, then Bluetooth is disabled. 222The following snippet checks whether Bluetooth is enabled. If it isn't, the 223snippet displays an error prompting the user to go to Settings to enable 224Bluetooth:</p> 225<pre>private BluetoothAdapter mBluetoothAdapter; 226... 227// Ensures Bluetooth is available on the device and it is enabled. If not, 228// displays a dialog requesting user permission to enable Bluetooth. 229if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) { 230 Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); 231 startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT); 232} 233</li> 234</ol> 235 236 237 238<h2 id="find">Finding BLE Devices</h2> 239 240<p>To find BLE devices, you use the 241{@link android.bluetooth.BluetoothAdapter#startLeScan startLeScan()} method. 242This method takes a {@link android.bluetooth.BluetoothAdapter.LeScanCallback} 243as a parameter. You must implement this callback, because that is how scan 244results are returned. Because scanning is battery-intensive, you should observe 245the following guidelines:</p> 246<ul> 247 <li>As soon as you find the desired device, stop scanning.</li> 248 <li>Never scan on a loop, and set a time limit on your scan. A device that was 249previously available may have moved out of range, and continuing to scan drains 250the battery.</li> 251</ul> 252 253<p>The following snippet shows how to start and stop a scan:</p> 254 255<pre>/** 256 * Activity for scanning and displaying available BLE devices. 257 */ 258public class DeviceScanActivity extends ListActivity { 259 260 private BluetoothAdapter mBluetoothAdapter; 261 private boolean mScanning; 262 private Handler mHandler; 263 264 // Stops scanning after 10 seconds. 265 private static final long SCAN_PERIOD = 10000; 266 ... 267 private void scanLeDevice(final boolean enable) { 268 if (enable) { 269 // Stops scanning after a pre-defined scan period. 270 mHandler.postDelayed(new Runnable() { 271 @Override 272 public void run() { 273 mScanning = false; 274 mBluetoothAdapter.stopLeScan(mLeScanCallback); 275 } 276 }, SCAN_PERIOD); 277 278 mScanning = true; 279 mBluetoothAdapter.startLeScan(mLeScanCallback); 280 } else { 281 mScanning = false; 282 mBluetoothAdapter.stopLeScan(mLeScanCallback); 283 } 284 ... 285 } 286... 287} 288</pre> 289 290<p>If you want to scan for only specific types of peripherals, you can instead 291call {@link android.bluetooth.BluetoothAdapter#startLeScan startLeScan(UUID[], BluetoothAdapter.LeScanCallback)}, 292providing an array of {@link java.util.UUID} objects that specify the GATT 293services your app supports.</p> 294 295<p>Here is an implementation of the 296{@link android.bluetooth.BluetoothAdapter.LeScanCallback}, 297which is the interface used to deliver BLE scan results:</p> 298 299<pre> 300private LeDeviceListAdapter mLeDeviceListAdapter; 301... 302// Device scan callback. 303private BluetoothAdapter.LeScanCallback mLeScanCallback = 304 new BluetoothAdapter.LeScanCallback() { 305 @Override 306 public void onLeScan(final BluetoothDevice device, int rssi, 307 byte[] scanRecord) { 308 runOnUiThread(new Runnable() { 309 @Override 310 public void run() { 311 mLeDeviceListAdapter.addDevice(device); 312 mLeDeviceListAdapter.notifyDataSetChanged(); 313 } 314 }); 315 } 316};</pre> 317 318 319<p class="note"><strong>Note:</strong> You can only scan for Bluetooth LE devices 320<em>or</em> scan for Classic Bluetooth devices, as described in 321<a href="{@docRoot}guide/topics/connectivity/bluetooth.html">Bluetooth</a>. You cannot 322scan for both Bluetooth LE and classic devices at the same time.</p> 323 324<h2 id="connect">Connecting to a GATT Server</h2> 325 326<p>The first step in interacting with a BLE device is connecting to it— 327more specifically, connecting to the GATT server on the device. To 328connect to a GATT server on a BLE device, you use the 329{@link android.bluetooth.BluetoothDevice#connectGatt connectGatt()} method. 330This method takes three parameters: a {@link android.content.Context} object, 331<code>autoConnect</code> (boolean indicating whether to automatically connect to 332the BLE device as soon as it becomes available), and a reference to a 333{@link android.bluetooth.BluetoothGattCallback}: </p> 334 335<pre>mBluetoothGatt = device.connectGatt(this, false, mGattCallback);</pre> 336 337<p>This connects to the GATT server hosted by the BLE device, and returns a 338{@link android.bluetooth.BluetoothGatt} instance, which you can then use to 339conduct GATT client operations. The caller (the Android app) is the GATT client. 340The {@link android.bluetooth.BluetoothGattCallback} is used to deliver results 341to the client, such as connection status, as well as any further GATT client 342operations.</p> 343 344<p>In this example, the BLE app provides an activity 345(<code>DeviceControlActivity</code>) to connect, 346display data, and display GATT services and characteristics 347supported by the device. Based on user input, this activity communicates with a 348{@link android.app.Service} called {@code BluetoothLeService}, 349which interacts with the BLE device via the Android BLE API:</p> 350 351<pre> 352// A service that interacts with the BLE device via the Android BLE API. 353public class BluetoothLeService extends Service { 354 private final static String TAG = BluetoothLeService.class.getSimpleName(); 355 356 private BluetoothManager mBluetoothManager; 357 private BluetoothAdapter mBluetoothAdapter; 358 private String mBluetoothDeviceAddress; 359 private BluetoothGatt mBluetoothGatt; 360 private int mConnectionState = STATE_DISCONNECTED; 361 362 private static final int STATE_DISCONNECTED = 0; 363 private static final int STATE_CONNECTING = 1; 364 private static final int STATE_CONNECTED = 2; 365 366 public final static String ACTION_GATT_CONNECTED = 367 "com.example.bluetooth.le.ACTION_GATT_CONNECTED"; 368 public final static String ACTION_GATT_DISCONNECTED = 369 "com.example.bluetooth.le.ACTION_GATT_DISCONNECTED"; 370 public final static String ACTION_GATT_SERVICES_DISCOVERED = 371 "com.example.bluetooth.le.ACTION_GATT_SERVICES_DISCOVERED"; 372 public final static String ACTION_DATA_AVAILABLE = 373 "com.example.bluetooth.le.ACTION_DATA_AVAILABLE"; 374 public final static String EXTRA_DATA = 375 "com.example.bluetooth.le.EXTRA_DATA"; 376 377 public final static UUID UUID_HEART_RATE_MEASUREMENT = 378 UUID.fromString(SampleGattAttributes.HEART_RATE_MEASUREMENT); 379 380 // Various callback methods defined by the BLE API. 381 private final BluetoothGattCallback mGattCallback = 382 new BluetoothGattCallback() { 383 @Override 384 public void onConnectionStateChange(BluetoothGatt gatt, int status, 385 int newState) { 386 String intentAction; 387 if (newState == BluetoothProfile.STATE_CONNECTED) { 388 intentAction = ACTION_GATT_CONNECTED; 389 mConnectionState = STATE_CONNECTED; 390 broadcastUpdate(intentAction); 391 Log.i(TAG, "Connected to GATT server."); 392 Log.i(TAG, "Attempting to start service discovery:" + 393 mBluetoothGatt.discoverServices()); 394 395 } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { 396 intentAction = ACTION_GATT_DISCONNECTED; 397 mConnectionState = STATE_DISCONNECTED; 398 Log.i(TAG, "Disconnected from GATT server."); 399 broadcastUpdate(intentAction); 400 } 401 } 402 403 @Override 404 // New services discovered 405 public void onServicesDiscovered(BluetoothGatt gatt, int status) { 406 if (status == BluetoothGatt.GATT_SUCCESS) { 407 broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED); 408 } else { 409 Log.w(TAG, "onServicesDiscovered received: " + status); 410 } 411 } 412 413 @Override 414 // Result of a characteristic read operation 415 public void onCharacteristicRead(BluetoothGatt gatt, 416 BluetoothGattCharacteristic characteristic, 417 int status) { 418 if (status == BluetoothGatt.GATT_SUCCESS) { 419 broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic); 420 } 421 } 422 ... 423 }; 424... 425}</pre> 426 427<p>When a particular callback is triggered, it calls the appropriate 428{@code broadcastUpdate()} helper method and passes it an action. Note that the data 429parsing in this section is performed in accordance with the Bluetooth Heart Rate 430Measurement 431<a href="http://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.heart_rate_measurement.xml"> 432profile specifications</a>:</p> 433 434<pre>private void broadcastUpdate(final String action) { 435 final Intent intent = new Intent(action); 436 sendBroadcast(intent); 437} 438 439private void broadcastUpdate(final String action, 440 final BluetoothGattCharacteristic characteristic) { 441 final Intent intent = new Intent(action); 442 443 // This is special handling for the Heart Rate Measurement profile. Data 444 // parsing is carried out as per profile specifications. 445 if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) { 446 int flag = characteristic.getProperties(); 447 int format = -1; 448 if ((flag & 0x01) != 0) { 449 format = BluetoothGattCharacteristic.FORMAT_UINT16; 450 Log.d(TAG, "Heart rate format UINT16."); 451 } else { 452 format = BluetoothGattCharacteristic.FORMAT_UINT8; 453 Log.d(TAG, "Heart rate format UINT8."); 454 } 455 final int heartRate = characteristic.getIntValue(format, 1); 456 Log.d(TAG, String.format("Received heart rate: %d", heartRate)); 457 intent.putExtra(EXTRA_DATA, String.valueOf(heartRate)); 458 } else { 459 // For all other profiles, writes the data formatted in HEX. 460 final byte[] data = characteristic.getValue(); 461 if (data != null && data.length > 0) { 462 final StringBuilder stringBuilder = new StringBuilder(data.length); 463 for(byte byteChar : data) 464 stringBuilder.append(String.format("%02X ", byteChar)); 465 intent.putExtra(EXTRA_DATA, new String(data) + "\n" + 466 stringBuilder.toString()); 467 } 468 } 469 sendBroadcast(intent); 470}</pre> 471 472 473 474<p>Back in <code>DeviceControlActivity</code>, these events are handled by a 475{@link android.content.BroadcastReceiver}:</p> 476 477<pre>// Handles various events fired by the Service. 478// ACTION_GATT_CONNECTED: connected to a GATT server. 479// ACTION_GATT_DISCONNECTED: disconnected from a GATT server. 480// ACTION_GATT_SERVICES_DISCOVERED: discovered GATT services. 481// ACTION_DATA_AVAILABLE: received data from the device. This can be a 482// result of read or notification operations. 483private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() { 484 @Override 485 public void onReceive(Context context, Intent intent) { 486 final String action = intent.getAction(); 487 if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) { 488 mConnected = true; 489 updateConnectionState(R.string.connected); 490 invalidateOptionsMenu(); 491 } else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) { 492 mConnected = false; 493 updateConnectionState(R.string.disconnected); 494 invalidateOptionsMenu(); 495 clearUI(); 496 } else if (BluetoothLeService. 497 ACTION_GATT_SERVICES_DISCOVERED.equals(action)) { 498 // Show all the supported services and characteristics on the 499 // user interface. 500 displayGattServices(mBluetoothLeService.getSupportedGattServices()); 501 } else if (BluetoothLeService.ACTION_DATA_AVAILABLE.equals(action)) { 502 displayData(intent.getStringExtra(BluetoothLeService.EXTRA_DATA)); 503 } 504 } 505}; 506</pre> 507 508<h2 id="read">Reading BLE Attributes</h2> 509 510<p>Once your Android app has connected to a GATT server and discovered services, 511it can read and write attributes, where supported. For example, this snippet iterates 512through the server's services and characteristics and displays them in the UI:</p> 513 514<pre> 515public class DeviceControlActivity extends Activity { 516 ... 517 // Demonstrates how to iterate through the supported GATT 518 // Services/Characteristics. 519 // In this sample, we populate the data structure that is bound to the 520 // ExpandableListView on the UI. 521 private void displayGattServices(List<BluetoothGattService> gattServices) { 522 if (gattServices == null) return; 523 String uuid = null; 524 String unknownServiceString = getResources(). 525 getString(R.string.unknown_service); 526 String unknownCharaString = getResources(). 527 getString(R.string.unknown_characteristic); 528 ArrayList<HashMap<String, String>> gattServiceData = 529 new ArrayList<HashMap<String, String>>(); 530 ArrayList<ArrayList<HashMap<String, String>>> gattCharacteristicData 531 = new ArrayList<ArrayList<HashMap<String, String>>>(); 532 mGattCharacteristics = 533 new ArrayList<ArrayList<BluetoothGattCharacteristic>>(); 534 535 // Loops through available GATT Services. 536 for (BluetoothGattService gattService : gattServices) { 537 HashMap<String, String> currentServiceData = 538 new HashMap<String, String>(); 539 uuid = gattService.getUuid().toString(); 540 currentServiceData.put( 541 LIST_NAME, SampleGattAttributes. 542 lookup(uuid, unknownServiceString)); 543 currentServiceData.put(LIST_UUID, uuid); 544 gattServiceData.add(currentServiceData); 545 546 ArrayList<HashMap<String, String>> gattCharacteristicGroupData = 547 new ArrayList<HashMap<String, String>>(); 548 List<BluetoothGattCharacteristic> gattCharacteristics = 549 gattService.getCharacteristics(); 550 ArrayList<BluetoothGattCharacteristic> charas = 551 new ArrayList<BluetoothGattCharacteristic>(); 552 // Loops through available Characteristics. 553 for (BluetoothGattCharacteristic gattCharacteristic : 554 gattCharacteristics) { 555 charas.add(gattCharacteristic); 556 HashMap<String, String> currentCharaData = 557 new HashMap<String, String>(); 558 uuid = gattCharacteristic.getUuid().toString(); 559 currentCharaData.put( 560 LIST_NAME, SampleGattAttributes.lookup(uuid, 561 unknownCharaString)); 562 currentCharaData.put(LIST_UUID, uuid); 563 gattCharacteristicGroupData.add(currentCharaData); 564 } 565 mGattCharacteristics.add(charas); 566 gattCharacteristicData.add(gattCharacteristicGroupData); 567 } 568 ... 569 } 570... 571}</pre> 572 573<h2 id="notification">Receiving GATT Notifications</h2> 574 575<p>It's common for BLE apps to ask to be notified when a particular 576characteristic changes on the device. This snippet shows how to set a notification 577for a characteristic, using the 578{@link android.bluetooth.BluetoothGatt#setCharacteristicNotification setCharacteristicNotification()} 579method:</p> 580 581<pre> 582private BluetoothGatt mBluetoothGatt; 583BluetoothGattCharacteristic characteristic; 584boolean enabled; 585... 586mBluetoothGatt.setCharacteristicNotification(characteristic, enabled); 587... 588BluetoothGattDescriptor descriptor = characteristic.getDescriptor( 589 UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG)); 590descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); 591mBluetoothGatt.writeDescriptor(descriptor);</pre> 592 593<p>Once notifications are enabled for a characteristic, 594an {@link android.bluetooth.BluetoothGattCallback#onCharacteristicChanged onCharacteristicChanged()} 595callback is triggered if the characteristic changes on the remote device:</p> 596 597<pre>@Override 598// Characteristic notification 599public void onCharacteristicChanged(BluetoothGatt gatt, 600 BluetoothGattCharacteristic characteristic) { 601 broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic); 602} 603</pre> 604 605<h2 id="close">Closing the Client App</h2> 606 607<p>Once your app has finished using a BLE device, it should call 608{@link android.bluetooth.BluetoothGatt#close close()} 609so the system can release resources appropriately:</p> 610 611<pre>public void close() { 612 if (mBluetoothGatt == null) { 613 return; 614 } 615 mBluetoothGatt.close(); 616 mBluetoothGatt = null; 617}</pre> 618