1) The circumstances: iPADOS~>18, USB still-imaging-gadget, 1-3 gadgets might connect simultaneously via USB-hub, proprietary DExt, UserClient App in developers' iPADs or in TestFlight.
= 1 variation A) UserClientApp has attribute [Background modes][Enable external communications].
= 1 variation B) "active USB-hub" vs "passive".
= 1 variation C) "ConsoleApp logs iPAD" vs "ConsoleApp is not started".
= 1 the term "zombie" below assume issue:
== after plug-in ConsoleApp logs "IOUsbUserInterface::init", "::start";
== UserClient never receives respective callback from IOServiceAddMatchingNotification
== further IOKit APIs "teardown", "restart" and "re-enumeration of connected gadgets" doesn't reveal the zombie (while it sees another simultaneous gadget);
== unplug of the gadget logs "IOUsbUserInterface::stop".
=2) The situation when UserClient is in foreground: ~ everything is fine. Note in MAC OS everything (same DExt and UserClient-code) works fine in background and in foreground.
=3) The situation when UserClient is in background (beneath ~"Files" or "Safari"):
=3A) "1A=true" phys plugin causes zombie in ~1/20 cases (satisfactory)
=3B) "1A=false" phys plugin causes zombie in ~1/4 cases (non-satisfactory)
=3C) "1 variation B" and "1 variation C" reduce zombies by ~>30%.
=3D) ConsoleApp logs with filter "IOAccessory" (~about USB-energy) always look similar by my eyes.
=4) From Apple-store: "The app declares support for external-accessory in the UIBackgroundModes"..."The external accessory background mode is intended for apps that communicate with hardware accessories through the External Accessory framework."..."Additionally, the app must be authorized by MFi,"
=5) My wish/ask:
=5 option A) A clue to resolve the "zombie-plugin-issue in background" in a technical manner.
= E.g. find a straightforward solution that ~elevates UserClient's "privileges" in background like [Enable external communication]...
E.g. what ConsoleApp-logs to add/watch to reveal my potential bugs in DExt or in UserClient?
=5 option B) A way to pursue Apple-store to accept (without MFI) an UserClientApp with [Enable external communication].
So, first off, there are two critical details I need to clarify:
Note in MAC OS everything (same DExt and UserClient-code) works fine in the background and in the foreground.
macOS doesn't implement any of the app suspension semantics iPadOS does, which means you can't really compare the behavior of the two systems.
That leads to here:
1 variation A) UserClientApp has attribute [Background modes][Enable external communications].
The "external-accessory" background mode is totally unrelated to DriverKit and isn't actually "doing" anything in your app. It allows apps using the ExternalAccessory framework to communicate with their accessory in the background, but that framework (iOS 3.0) and the background mode (iOS 4.0) are MUCH older than DriverKit.
Making this as explicit as possible:
B) A way to pursue Apple-store to accept (without MFI) an UserClientApp with [Enable external communication].
...adding "external-accessory" is not changing ANYTHING about how your app wakes/sleep in the background. You will not be allowed to use it, but that's because it's not relevant to your app.
I want to be clear on this point because I've seen many cases in the past where developers have convinced themselves that "something" was providing them some special power/capability that it simply did not provide. The system’s background support is complicated enough that it can be easy to misunderstand what's going on.
That leads to what's going on here:
after plug-in ConsoleApp logs "IOUsbUserInterface::init", "::start";
This is your DEXT loading, as expected.
UserClient never receives the respective callback from IOServiceAddMatchingNotification
Correct. Your app is suspended in the background, so it isn't notified of your accessory when it's initially attached. DriverKit doesn't have any "background app" support, so unless your app was already awake, it won't be notified at attach.
further IOKit APIs "teardown", "restart" and "re-enumeration of connected gadgets" doesn't reveal the zombie (while it sees another simultaneous gadget);
What did you actually try here? Do you find the accessory if you:
-
Force quit the app (swipe "up" in the fast app switcher) and then relaunch?
-
Use IOServiceGetMatchingServices() to do a search of the registry when your app wakes up?
-
Tear down and recreate your device notification code and start a new monitoring session with IOServiceAddMatchingNotification:
While you may sometimes get service notifications when your app wakes from suspension, I'm not sure that mechanism can be relied on across an extended app suspension. However, you should get any existing accessories if you call IOServiceAddMatchingNotification again.
*Part of this is that I believe the original mach messages may time out and be discarded, but the bigger issue is that, after a long period of suspension, it's possible for the device state to be completely different than when you originally suspended. For example, "your accessory" may have been plugged/unplugged multiple times, meaning it's still "there" but is actually a totally different DEXT instance than when you started. Even worse, your own connections may prevent normal driver teardown, leaving you with a connection to a nonfunctional device.
The better approach is to disconnect from your accessory and stop IOKit monitoring before you suspend, then recreate it when your app wakes again.
unplug of the gadget logs "IOUsbUserInterface::stop".
This is your DEXT tearing down.
The key point to understand here is that your DEXT and app operate as independent components. Your DEXT loads/unloads when your hardware attaches and your app follows the normal system behavior for apps waking/sleeping.
All of which then leads to here:
- My wish/ask:
5 option A) A clue to resolve the "zombie-plugin-issue in background" in a technical manner.
Why? What are you trying to do? More specifically, why does your app need to be awake in the background?
__
Kevin Elliott
DTS Engineer, CoreOS/Hardware