This is a post-mortem where the very dangerous permission, READ_PHONE_STATE , unintentionally sneaked into our app. Here’s how this could happen, how we debugged and finally how we solved it.

Prologue

Sprint comes to an end and we’re happy to deliver a new release of our app. After rolling it out to our beta community without issues, we move ahead to production.

While everything looks fine at first, after a while we see users complaining:

Honestly we were completely taken aback by this… But sure enough looking at the play store:

Root cause

If you ever run into a similar issue, the Android Studio merged manifest view is the way to go. Just open your manifest and click the Merged manifest tab at the bottom.

Sure enough, the READ_PHONE_STATE permission is there.

Unfortunately, this view couldn’t help us find where the permission was merged from:

Double-clicking the permission led us back to normal manifest view

Color coding palette is so subtle that we couldn’t see what color the permission was highlighted in

Fortunately, the manifest merger also prints a log file to build/outputs/logs that describes where everything is merged from.

This file gave a clear answer:

uses-permission#android.permission.READ_PHONE_STATE IMPLIED from /app/src/debug/AndroidManifest.xml:8:1-15:12 reason: hue.libraries.translations has a targetSdkVersion < 4

Wow… That’s nasty!

A while ago we decided to move all our translations to a new module, with an empty manifest and a bare-bones build.gradle file:

apply plugin: 'com.android.library' android { compileSdkVersion Config . compileSdk }

And because we didn’t explicitly set the targetSdk, a targetSdk of 1 is assumed and hence we end up with a dangerous permission!

To be fair, the documentation does warn you about this:

But still… wow!

Solution

While a solution could be to simply set the targetSdk in our translations module. This wouldn’t prevent something similar from happening in the future.

Therefore we decided to set the targetSdk (and others) for all our modules in the top level build.gradle file. This also keeps submodule build.gradle files lean.

subprojects { afterEvaluate { project -> if ( project . plugins . findPlugin ( 'android' ) ?: project . plugins . findPlugin ( 'android-library' )) { android { buildToolsVersion Config . buildTools compileSdkVersion Config . compileSdk defaultConfig { minSdkVersion Config . minSdk targetSdkVersion Config . compileSdk } compileOptions { sourceCompatibility Config . javaVersion targetCompatibility Config . javaVersion } } } } }

Additionally, we also wanted to protect ourselves against 3rd party library developers that could make this mistake. To do so, you can inform the manifest merger to remove the permission while merging by adding the following to your manifest:

<uses-permission android:name= "android.permission.READ_PHONE_STATE" tools:node= "remove" />

That is overkill you say?

Well, Firebase AND Google play services already made this booboo in the past.

… wow!

Wrap-up

Not explicitly setting your target SDK version will cause a dangerous permission to sneak into your app. Make sure you set the target SDK in every module and protect yourself from 3rd party libraries.

If you’ve made it this far you should probably follow me on Twitter . Feel free leave a comment below!