Upgrade to Pro — share decks privately, control downloads, hide ads and more …

What's all that hype about BLE

What's all that hype about BLE

Whether you want to monitor the heating of your house, interact with wearable devices or communicate with your car while driving, Bluetooth Low Energy is going to be your companion along the way. For gamers in particular, you are no longer limited to a screen, you now can interact with the world around you making it possible to create context aware experiences.

In this talk you will learn about all the possibilities that Android provides using BLE, both acting as a master, being able to connect to sensors and actuators around you, and playing as a peripheral, advertising packets and beacons for the world around you to see.

I will share some tips and tricks learned from building a SDK for interacting with the WunderBar, a BLE enabled starter kit for the Internet of Things. Finally, I will explain how to make your connections secure and fast, and we will discuss some compromises we had to make to support iOS devices like as well.

Hugo Doménech Juárez

April 27, 2015
Tweet

More Decks by Hugo Doménech Juárez

Other Decks in Programming

Transcript

  1. 3

  2. 4

  3. 5

  4. 6

  5. 7

  6. 10

  7. 11

  8. 12

  9. Eclair 2.0 (API 5) BlueZ Android 1.x 4.3 (API 18)

    Bluedroid JellyBean Bluetooth Smart 5.0 (API 20) Lollipop Full BLE Older Broadcom stack - Samsung & HTC < 4.0 Motorola Bluetooth Stack 17
  10. 20

  11. BLE Scanning private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() { @Override

    public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) { // You are not in the UI thread // or in the thread you subscribed for what matters! } }; 25
  12. BLE Scanning /** * Decodes the services uuid from the

    advertisement packet since * {@link android.bluetooth.BluetoothDevice#getUuids()} returns null most of * the times. */ public static List<String> decodeServicesUuid(byte[] data); /** * Decodes the device name from the Complete Local Name or Shortened * Local Name field in the * Advertisement packet. {@link * android.bluetooth.BluetoothDevice#getName()} does it already, * although some phones skip it. i.e. Sony Xperia Z1 (C6903) with Android 4.3 * where getName() always returns <code>null</code>. */ public static String decodeDeviceName(byte[] data); 26
  13. BLE Scanning: Lollipop improvements mBluetoothAdapter.startScan(filters, scanSettings, scanCallback) name deviceAddress uuid

    serviceDataUuid serviceData manufacturerId manufacturerData Low Power Balanced Low Latency 27
  14. interface ScanCallback { public void onScanResult(int callbackType, ScanResult result); public

    void onBatchScanResults(List<ScanResult> results); public void onScanFailed(int errorCode) } BLE Scanning: Lollipop improvements 28
  15. Advertisement GATT server Hardware feature - chipset needs to support

    it - nexus 9 vs nexus 5 & 7 BluetoothAdapter.isMultipleAdvertisementSupported() Peripheral mode: Lollipop features 29
  16. The GATT Protocol Peripheral Central Read - Write - Subscribe

    Respond - Push GATT Peripheral GATT protocol Service Service Characteristic Characteristic Value Indications Notifications Descriptor Descriptor Descriptor 31 Server Client Generic Attribute Profile
  17. The GATT Protocol class BluetoothAdapterLeScanCallback { void onLeScan(BluetoothDevice device, int

    rssi, byte[] scanRecord) { device.connectGatt(context, false, mBluetoothGattReceiver); } } class BluetoothGattReceiver { @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {} } 32
  18. 36

  19. 37

  20. 38

  21. Data subscriptions: Indication / Notification gatt.setCharacteristicNotification(characteristic, enable); descriptor.setValue(ENABLE_NOTIFICATION_VALUE); gatt.writeDescriptor(descriptor); gatt.setCharacteristicNotification(characteristic,

    enable); descriptor.setValue(ENABLE_INDICATION_VALUE); gatt.writeDescriptor(descriptor); gatt.setCharacteristicNotification(characteristic, enable); descriptor.setValue(DISABLE_NOTIFICATION_VALUE); gatt.writeDescriptor(descriptor); @Override public void onCharacteristicChanged( BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { } 39
  22. Reliable Write ?= Long Write gatt.beginReliableWrite(); characteristic.setValue(data); gatt.reliableWriteCharacteristic(mBluetoothGatt, characteristic, subscriber);

    gatt.executeReliableWrite(); 40 18 byte: 16 bytes content + 1 offset + 1 length package Regular Write supports 18 bytes
  23. Disconnecting GATT vs Closing GATT gatt.disconnect(); @Override public void onConnectionStateChange(BluetoothGatt

    gatt, int status, int newState) { if (newState == STATE_DISCONNECTED && noLongerInterestedInTheDevice) { gatt.close(); } } gatt.close(); 41
  24. Stack metrics: why you should close your GATT - Constants

    defined in BlueDroid - Max concurrent active notifications - BTA_GATTC_NOTIF_REG_MAX - (4) Android 4.3 - (7) Android 4.4 - (15) Android 5.0+ - Max concurrent GATT connections - BTA_GATTC_CON_MAX - (4) Android 4.3 - (7) Android 4.4+ 42
  25. Security: Instability static class UndocumentedBleStuff { static boolean isUndocumentedErrorStatus(int status)

    { return status == 133 || status == 137; } static void fixUndocumentedBleStatusProblem(BluetoothGatt gatt, BluetoothGattReceiver receiver) { DeviceCompatibilityUtils.refresh(gatt); gatt.getDevice().connectGatt(RelayrApp.get(), false, receiver); } } GATT_INSUFFICIENT_AUTHENTICATION GATT_INSUFFICIENT_ENCRYPTION 45
  26. Security: Instability boolean refresh(BluetoothGatt gatt) { try { Method localMethod

    = gatt.getClass().getMethod("refresh", new Class[0]); if (localMethod != null) { return (Boolean) localMethod.invoke(gatt); } } catch (Exception localException) { Log.e(TAG, "An exception occurred while performing: refresh”, localException.getCause()); } return false; } 46
  27. Security: Instability class DeviceCompatibilityUtils { boolean createBond(BluetoothDevice device) { if

    (isSdk19()) return doCreateBond(device); return callMethod(device, "createBond"); } @TargetApi(Build.VERSION_CODES.KITKAT) boolean doCreateBond(BluetoothDevice device) { return device.createBond(); } boolean removeBond(BluetoothDevice device) { return callMethod(device, "removeBond"); } } 47
  28. 50

  29. 52