bluetooth-le.jd revision 64fedb7725f082ce6bde94c3599d2e7c203ae077
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>&mdash;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>&mdash;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>&mdash;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>&mdash;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>&mdash;A service is a collection of
100characteristics. For example, you could have a service called
101&quot;Heart Rate Monitor&quot; that includes characteristics such as
102&quot;heart rate measurement.&quot; 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&mdash;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&lt;uses-permission android:name=&quot;android.permission.BLUETOOTH&quot;/&gt;
165&lt;uses-permission android:name=&quot;android.permission.BLUETOOTH_ADMIN&quot;/&gt;</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>&lt;uses-feature android:name=&quot;android.hardware.bluetooth_le&quot; android:required=&quot;true&quot;/&gt;
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 &lt;uses-feature.../&gt;}
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                &#64;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    &#64;Override
306    public void onLeScan(final BluetoothDevice device, int rssi,
307            byte[] scanRecord) {
308        runOnUiThread(new Runnable() {
309           &#64;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&mdash;
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        &#64;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        &#64;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        &#64;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 &amp; 0x01) != 0) {
449            format = BluetoothGattCharacteristic.FORMAT_UINT16;
450            Log.d(TAG, &quot;Heart rate format UINT16.&quot;);
451        } else {
452            format = BluetoothGattCharacteristic.FORMAT_UINT8;
453            Log.d(TAG, &quot;Heart rate format UINT8.&quot;);
454        }
455        final int heartRate = characteristic.getIntValue(format, 1);
456        Log.d(TAG, String.format(&quot;Received heart rate: %d&quot;, 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 &amp;&amp; data.length &gt; 0) {
462            final StringBuilder stringBuilder = new StringBuilder(data.length);
463            for(byte byteChar : data)
464                stringBuilder.append(String.format(&quot;%02X &quot;, byteChar));
465            intent.putExtra(EXTRA_DATA, new String(data) + &quot;\n&quot; +
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    &#64;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&lt;BluetoothGattService&gt; 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&lt;HashMap&lt;String, String&gt;&gt; gattServiceData =
529                new ArrayList&lt;HashMap&lt;String, String&gt;&gt;();
530        ArrayList&lt;ArrayList&lt;HashMap&lt;String, String&gt;&gt;&gt; gattCharacteristicData
531                = new ArrayList&lt;ArrayList&lt;HashMap&lt;String, String&gt;&gt;&gt;();
532        mGattCharacteristics =
533                new ArrayList&lt;ArrayList&lt;BluetoothGattCharacteristic&gt;&gt;();
534
535        // Loops through available GATT Services.
536        for (BluetoothGattService gattService : gattServices) {
537            HashMap&lt;String, String&gt; currentServiceData =
538                    new HashMap&lt;String, String&gt;();
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&lt;HashMap&lt;String, String&gt;&gt; gattCharacteristicGroupData =
547                    new ArrayList&lt;HashMap&lt;String, String&gt;&gt;();
548            List&lt;BluetoothGattCharacteristic&gt; gattCharacteristics =
549                    gattService.getCharacteristics();
550            ArrayList&lt;BluetoothGattCharacteristic&gt; charas =
551                    new ArrayList&lt;BluetoothGattCharacteristic&gt;();
552           // Loops through available Characteristics.
553            for (BluetoothGattCharacteristic gattCharacteristic :
554                    gattCharacteristics) {
555                charas.add(gattCharacteristic);
556                HashMap&lt;String, String&gt; currentCharaData =
557                        new HashMap&lt;String, String&gt;();
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>&#64;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