minSdkVersion 18
minSdkVersion 21
project/build.gradle
repositories {
maven { url "https://github.com/im97mori-github/maven/raw/master" }
google()
mavenCentral()
}
project/module/build.gradle
dependencies {
implementation 'org.im97mori:ble-central:0.7.6' // central feature
implementation 'org.im97mori:ble-peripheral:0.3.6' // peripheral feature
}
It analyzes Advertising data byte array delivered from LeScanCallback or ScanCallback as data class according to type defined in BLE5.1.
class TestLeScanCallback implements BluetoothAdapter.LeScanCallback {
@Override
public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
AdvertisingDataParser.Builder builder = new AdvertisingDataParser.Builder(true);
AdvertisingDataParser parser = builder.build();
AdvertisingDataParser.AdvertisingDataParseResult result = parser.parse(scanRecord);
class TestScanCallback extends ScanCallback {
@Override
public void onScanResult(int callbackType, ScanResult result) {
ScanRecord scanRecord = result.getScanRecord();
if (scanRecord != null) {
AdvertisingDataParser.Builder builder = new AdvertisingDataParser.Builder(true);
AdvertisingDataParser parser = builder.build();
AdvertisingDataParser.AdvertisingDataParseResult result = parser.parse(scanRecord.getBytes());
All analysis results are included in AdvertisingDataParseResult.
Or software filtering callback.
ex) Callback only when Advertising data Shortend local name is "DeviceName".
BluetoothAdapter.getDefaultAdapter().startLeScan(new FilteredLeScanCallback.Builder().addShortenedLocalNameFilter(new byte[]{
11 // length of data
, 8 // Shortened Local Name Data Type
, 68 // D
, 101 // e
, 118 // v
, 105 // i
, 99 // c
, 101 // e
, 78 // N
, 97 // a
, 109 // m
, 101 // e
}).build());
BluetoothAdapter.getDefaultAdapter().getBluetoothLeScanner().startScan(new FilteredScanCallback.Builder().addShortenedLocalNameFilter(new byte[]{
11 // length of data
, 8 // Shortened Local Name Data Type
, 68 // D
, 101 // e
, 118 // v
, 105 // i
, 99 // c
, 101 // e
, 78 // N
, 97 // a
, 109 // m
, 101 // e
}
).build());
}
Every connection has one Handler(aka worker thread).
Request and response processing is queing as a task and processed in order within the handler.
Therefore, only one task is always executed for the peripheral, and continuous request issuance that can not be processed by the peripheral is suppressed.
The task is always queinged with a timeout value to prevent the remaining tasks that are not processing complete.
Begin connection
public class CentralSampleActivity extends Activity implements BLECallback {
private BLEConnection mBleConnection;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
BluetoothDevice bluetoothDevice = intent.getParcelableExtra("device");
mBleConnection = BLEConnectionHolder.getInstance(this, target);
mBleConnection.attach(mBLECallbackSample);
mBleConnection.connect(ConnectTask.TIMEOUT_MILLIS);
}
On success
@Override
public void onBLEConnected(long taskId, BluetoothDevice bluetoothDevice, Bundle argument);
// Success!
}
On fail
@Override
public void onBLEConnectFailed(long taskId, BluetoothDevice bluetoothDevice, int status, Bundle argument);
// Failed
}
On timeout(default 50sec)
@Override
public void onBLEConnectTimeout(BluetoothDevice bluetoothDevice) {onBLEConnectTimeout(long taskId, BluetoothDevice bluetoothDevice, Bundle argument);
// Timeout
}
Finish connection(release Handler)
@Override
protected void onDestroy() {
mBleConnection.quit();
super.onDestroy();
}
or detach callback(keep connection and Handler in BLEConnectionHolder)
@Override
protected void onDestroy() {
mBleConnection.detach(mBLECallbackSample);
super.onDestroy();
}
Read characteristic
@Override
public void onBLEConnected(long taskId, BluetoothDevice bluetoothDevice, Bundle argument);
// create read task
mBleConnection.createReadCharacteristicTask(ServiceUUID.GENERIC_ACCESS_SERVICE
, CharacteristicUUID.DEVICE_NAME_CHARACTERISTIC
, ReadCharacteristicTask.TIMEOUT_MILLIS);
}
// read success
@Override
public void onCharacteristicReadSuccess(long taskId, BluetoothDevice bluetoothDevice, UUID serviceUUID, UUID characteristicUUID, byte[] values, Bundle argument);
if (CharacteristicUUID.DEVICE_NAME_CHARACTERISTIC.equals(characteristicUUID)) {
DeviceName deviceName = DeviceName.CREATOR.createFromByteArray(values);
System.out.println(deviceName.getName());
}
}
Write characteristic
@Override
public void onBLEConnected(long taskId, BluetoothDevice bluetoothDevice, Bundle argument);
mBleConnection.createWriteCharacteristicTask(UUID.fromString("your writable characteristic's service uuid")
, UUID.fromString(" your writable charcteristic uuid")
, new ByteArrayInterface() {
@Override
public byte[] getBytes() {
return new byte[]{0x00, 0x00, 0x00, 0x00, 0x00};
}
}
, WriteCharacteristicTask.TIMEOUT_MILLIS);
}
// write success
@Override
public onCharacteristicWriteSuccess(long taskId, BluetoothDevice bluetoothDevice, UUID serviceUUID, UUID characteristicUUID, byte[] values, Bundle argument);
if (UUID.fromString(" your writable charcteristic uuid").equals(characteristicUUID)) {
// create your data class from byte arrays
// or BluetoothGattCharacteristic instance
BluetoothGattCharacteristic bluetoothGattCharacteristic = new BluetoothGattCharacteristic(characteristicUUID, 0, 0);
bluetoothGattCharacteristic.setValue(values);
}
}
Begin connection to finish connection
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
new Thread() {
@Override
public void run() {
Intent intent = getIntent();
BluetoothDevice target = intent.getParcelableExtra("device");
BLESyncConnection bleSyncConnection = new BLESyncConnection(MainActivity.this, target);
BLESyncConnection.BLEResult result = bleSyncConnection.connect(ConnectTask.TIMEOUT_MILLIS, ConnectTask.TIMEOUT_MILLIS, null);
if (BLESyncConnection.BLEResult.RESULT_SUCCESS == result.getResultCode()) {
// Success!
// finish
result = bleSyncConnection.quit();
} else if (BLESyncConnection.BLEResult.RESULT_FAILED == result.getResultCode()) {
// Failed
} else if (BLESyncConnection.BLEResult.RESULT_TIMEOUT == result.getResultCode()) {
// Timeout
}
}
}.start();
Read characteristic
result = bleSyncConnection.createReadCharacteristicTask(ServiceUUID.GENERIC_ACCESS_SERVICE
, CharacteristicUUID.DEVICE_NAME_CHARACTERISTIC
, ReadCharacteristicTask.TIMEOUT_MILLIS
, ReadCharacteristicTask.TIMEOUT_MILLIS
, null);
if (BLESyncConnection.BLEResult.RESULT_SUCCESS == result.getResultCode()) {
DeviceName deviceName = DeviceName.CREATOR.createFromByteArray(result.getValues());
System.out.println(deviceName.getName());
}
Instant read characteristic with connected BLEConnection
if (bleConnection == null || !bleConnection.isConnected()) return;
BLESyncConnection.BLEResult result = BLESyncConnection.createReadCharacteristicTask(
bleConnection
, ServiceUUID.GENERIC_ACCESS_SERVICE
, CharacteristicUUID.DEVICE_NAME_CHARACTERISTIC
, ReadCharacteristicTask.TIMEOUT_MILLIS
, ReadCharacteristicTask.TIMEOUT_MILLIS
, null);
if (BLESyncConnection.BLEResult.RESULT_SUCCESS == result.getResultCode()) {
DeviceName deviceName = DeviceName.CREATOR.createFromByteArray(result.getValues());
System.out.println(deviceName.getName());
}
This is experimental feature.
Feature list
- Start / Stop Gatt server from peripheral
- Advertising with Incomplete List of 128-bit Service Class UUIDs for find, bonding, connect from central
- Mock peripheral's response
Change characteristic / descriptor / notification / indication response from central(not available now)- Change response and Service / Characteristic / Descriptor setting from peripheral
public class PeripheralSampleActivity extends BaseActivity {
private BLEServerConnection mBLEServerConnection;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBLEServerConnection = new BLEServerConnection(this);
mBLEServerConnection.attach(new BLECallbackSample(this));
}
@Override
protected void onDestroy() {
mBLEServerConnection.quit();
super.onDestroy();
}
Advertising with Incomplete List of 128-bit Service Class UUIDs for find, bonding, connect from central
Advertising automaticaly start when gatt server started.
Advertising data include custom "00000000-a087-4fa3-add4-3b8a7d5d491f" Incomplete List of 128-bit Service Class UUIDs.
Example(All setting change from peripheral)
public class PeripheralSampleActivity extends BaseActivity implements View.OnClickListener {
private BLEServerConnection mBLEServerConnection;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBLEServerConnection = new BLEServerConnection(this);
mBLEServerConnection.attach(new BLECallbackSample(this));
}
@Override
public void onClick(View v) {
mBLEServerConnection.start();
mBLEServerConnection.startAdvertising();
}
@Override
protected void onDestroy() {
mBLEServerConnection.quit();
super.onDestroy();
}
}
public class BLECallbackSample extends BaseMockCallback implements BLECallback {
BLECallbackSample(SampleCallback sampleCallback) {
super(new SampleMockData.Builder().build(), true);
mSampleCallback = sampleCallback;
}
...
}
public class SampleMockData extends MockData {
public static final UUID SAMPLE_PRIMARY_SERVICE_1 = UUID.fromString("00000001-a087-4fa3-add4-3b8a7d5d4921");
public static final UUID SAMPLE_READABLE_CHARACTERISTIC = UUID.fromString("00000010-a087-4fa3-add4-3b8a7d5d4921");
public static final UUID SAMPLE_READABLE_DESCRIPTOR = UUID.fromString("00000100-a087-4fa3-add4-3b8a7d5d4921");
public static final UUID SAMPLE_WRITABLE_CHARACTERISTIC = UUID.fromString("00000020-a087-4fa3-add4-3b8a7d5d4921");
public static final UUID SAMPLE_WRITABLE_DESCRIPTOR = UUID.fromString("00000200-a087-4fa3-add4-3b8a7d5d4921");
public static final UUID SAMPLE_NOTIFICATABLE_CHARACTERISTIC = UUID.fromString("00000030-a087-4fa3-add4-3b8a7d5d4921");
public static final UUID SAMPLE_INDICATABLE_CHARACTERISTIC = UUID.fromString("00000040-a087-4fa3-add4-3b8a7d5d4921");
public static final UUID SAMPLE_WRITE_CHARACTERISTIC_RELIABLE = UUID.fromString("00000050-a087-4fa3-add4-3b8a7d5d4921");
public static final UUID SAMPLE_PRIMARY_SERVICE_2 = UUID.fromString("00000002-a087-4fa3-add4-3b8a7d5d4922");
public static final UUID SAMPLE_READABLE_CHARACTERISTIC_2 = UUID.fromString("00000060-a087-4fa3-add4-3b8a7d5d4921");
public static class Builder {
private final List<ServiceData> mServiceDataList = new LinkedList<>();
public Builder() {
ServiceData serviceData;
CharacteristicData characteristicData;
List<CharacteristicData> characteristicDataList;
DescriptorData descriptorData;
List<DescriptorData> descriptorDataList;
characteristicDataList = new LinkedList<>();
descriptorDataList = new LinkedList<>();
descriptorData = new DescriptorData();
descriptorData.uuid = SAMPLE_READABLE_DESCRIPTOR;
descriptorData.permission = BluetoothGattDescriptor.PERMISSION_READ;
descriptorData.responseCode = BluetoothGatt.GATT_SUCCESS;
descriptorData.delay = 0;
descriptorData.data = SAMPLE_READABLE_DESCRIPTOR.toString().getBytes();
descriptorDataList.add(descriptorData);
characteristicData = new CharacteristicData();
characteristicData.uuid = SAMPLE_READABLE_CHARACTERISTIC;
characteristicData.property = BluetoothGattCharacteristic.PROPERTY_READ;
characteristicData.permission = BluetoothGattCharacteristic.PERMISSION_READ;
characteristicData.descriptorDataList = descriptorDataList;
characteristicData.responseCode = BluetoothGatt.GATT_SUCCESS;
characteristicData.delay = 0;
characteristicData.data = SAMPLE_READABLE_CHARACTERISTIC.toString().getBytes();
characteristicDataList.add(characteristicData);
descriptorDataList = new LinkedList<>();
descriptorData = new DescriptorData();
descriptorData.uuid = SAMPLE_WRITABLE_DESCRIPTOR;
descriptorData.permission = BluetoothGattDescriptor.PERMISSION_WRITE;
descriptorData.responseCode = BluetoothGatt.GATT_SUCCESS;
descriptorData.delay = 0;
descriptorData.data = null;
descriptorDataList.add(descriptorData);
characteristicData = new CharacteristicData();
characteristicData.uuid = SAMPLE_WRITABLE_CHARACTERISTIC;
characteristicData.property = BluetoothGattCharacteristic.PROPERTY_WRITE;
characteristicData.permission = BluetoothGattCharacteristic.PERMISSION_WRITE;
characteristicData.descriptorDataList = descriptorDataList;
characteristicData.responseCode = BluetoothGatt.GATT_SUCCESS;
characteristicData.delay = 0;
characteristicData.data = null;
characteristicDataList.add(characteristicData);
descriptorDataList = new LinkedList<>();
descriptorData = new DescriptorData();
descriptorData.uuid = CLIENT_CHARACTERISTIC_CONFIGURATION_DESCRIPTOR;
descriptorData.permission = BluetoothGattDescriptor.PERMISSION_WRITE;
descriptorData.responseCode = BluetoothGatt.GATT_SUCCESS;
descriptorData.delay = 0;
descriptorData.data = null;
descriptorDataList.add(descriptorData);
characteristicData = new CharacteristicData();
characteristicData.uuid = SAMPLE_NOTIFICATABLE_CHARACTERISTIC;
characteristicData.property = BluetoothGattCharacteristic.PROPERTY_NOTIFY;
characteristicData.permission = 0;
characteristicData.descriptorDataList = descriptorDataList;
characteristicData.responseCode = BluetoothGatt.GATT_SUCCESS;
characteristicData.delay = 0;
characteristicData.data = SAMPLE_NOTIFICATABLE_CHARACTERISTIC.toString().getBytes();
characteristicDataList.add(characteristicData);
descriptorDataList = new LinkedList<>();
descriptorData = new DescriptorData();
descriptorData.uuid = CLIENT_CHARACTERISTIC_CONFIGURATION_DESCRIPTOR;
descriptorData.permission = BluetoothGattDescriptor.PERMISSION_WRITE;
descriptorData.responseCode = BluetoothGatt.GATT_SUCCESS;
descriptorData.delay = 0;
descriptorData.data = null;
descriptorDataList.add(descriptorData);
characteristicData = new CharacteristicData();
characteristicData.uuid = SAMPLE_INDICATABLE_CHARACTERISTIC;
characteristicData.property = BluetoothGattCharacteristic.PROPERTY_INDICATE;
characteristicData.permission = 0;
characteristicData.descriptorDataList = descriptorDataList;
characteristicData.responseCode = BluetoothGatt.GATT_SUCCESS;
characteristicData.delay = 0;
characteristicData.data = SAMPLE_INDICATABLE_CHARACTERISTIC.toString().getBytes();
characteristicDataList.add(characteristicData);
descriptorDataList = new LinkedList<>();
characteristicData = new CharacteristicData();
characteristicData.uuid = SAMPLE_WRITE_CHARACTERISTIC_RELIABLE;
characteristicData.property = BluetoothGattCharacteristic.PROPERTY_WRITE;
characteristicData.permission = BluetoothGattCharacteristic.PERMISSION_WRITE;
characteristicData.descriptorDataList = descriptorDataList;
characteristicData.responseCode = BluetoothGatt.GATT_SUCCESS;
characteristicData.delay = 0;
characteristicData.data = null;
characteristicDataList.add(characteristicData);
serviceData = new ServiceData();
serviceData.uuid = SAMPLE_PRIMARY_SERVICE_1;
serviceData.type = BluetoothGattService.SERVICE_TYPE_PRIMARY;
serviceData.characteristicDataList = characteristicDataList;
mServiceDataList.add(serviceData);
characteristicDataList = new LinkedList<>();
characteristicData = new CharacteristicData();
characteristicData.uuid = SAMPLE_READABLE_CHARACTERISTIC_2;
characteristicData.property = BluetoothGattCharacteristic.PROPERTY_READ;
characteristicData.permission = BluetoothGattCharacteristic.PERMISSION_READ;
characteristicData.descriptorDataList = descriptorDataList;
characteristicData.responseCode = BluetoothGatt.GATT_SUCCESS;
characteristicData.delay = 0;
characteristicData.data = SAMPLE_READABLE_CHARACTERISTIC_2.toString().getBytes();
characteristicDataList.add(characteristicData);
characteristicData = new CharacteristicData();
characteristicData.uuid = SAMPLE_READABLE_CHARACTERISTIC_2;
characteristicData.property = BluetoothGattCharacteristic.PROPERTY_READ;
characteristicData.permission = BluetoothGattCharacteristic.PERMISSION_READ;
characteristicData.descriptorDataList = descriptorDataList;
characteristicData.responseCode = BluetoothGatt.GATT_SUCCESS;
characteristicData.delay = 0;
characteristicData.data = SAMPLE_READABLE_CHARACTERISTIC_2.toString().getBytes();
characteristicDataList.add(characteristicData);
serviceData = new ServiceData();
serviceData.uuid = SAMPLE_PRIMARY_SERVICE_2;
serviceData.type = BluetoothGattService.SERVICE_TYPE_PRIMARY;
serviceData.characteristicDataList = characteristicDataList;
mServiceDataList.add(serviceData);
}
public SampleMockData build() {
SampleMockData sampleMockData = new SampleMockData();
sampleMockData.serviceDataList = mServiceDataList;
return sampleMockData;
}
}
}
Warning change peripheral's service / characteristic / descriptor setting after bonded, central's
BluetoothGattCallback#onConnectionStateChange return 133 error.
if you change peripheral's service / characteristic / descriptor setting, need re-bonding.