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