itt 2015 - hugo domenech-juarez - what's all that hype about ble?
TRANSCRIPT
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.0Motorola Bluetooth Stack
17
BLE Scanning
<uses-permission android:name="android.permission.BLUETOOTH"/> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-feature android:name="android.hardware.bluetooth_le" android:required="false"/>
boolean hasBleFeature() { return getPackageManager().hasSystemFeature(FEATURE_BLUETOOTH_LE)); }
23
BLE Scanning
mBluetoothAdapter.startLeScan(mLeScanCallback);
mBluetoothAdapter.stopLeScan(mLeScanCallback);
BluetoothManager manager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE); mBluetoothAdapter = bluetoothManager.getAdapter();
24
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
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
BLE Scanning: Lollipop improvements
mBluetoothAdapter.startScan(filters, scanSettings, scanCallback)
namedeviceAddress
uuid
serviceDataUuid
serviceData
manufacturerId
manufacturerData
Low Power
Balanced
Low Latency
27
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
Advertisement
GATT server
Hardware feature
- chipset needs to support it - nexus 9 vs nexus 5 & 7
BluetoothAdapter.isMultipleAdvertisementSupported()
Peripheral mode: Lollipop features
29
The GATT Protocol
Peripheral Central
Read - Write - Subscribe
Respond - Push
GATT Peripheral GATT protocol
Service
Service
Characteristic
Characteristic
Value
Indications Notifications
Descriptor
Descriptor Descriptor
31Server Client
Generic Attribute Profile
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
The BluetoothGatt
discoverServices
writeCharacteristic
readCharacteristic
writeDescriptor
setCharacteristicNotification
readDescriptor
readRemoteRssi
beginReliableWrite
executeReliableWrite
abortReliableWrite
readMtu
33
The BluetoothGattCallback
onServicesDiscovered
onCharacteristicWrite
onCharacteristicRead
onDescriptorWrite
onCharacteristicChanged
onDescriptorRead
onRemoteRssiRead
onReliableWriteCompleted
onMtuChanged
34
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
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
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
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
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
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
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