advertising: add manufacturer data field to advertisement payload
This commit is contained in:
parent
20f0ce6119
commit
9b9512fbc9
6 changed files with 273 additions and 8 deletions
|
@ -126,6 +126,13 @@ func makeScanResult(prph cbgo.Peripheral, advFields cbgo.AdvFields, rssi int) Sc
|
|||
serviceUUIDs = append(serviceUUIDs, parsedUUID)
|
||||
}
|
||||
|
||||
manufacturerData := make(map[uint16][]byte)
|
||||
if len(advFields.ManufacturerData) > 2 {
|
||||
manufacturerID := uint16(advFields.ManufacturerData[0])
|
||||
manufacturerID += uint16(advFields.ManufacturerData[1]) << 8
|
||||
manufacturerData[manufacturerID] = advFields.ManufacturerData[2:]
|
||||
}
|
||||
|
||||
// Peripheral UUID is randomized on macOS, which means to
|
||||
// different centrals it will appear to have a different UUID.
|
||||
return ScanResult{
|
||||
|
@ -135,8 +142,9 @@ func makeScanResult(prph cbgo.Peripheral, advFields cbgo.AdvFields, rssi int) Sc
|
|||
},
|
||||
AdvertisementPayload: &advertisementFields{
|
||||
AdvertisementFields{
|
||||
LocalName: advFields.LocalName,
|
||||
ServiceUUIDs: serviceUUIDs,
|
||||
LocalName: advFields.LocalName,
|
||||
ServiceUUIDs: serviceUUIDs,
|
||||
ManufacturerData: manufacturerData,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
31
gap.go
31
gap.go
|
@ -126,6 +126,10 @@ type AdvertisementPayload interface {
|
|||
// Bytes returns the raw advertisement packet, if available. It returns nil
|
||||
// if this data is not available.
|
||||
Bytes() []byte
|
||||
|
||||
// ManufacturerData returns a map with all the manufacturer data present in the
|
||||
//advertising. IT may be empty.
|
||||
ManufacturerData() map[uint16][]byte
|
||||
}
|
||||
|
||||
// AdvertisementFields contains advertisement fields in structured form.
|
||||
|
@ -138,6 +142,9 @@ type AdvertisementFields struct {
|
|||
// part of the advertisement packet, in data types such as "complete list of
|
||||
// 128-bit UUIDs".
|
||||
ServiceUUIDs []UUID
|
||||
|
||||
// ManufacturerData is the manufacturer data of the advertisement.
|
||||
ManufacturerData map[uint16][]byte
|
||||
}
|
||||
|
||||
// advertisementFields wraps AdvertisementFields to implement the
|
||||
|
@ -170,6 +177,11 @@ func (p *advertisementFields) Bytes() []byte {
|
|||
return nil
|
||||
}
|
||||
|
||||
// ManufacturerData returns the underlying ManufacturerData field.
|
||||
func (p *advertisementFields) ManufacturerData() map[uint16][]byte {
|
||||
return p.AdvertisementFields.ManufacturerData
|
||||
}
|
||||
|
||||
// rawAdvertisementPayload encapsulates a raw advertisement packet. Methods to
|
||||
// get the data (such as LocalName()) will parse just the needed field. Scanning
|
||||
// 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 {
|
|||
}
|
||||
}
|
||||
|
||||
// ManufacturerData returns the manufacturer data in the advertisement payload.
|
||||
func (buf *rawAdvertisementPayload) ManufacturerData() map[uint16][]byte {
|
||||
mData := make(map[uint16][]byte)
|
||||
data := buf.Bytes()
|
||||
for len(data) >= 2 {
|
||||
fieldLength := data[0]
|
||||
if int(fieldLength)+1 > len(data) {
|
||||
// Invalid field length.
|
||||
return nil
|
||||
}
|
||||
// If this is the manufacturer data
|
||||
if byte(0xFF) == data[1] {
|
||||
mData[uint16(data[2])+(uint16(data[3])<<8)] = data[4 : fieldLength+1]
|
||||
}
|
||||
data = data[fieldLength+1:]
|
||||
}
|
||||
return mData
|
||||
}
|
||||
|
||||
// reset restores this buffer to the original state.
|
||||
func (buf *rawAdvertisementPayload) reset() {
|
||||
// The data is not reset (only the length), because with a zero length the
|
||||
|
|
13
gap_linux.go
13
gap_linux.go
|
@ -197,6 +197,8 @@ func (a *Adapter) Scan(callback func(*Adapter, ScanResult)) error {
|
|||
props.Name = val.Value().(string)
|
||||
case "UUIDs":
|
||||
props.UUIDs = val.Value().([]string)
|
||||
case "ManufacturerData":
|
||||
props.ManufacturerData = val.Value().(map[uint16]interface{})
|
||||
}
|
||||
}
|
||||
callback(a, makeScanResult(props))
|
||||
|
@ -237,13 +239,20 @@ func makeScanResult(props *device.Device1Properties) ScanResult {
|
|||
a := Address{MACAddress{MAC: addr}}
|
||||
a.SetRandom(props.AddressType == "random")
|
||||
|
||||
mData := make(map[uint16][]byte)
|
||||
for k, v := range props.ManufacturerData {
|
||||
temp := v.(dbus.Variant)
|
||||
mData[k] = temp.Value().([]byte)
|
||||
}
|
||||
|
||||
return ScanResult{
|
||||
RSSI: props.RSSI,
|
||||
Address: a,
|
||||
AdvertisementPayload: &advertisementFields{
|
||||
AdvertisementFields{
|
||||
LocalName: props.Name,
|
||||
ServiceUUIDs: serviceUUIDs,
|
||||
LocalName: props.Name,
|
||||
ServiceUUIDs: serviceUUIDs,
|
||||
ManufacturerData: mData,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -26,19 +26,31 @@ func (a *Adapter) Scan(callback func(*Adapter, ScanResult)) (err error) {
|
|||
|
||||
// Listen for incoming BLE advertisement packets.
|
||||
err = a.watcher.AddReceivedEvent(func(watcher *winbt.IBluetoothLEAdvertisementWatcher, args *winbt.IBluetoothLEAdvertisementReceivedEventArgs) {
|
||||
var result ScanResult
|
||||
result.RSSI = args.RawSignalStrengthInDBm()
|
||||
// parse bluetooth address
|
||||
addr := args.BluetoothAddress()
|
||||
adr := result.Address.(Address)
|
||||
adr := Address{}
|
||||
for i := range adr.MAC {
|
||||
adr.MAC[i] = byte(addr)
|
||||
addr >>= 8
|
||||
}
|
||||
result := ScanResult{
|
||||
RSSI: args.RawSignalStrengthInDBm(),
|
||||
Address: adr,
|
||||
}
|
||||
|
||||
var manufacturerData map[uint16][]byte
|
||||
if winAdv := args.Advertisement(); winAdv != nil {
|
||||
manufacturerData = winAdv.ManufacturerData()
|
||||
} else {
|
||||
manufacturerData = make(map[uint16][]byte)
|
||||
}
|
||||
|
||||
// Note: the IsRandom bit is never set.
|
||||
advertisement := args.Advertisement()
|
||||
result.AdvertisementPayload = &advertisementFields{
|
||||
AdvertisementFields{
|
||||
LocalName: advertisement.LocalName(),
|
||||
LocalName: advertisement.LocalName(),
|
||||
ManufacturerData: manufacturerData,
|
||||
},
|
||||
}
|
||||
callback(a, result)
|
||||
|
|
|
@ -173,6 +173,47 @@ type IBluetoothLEAdvertisementWatcherStoppedEventArgs struct {
|
|||
ole.IInspectable
|
||||
}
|
||||
|
||||
type IBluetoothLEManufacturerData struct {
|
||||
ole.IInspectable
|
||||
}
|
||||
|
||||
type IBluetoothLEManufacturerDataVtbl struct {
|
||||
ole.IInspectableVtbl
|
||||
|
||||
GetCompanyId uintptr // ([out] [retval] UINT16* value);
|
||||
SetCompanyId uintptr // ([in] UINT16 value);
|
||||
GetData uintptr // ([out] [retval] Windows.Storage.Streams.IBuffer** value);
|
||||
SetData uintptr // ([in] Windows.Storage.Streams.IBuffer* value);
|
||||
|
||||
}
|
||||
|
||||
func (v *IBluetoothLEManufacturerData) VTable() *IBluetoothLEManufacturerDataVtbl {
|
||||
return (*IBluetoothLEManufacturerDataVtbl)(unsafe.Pointer(v.RawVTable))
|
||||
}
|
||||
|
||||
func (v *IBluetoothLEManufacturerData) GetCompanyID() uint16 {
|
||||
var manufacturerID uint16
|
||||
hr, _, _ := syscall.SyscallN(
|
||||
v.VTable().GetCompanyId,
|
||||
uintptr(unsafe.Pointer(v)),
|
||||
uintptr(unsafe.Pointer(&manufacturerID)),
|
||||
)
|
||||
mustSucceed(hr)
|
||||
return manufacturerID
|
||||
}
|
||||
|
||||
func (v *IBluetoothLEManufacturerData) GetData() *IBuffer {
|
||||
var buf *IBuffer
|
||||
hr, _, _ := syscall.SyscallN(
|
||||
v.VTable().GetData,
|
||||
uintptr(unsafe.Pointer(v)),
|
||||
uintptr(unsafe.Pointer(&buf)),
|
||||
)
|
||||
mustSucceed(hr)
|
||||
|
||||
return buf
|
||||
}
|
||||
|
||||
type IBluetoothLEAdvertisement struct {
|
||||
ole.IInspectable
|
||||
}
|
||||
|
@ -211,6 +252,33 @@ func (v *IBluetoothLEAdvertisement) LocalName() string {
|
|||
return name
|
||||
}
|
||||
|
||||
func (v *IBluetoothLEAdvertisement) ManufacturerData() map[uint16][]byte {
|
||||
// ([out] [retval] Windows.Foundation.Collections.IVector<Windows.Devices.Bluetooth.Advertisement.BluetoothLEManufacturerData*>** value);
|
||||
var vector *IVector
|
||||
hr, _, _ := syscall.SyscallN(
|
||||
v.VTable().GetManufacturerData,
|
||||
uintptr(unsafe.Pointer(v)),
|
||||
uintptr(unsafe.Pointer(&vector)),
|
||||
)
|
||||
mustSucceed(hr)
|
||||
|
||||
manufacturerData := make(map[uint16][]byte)
|
||||
// convert manufacturer data to go bytes
|
||||
for i := 0; i < vector.Size(); i++ {
|
||||
mData := (*IBluetoothLEManufacturerData)(vector.At(i))
|
||||
|
||||
mID := mData.GetCompanyID()
|
||||
mPayload := mData.GetData()
|
||||
|
||||
b, err := mPayload.Bytes()
|
||||
if err == nil {
|
||||
manufacturerData[mID] = b
|
||||
}
|
||||
}
|
||||
|
||||
return manufacturerData
|
||||
}
|
||||
|
||||
func (v *IBluetoothLEAdvertisement) DataSections() (vector *IVector) {
|
||||
hr, _, _ := syscall.Syscall(
|
||||
v.VTable().GetDataSections,
|
||||
|
|
137
winbt/buffer.go
Normal file
137
winbt/buffer.go
Normal file
|
@ -0,0 +1,137 @@
|
|||
package winbt
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/go-ole/go-ole"
|
||||
)
|
||||
|
||||
// IBuffer Represents a referenced array of bytes used by
|
||||
// byte stream read and write interfaces. Buffer is the class
|
||||
// implementation of this interface.
|
||||
type IBuffer struct {
|
||||
ole.IInspectable
|
||||
}
|
||||
|
||||
type IBufferVtbl struct {
|
||||
ole.IInspectableVtbl
|
||||
|
||||
// These methods have been obtained from windows.storage.streams.h in the WinRT API.
|
||||
|
||||
// read methods
|
||||
GetCapacity uintptr // ([out] [retval] UINT32* value)
|
||||
GetLength uintptr // ([out] [retval] UINT32* value)
|
||||
|
||||
// write methods
|
||||
SetLength uintptr // ([in] UINT32 value);
|
||||
}
|
||||
|
||||
func (v *IBuffer) VTable() *IBufferVtbl {
|
||||
return (*IBufferVtbl)(unsafe.Pointer(v.RawVTable))
|
||||
}
|
||||
|
||||
func (v *IBuffer) Length() int {
|
||||
var n int
|
||||
hr, _, _ := syscall.SyscallN(
|
||||
v.VTable().GetLength,
|
||||
uintptr(unsafe.Pointer(v)),
|
||||
uintptr(unsafe.Pointer(&n)),
|
||||
)
|
||||
mustSucceed(hr)
|
||||
return n
|
||||
}
|
||||
|
||||
func (v *IBuffer) Bytes() ([]byte, error) {
|
||||
// Get DataReaderStatics: we need to pass the class name, and the iid of the interface
|
||||
// GUID: https://github.com/tpn/winsdk-10/blob/9b69fd26ac0c7d0b83d378dba01080e93349c2ed/Include/10.0.14393.0/winrt/windows.storage.streams.idl#L311
|
||||
inspectable, err := ole.RoGetActivationFactory("Windows.Storage.Streams.DataReader", ole.NewGUID("11FCBFC8-F93A-471B-B121-F379E349313C"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
drStatics := (*IDataReaderStatics)(unsafe.Pointer(inspectable))
|
||||
|
||||
// Call FromBuffer to create new DataReader
|
||||
var dr *IDataReader
|
||||
hr, _, _ := syscall.SyscallN(
|
||||
drStatics.VTable().FromBuffer,
|
||||
0, // this is a static func, so there's no this
|
||||
uintptr(unsafe.Pointer(v)), // in buffer
|
||||
uintptr(unsafe.Pointer(&dr)), // out DataReader
|
||||
)
|
||||
err = makeError(hr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data := make([]byte, v.Length())
|
||||
err = dr.Bytes(data)
|
||||
return data, err
|
||||
}
|
||||
|
||||
type IDataReaderStatics struct {
|
||||
ole.IInspectable
|
||||
}
|
||||
|
||||
type IDataReaderStaticsVtbl struct {
|
||||
ole.IInspectableVtbl
|
||||
|
||||
FromBuffer uintptr // ([in] Windows.Storage.Streams.IBuffer* buffer, [out] [retval] Windows.Storage.Streams.DataReader** dataReader);
|
||||
}
|
||||
|
||||
func (v *IDataReaderStatics) VTable() *IDataReaderStaticsVtbl {
|
||||
return (*IDataReaderStaticsVtbl)(unsafe.Pointer(v.RawVTable))
|
||||
}
|
||||
|
||||
type IDataReader struct {
|
||||
ole.IInspectable
|
||||
}
|
||||
|
||||
type IDataReaderVtbl struct {
|
||||
ole.IInspectableVtbl
|
||||
|
||||
GetUnconsumedBufferLength uintptr // ([out] [retval] UINT32* value);
|
||||
GetUnicodeEncoding uintptr // ([out] [retval] Windows.Storage.Streams.UnicodeEncoding* value);
|
||||
PutUnicodeEncoding uintptr // ([in] Windows.Storage.Streams.UnicodeEncoding value);
|
||||
GetByteOrder uintptr // ([out] [retval] Windows.Storage.Streams.ByteOrder* value);
|
||||
PutByteOrder uintptr // ([in] Windows.Storage.Streams.ByteOrder value);
|
||||
GetInputStreamOptions uintptr // ([out] [retval] Windows.Storage.Streams.InputStreamOptions* value);
|
||||
PutInputStreamOptions uintptr // ([in] Windows.Storage.Streams.InputStreamOptions value);
|
||||
ReadByte uintptr // ([out] [retval] BYTE* value);
|
||||
ReadBytes uintptr // ([in] UINT32 __valueSize, [out] [size_is(__valueSize)] BYTE* value);
|
||||
ReadBuffer uintptr // ([in] UINT32 length, [out] [retval] Windows.Storage.Streams.IBuffer** buffer);
|
||||
ReadBoolean uintptr // ([out] [retval] boolean* value);
|
||||
ReadGuid uintptr // ([out] [retval] GUID* value);
|
||||
ReadInt16 uintptr // ([out] [retval] INT16* value);
|
||||
ReadInt32 uintptr // ([out] [retval] INT32* value);
|
||||
ReadInt64 uintptr // ([out] [retval] INT64* value);
|
||||
ReadUInt16 uintptr // ([out] [retval] UINT16* value);
|
||||
ReadUInt32 uintptr // ([out] [retval] UINT32* value);
|
||||
ReadUInt64 uintptr // ([out] [retval] UINT64* value);
|
||||
ReadSingle uintptr // ([out] [retval] FLOAT* value);
|
||||
ReadDouble uintptr // ([out] [retval] DOUBLE* value);
|
||||
ReadString uintptr // ([in] UINT32 codeUnitCount, [out] [retval] HSTRING* value);
|
||||
ReadDateTime uintptr // ([out] [retval] Windows.Foundation.DateTime* value);
|
||||
ReadTimeSpan uintptr // ([out] [retval] Windows.Foundation.TimeSpan* value);
|
||||
LoadAsync uintptr // ([in] UINT32 count, [out] [retval] Windows.Storage.Streams.DataReaderLoadOperation** operation);
|
||||
DetachBuffer uintptr // ([out] [retval] Windows.Storage.Streams.IBuffer** buffer);
|
||||
DetachStream uintptr // ([out] [retval] Windows.Storage.Streams.IInputStream** stream);*/
|
||||
}
|
||||
|
||||
func (v *IDataReader) VTable() *IDataReaderVtbl {
|
||||
return (*IDataReaderVtbl)(unsafe.Pointer(v.RawVTable))
|
||||
}
|
||||
|
||||
// Bytes fills the incoming array with the data from the buffer
|
||||
func (v *IDataReader) Bytes(b []byte) error {
|
||||
// ([in] UINT32 __valueSize, [out] [size_is(__valueSize)] BYTE* value);
|
||||
size := len(b)
|
||||
hr, _, _ := syscall.SyscallN(
|
||||
v.VTable().ReadBytes,
|
||||
uintptr(unsafe.Pointer(v)),
|
||||
uintptr(size),
|
||||
uintptr(unsafe.Pointer(&b[0])),
|
||||
)
|
||||
return makeError(hr)
|
||||
}
|
Loading…
Reference in a new issue