SSL is nice, but there are risks of fraudulent certificates being
issued by rogue certificate authorities. One approach to dealing with
this risk is to use certificate pinning, teaching your Android app
details about the expected SSL certificate, so the app can fail if it
encounters a fraudulent one. This reduces the risk of somebody with
a fake cert intercepting your communications. However, it can increase
other risks, as Square’s Jesse Wilson pointed out earlier today.
If you pin to your own certificate, that works, until you need to
replace that certificate, because it is expiring or for other reasons
(e.g., the Heartbleed attack “OMG replace all teh certs!” response)
If you pin to some other certificate up the chain towards the CA’s
root certificate, that works, until the intermediary needs to replace
I imagine that this is why Android 7.0’s
network security configuration’s
certificate pinning implementation fails open. In other words, when
you put an expiration date on a pinned certificate, when that date arrives,
that pin is ignored. For planned certificate obsolescence —
such as the certificate’s own expiration date — this will allow your
app to keep working, even for those users who failed to update their
app. Users who update their app can be moved over to a new pin for a new
certificate. Since CWAC-NetSecurity
is a backport of Android 7.0’s network security configuration, it too
supports expiring pins.
If you elect to pin certificates, whether using network security configuration
or something else:
Set an expiration date on the pin, and fail open. Yes, this reduces
security, but only for those users running older editions of your app.
Have a plan for an emergency app update, in case you need to replace
the server’s SSL certificate in a hurry (e.g., Heartbleed-style
For a server that only handles mobile apps, not Web browsers, you could
avoid some of this by switching to a self-signed certificate and effectively
“pinning” to that. Since there is no certificate authority in the mix,
you are not at risk of a certificate authority screwing up, being hacked,
or being suborned into issuing a fraudulent certificate.
Remember: the rationale
behind a certificate authority is to help the user with a generic client
app (Web browser) determine if a certificate is valid. It does not add
as much value in scenarios with a specific client, such as an Android app
dealing exclusively with your server.
—Nov 28, 2016
Each time Google releases a new developer preview, I try to
talk about the changes that may not be obvious when reading the
However, in this case, there does not seem to be much that is different
that affects most developers, beyond the items that I mentioned in
my Developer Preview 1 post.
Near as I can tell, this is just bug fixes, which makes sense, if the
rollout of 7.1 to non-Pixel phones is only weeks away.
—Nov 22, 2016
Many times, when we work with a
Uri, we get an
read in the content, and we’re good to go.
Sometimes, though, consumers of content make some unfortunate assumptions.
One assumption is that you can pass
rw in for the mode on
and kin. First, this assumes that you have write access, which may or
may not be true. Plus,
rw requires that the content that you are
working with be stored as a plain file on the filesystem somewhere.
Another related assumption is that the
InputStream is seekable,
reset(). Once again, this only works
if the content in question is backed by a plain file on the filesystem.
However, increasingly, that will not be the case, as more and more
developers turn to publishing content, to get around things like
Uri ban in Android 7.0.
While content may be backed by a plain file on the filesystem,
it might also be backed by:
A file, but one that needs to be transformed on the fly by the
provider (e.g., decrypted)
A piece of a file (e.g., an asset packaged in the app’s APK)
Something in memory (e.g., the contents of a
BLOB column that
was read in from a database)
Something being streamed from the Internet (though this is kinda
risky IMHO, on the provider’s part)
ContentProvider publishing this content will be using something
other than a file-based
ParcelFileDescriptor, usually in the form
of a pipe or socket pair. Those do not support
rw mode, nor do they
In particular, if your app is usable with read-only non-seekable content,
but perhaps with a subset of functionality, make sure that you support
it. For example, a couple of Android PDF viewers — including Google’s
Drive PDF Viewer — seem to require
rw access and crash in situations
where other apps (e.g., Adobe Reader) work fine. Gracefully degrade in
these cases: try for
rw access, and then try for
r access if the
If seekability is the issue, more so than
rw access, you can try
getStatSize() on the
ParcelFileDescriptor that you get
openFileDescriptor() on the
ContentResolver. If this
returns -1, then there is a good chance that the stream is not file-backed
and will not support seeking. If this returns 0 or a positive value,
that should be the size of the file that the stream is delivering to you.
If you need a test case for these cases, toss
in your test suite and test against an asset served via a
Uri. This will be a non-seekable stream on which
rw is not an
option (as you cannot modify an asset).
The majority of the time, when you get a
Uri, it will
be served by a provider that is using a file for the content. In that
rw mode may be available, and seeking should work. Just do
not assume that you can always do that for every
sure that you test cases where the
Uri is something else, and that
you handle those cases as best as you can.
—Nov 21, 2016
Android 7.0 added scoped directory access.
This API allows you to ask the user for blanket access to a common
public directory on an arbitrary
StorageVolume, such as a USB OTG
drive or other removable storage. If the user grants access, you get
Uri back, akin to as if you had used
and the user happened to have chosen that particular directory. The
difference is that you are choosing the directory, from
a list of candidates,
rather than granting the user full freedom to choose any directory
or other “document tree” that the user wants.
However, there is a UX flaw with scoped directory access.
The flow of the permission dialogs resembles that of Android 6.0’s
When you first ask for access, the user can allow or deny
If the user denied access, and you later ask for access again,
the dialog now has a “Don’t ask again” checkbox
If the user checked that checkbox and denied access again, any future
attempts you make to request access will be denied immediately,
without the user seeing a dialog
One problem is that we have no good way of knowing that the user
has previously denied our request, let alone checked the “Don’t ask
again” checkbox. With Android 6.0’s runtime permissions, we have
for those things. We have no equivalents for scoped directory access.
However, the bigger problem is that once the user checks “Don’t ask
again” and denies access, the user has no further recourse. With
runtime permissions, the user can always go into the Permissions
area of your app’s page in Settings and manually grant permissions.
There is no equivalent of this for the scoped directory access API.
On one device (a Nexus 5X running the 7.1 preview), the user
can use “Clear Data” to reset these dialogs, causing future dialogs
to appear again even if “Don’t ask again” had been checked. Of course,
“Clear Data” has somewhat broader impact than this, and the user might
not appreciate wiping out all the app’s local data.
Worse, on two test devices (a Nexus 5X running 7.0 and a Google Pixel
running 7.1), not only does “Clear Data” not fix this, but
a full uninstall of the app does not fix this. AFAICT, nothing short
of a factory reset would allow the app to ask the user for permission
and the user have an opportunity again to grant permission.
Admitttedly, this is an edge case, but it is one that you should
keep in mind if you are using
createAccessIntent() and the
scoped directory access API. I filed
to try to get some resolution to how the user is supposed to manually
revert the “Don’t ask again” status.
—Nov 18, 2016
Subscribers now have
access to the latest release of The Busy Coder’s Guide to Android Development,
known as Version 8.1, in all formats. Just log into
your Warescription page and download
away, or set up an account and subscribe!
This time around, I:
Added a new chapter on the basic steps for writing Gradle plugins
Added a new chapter on compile-time code generation,
by means of a Gradle plugin and Square’s JavaPoet
Added a new chapter on updating your app’s code dynamically,
as a counterpart to updating the app’s code by publishing a new APK
Added a new chapter on Android 7.1’s app shortcuts, both the static
kind (which can be used by pre-7.1 home screens) and the dynamic kind
Added a new chapter on Java 8 lambda expressions,
enabled via the Jack compiler
Added a new section to the
DiffUtil to identify and animate changes to the
contents of a
Overhauled the chapter on NetCipher
Added a bit more material on
Made other minor improvements and errata fixes
Also, the APK edition of the book has a new appinar on drag-and-drop.
Barring some major code drop from Google here in the waning weeks of 2016,
this is the eighth and final update for this year, adding about 500
pages of material, along with the first 24 appinars.
The next update is tentatively scheduled for early January 2017.
—Nov 14, 2016