Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions lib/dbus/bindings.js
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,7 @@ class DbusBindings extends EventEmitter {
if (this._scanServiceUuids.length > 0) {
filter.UUIDs = new Variant('as', this._scanServiceUuids.map(expandUuid));
}
debug('startScanning: filter UUIDs=%j duplicates=%s', this._scanServiceUuids, !!allowDuplicates);
try {
await this._adapterIface.SetDiscoveryFilter(filter);
} catch (err) {
Expand All @@ -307,7 +308,37 @@ class DbusBindings extends EventEmitter {
if (!this._isScanning) {
await this._adapterIface.StartDiscovery();
this._isScanning = true;
debug('startScanning: StartDiscovery issued');
} else {
debug('startScanning: already scanning, skipping StartDiscovery');
}

// Re-read BlueZ's object tree and surface cached devices.
// BlueZ won't emit InterfacesAdded for devices already in its cache,
// so without this refresh a new scan would miss them entirely.
try {
const managed = await this._objectManager.GetManagedObjects();
let surfacedCount = 0;
for (const [path, ifaces] of Object.entries(managed)) {
const unwrapped = {};
for (const [iface, props] of Object.entries(ifaces)) {
unwrapped[iface] = unwrapDict(props);
}
this._objects.set(path, Object.assign(this._objects.get(path) || {}, unwrapped));
if (unwrapped[DEVICE_IFACE] && this._isUnderAdapter(path)) {
surfacedCount++;
const addr = unwrapped[DEVICE_IFACE].Address || 'unknown';
const name = unwrapped[DEVICE_IFACE].Name || unwrapped[DEVICE_IFACE].Alias || '';
const uuids = unwrapped[DEVICE_IFACE].UUIDs || [];
debug('startScanning: re-surfacing cached device %s addr=%s name=%s uuids=%j', path, addr, name, uuids);
this._handleDeviceProps(path, unwrapped[DEVICE_IFACE]);
}
}
debug('startScanning: re-surfaced %d cached device(s) from BlueZ', surfacedCount);
} catch (err) {
debug('startScanning: GetManagedObjects refresh failed: %s', err.message);
}

this.emit('scanStart', !!allowDuplicates);
}

Expand Down Expand Up @@ -341,6 +372,9 @@ class DbusBindings extends EventEmitter {
this._objects.set(path, Object.assign(existing, unwrapped));

if (unwrapped[DEVICE_IFACE] && this._isUnderAdapter(path)) {
const addr = unwrapped[DEVICE_IFACE].Address || 'unknown';
const name = unwrapped[DEVICE_IFACE].Name || unwrapped[DEVICE_IFACE].Alias || '';
debug('InterfacesAdded: new device %s addr=%s name=%s', path, addr, name);
this._handleDeviceProps(path, unwrapped[DEVICE_IFACE]);
}
// Trigger services-resolved processing if a device just gained ServicesResolved
Expand Down Expand Up @@ -384,6 +418,10 @@ class DbusBindings extends EventEmitter {
const address = props.Address || devicePathToAddress(path);
if (!address) return;
const id = addressToId(address);
const name = props.Name || props.Alias || '';
const serviceData = props.ServiceData ? Object.keys(props.ServiceData) : [];
const uuids = props.UUIDs || [];
debug('handleDeviceProps: id=%s addr=%s name=%s uuids=%j serviceData=%j rssi=%s', id, address, name, uuids, serviceData, props.RSSI);
let device = this._devices.get(id);
if (!device) {
device = {
Expand Down
28 changes: 22 additions & 6 deletions lib/hci-socket/gap.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,14 +93,19 @@ Gap.prototype.onHciLeScanEnableSet = function (status) {

// Called when we see the actual command "LE Set Scan Enable"
Gap.prototype.onLeScanEnableSetCmd = function (enable, filterDuplicates) {
debug('onLeScanEnableSetCmd: enable=%s filterDuplicates=%s scanState=%s ownFilterDuplicates=%s',
enable, filterDuplicates, this._scanState, this._scanFilterDuplicates);
// Check to see if the new settings differ from what we expect.
// If we are scanning, then a change happens if the new command stops
// scanning or if duplicate filtering changes.
// If we are not scanning, then a change happens if scanning was enabled.
if (this._scanState === 'starting' || this._scanState === 'started') {
if (!enable) {
debug('onLeScanEnableSetCmd: external process stopped our scan');
this.emit('scanStop');
} else if (this._scanFilterDuplicates !== filterDuplicates) {
debug('onLeScanEnableSetCmd: external process changed filterDuplicates from %s to %s',
this._scanFilterDuplicates, filterDuplicates);
this._scanFilterDuplicates = filterDuplicates;

this.emit('scanStart', this._scanFilterDuplicates);
Expand All @@ -109,6 +114,7 @@ Gap.prototype.onLeScanEnableSetCmd = function (enable, filterDuplicates) {
(this._scanState === 'stopping' || this._scanState === 'stopped') &&
enable
) {
debug('onLeScanEnableSetCmd: external process started scanning while we were %s', this._scanState);
// Someone started scanning on us.
this.emit('scanStart', this._scanFilterDuplicates);
}
Expand Down Expand Up @@ -165,12 +171,17 @@ Gap.prototype.onHciLeAdvertisingReport = function (
};

// only report after a scan response event or if non-connectable or more than one discovery without a scan response, so more data can be collected
if (
const willEmit =
type === LE_META_EVENT_TYPE_SCAN_RESPONSE ||
!connectable ||
(discoveryCount > 1 && !hasScanResponse) ||
process.env.NOBLE_REPORT_ALL_HCI_EVENTS
) {
process.env.NOBLE_REPORT_ALL_HCI_EVENTS;

debug('adv: addr=%s type=0x%s name=%s rssi=%d count=%d connectable=%s hasScanResponse=%s → %s',
address, type.toString(16), advertisement.localName || '', rssi, discoveryCount,
connectable, hasScanResponse, willEmit ? 'EMIT' : 'SUPPRESS (waiting for scan response)');

if (willEmit) {
this.emit(
'discover',
status,
Expand Down Expand Up @@ -238,12 +249,17 @@ Gap.prototype.onHciLeExtendedAdvertisingReport = function (
};

// only report after a scan response event or if non-connectable or more than one discovery without a scan response, so more data can be collected
if (
const willEmit =
type & LE_META_EXTENDED_EVENT_TYPE_SCAN_RESPONSE_MASK ||
(!connectable && !incomplete) ||
(discoveryCount > 1 && !hasScanResponse) ||
process.env.NOBLE_REPORT_ALL_HCI_EVENTS
) {
process.env.NOBLE_REPORT_ALL_HCI_EVENTS;

debug('ext-adv: addr=%s type=0x%s name=%s rssi=%d count=%d connectable=%s incomplete=%s hasScanResponse=%s → %s',
address, type.toString(16), advertisement.localName || '', rssi, discoveryCount,
connectable, incomplete, hasScanResponse, willEmit ? 'EMIT' : 'SUPPRESS');

if (willEmit) {
this.emit(
'discover',
status,
Expand Down
Loading