Christopher Orr

Efficiently filtering Android intents with 'android:ssp'

I discovered that Android 4.4 (API level 19) added a set of not-yet-documented XML attributes to its <data> tag for intent filtering.

The android:ssp attribute is intended to match against URIs — the “ssp” stands for “scheme-specific part” and essentially means everything that appears after the scheme. e.g. The URI "https://example.com/foo/bar" can be split into the scheme "https" and scheme-specific part "//example.com/foo/bar".

While this isn’t of much use for regular HTTP URLS, as the android:host and android:path* attributes exist to easily build intent filters, it makes monitoring certain types of events much more efficient.

The problem

The broadcast receiver mechanism on Android is a great way to ensure that your app — regardless of whether its process is already running — can be informed of system events, like whether there’s currently network connectivity, or the battery is low etc.

However, when multiple apps subscribe to the same, oft-occurring events, this causes a lot of processes to started by the system simultaneously, bringing the whole system to a crawl. The most common case in which this occurs is when installing, updating or removing an Android package.

Apparently a lot of apps with analytics SDKs built in like to monitor and report on app installs or removals, and do so with a broadcast receiver defined like this:

<receiver android:name=".PackageReceiver">
  <intent-filter>
    <action android:name="android.intent.action.PACKAGE_ADDED" />
    <data android:scheme="package" />
  </intent-filter>
</receiver>

If we assume that we’re only interested in actions whose URI look like package:com.example.someapp, but because this is not a hierarchical URI — there is no host, no port, no path — we cannot be any more precise about exactly which package(s) we want to be notified for. So all apps get woken up for every package install event!

Enter android:ssp and friends

Android 4.4 allows us to match against the scheme-specific part of the URI with the android:ssp, android:sspPrefix and android:sspPattern attributes.

In the case of packages, we can now specifically target one or more packages we are interested in. For example, an app I worked on had three different package IDs for development, beta and release builds. We could match all three apps (and no others) with a filter like this:

<receiver android:name=".DataClearedReceiver">
  <intent-filter>
    <action android:name="android.intent.action.PACKAGE_DATA_CLEARED" />
    <data android:scheme="package"
          android:sspPrefix="com.myswitzerland.hotels" />
   </intent-filter>
</receiver>

So only intents containing package URIs which start with that exact text would cause the app process to start and the receiver to be triggered. If any other app has its data cleared, our process does not start. Excellent!

Of course, this works for other non-hierachical URI types like mailto or tel.

In a slightly-contrived example, we could intercept specific email addresses and let the user choose to use our specific Activity, rather than the default email client:

<activity android:name=".PremiumSupportActivity">
  <intent-filter>
    <action android:name="android.intent.action.SENDTO" />
    <category android:name="android.intent.category.DEFAULT" />
    <data android:scheme="mailto"
          android:sspPattern="support-.*@example.com" />
   </intent-filter>
</activity>

The SSP attributes are not yet mentioned in the <data> element documentation, but their Java equivalent is described in the IntentFilter documentation.

So it is also possible to register such intent filters at runtime if required, e.g.:

IntentFilter pkgFilter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED);
pkgFilter.addDataScheme("package");
pkgFilter.addDataSchemeSpecificPart("com.example.someapp",
                                    PatternMatcher.PATTERN_LITERAL);

These examples work for me on an Android 4.4 device, but the XML attributes should also be safe to use on earlier Android versions, as the system will just ignore the unknown SSP attributes.

Let me know if you can think of other use cases that could be improved by the use of scheme-specific part matching.