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