Don't Advertise Intent Filters That Are Not Yours
Subtitle: What Brown Should Not Be Doing For You
About 12 hours ago, somebody
posted a question to StackOverflow
that hits upon another Android anti-pattern: copying and pasting
<intent-filters>
without thinking.
The UPS Mobile app allows you to track packages and do a handful of other things that you might ordinarily do via the UPS Web site. It generally seems to be well-regarded, but it has an annoying flaw:
It claims to be Barcode Scanner, and does a lousy job at it.
Barcode Scanner, from ZXing, is a favorite among Android developers for its integration possibilities. However, some people do not like having a dependence upon the Barcode Scanner app, so they grab the open source code and attempt to blend it into their own apps. This is neither endorsed nor supported by the ZXing team, but since it is open source, it is also perfectly legitimate.
However, UPS (or whoever they hired to build the app) screwed up. They not only copied the source code, but they copied the manifest entry for the scanning activity. And, their activity has:
<intent-filter>
<action android:name="com.google.zxing.client.android.SCAN" />
<category android:name="android.intent.category.DEFAULT" />
<intent-filter>
This means that on any device that has UPS Mobile installed, they
will be an option for handling Barcode Scanner Intent
s. What
happened was that the person asking the question was manually
invoking startActivityForResult()
to bring up Barcode Scanner,
was getting a chooser with UPS Mobile in it, and then was crashing
upon choosing UPS Mobile… because UPS Mobile declared this activity
to be not exported.
Due to this bug, Android will display non-exported activities in a chooser, despite the fact that they can never be successfully used by the user.
So, what should we learn from this?
First, UPS Mobile should not have used that <intent-filter>
.
As Dianne Hackborn has pointed out, your <intent-filter>
mix
is effectively part of your app’s API,
and so you need to think long and hard about every <intent-filter>
you publish. UPS Mobile is not Barcode Scanner and should not
be advertising that they handle such Intent
s, despite the activity
being not exported.
Second, UPS Mobile probably should not have had any <intent-filter>
elements for this activity, if they intend to use it purely internally.
They could just as easily use an explicit Intent
to identify the
activity and avoid all of this nonsense.
Third, the person who filed the SO question ideally would have
been using ZXing’s IntentIntegrator
. As Sean Owen of the ZXing project
noted in a comment on my answer, IntentIntegrator
ensures that
only Barcode Scanner or official brethren will handle any scan
requests, so this problem would not have appeared.
Fourth, Android really should not be showing non-exported activities
in a chooser, which means probably that PackageManager
should
be filtering out non-exported activities from methods like
queryIntentActivities()
, which I presume lies at the heart of
the chooser.