Libraries and Dangerous Permissions

If you publish a library for Android, you can request that developers have specific <uses-permission> elements, to allow your library to perform secured actions. If you are publishing an AAR, you can have those <uses-permission> elements in your own manifest, and those will be merged automatically into apps’ manifests, without those developers having to lift a finger. You can even check at runtime — perhaps as part of library initialization — whether or not those permissions actually appear in the manifest, failing fast if they do not.

This is all fine.

However, if you publish a library for Android, there are some basic rules to follow, particularly given Android 6.0’s runtime permissions.

Rule #1: Automatic Merging Does Not Mean You Can Skimp on Documentation

Developers get confused when permissions show up unexpectedly. Just because you don’t need developers to add <uses-permission> elements manually does not mean that you can skip telling them about the permissions. This is particularly true for dangerous permissions, as it is fairly likely that the app developer will need to be the one to request those permissions from the user at runtime.

Rule #2: Gracefully Degrade If Permissions Are Missing

For dangerous permissions, users may elect to not grant them to the app, and by extension your library. Moreover, developers may not even want to ask the users for certain permissions, either by having the permissions in the manifest (and triggering prompts at install time prior to Android 6.0) or by popping up the permission dialog at runtime on Android 6.0+. Just because you want the permission does not mean that anyone else wants you to have the permission.

For permissions that are clearly essential, requiring them is fine. But if developers or users cannot clearly see why the permission is needed, you need to be able to live without that permission, gracefully degrading and performing a subset of your functionality, with sufficient notification to developers about the reduced functionality.

Rule #3: Document What Happens When Permissions Are Denied

Developers cannot somehow force users at gunpoint to grant dangerous permissions. Yet, developers need to be in position to support users who deny those permissions, then wonder why certain things do not work. Hence, if those permissions stem from libraries, the developers using the libraries need to know exactly what will stop working if the user elects to deny the permissions.

Rule #4: Make Sure That You Really Need Those Permissions

Asking for permissions that are not necessary, particularly dangerous ones, are good ways to aggravate developers and users alike.

For example, let’s review this Twitter thread.

Ty Smith, a well-known Android developer at Twitter, pointed out that the StockTwits app pops up a dialog indicating that the user needs to grant access to contacts in order to receive push notifications. That dialog appears to be tied to shouldShowPermissionRequestRationale(), displayed to help explain to the user why StockTwits is asking for access to contacts.

Further discussion on that thread points out that StockTwits uses Parse, and Parse requires GET_ACCOUNTS for push notifications. It is unclear why Parse needs GET_ACCOUNTS for push notifications, other than that GCM requires it on older Android devices. And, if I am understanding one tweet properly, Parse complains if your manifest lacks GET_ACCOUNTS.

It’s entirely possible that I am missing pieces, as I don’t use Parse, I don’t use StockTwits, and I haven’t played with GCM in quite some time.

UPDATE: Mike Evans pointed out that Parse no longer requires GET_ACCOUNTS. This means that Parse’s documentation needs updating.

But, if my synopsis is correct:

  • Parse should point out that GET_ACCOUNTS is not needed, for apps with a minSdkVersion of 15 or higher, in the documentation for Android developers. Ideally, they would provide code snippets showing how to block this permission from the manifest merger process in the app’s own manifest. If Parse does not need GET_ACCOUNTS for anything else, this avoids bothering users of most devices with this permission. Since this permission has some scary-sounding language around it, this would help improve adoption and usage of apps integrating with Parse.

UPDATE: An earlier version of this blog post suggested the use of android:maxSdkVersion, but Steven Jones pointed out that android:maxSdkVersion is new to API Level 19, which makes it not a good choice for this circumstance.

  • Even if Parse would like GET_ACCOUNTS for some other reason, Parse needs to gracefully degrade if GET_ACCOUNTS is skipped by the developer or denied by the user. Again, specifically for GCM, GET_ACCOUNTS is not needed on over 90% of Android devices; on those devices, Parse should not be complaining if this permission is missing. And, since this permission is part of the contacts permission group on Android 6.0, this really needs to be optional on Android 6.0+ devices, as users may say, as Ty did, that they do not want to give apps that permission.

The problem here is that Parse is a library, backed by Web services. From a brand standpoint, it is a brand for developers; users do not see Parse. So, when users complain about StockTwits wanting access to contacts, StockTwits gets blamed, even if StockTwits is just doing what Parse asks. Of course, StockTwits gets blamed because their CEO seems to be a jerk, but that’s a separate issue.

On Android 6.0+, dangerous permissions demand transparency. Users need to understand what the rationale is for the dangerous permission, and educated users may look askance at what appears to be flimsy rationale. In turn, developers need to understand why libraries want those dangerous permissions and what happens when the permissions are not granted or not requested. And that means that library authors need to do a good job of knowing why they need permissions, not only to minimize their requests for permissions, but to explain to developers the reasons, so developers can in turn explain this to users.

IOW, manifest merger is not enough.