• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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>&mdash;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>&mdash;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>&mdash;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>&mdash;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>&mdash;A service is a collection of
91characteristics. For example, you could have a service called
92&quot;Heart Rate Monitor&quot; that includes characteristics such as
93&quot;heart rate measurement.&quot; 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&mdash;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&lt;uses-permission android:name=&quot;android.permission.BLUETOOTH&quot;/&gt;
156&lt;uses-permission android:name=&quot;android.permission.BLUETOOTH_ADMIN&quot;/&gt;</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>&lt;uses-feature android:name=&quot;android.hardware.bluetooth_le&quot; android:required=&quot;true&quot;/&gt;
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 &lt;uses-feature.../&gt;}
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                &#64;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    &#64;Override
297    public void onLeScan(final BluetoothDevice device, int rssi,
298            byte[] scanRecord) {
299        runOnUiThread(new Runnable() {
300           &#64;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&mdash;
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        &#64;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        &#64;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        &#64;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 &amp; 0x01) != 0) {
440            format = BluetoothGattCharacteristic.FORMAT_UINT16;
441            Log.d(TAG, &quot;Heart rate format UINT16.&quot;);
442        } else {
443            format = BluetoothGattCharacteristic.FORMAT_UINT8;
444            Log.d(TAG, &quot;Heart rate format UINT8.&quot;);
445        }
446        final int heartRate = characteristic.getIntValue(format, 1);
447        Log.d(TAG, String.format(&quot;Received heart rate: %d&quot;, 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 &amp;&amp; data.length &gt; 0) {
453            final StringBuilder stringBuilder = new StringBuilder(data.length);
454            for(byte byteChar : data)
455                stringBuilder.append(String.format(&quot;%02X &quot;, byteChar));
456            intent.putExtra(EXTRA_DATA, new String(data) + &quot;\n&quot; +
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    &#64;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&lt;BluetoothGattService&gt; 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&lt;HashMap&lt;String, String&gt;&gt; gattServiceData =
520                new ArrayList&lt;HashMap&lt;String, String&gt;&gt;();
521        ArrayList&lt;ArrayList&lt;HashMap&lt;String, String&gt;&gt;&gt; gattCharacteristicData
522                = new ArrayList&lt;ArrayList&lt;HashMap&lt;String, String&gt;&gt;&gt;();
523        mGattCharacteristics =
524                new ArrayList&lt;ArrayList&lt;BluetoothGattCharacteristic&gt;&gt;();
525
526        // Loops through available GATT Services.
527        for (BluetoothGattService gattService : gattServices) {
528            HashMap&lt;String, String&gt; currentServiceData =
529                    new HashMap&lt;String, String&gt;();
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&lt;HashMap&lt;String, String&gt;&gt; gattCharacteristicGroupData =
538                    new ArrayList&lt;HashMap&lt;String, String&gt;&gt;();
539            List&lt;BluetoothGattCharacteristic&gt; gattCharacteristics =
540                    gattService.getCharacteristics();
541            ArrayList&lt;BluetoothGattCharacteristic&gt; charas =
542                    new ArrayList&lt;BluetoothGattCharacteristic&gt;();
543           // Loops through available Characteristics.
544            for (BluetoothGattCharacteristic gattCharacteristic :
545                    gattCharacteristics) {
546                charas.add(gattCharacteristic);
547                HashMap&lt;String, String&gt; currentCharaData =
548                        new HashMap&lt;String, String&gt;();
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>&#64;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