I am trying to write a code which will handle turning on/off screen on iOS (You can take a look at this question where similar problem is discussed). I included OSX tag for this question, because OSX has the same system wide notification facility. And the problem described below is inherit to the notifications facility (vs to iOS or OSX).
There is well known method of registering for system wide notification com.apple.springboard.hasBlankedScreen to receive notifications when the screen is turned off or on.
Just for references (here are the API which are used for registering for system wide notifications):
- notify_post, notify_check_ notify_get_state and friends
- CFNotificationCenterPostNotification, CFNotificationCenterAddObserver and friends (which uses notify_post and etc internally)
However, there are two interrelated problems with this approach:
- Notifications for both screen turning off and on come with the same name (com.apple.springboard.hasBlankedScreen)
- Observer doesn't receive a state as part of the notification.
So, as result we need to implement some solution which will differ screen turning on and off (since the same notification callback will be called and none of parameters will have a state).
Generally speaking, the core problem that the state is decoupled from notification callback. I don't see how to handle this gracefully.
I come up with two straightforward approaches (each of them are flawed). And looking for ideas of another approaches or improvements over this approach.
Counting solution
We can implement a counter which counts how many notification we already received and based on this information we will know whether it's notification for turning screen on or off (based on oddity of our counter).
However, it has two downsides:
1) In the case, if the system (for unknown in design time reason) will send additional notifications with the same name, our logic will be screwed, because it will break oddity check.
2) Also, we need to set initial state correctly. So somewhere in the code we will have something like that:
counter = getInitialState();
registerForNotification();
In this case we have one race condition. If system will send notification and change the state after we did getInitialState(), but before registerForNotification() we will end up with wrong counter value.
If we will do following code:
registerForNotification();
counter = getInitialState();
In this case we have another race condition. If system will send notification and change the state after we did registerForNotification(), but before getInitialState(), we will get a counter, will enter notification callback and will increase a counter (which will make it wrong).
Determining state when notification received solution
In this case, we don't store any counter, but rather use API notify_get_state in notification callback to get current state.
This has it's own problem:
1) Notification delivered to the application asynchronously. So, as result if you turn off and on screen really fast, you can receive both notifications when the screen was already on. So, notify_check will get a current state (vs the state at the moment when notification was send).
As result, when the application will use notify_get_state in notification callback it will determine that there was two notification "screen was turned on", instead of one notification "screen was turned off" and another "screen was turned on".
P.S. Generally speaking, all described problems aren't specific to screen on/off case. They are actual for any system wide notifications which has distinctive states and send with the same notification name.
Update 1
I didn't test exactly scenario with turning screen on/off very fast and getting the same results from notify_get_state().
However, I have kind-of similar scenario when I received two notifications com.apple.springboard.lockstate (subscribed through CFNotificationCenterAddObserver) and I used another API to get a current device locked status and received the same values for both notifications.
So it's only my assumption that notify_get_state would return same values too. However, I think it's educated guess. The input parameter for notify_get_state will be the same for two calls (it doesn't change). And I don't think that system stores FIFO queue of states which should be returned by notify_get_state.
See Question&Answers more detail:
os