Skip to content

Commit 53e82f3

Browse files
committed
[Android] Manufacturer specific data, service data, service uuids in AD and SRD.
1 parent 77a3358 commit 53e82f3

File tree

10 files changed

+343
-58
lines changed

10 files changed

+343
-58
lines changed

android/src/main/java/com/pauldemarco/flutterblue/AdvertisementParser.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ static AdvertisementData parse(byte[] rawData) {
8484
break;
8585
}
8686
case 0x0A: { // Power level.
87-
ret.setTxPowerLevel(data.get());
87+
ret.setTxPowerLevel(Protos.Int32Value.newBuilder().setValue(data.get()));
8888
break;
8989
}
9090
case 0x16: // Service Data with 16 bit UUID.
@@ -114,9 +114,15 @@ static AdvertisementData parse(byte[] rawData) {
114114
break;
115115
}
116116
case 0xFF: {// Manufacturer specific data.
117-
byte[] msd = new byte[length];
118-
data.get(msd);
119-
ret.setManufacturerData(ByteString.copyFrom(msd));
117+
if(length < 2) {
118+
throw new ArrayIndexOutOfBoundsException("Not enough data for Manufacturer specific data.");
119+
}
120+
int manufacturerId = data.getShort();
121+
if((length - 2) > 0) {
122+
byte[] msd = new byte[length - 2];
123+
data.get(msd);
124+
ret.putManufacturerData(manufacturerId, ByteString.copyFrom(msd));
125+
}
120126
break;
121127
}
122128
default: {

android/src/main/java/com/pauldemarco/flutterblue/FlutterBluePlugin.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -622,7 +622,7 @@ private ScanCallback getScanCallback21() {
622622
public void onScanResult(int callbackType, ScanResult result) {
623623
super.onScanResult(callbackType, result);
624624
if(scanResultsSink != null) {
625-
Protos.ScanResult scanResult = ProtoMaker.from(result.getDevice(), result.getScanRecord().getBytes(), result.getRssi());
625+
Protos.ScanResult scanResult = ProtoMaker.from(result.getDevice(), result);
626626
scanResultsSink.success(scanResult.toByteArray());
627627
}
628628
}

android/src/main/java/com/pauldemarco/flutterblue/ProtoMaker.java

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,26 @@
44

55
package com.pauldemarco.flutterblue;
66

7+
import android.annotation.TargetApi;
78
import android.bluetooth.BluetoothDevice;
89
import android.bluetooth.BluetoothGatt;
910
import android.bluetooth.BluetoothGattCharacteristic;
1011
import android.bluetooth.BluetoothGattDescriptor;
1112
import android.bluetooth.BluetoothGattService;
1213
import android.bluetooth.BluetoothProfile;
14+
import android.bluetooth.le.ScanRecord;
15+
import android.bluetooth.le.ScanResult;
16+
import android.os.Build;
17+
import android.os.Parcel;
18+
import android.os.ParcelUuid;
19+
import android.util.Log;
20+
import android.util.SparseArray;
1321

1422
import com.google.protobuf.ByteString;
1523

24+
import java.util.Iterator;
25+
import java.util.List;
26+
import java.util.Map;
1627
import java.util.UUID;
1728

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

46+
@TargetApi(21)
47+
static Protos.ScanResult from(BluetoothDevice device, ScanResult scanResult) {
48+
Protos.ScanResult.Builder p = Protos.ScanResult.newBuilder();
49+
p.setDevice(from(device));
50+
Protos.AdvertisementData.Builder a = Protos.AdvertisementData.newBuilder();
51+
ScanRecord scanRecord = scanResult.getScanRecord();
52+
if(Build.VERSION.SDK_INT >= 26) {
53+
a.setConnectable(scanResult.isConnectable());
54+
} else {
55+
if(scanRecord != null) {
56+
int flags = scanRecord.getAdvertiseFlags();
57+
a.setConnectable((flags & 0x2) > 0);
58+
}
59+
}
60+
if(scanRecord != null) {
61+
String deviceName = scanRecord.getDeviceName();
62+
if(deviceName != null) {
63+
a.setLocalName(deviceName);
64+
}
65+
int txPower = scanRecord.getTxPowerLevel();
66+
if(txPower != Integer.MIN_VALUE) {
67+
a.setTxPowerLevel(Protos.Int32Value.newBuilder().setValue(txPower));
68+
}
69+
// Manufacturer Specific Data
70+
SparseArray<byte[]> msd = scanRecord.getManufacturerSpecificData();
71+
for (int i = 0; i < msd.size(); i++) {
72+
int key = msd.keyAt(i);
73+
byte[] value = msd.valueAt(i);
74+
a.putManufacturerData(key, ByteString.copyFrom(value));
75+
}
76+
// Service Data
77+
Map<ParcelUuid, byte[]> serviceData = scanRecord.getServiceData();
78+
for (Map.Entry<ParcelUuid, byte[]> entry : serviceData.entrySet()) {
79+
ParcelUuid key = entry.getKey();
80+
byte[] value = entry.getValue();
81+
a.putServiceData(key.getUuid().toString(), ByteString.copyFrom(value));
82+
}
83+
// Service UUIDs
84+
List<ParcelUuid> serviceUuids = scanRecord.getServiceUuids();
85+
if(serviceUuids != null) {
86+
for (ParcelUuid s : serviceUuids) {
87+
a.addServiceUuids(s.getUuid().toString());
88+
}
89+
}
90+
}
91+
p.setRssi(scanResult.getRssi());
92+
p.setAdvertisementData(a.build());
93+
return p.build();
94+
}
95+
3596
static Protos.BluetoothDevice from(BluetoothDevice device) {
3697
Protos.BluetoothDevice.Builder p = Protos.BluetoothDevice.newBuilder();
3798
p.setRemoteId(device.getAddress());

example/lib/widgets.dart

Lines changed: 66 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,26 +25,83 @@ class ScanResultTile extends StatelessWidget {
2525
}
2626
}
2727

28+
Widget _buildAdvRow(BuildContext context, String title, String value) {
29+
return Padding(
30+
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 4.0),
31+
child: Row(
32+
crossAxisAlignment: CrossAxisAlignment.start,
33+
children: <Widget>[
34+
Text(title, style: Theme.of(context).textTheme.caption),
35+
SizedBox(
36+
width: 12.0,
37+
),
38+
Expanded(
39+
child: Text(
40+
value,
41+
style: Theme.of(context)
42+
.textTheme
43+
.caption
44+
.apply(color: Colors.black),
45+
softWrap: true,
46+
),
47+
),
48+
],
49+
),
50+
);
51+
}
52+
53+
String getNiceManufacturerData(Map<int, List<int>> data) {
54+
if (data.isEmpty) {
55+
return null;
56+
}
57+
List<String> res = [];
58+
data.forEach((id, bytes) {
59+
res.add('${id.toRadixString(16).toUpperCase()}: $bytes');
60+
});
61+
return res.join(', ');
62+
}
63+
64+
String getNiceServiceData(Map<String, List<int>> data) {
65+
if (data.isEmpty) {
66+
return null;
67+
}
68+
List<String> res = [];
69+
data.forEach((id, bytes) {
70+
res.add('$id: $bytes');
71+
});
72+
return res.join(', ');
73+
}
74+
2875
@override
2976
Widget build(BuildContext context) {
30-
print('MANU DATA: ${result.advertisementData.manufacturerData}');
31-
print('TX POWER: ${result.advertisementData.txPowerLevel}');
3277
return ExpansionTile(
3378
title: _buildTitle(context),
3479
leading: Text(result.rssi.toString()),
3580
trailing: RaisedButton(
3681
child: Text('CONNECT'),
3782
color: Colors.black,
3883
textColor: Colors.white,
39-
onPressed: onTap,
84+
onPressed: (result.advertisementData.connectable) ? onTap : null,
4085
),
4186
children: <Widget>[
42-
Row(
43-
children: <Widget>[
44-
Text('Complete Local Name:'),
45-
Text(result.advertisementData.localName)
46-
],
47-
)
87+
_buildAdvRow(
88+
context, 'Complete Local Name', result.advertisementData.localName),
89+
_buildAdvRow(context, 'Tx Power Level',
90+
'${result.advertisementData.txPowerLevel ?? 'N/A'}'),
91+
_buildAdvRow(
92+
context,
93+
'Manufacturer Data',
94+
getNiceManufacturerData(
95+
result.advertisementData.manufacturerData) ??
96+
'N/A'),
97+
_buildAdvRow(
98+
context,
99+
'Service UUIDs',
100+
(result.advertisementData.serviceUuids.isNotEmpty)
101+
? result.advertisementData.serviceUuids.join(', ')
102+
: 'N/A'),
103+
_buildAdvRow(context, 'Service Data',
104+
getNiceServiceData(result.advertisementData.serviceData) ?? 'N/A'),
48105
],
49106
);
50107
}

ios/gen/Flutterblue.pbobjc.h

Lines changed: 26 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ios/gen/Flutterblue.pbobjc.m

Lines changed: 51 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)