Multi-accessory DeviceAccess routing issues: notification sessions invalidated and replies wake the wrong Transport

Title: Multi-accessory DeviceAccess bugs: wrong PeripheralID in Transport wake-up; cross-device NotificationsForwarding session invalidation

Topic: App & System Services → Core OS Tags: Core Bluetooth, AccessorySetupKit, Notifications

Body:

Context: our app uses AccessorySetupKit and a DeviceAccess-based accessory Transport + DataProvider extension pair (one shared extension bundle serves all accessories). Everything works with a single bound accessory, but with two or more ASK accessories on the same app we consistently hit two independent system-level failures. Both fail BEFORE any app callback runs (no DataProvider addNotification, no Transport messageReceived), so this is not app-side parsing or business logic. Tested on iPhone 11 Pro, current iOS 26 release; full logarchives available and can be attached to a Feedback Assistant case.

Problem 1 — Transport wake-up requests use the wrong PeripheralID (uplink: watch reply → iPhone)

Reproducible timeline from bluetoothd/deviceaccessd logs:

  1. Device A's GATT indication reaches bluetoothd; CoreBluetooth routes it to device A's CoreBluetooth session (its Transport PID).
  2. ~8 ms later, the DAExtensionRuntimeAssertion that bluetoothd sends to deviceaccessd carries device B's PeripheralID instead of device A's.
  3. deviceaccessd then resumes/updates device B's Transport. Device A's Transport stays suspended and never receives the data; the reply is lost.

Two outcomes depending on whether the wrongly-used PeripheralID still resolves:

  • Belongs to another still-bound accessory: the assertion "succeeds" and the wrong Transport instance is resumed — no error at all.
  • Belongs to an already-removed accessory: DAErrorDomain 350002 "device not found".

So the absence of error 350002 does not mean the mapping is correct. Re-authorizing the affected accessory via ASK does NOT repair the mapping: after re-ASK, notification delivery works again, but once the new Transport suspends, subsequent wake-ups still carry the other accessory's PeripheralID.

Problem 2 — NotificationsForwarding DAExtensionSession lifecycle is not isolated per DeviceID (downlink: iPhone notification → watch)

Expected: usernotificationsd holds one NotificationsForwarding DAExtensionSession per authorized accessory with notification forwarding enabled, strictly isolated by DeviceID. Observed instead:

  1. A session is "activated and stored" and then immediately invalidated by usernotificationsd itself, with no user action (four-accessory setup, three forwarding-capable).
  2. Any accessory's DeviceLost invalidates OTHER accessories' sessions. Logs show the event DeviceID differs from the invalidated session's DeviceID. This also happens when the removed accessory does not support notification forwarding at all (so there was no session of its own to clean up).
  3. After such mass invalidation, usernotificationsd may rebuild only some sessions or none — even when BLE, Transport, and DataProvider capability fully recover (e.g. after toggling Bluetooth). Notifications then stop at "post() failed: no connection (hasExtensionSession: false)".
  4. A new accessory's ASK DeviceFound triggers a rescan that rebuilds missing sessions for the older devices (consistent across our samples, but we don't know whether this is a stable contract).

What we ruled out on our side:

  • ASAccessorySession.removeAccessory is called with the correct target; nothing in the app touches other accessories' sessions or permissions.
  • The failures happen inside system daemons before app callbacks; single-accessory flows work fine with identical code.
  • Multiple DataProvider DAExtension records sharing one host PID behaved normally — expected ExtensionKit hosting, not the issue.

Questions:

  1. Is the PeripheralID substitution in the bluetoothd → deviceaccessd DAExtensionRuntimeAssertion a known issue when one transport extension bundle serves multiple accessories? Any supported workaround?
  2. Should usernotificationsd session maintenance be strictly isolated by DeviceID? Is there a supported way to force-rebuild all NotificationsForwarding DAExtensionSessions without unbinding accessories?
  3. Is the DeviceFound-triggered full session rescan/rebuild a contract we may rely on?

Happy to provide logarchives and detailed timelines via Feedback Assistant. Thanks!

Multi-accessory DeviceAccess routing issues: notification sessions invalidated and replies wake the wrong Transport
 
 
Q