Skip to content

Commit 9b9512f

Browse files
jagobagascondeadprogram
authored andcommitted
advertising: add manufacturer data field to advertisement payload
1 parent 20f0ce6 commit 9b9512f

File tree

6 files changed

+273
-8
lines changed

6 files changed

+273
-8
lines changed

adapter_darwin.go

+10-2
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,13 @@ func makeScanResult(prph cbgo.Peripheral, advFields cbgo.AdvFields, rssi int) Sc
126126
serviceUUIDs = append(serviceUUIDs, parsedUUID)
127127
}
128128

129+
manufacturerData := make(map[uint16][]byte)
130+
if len(advFields.ManufacturerData) > 2 {
131+
manufacturerID := uint16(advFields.ManufacturerData[0])
132+
manufacturerID += uint16(advFields.ManufacturerData[1]) << 8
133+
manufacturerData[manufacturerID] = advFields.ManufacturerData[2:]
134+
}
135+
129136
// Peripheral UUID is randomized on macOS, which means to
130137
// different centrals it will appear to have a different UUID.
131138
return ScanResult{
@@ -135,8 +142,9 @@ func makeScanResult(prph cbgo.Peripheral, advFields cbgo.AdvFields, rssi int) Sc
135142
},
136143
AdvertisementPayload: &advertisementFields{
137144
AdvertisementFields{
138-
LocalName: advFields.LocalName,
139-
ServiceUUIDs: serviceUUIDs,
145+
LocalName: advFields.LocalName,
146+
ServiceUUIDs: serviceUUIDs,
147+
ManufacturerData: manufacturerData,
140148
},
141149
},
142150
}

gap.go

+31
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,10 @@ type AdvertisementPayload interface {
126126
// Bytes returns the raw advertisement packet, if available. It returns nil
127127
// if this data is not available.
128128
Bytes() []byte
129+
130+
// ManufacturerData returns a map with all the manufacturer data present in the
131+
//advertising. IT may be empty.
132+
ManufacturerData() map[uint16][]byte
129133
}
130134

131135
// AdvertisementFields contains advertisement fields in structured form.
@@ -138,6 +142,9 @@ type AdvertisementFields struct {
138142
// part of the advertisement packet, in data types such as "complete list of
139143
// 128-bit UUIDs".
140144
ServiceUUIDs []UUID
145+
146+
// ManufacturerData is the manufacturer data of the advertisement.
147+
ManufacturerData map[uint16][]byte
141148
}
142149

143150
// advertisementFields wraps AdvertisementFields to implement the
@@ -170,6 +177,11 @@ func (p *advertisementFields) Bytes() []byte {
170177
return nil
171178
}
172179

180+
// ManufacturerData returns the underlying ManufacturerData field.
181+
func (p *advertisementFields) ManufacturerData() map[uint16][]byte {
182+
return p.AdvertisementFields.ManufacturerData
183+
}
184+
173185
// rawAdvertisementPayload encapsulates a raw advertisement packet. Methods to
174186
// get the data (such as LocalName()) will parse just the needed field. Scanning
175187
// the data should be fast as most advertisement packets only have a very small
@@ -258,6 +270,25 @@ func (buf *rawAdvertisementPayload) HasServiceUUID(uuid UUID) bool {
258270
}
259271
}
260272

