We use ExternalAccessory to talk to a smart card, i.e. a YubiKey 5Ci, over Lightning. On iOS 27.0, most EASession objects we open are unable to receive. NSStreamEventHasBytesAvailable never fires on the input stream and not one byte arrives, yet the session reports no problem anywhere.
Environment
iPhone 11, iOS 27.0 build 24A437 (public release), YubiKey 5Ci on Lightning, protocol com.yubico.ylp.
What we see on an affected session
both streams streamStatus 2 (NSStreamStatusOpen)
streamError on both nil
NSStreamEventOpenCompleted fires on both
hasSpaceAvailable before write YES
write:maxLength: returns 18 of 18
NSStreamEventHasSpaceAvailable fires
NSStreamEventErrorOccurred never
NSStreamEventEndEncountered never
NSStreamEventHasBytesAvailable never fires
inputStream.hasBytesAvailable false on every poll over 10 s
Everything reports success. Only the reply is missing.
Behaviour
A healthy session replies in single digit milliseconds. An affected one returns nothing at all, ever. The condition is set when the session is created and never changes. Opening another session on the same accessory is an independent roll of the dice. Quitting and relaunching the app does not help. Only physically detaching and reattaching the key does, after which connectionID increments and the next session works.
15 of 25 sessions affected on 24A437. Also 9 of 20 on the 24A5430a beta, so it predates the public release.
Already ruled out
Not a late reply, we waited 10 s. Not a missed event, we polled hasBytesAvailable directly about 200 times per failure. Not a leaked session, teardown is instrumented and every session deallocates exactly once. Not the hardware, another app on the same phone and key reads its serial and firmware every time, and that app opens one session and never closes it. The USB C interface of the same key, via CryptoTokenKit, never fails.
Questions
-
Is there any supported way to tell at creation time that an EASession input stream will not deliver data? Nothing we can read distinguishes the two cases.
-
Is repeatedly creating and releasing EASession objects for the same accessory and protocol supported, and is any teardown step needed beyond clearing the delegates, closing the streams, removing them from the run loop and releasing the session?
-
Is there a supported way to recover without asking the user to unplug the accessory?