Update: we found a configuration that works without the temporary-exception entitlement. We
would still like to understand whether it is the intended approach, and why the modern
IOUSBHost interface API is not usable here.
WHAT WORKS NOW
We kept AccessoryAccess and IOUSBHostDevice for the device itself, and switched only the
interfaces and pipes to the IOUSBLib plug-in API:
The helper still rebuilds the AAUSBAccessory from its XPC representation and calls
-openWithServiceQueue:completionHandler:. It uses the returned IOUSBHostDevice for control
requests (-sendDeviceRequest:...) and for -configureWithValue:matchInterfaces:NO error:.
For each IOUSBHostInterface child of the device it now calls IOCreatePlugInInterfaceForService
with kIOUSBInterfaceUserClientTypeID, queries kIOUSBInterfaceInterfaceID942, and calls
USBInterfaceOpen. Async completions come through CreateInterfaceAsyncEventSource on a
dedicated run loop thread.
Bulk and interrupt transfers use ReadPipeAsync and WritePipeAsync. Stall recovery uses
ClearPipeStallBothEnds and alternate settings use SetAlternateInterface.
The main app's entitlements are now only the standard ones, plus
com.apple.developer.accessory-access.usb and com.apple.security.device.usb. The helper inherits
the app's sandbox. With this, a USB 3 flash drive mounts in the guest and a USB 2.5GbE Ethernet
adapter (Realtek 0x0BDA:0x8156) gets a DHCP address and passes traffic.
We confirmed the cause twice:
With the same entitlements (com.apple.security.device.usb, no temporary exception), the
IOUSBHostInterface version of our helper still fails with "Failed to create
IOUSBHostInterface" (kIOReturnInternalError, 0xe00002c9) for every interface. We retested
this today on the same devices.
A test tool running under a sandbox profile that allows only the two user-client classes
application.sb grants for com.apple.security.device.usb:
(deny iokit-open-user-client)
(allow iokit-open-user-client
(iokit-user-client-class "IOUSBDeviceUserClientV2"
"IOUSBInterfaceUserClientV3"))
On an interface of a device AccessoryAccess currently holds,
-[IOUSBHostInterface initWithIOService:...] fails with the same error, while IOUSBLib's
USBInterfaceOpen succeeds and reports the interface's pipes. Without the sandbox, both APIs
open the interface.
So the difference is entirely the App Sandbox rule for AppleUSBHostFrameworkInterfaceClient.
FOLLOW-UP QUESTIONS
Why does com.apple.security.device.usb allow IOUSBLib's interface user client but not
AppleUSBHostFrameworkInterfaceClient, the client -[IOUSBHostInterface initWithIOService:...]
creates? IOUSBHost is the API the documentation recommends for new code, and the one
AccessoryAccess returns for the device. Is the missing sandbox rule intentional, or an
oversight we should report through Feedback Assistant?
Is there a supported way for a sandboxed app, or a helper inheriting its sandbox, to obtain
an IOUSBHostInterface for an accessory it opened through AccessoryAccess, without a
temporary-exception entitlement? For example, a planned API on AAUSBAccessory or
IOUSBHostDevice that returns its interfaces.
Is it supported long term to mix the IOUSBHostDevice returned by AccessoryAccess with
IOUSBLib interface objects for the same device? Should we expect it to keep working, given
that IOUSBLib is the older API?
If IOUSBHostInterface does gain a sandbox rule later, we would prefer to move back to it. Is
there anything we should watch for, such as a new entitlement name or a release in which
the rule is added?
Thank you. We are happy to provide the test tool, the sandbox profile, and IORegistry dumps.