273+
// ManufacturerData returns the manufacturer data in the advertisement payload.
274+
func (buf *rawAdvertisementPayload) ManufacturerData() map[uint16][]byte {
275+
mData := make(map[uint16][]byte)
276+
data := buf.Bytes()
277+
for len(data) >= 2 {
278+
fieldLength := data[0]
279+
if int(fieldLength)+1 > len(data) {
280+
// Invalid field length.
281+
return nil
282+
}
283+
// If this is the manufacturer data
284+
if byte(0xFF) == data[1] {
285+
mData[uint16(data[2])+(uint16(data[3])<<8)] = data[4 : fieldLength+1]
286+
}
287+
data = data[fieldLength+1:]
288+
}
289+
return mData
290+
}
291+
261292
// reset restores this buffer to the original state.
262293
func (buf *rawAdvertisementPayload) reset() {
263294
// The data is not reset (only the length), because with a zero length the

gap_linux.go

+11-2
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,8 @@ func (a *Adapter) Scan(callback func(*Adapter, ScanResult)) error {
197197
props.Name = val.Value().(string)
198198
case "UUIDs":
199199
props.UUIDs = val.Value().([]string)
200+
case "ManufacturerData":
201+
props.ManufacturerData = val.Value().(map[uint16]interface{})
200202
}
201203
}
202204
callback(a, makeScanResult(props))
@@ -237,13 +239,20 @@ func makeScanResult(props *device.Device1Properties) ScanResult {
237239
a := Address{MACAddress{MAC: addr}}
238240
a.SetRandom(props.AddressType == "random")
239241

242+
mData := make(map[uint16][]byte)
243+
for k, v := range props.ManufacturerData {
244+
temp := v.(dbus.Variant)
245+
mData[k] = temp.Value().([]byte)
246+
}
247+
240248
return ScanResult{
241249
RSSI: props.RSSI,
242250
Address: a,
243251
AdvertisementPayload: &advertisementFields{
244252
AdvertisementFields{
245-
LocalName: props.Name,
246-
ServiceUUIDs: serviceUUIDs,
253+
LocalName: props.Name,
254+
ServiceUUIDs: serviceUUIDs,
255+
ManufacturerData: mData,
247256
},
248257
},
249258
}

gap_windows.go

+16-4
Original file line numberDiff line numberDiff line change
@@ -26,19 +26,31 @@ func (a *Adapter) Scan(callback func(*Adapter, ScanResult)) (err error) {
2626

2727
// Listen for incoming BLE advertisement packets.
2828
err = a.watcher.AddReceivedEvent(func(watcher *winbt.IBluetoothLEAdvertisementWatcher, args *winbt.IBluetoothLEAdvertisementReceivedEventArgs) {
29-
var result ScanResult
30-
result.RSSI = args.RawSignalStrengthInDBm()
29+
// parse bluetooth address
3130
addr := args.BluetoothAddress()
32-
adr := result.Address.(Address)
31+
adr := Address{}
3332
for i := range adr.MAC {
3433
adr.MAC[i] = byte(addr)
3534
addr >>= 8
3635
}
36+
result := ScanResult{
37+
RSSI: args.RawSignalStrengthInDBm(),
38+
Address: adr,
39+
}
40+
41+
var manufacturerData map[uint16][]byte
42+
if winAdv := args.Advertisement(); winAdv != nil {
43+
manufacturerData = winAdv.ManufacturerData()
44+
} else {
45+
manufacturerData = make(map[uint16][]byte)
46+
}
47+
3748
// Note: the IsRandom bit is never set.
3849
advertisement := args.Advertisement()
3950
result.AdvertisementPayload = &advertisementFields{
4051
AdvertisementFields{
41-
LocalName: advertisement.LocalName(),
52+
LocalName: advertisement.LocalName(),
53+
ManufacturerData: manufacturerData,
4254
},
4355
}
4456
callback(a, result)

winbt/advertisement.go

+68
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,47 @@ type IBluetoothLEAdvertisementWatcherStoppedEventArgs struct {
173173
ole.IInspectable
174174
}
175175

176+
type IBluetoothLEManufacturerData struct {
177+
ole.IInspectable
178+
}
179+
180+
type IBluetoothLEManufacturerDataVtbl struct {
181+
ole.IInspectableVtbl
182+
183+
GetCompanyId uintptr // ([out] [retval] UINT16* value);
184+
SetCompanyId uintptr // ([in] UINT16 value);
185+
GetData uintptr // ([out] [retval] Windows.Storage.Streams.IBuffer** value);
186+
SetData uintptr // ([in] Windows.Storage.Streams.IBuffer* value);
187+
188+
}
189+
190+
func (v *IBluetoothLEManufacturerData) VTable() *IBluetoothLEManufacturerDataVtbl {
191+
return (*IBluetoothLEManufacturerDataVtbl)(unsafe.Pointer(v.RawVTable))
192+
}
193+
194+
func (v *IBluetoothLEManufacturerData) GetCompanyID() uint16 {
195+
var manufacturerID uint16
196+
hr, _, _ := syscall.SyscallN(
197+
v.VTable().GetCompanyId,
198+
uintptr(unsafe.Pointer(v)),
199+
uintptr(unsafe.Pointer(&manufacturerID)),
200+
)
201+
mustSucceed(hr)
202+
return manufacturerID
203+
}
204+
205+
func (v *IBluetoothLEManufacturerData) GetData() *IBuffer {
206+
var buf *IBuffer
207+
hr, _, _ := syscall.SyscallN(
208+
v.VTable().GetData,
209+
uintptr(unsafe.Pointer(v)),
210+
uintptr(unsafe.Pointer(&buf)),
211+
)
212+
mustSucceed(hr)
213+
214+
return buf
215+
}
216+
176217
type IBluetoothLEAdvertisement struct {
177218
ole.IInspectable
178219
}
@@ -211,6 +252,33 @@ func (v *IBluetoothLEAdvertisement) LocalName() string {
211252
return name
212253
}
213254

255+
func (v *IBluetoothLEAdvertisement) ManufacturerData() map[uint16][]byte {
256+
// ([out] [retval] Windows.Foundation.Collections.IVector<Windows.Devices.Bluetooth.Advertisement.BluetoothLEManufacturerData*>** value);
257+
var vector *IVector
258+
hr, _, _ := syscall.SyscallN(
259+
v.VTable().GetManufacturerData,
260+
uintptr(unsafe.Pointer(v)),
261+
uintptr(unsafe.Pointer(&vector)),
262+
)
263+
mustSucceed(hr)
264+
265+
manufacturerData := make(map[uint16][]byte)
266+
// convert manufacturer data to go bytes
267+
for i := 0; i < vector.Size(); i++ {
268+
mData := (*IBluetoothLEManufacturerData)(vector.At(i))
269+
270+
mID := mData.GetCompanyID()
271+
mPayload := mData.GetData()
272+
273+
b, err := mPayload.Bytes()
274+
if err == nil {
275+
manufacturerData[mID] = b
276+
}
277+
}
278+
279+
return manufacturerData
280+
}
281+
214282
func (v *IBluetoothLEAdvertisement) DataSections() (vector *IVector) {
215283
hr, _, _ := syscall.Syscall(
216284
v.VTable().GetDataSections,

winbt/buffer.go

+137
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
package winbt
2+
3+
import (
4+
"syscall"
5+
"unsafe"
6+
7+
"github.com/go-ole/go-ole"
8+
)
9+
10+
// IBuffer Represents a referenced array of bytes used by
11+
// byte stream read and write interfaces. Buffer is the class
12+
// implementation of this interface.
13+
type IBuffer struct {
14+
ole.IInspectable
15+
}
16+
17+
type IBufferVtbl struct {
18+
ole.IInspectableVtbl
19+
20+
// These methods have been obtained from windows.storage.streams.h in the WinRT API.
21+
22+
// read methods
23+
GetCapacity uintptr // ([out] [retval] UINT32* value)
24+
GetLength uintptr // ([out] [retval] UINT32* value)
25+
26+
// write methods
27+
SetLength uintptr // ([in] UINT32 value);
28+
}
29+
30+
func (v *IBuffer) VTable() *IBufferVtbl {
31+
return (*IBufferVtbl)(unsafe.Pointer(v.RawVTable))
32+
}
33+
34+
func (v *IBuffer) Length() int {
35+
var n int
36+
hr, _, _ := syscall.SyscallN(
37+
v.VTable().GetLength,
38+
uintptr(unsafe.Pointer(v)),
39+
uintptr(unsafe.Pointer(&n)),
40+
)
41+
mustSucceed(hr)
42+
return n
43+
}
44+
45+
func (v *IBuffer) Bytes() ([]byte, error) {
46+
// Get DataReaderStatics: we need to pass the class name, and the iid of the interface
47+
// GUID: https://github.com/tpn/winsdk-10/blob/9b69fd26ac0c7d0b83d378dba01080e93349c2ed/Include/10.0.14393.0/winrt/windows.storage.streams.idl#L311
48+
inspectable, err := ole.RoGetActivationFactory("Windows.Storage.Streams.DataReader", ole.NewGUID("11FCBFC8-F93A-471B-B121-F379E349313C"))
49+
if err != nil {
50+
return nil, err
51+
}
52+
53+
drStatics := (*IDataReaderStatics)(unsafe.Pointer(inspectable))
54+
55+
// Call FromBuffer to create new DataReader
56+
var dr *IDataReader
57+
hr, _, _ := syscall.SyscallN(
58+
drStatics.VTable().FromBuffer,
59+
0, // this is a static func, so there's no this
60+
uintptr(unsafe.Pointer(v)), // in buffer
61+
uintptr(unsafe.Pointer(&dr)), // out DataReader
62+
)
63+
err = makeError(hr)
64+
if err != nil {
65+
return nil, err
66+
}
67+
68+
data := make([]byte, v.Length())
69+
err = dr.Bytes(data)
70+
return data, err
71+
}
72+
73+
type IDataReaderStatics struct {
74+
ole.IInspectable
75+
}
76+
77+
type IDataReaderStaticsVtbl struct {
78+
ole.IInspectableVtbl
79+
80+
FromBuffer uintptr // ([in] Windows.Storage.Streams.IBuffer* buffer, [out] [retval] Windows.Storage.Streams.DataReader** dataReader);
81+
}
82+
83+
func (v *IDataReaderStatics) VTable() *IDataReaderStaticsVtbl {
84+
return (*IDataReaderStaticsVtbl)(unsafe.Pointer(v.RawVTable))
85+
}
86+
87+
type IDataReader struct {
88+
ole.IInspectable
89+
}
90+
91+
type IDataReaderVtbl struct {
92+
ole.IInspectableVtbl
93+
94+
GetUnconsumedBufferLength uintptr // ([out] [retval] UINT32* value);
95+
GetUnicodeEncoding uintptr // ([out] [retval] Windows.Storage.Streams.UnicodeEncoding* value);
96+
PutUnicodeEncoding uintptr // ([in] Windows.Storage.Streams.UnicodeEncoding value);
97+
GetByteOrder uintptr // ([out] [retval] Windows.Storage.Streams.ByteOrder* value);
98+
PutByteOrder uintptr // ([in] Windows.Storage.Streams.ByteOrder value);
99+
GetInputStreamOptions uintptr // ([out] [retval] Windows.Storage.Streams.InputStreamOptions* value);
100+
PutInputStreamOptions uintptr // ([in] Windows.Storage.Streams.InputStreamOptions value);
101+
ReadByte uintptr // ([out] [retval] BYTE* value);
102+
ReadBytes uintptr // ([in] UINT32 __valueSize, [out] [size_is(__valueSize)] BYTE* value);
103+
ReadBuffer uintptr // ([in] UINT32 length, [out] [retval] Windows.Storage.Streams.IBuffer** buffer);
104+
ReadBoolean uintptr // ([out] [retval] boolean* value);
105+
ReadGuid uintptr // ([out] [retval] GUID* value);
106+
ReadInt16 uintptr // ([out] [retval] INT16* value);
107+
ReadInt32 uintptr // ([out] [retval] INT32* value);
108+
ReadInt64 uintptr // ([out] [retval] INT64* value);
109+
ReadUInt16 uintptr // ([out] [retval] UINT16* value);
110+
ReadUInt32 uintptr // ([out] [retval] UINT32* value);
111+
ReadUInt64 uintptr // ([out] [retval] UINT64* value);
112+
ReadSingle uintptr // ([out] [retval] FLOAT* value);
113+
ReadDouble uintptr // ([out] [retval] DOUBLE* value);
114+
ReadString uintptr // ([in] UINT32 codeUnitCount, [out] [retval] HSTRING* value);
115+
ReadDateTime uintptr // ([out] [retval] Windows.Foundation.DateTime* value);
116+
ReadTimeSpan uintptr // ([out] [retval] Windows.Foundation.TimeSpan* value);
117+
LoadAsync uintptr // ([in] UINT32 count, [out] [retval] Windows.Storage.Streams.DataReaderLoadOperation** operation);
118+
DetachBuffer uintptr // ([out] [retval] Windows.Storage.Streams.IBuffer** buffer);
119+
DetachStream uintptr // ([out] [retval] Windows.Storage.Streams.IInputStream** stream);*/
120+
}
121+
122+
func (v *IDataReader) VTable() *IDataReaderVtbl {
123+
return (*IDataReaderVtbl)(unsafe.Pointer(v.RawVTable))
124+
}
125+
126+
// Bytes fills the incoming array with the data from the buffer
127+
func (v *IDataReader) Bytes(b []byte) error {
128+
// ([in] UINT32 __valueSize, [out] [size_is(__valueSize)] BYTE* value);
129+
size := len(b)
130+
hr, _, _ := syscall.SyscallN(
131+
v.VTable().ReadBytes,
132+
uintptr(unsafe.Pointer(v)),
133+
uintptr(size),
134+
uintptr(unsafe.Pointer(&b[0])),
135+
)
136+
return makeError(hr)
137+
}

0 commit comments

Comments
 (0)