Skip to content

Commit

Permalink
[Android] Manufacturer specific data, service data, service uuids in …
Browse files Browse the repository at this point in the history
…AD and SRD.
  • Loading branch information
pauldemarco committed Aug 11, 2018
1 parent 77a3358 commit 53e82f3
Show file tree
Hide file tree
Showing 10 changed files with 343 additions and 58 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ static AdvertisementData parse(byte[] rawData) {
break;
}
case 0x0A: { // Power level.
ret.setTxPowerLevel(data.get());
ret.setTxPowerLevel(Protos.Int32Value.newBuilder().setValue(data.get()));
break;
}
case 0x16: // Service Data with 16 bit UUID.
Expand Down Expand Up @@ -114,9 +114,15 @@ static AdvertisementData parse(byte[] rawData) {
break;
}
case 0xFF: {// Manufacturer specific data.
byte[] msd = new byte[length];
data.get(msd);
ret.setManufacturerData(ByteString.copyFrom(msd));
if(length < 2) {
throw new ArrayIndexOutOfBoundsException("Not enough data for Manufacturer specific data.");
}
int manufacturerId = data.getShort();
if((length - 2) > 0) {
byte[] msd = new byte[length - 2];
data.get(msd);
ret.putManufacturerData(manufacturerId, ByteString.copyFrom(msd));
}
break;
}
default: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -622,7 +622,7 @@ private ScanCallback getScanCallback21() {
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
if(scanResultsSink != null) {
Protos.ScanResult scanResult = ProtoMaker.from(result.getDevice(), result.getScanRecord().getBytes(), result.getRssi());
Protos.ScanResult scanResult = ProtoMaker.from(result.getDevice(), result);
scanResultsSink.success(scanResult.toByteArray());
}
}
Expand Down
61 changes: 61 additions & 0 deletions android/src/main/java/com/pauldemarco/flutterblue/ProtoMaker.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,26 @@

package com.pauldemarco.flutterblue;

import android.annotation.TargetApi;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.le.ScanRecord;
import android.bluetooth.le.ScanResult;
import android.os.Build;
import android.os.Parcel;
import android.os.ParcelUuid;
import android.util.Log;
import android.util.SparseArray;

import com.google.protobuf.ByteString;

import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.UUID;

/**
Expand All @@ -32,6 +43,56 @@ static Protos.ScanResult from(BluetoothDevice device, byte[] advertisementData,
return p.build();
}

@TargetApi(21)
static Protos.ScanResult from(BluetoothDevice device, ScanResult scanResult) {
Protos.ScanResult.Builder p = Protos.ScanResult.newBuilder();
p.setDevice(from(device));
Protos.AdvertisementData.Builder a = Protos.AdvertisementData.newBuilder();
ScanRecord scanRecord = scanResult.getScanRecord();
if(Build.VERSION.SDK_INT >= 26) {
a.setConnectable(scanResult.isConnectable());
} else {
if(scanRecord != null) {
int flags = scanRecord.getAdvertiseFlags();
a.setConnectable((flags & 0x2) > 0);
}
}
if(scanRecord != null) {
String deviceName = scanRecord.getDeviceName();
if(deviceName != null) {
a.setLocalName(deviceName);
}
int txPower = scanRecord.getTxPowerLevel();
if(txPower != Integer.MIN_VALUE) {
a.setTxPowerLevel(Protos.Int32Value.newBuilder().setValue(txPower));
}
// Manufacturer Specific Data
SparseArray<byte[]> msd = scanRecord.getManufacturerSpecificData();
for (int i = 0; i < msd.size(); i++) {
int key = msd.keyAt(i);
byte[] value = msd.valueAt(i);
a.putManufacturerData(key, ByteString.copyFrom(value));
}
// Service Data
Map<ParcelUuid, byte[]> serviceData = scanRecord.getServiceData();
for (Map.Entry<ParcelUuid, byte[]> entry : serviceData.entrySet()) {
ParcelUuid key = entry.getKey();
byte[] value = entry.getValue();
a.putServiceData(key.getUuid().toString(), ByteString.copyFrom(value));
}
// Service UUIDs
List<ParcelUuid> serviceUuids = scanRecord.getServiceUuids();
if(serviceUuids != null) {
for (ParcelUuid s : serviceUuids) {
a.addServiceUuids(s.getUuid().toString());
}
}
}
p.setRssi(scanResult.getRssi());
p.setAdvertisementData(a.build());
return p.build();
}

static Protos.BluetoothDevice from(BluetoothDevice device) {
Protos.BluetoothDevice.Builder p = Protos.BluetoothDevice.newBuilder();
p.setRemoteId(device.getAddress());
Expand Down
75 changes: 66 additions & 9 deletions example/lib/widgets.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,26 +25,83 @@ class ScanResultTile extends StatelessWidget {
}
}

Widget _buildAdvRow(BuildContext context, String title, String value) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 4.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(title, style: Theme.of(context).textTheme.caption),
SizedBox(
width: 12.0,
),
Expanded(
child: Text(
value,
style: Theme.of(context)
.textTheme
.caption
.apply(color: Colors.black),
softWrap: true,
),
),
],
),
);
}

String getNiceManufacturerData(Map<int, List<int>> data) {
if (data.isEmpty) {
return null;
}
List<String> res = [];
data.forEach((id, bytes) {
res.add('${id.toRadixString(16).toUpperCase()}: $bytes');
});
return res.join(', ');
}

String getNiceServiceData(Map<String, List<int>> data) {
if (data.isEmpty) {
return null;
}
List<String> res = [];
data.forEach((id, bytes) {
res.add('$id: $bytes');
});
return res.join(', ');
}

@override
Widget build(BuildContext context) {
print('MANU DATA: ${result.advertisementData.manufacturerData}');
print('TX POWER: ${result.advertisementData.txPowerLevel}');
return ExpansionTile(
title: _buildTitle(context),
leading: Text(result.rssi.toString()),
trailing: RaisedButton(
child: Text('CONNECT'),
color: Colors.black,
textColor: Colors.white,
onPressed: onTap,
onPressed: (result.advertisementData.connectable) ? onTap : null,
),
children: <Widget>[
Row(
children: <Widget>[
Text('Complete Local Name:'),
Text(result.advertisementData.localName)
],
)
_buildAdvRow(
context, 'Complete Local Name', result.advertisementData.localName),
_buildAdvRow(context, 'Tx Power Level',
'${result.advertisementData.txPowerLevel ?? 'N/A'}'),
_buildAdvRow(
context,
'Manufacturer Data',
getNiceManufacturerData(
result.advertisementData.manufacturerData) ??
'N/A'),
_buildAdvRow(
context,
'Service UUIDs',
(result.advertisementData.serviceUuids.isNotEmpty)
? result.advertisementData.serviceUuids.join(', ')
: 'N/A'),
_buildAdvRow(context, 'Service Data',
getNiceServiceData(result.advertisementData.serviceData) ?? 'N/A'),
],
);
}
Expand Down
28 changes: 26 additions & 2 deletions ios/gen/Flutterblue.pbobjc.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

59 changes: 51 additions & 8 deletions ios/gen/Flutterblue.pbobjc.m

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 53e82f3

Please sign in to comment.