From: Luiz Augusto von Dentz <luiz.von.dentz@xxxxxxxxx> MGMT Set Device Flags overwrites the current_flags so only the last flags set this way would remain active which can be seem in the following sequence when LL Privacy is enabled: @ MGMT Command: Set Device Flags (0x0050) plen 11 LE Address: CF:AC:A6:79:3D:B9 (Static) Current Flags: 0x00000001 Remote Wakeup @ MGMT Event: Command Complete (0x0001) plen 10 Set Device Flags (0x0050) plen 7 Status: Success (0x00) LE Address: CF:AC:A6:79:3D:B9 (Static) @ MGMT Command: Set Device Flags (0x0050) plen 11 LE Address: CF:AC:A6:79:3D:B9 (Static) Current Flags: 0x00000002 Device Privacy Mode @ MGMT Event: Command Complete (0x0001) plen 10 Set Device Flags (0x0050) plen 7 Status: Success (0x00) LE Address: CF:AC:A6:79:3D:B9 (Static) In order to do this properly the code needs to track the pending_flags being set and also call btd_device_flags_changed whenever a change is complete since that event is not generated when MGMT_OP_SET_DEVICE_FLAGS is sent by bluetoothd itself. --- src/adapter.c | 20 +++++++++++++++++--- src/device.c | 20 +++++++++++++++++++- src/device.h | 2 ++ 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/src/adapter.c b/src/adapter.c index bb49a1ecad23..85ddfc16568f 100644 --- a/src/adapter.c +++ b/src/adapter.c @@ -5569,6 +5569,7 @@ void adapter_accept_list_remove(struct btd_adapter *adapter, static void set_device_privacy_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { + struct btd_device *dev = user_data; const struct mgmt_rp_set_device_flags *rp = param; if (status != MGMT_STATUS_SUCCESS) { @@ -5581,6 +5582,9 @@ static void set_device_privacy_complete(uint8_t status, uint16_t length, error("Too small Set Device Flags complete event: %d", length); return; } + + btd_device_flags_changed(dev, btd_device_get_supported_flags(dev), + btd_device_get_pending_flags(dev)); } static void add_device_complete(uint8_t status, uint16_t length, @@ -5626,7 +5630,7 @@ static void add_device_complete(uint8_t status, uint16_t length, adapter_set_device_flags(adapter, dev, flags | DEVICE_FLAG_DEVICE_PRIVACY, set_device_privacy_complete, - NULL); + dev); } } } @@ -5676,6 +5680,7 @@ void adapter_set_device_flags(struct btd_adapter *adapter, { struct mgmt_cp_set_device_flags cp; uint32_t supported = btd_device_get_supported_flags(device); + uint32_t pending = btd_device_get_pending_flags(device); const bdaddr_t *bdaddr; uint8_t bdaddr_type; @@ -5683,6 +5688,14 @@ void adapter_set_device_flags(struct btd_adapter *adapter, (supported | flags) != supported) return; + /* Check if changing flags are pending */ + if (flags == (flags & pending)) + return; + + /* Set Device Privacy Mode if it has not set the flag yet. */ + if (btd_opts.device_privacy && !(flags & DEVICE_FLAG_DEVICE_PRIVACY)) + flags |= DEVICE_FLAG_DEVICE_PRIVACY & supported & ~pending; + bdaddr = device_get_address(device); bdaddr_type = btd_device_get_bdaddr_type(device); @@ -5691,8 +5704,9 @@ void adapter_set_device_flags(struct btd_adapter *adapter, cp.addr.type = bdaddr_type; cp.current_flags = cpu_to_le32(flags); - mgmt_send(adapter->mgmt, MGMT_OP_SET_DEVICE_FLAGS, adapter->dev_id, - sizeof(cp), &cp, func, user_data, NULL); + if (mgmt_send(adapter->mgmt, MGMT_OP_SET_DEVICE_FLAGS, adapter->dev_id, + sizeof(cp), &cp, func, user_data, NULL)) + btd_device_set_pending_flags(device, flags); } static void device_flags_changed_callback(uint16_t index, uint16_t length, diff --git a/src/device.c b/src/device.c index 097b1fbba37d..a1dc0750ca41 100644 --- a/src/device.c +++ b/src/device.c @@ -214,6 +214,7 @@ struct btd_device { GDBusPendingPropertySet wake_id; uint32_t supported_flags; + uint32_t pending_flags; uint32_t current_flags; GSList *svc_callbacks; GSList *eir_uuids; @@ -1569,7 +1570,7 @@ static void set_wake_allowed_complete(uint8_t status, uint16_t length, return; } - device_set_wake_allowed_complete(dev); + btd_device_flags_changed(dev, dev->supported_flags, dev->pending_flags); } void device_set_wake_allowed(struct btd_device *device, bool wake_allowed, @@ -7243,6 +7244,22 @@ uint32_t btd_device_get_supported_flags(struct btd_device *dev) return dev->supported_flags; } +void btd_device_set_pending_flags(struct btd_device *dev, uint32_t flags) +{ + if (!dev) + return; + + dev->pending_flags = flags; +} + +uint32_t btd_device_get_pending_flags(struct btd_device *dev) +{ + if (!dev) + return 0; + + return dev->pending_flags; +} + /* This event is sent immediately after add device on all mgmt sockets. * Afterwards, it is only sent to mgmt sockets other than the one which called * set_device_flags. @@ -7255,6 +7272,7 @@ void btd_device_flags_changed(struct btd_device *dev, uint32_t supported_flags, dev->supported_flags = supported_flags; dev->current_flags = current_flags; + dev->pending_flags = 0; if (!changed_flags) return; diff --git a/src/device.h b/src/device.h index 0794f92d0178..3742f6028040 100644 --- a/src/device.h +++ b/src/device.h @@ -191,6 +191,8 @@ int btd_device_connect_services(struct btd_device *dev, GSList *services); uint32_t btd_device_get_current_flags(struct btd_device *dev); uint32_t btd_device_get_supported_flags(struct btd_device *dev); +uint32_t btd_device_get_pending_flags(struct btd_device *dev); +void btd_device_set_pending_flags(struct btd_device *dev, uint32_t flags); void btd_device_flags_changed(struct btd_device *dev, uint32_t supported_flags, uint32_t current_flags); -- 2.45.2