We have a problem in a scenario that SIM lock is disabled so after a phone reboots it has the Internet connection but it is still locked.
When you call into the VOIP app the app is not being launched as the result (it seems reasonable because it wouldn't be able to access the keychain items etc...) but the OS still seem to enforce the rule that the app needs to report the new incoming call.
When we then unlock the app we can see no more pushkit pushes are arriving (dropped on the floor in the console) but we get the three initial pushes that were send during the locked phase right after the app launch.
We have a problem in a scenario that SIM lock is disabled so after a phone reboots it has the Internet connection but it is still locked.
When you call into the VOIP app the app is not being launched as the result (it seems reasonable because it wouldn't be able to access the keychain items etc...)
So first off, yes, this can happen. There are a small number of edge cases (one of which is voip) where the system may launch the app prior to first unlock. This behavior has never been documented and can be somewhat inconsistent, however, it's is also VERY old (it comes the original, iOS 4, voip architecture). Because of that, my advice to voip developers has long been:
-
Don't assume/rely on it always happening. As you've noted the behavior is somewhat odd and there are situations where it will not occur.
-
Be prepared for it to happen and handle it as "gracefully" as you can/choose. Depending on your app architecture, that could mean handling the call completely or simply reporting and then ending the call.
One note on this point:
(it seems reasonable because it wouldn't be able to access the keychain items etc...)
That is not correct. The keychain provides the "Always" accessibility attribute, the point of which is specifically to allow access prior to first unlock. While that attribute is marked as deprecated, that's primarily to highlight that it should not be used for most cases, not to warn of it's active removal*. Also, note that URLFileProtection.none allows file to be accessed prior to first unlock.
*In order to remove the key, we'd need to actively prevent exactly the case we're discussing here... at which point you wouldn't need the key.
The big issue to be aware of for this edge case is that your app can ONLY rely on accessing data (files or keys) that is directly controls and has specifically marked (using the APIs above). Note that specifically means that you CANNOT use NSUserDefaults.
Moving to here:
but the OS still seem to enforce the rule that the app needs to report the new incoming call.
While bugs are always possible, I don't think that's what happening here. In every case I've looked at, what actually happened is that your app WAS launched but issue with it's internal logic meant that it never created a PKPushRegistry object, so the system then terminated it a short time (~7s) later.
In terms of next steps, here is what I would actually recommend:
-
Install the FaceTime and APNs debug profiles on your test device. This isn't absolutely necessary but there's no reason not to get the best possible data from the beginning. More to the point, IF this is a bug you don't want to end up needing to run the test all over again to get the data the engineering team needs.
-
Turn your device completely off and then leave it off for "awhile", the longer the better (this is a great time to go eat lunch). From past experience, part of what can make sysdiagnose analysis difficult is clearly differentiating different launch cycle from each other, so the goal of the long delay here is simply to create a time gap in the log so it's easy to see when the device started up.
-
Turn the device on and reproduce the issue "a few times", making a note of the time you performed each test. In your case, I would first call the device (to confirm cell connectivity) without answering (to avoid unlock), then send a series of pushes space 1-2 minutes apart.
-
After your last push wait a minutes or so, then unlock the device and open your app to validate the multiple delivery issue you described.
-
Wait "awhile" (2-3 minutes), then trigger and collect a sysdiagnose using the directions from either of the profile installation instructions.
Once you've got a sysdiagnose, there are a few things I'd look at:
(1) Check for crashs from your app in the crashes_and_spins directory. Note that the absence of logs does not necessarily your app was not launched or terminated, as there are other edge cases/complications that can prevent collection.
(2) Take a look at the log yourself. I can't easily summarize how to do this kind of analysis, however, there's a forum thread here that's includes examples of my own analysis. Here are a few other suggestions/tips:
-
It's easy to overlook, Console.app has a popup button in the bottom left corner than controls the time window your actually looking at in the log. Make sure you extend that to cover the whole test window.
-
The critical daemons to this process are "apsd" (handles APNS messages), "callservicesd" (manages PushKit and CallKit), and "runningboardd" (launches/manages apps on behalf of other daemons).
-
Searching for your apps bundle ID can be a good way to quickly filter down to the messages that are actually relevant to your app.
-
Combining those two points, the "flow" you'll often use is to use your bundle id to find time points that might be interesting, then looking at the messages before and after that point to see what actually happened.
(3) If you've looked over the log and you haven't found an answer or think this could be a bug, then please file a bug on this and post the bug number back here. Once I've got the number, I'll take a look and see if I can figure out what's going on.
Finally, a quick explanation on what's happening here:
When we then unlock the app we can see no more pushkit pushes are arriving (dropped on the floor in the console) but we get the three initial pushes that were send during the locked phase right after the app launch.
What's going on here is a side effect of the architecture pattern callservicesd (and many of other daemons) use to deliver messages. Here is what's actually going on:
-
Push arrives to apsd. apsd passes that push to callservicesd.
-
callservicesd creates the object it uses to track and manage it's interaction with "you". Note that is NOT tied to a specific launch of your app.
-
callservicesd adds the new push to the list of pushes it's managing for your app.
-
callservicesd notices that your app is not running and asks runningboardd to launch your app.
-
Your app launches and create it's PKPushRegistry object. That object creates an XPC connection to callservicesd.
-
callservicesd accepts the connection, ties that new connection to the object in #2, and then delivers all the messages it queued up.
That sequence if what can cause message to "pile up" the way you're describing.
__
Kevin Elliott
DTS Engineer, CoreOS/Hardware