Home | History | Annotate | Download | only in connectivity
      1 page.title=Bluetooth Low Energy
      2 page.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>
     49 Android 4.3 (API Level 18) introduces built-in platform support for Bluetooth Low
     50 Energy in the <em>central role</em> and provides APIs that apps can use to discover
     51 devices, query for services, and read/write characteristics.
     52 In contrast to
     53 <a href="{@docRoot}guide/topics/connectivity/bluetooth.html">Classic Bluetooth</a>,
     54 Bluetooth Low Energy (BLE) is designed to provide significantly lower power consumption.
     55 This allows Android apps to communicate with BLE devices that have low power requirements,
     56 such 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
     62 is a general specification for sending and receiving short pieces of data known
     63 as "attributes" over a BLE link. All current Low Energy application profiles are
     64 based 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>
     68 for Low Energy devices. A profile is a specification for how a device works in a
     69 particular application. Note that a device can implement more than one profile.
     70 For example, a device could contain a heart rate monitor and a battery level
     71 detector.</li>
     72     </ul>
     73   </li>
     74   <li><strong>Attribute Protocol (ATT)</strong>&mdash;GATT is built on top of
     75 the Attribute Protocol (ATT). This is also referred to as GATT/ATT. ATT is
     76 optimized to run on BLE devices. To this end, it uses as few bytes as possible.
     77 Each attribute is uniquely identified by a Universally Unique Identifier (UUID),
     78 which is a standardized 128-bit format for a string ID used to uniquely
     79 identify information. The <em>attributes</em> transported by ATT are formatted
     80 as <em>characteristics</em> and <em>services</em>. </li>
     81 
     82   <li><strong>Characteristic</strong>&mdash;A characteristic contains a single
     83 value and 0-n descriptors that describe the characteristic's value. A
     84 characteristic can be thought of as a type, analogous to a class.</li>
     85   <li><strong>Descriptor</strong>&mdash;Descriptors are defined attributes that
     86 describe a characteristic value. For example, a descriptor might specify a
     87 human-readable description, an acceptable range for a characteristic's value, or
     88 a 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
     91 characteristics. 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
     94 profiles and services on
     95 <a href="https://www.bluetooth.org/en-us/specification/adopted-specifications">
     96 bluetooth.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
    103 an 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
    110 other once they've established the connection.</li>
    111 </ul>
    112 
    113 <p>To understand the distinction, imagine that you have an Android phone and
    114 an activity tracker that is a BLE device. The phone supports the
    115 central role; the activity tracker supports the peripheral role (to
    116 establish a BLE connection you need one of each&mdash;two things that only support
    117 peripheral couldn't talk to each other, nor could two things that only support
    118 central).</p>
    119 
    120 <p>Once the phone and the activity tracker have established a connection, they
    121 start transferring GATT metadata to one another. Depending on the kind of data they transfer,
    122 one or the other might act as the server. For example, if the activity tracker
    123 wants to report sensor data to the phone, it might make sense for the activity
    124 tracker to act as the server. If the activity tracker wants to receive updates
    125 from the phone, then it might make sense for the phone to act
    126 as the server.</p>
    127 
    128 <p>
    129 In the example used in this document, the Android app (running on an Android
    130 device) is the GATT client. The app gets data from the GATT server, which is a
    131 BLE heart rate monitor that supports the
    132 <a href="http://developer.bluetooth.org/TechnologyOverview/Pages/HRP.aspx">Heart
    133 Rate Profile</a>.  But you could alternatively design
    134 your Android app to play the GATT server
    135 role. See {@link android.bluetooth.BluetoothGattServer} for more
    136 information.</p>
    137 
    138 <h2 id="permissions">BLE Permissions</h2>
    139 
    140 <p>In order to use Bluetooth features in your application, you must declare
    141 the Bluetooth permission {@link android.Manifest.permission#BLUETOOTH}.
    142 You need this permission to perform any Bluetooth communication,
    143 such 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
    146 settings, you must also declare the {@link android.Manifest.permission#BLUETOOTH_ADMIN}
    147 permission. <strong>Note:</strong> If you use the
    148 {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission, then you must
    149 also have the {@link android.Manifest.permission#BLUETOOTH} permission.</p>
    150 
    151 <p>Declare the Bluetooth permission(s) in your application manifest file. For
    152 example:</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,
    159 include 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,
    165 you should still include this element in your app's manifest, but set {@code required="false"}. 
    166 Then 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.
    171 if (!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
    179 to verify that BLE is supported on the device, and if so, ensure that it is enabled.
    180 Note that this check is only necessary if {@code &lt;uses-feature.../&gt;}
    181 is set to false.</p>
    182 
    183 <p>If BLE is not supported, then you should gracefully disable any
    184 BLE features. If BLE is supported, but disabled, then you can request that the
    185 user enable Bluetooth without leaving your application. This setup is
    186 accomplished 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
    193 Bluetooth activity. The {@link android.bluetooth.BluetoothAdapter} represents
    194 the device's own Bluetooth adapter (the Bluetooth radio). There's one Bluetooth
    195 adapter for the entire system, and your application can interact with it using
    196 this object. The snippet below shows how to get the adapter. Note that this approach
    197 uses {@link android.content.Context#getSystemService getSystemService()} to return
    198 an instance of {@link android.bluetooth.BluetoothManager}, which is then
    199 used to get the adapter. Android 4.3 (API Level 18) introduces
    200 {@link android.bluetooth.BluetoothManager}:</p>
    201 
    202 <pre>// Initializes Bluetooth adapter.
    203 final BluetoothManager bluetoothManager =
    204         (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
    205 mBluetoothAdapter = bluetoothManager.getAdapter();
    206 </pre>
    207 </li>
    208 
    209 <li>Enable Bluetooth
    210 <p>Next, you need to ensure that Bluetooth is enabled. Call {@link
    211 android.bluetooth.BluetoothAdapter#isEnabled()} to check whether Bluetooth is
    212 currently enabled. If this method returns false, then Bluetooth is disabled.
    213 The following snippet checks whether Bluetooth is enabled. If it isn't, the
    214 snippet displays an error prompting the user to go to Settings to enable
    215 Bluetooth:</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.
    220 if (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.
    233 This method takes a  {@link android.bluetooth.BluetoothAdapter.LeScanCallback}
    234 as a parameter. You must implement this callback, because that is how scan
    235 results are returned. Because scanning is battery-intensive, you should observe
    236 the 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
    240 previously available may have moved out of range, and continuing to scan drains
    241 the 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  */
    249 public 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
    282 call {@link android.bluetooth.BluetoothAdapter#startLeScan startLeScan(UUID[], BluetoothAdapter.LeScanCallback)},
    283 providing an array of {@link java.util.UUID} objects that specify the GATT
    284 services your app supports.</p>
    285 
    286 <p>Here is an implementation of the
    287 {@link android.bluetooth.BluetoothAdapter.LeScanCallback},
    288 which is the interface used to deliver BLE scan results:</p>
    289 
    290 <pre>
    291 private LeDeviceListAdapter mLeDeviceListAdapter;
    292 ...
    293 // Device scan callback.
    294 private 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
    313 scan 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;
    318 more specifically, connecting to the GATT server on the device. To
    319 connect to a GATT server on a BLE device, you use the
    320 {@link android.bluetooth.BluetoothDevice#connectGatt connectGatt()} method.
    321 This method takes three parameters: a {@link android.content.Context} object,
    322 <code>autoConnect</code> (boolean indicating whether to automatically connect to
    323 the 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
    330 conduct GATT client operations. The caller (the Android app) is the GATT client.
    331 The {@link android.bluetooth.BluetoothGattCallback} is used to deliver results
    332 to the client, such as connection status, as well as any further GATT client
    333 operations.</p>
    334 
    335 <p>In this example, the BLE app provides an activity
    336 (<code>DeviceControlActivity</code>) to connect,
    337 display data, and display GATT services and characteristics
    338 supported by the device.  Based on user input, this activity communicates with a
    339 {@link android.app.Service} called {@code BluetoothLeService},
    340 which 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.
    344 public 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
    420 parsing in this section is performed in accordance with the Bluetooth Heart Rate
    421 Measurement
    422 <a href="http://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.heart_rate_measurement.xml">
    423 profile specifications</a>:</p>
    424 
    425 <pre>private void broadcastUpdate(final String action) {
    426     final Intent intent = new Intent(action);
    427     sendBroadcast(intent);
    428 }
    429 
    430 private 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.
    474 private 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,
    502 it can read and write attributes, where supported. For example, this snippet iterates
    503 through the server's services and characteristics and displays them in the UI:</p>
    504 
    505 <pre>
    506 public 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
    567 characteristic changes on the device. This snippet shows how to set a notification
    568 for a characteristic, using the
    569 {@link android.bluetooth.BluetoothGatt#setCharacteristicNotification setCharacteristicNotification()}
    570 method:</p>
    571 
    572 <pre>
    573 private BluetoothGatt mBluetoothGatt;
    574 BluetoothGattCharacteristic characteristic;
    575 boolean enabled;
    576 ...
    577 mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
    578 ...
    579 BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
    580         UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
    581 descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
    582 mBluetoothGatt.writeDescriptor(descriptor);</pre>
    583 
    584 <p>Once notifications are enabled for a characteristic,
    585 an {@link android.bluetooth.BluetoothGattCallback#onCharacteristicChanged onCharacteristicChanged()}
    586 callback is triggered if the characteristic changes on the remote device:</p>
    587 
    588 <pre>&#64;Override
    589 // Characteristic notification
    590 public 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()}
    600 so 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