Notifications, Foreground Services, and Android 4.3

Two unannounced changes were added to Android 4.3 related to the behavior of foreground services and their respective notifications.

One is that the Notification associated with a foreground service is immune to blocking via the “Show notifications” checkbox for the app in Settings (or the equivalent facility in App Ops). That Notification – and perhaps others raised by the foreground service – will always appear, even if “Show notifications” is unchecked.

I can see why this will cause some justified consternation.

However, it appears to be tied to another change to the behavior of startForeground(), one that will have more users and developers riled up.

You can roughly divide foreground services into three major buckets:

  1. Those that are in the foreground solely to complete some transaction. The fact that a Notification appears during this transaction is unlikely to be an issue for users, as the Notification is only there for a short time.

  2. Those that are in the foreground based upon a user operation, such as pressing “play” in a music player. In these cases, the user should understand why the Notification is there, and the Notification can help get the user back to a spot on the app to reverse their decision (e.g., press “stop” to stop the music).

  3. Those that are trying to live forever.

The everlasting service has been an anti-pattern in Android pretty much since Android 1.0. The T-Mobile G1 was released in October 2008; the first task killers showed up in a month or two. Particularly at that time, the memory footprint of an app was relatively large compared to the available system RAM, and so a service-laden process would have an outsized effect on multitasking. As the years progressed, Android has taken an increasingly hard line with such apps, but has had the notion of a “foreground service” for cases such as the first two buckets above. The exchange for being relatively “unkillable” is that the app had to display a Notification, to inform the user about the everlasting service, and ideally allowing the user to take steps to stop that service gracefully from within the app. The expectation was that the Notification requirement would help limit the foreground services to apps that truly need it in the eyes of developers and users alike (e.g., third-party VOIP clients).

However, give developers 2.54cm, and they’ll take 1.61km.

I filed an issue last year, pointing out what had become common knowledge on StackOverflow: that startForeground() could be passed an invalid Notification. The service would gain foreground privileges, without the user having a Notification informing them of this effect. Google probably knew about this issue already – leastways, they never acknowledged the one that I filed – and in Android 4.3, they have taken corrective action.

Specifically, to quote Dianne Hackborn:

…what we ultimately did in 4.3 is have the system put up its own notification when it detects this so that the user is aware of what is going on and the app doesn’t have incentive any more to do this.

This is better than their original plan, which was to start raising an exception, thereby crashing apps that supplied invalid an Notification to startForeground().

So, now users will see extra notifications, tied to foreground services in apps whose developers tried to cheat the system.

The comments on Ms. Hackborn’s post contain lots of spluttering, as developers who employ hacks like this tend to splutter when their hack is no longer effective. The fact that such notifications can no longer be blocked by users will cause user frustration, as users have no ability to opt into having a foreground service without such a notification anymore. And while I haven’t tested the 4.3 change, I suspect that it affects all foreground services, and one could argue that there should be a de minimis exception (e.g., only display the Notification if the process consumes >1% of system RAM).

That being said, if you were using this hack, stop, and start figuring out what you are going to do about it.

For example:

  • If you were using a foreground service because you were too lazy to fix some bug in your app caused by your process being terminated and later restarted (e.g., START_STICKY), fix the bug.

  • Consider making foreground behavior optional. Since the decision of marking a serivce as a foreground service is made in Java, not the manifest, it is relatively easy for you to add a checkbox to your app settings to allow the user to indicate whether the benefits of foreground-ness are worth the cost of having the Notification around.

  • Consider making the service optional. For example, some developers insist on writing services to listen for ACTION_SCREEN_OFF and/or ACTION_SCREEN_ON, in an attempt to optimize some behavior. I suspect that few have done much analysis to determine if this actually is an optimization, in part because doing such analysis is rather difficult to peform accurately without specialized equipment. Once again, though, you can elect to give the user control over this, giving them a checkbox to control whether you monitor for these events or fall back to more traditional behavior.

  • There is no need to have more than one simultaneous foreground service. One service can have multiple threads to do disparate operations.

Want an expert opinion on your Android app architecture decisions? Perhaps Mark Murphy can help!