Hey, Where Did These Permissions Come From?

We are getting a wave of questions like this one on Stack Overflow recently. Developers ask for a few permissions in their manifest, then go to install their app (by means other than the IDE) and wonder where all these other permissions are coming from.

The vast majority of the time, they are coming from libraries.

Android library projects — both in source and AAR form — can publish manifests. Those manifests can have <uses-permission> elements in them. Those permissions will blend with the ones from your manifest(s) and those of other libraries that you are using, and all of them will be requested by your app.

This is one highly-visible impact of manifest merging.

To diagnose what is going on, you can turn to two resources:

  1. The actual manifest in your app

  2. The manifest merger report

Let’s see how this works.


If you create a brand-new Android Studio 1.2.2 project, using the Blank Activity template, you will wind up with a manifest that looks a bit like this:

<?xml version=”1.0” encoding=”utf-8”?>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

Note: there are no <uses-permission> elements here.

Your app module’s build.gradle file will also be fairly bland:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 22
    buildToolsVersion "23.0.0 rc2"

    defaultConfig {
        applicationId "com.commonsware.myapplication"
        minSdkVersion 15
        targetSdkVersion 22
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:22.2.0'
}

But let’s make one change: adding compile 'com.google.android.gms:play-services:7.5.0' to the dependencies closure, to pull in all of Play Services.

Then, after building the project, we pull up app/build/intermediates/manifests/full/debug/AndroidManifest.xml. This is the manifest that will actually go into our APK, representing all the contributions from our own app and libraries.

As you can see, we have a few <uses-permission> elements now, even though our own manifest does not have any:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.commonsware.myapplication"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="15"
        android:targetSdkVersion="22" />

    <!-- Include required permissions for Google Mobile Ads to run -->
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

    <uses-feature
        android:glEsVersion="0x00020000"
        android:required="true" />

    <android:uses-permission
        android:name="android.permission.READ_EXTERNAL_STORAGE"
        android:maxSdkVersion="18" />

    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
    <uses-permission android:name="android.permission.USE_CREDENTIALS" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.commonsware.myapplication.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <!-- Include the AdActivity and InAppPurchaseActivity configChanges and themes. -->
        <activity
            android:name="com.google.android.gms.ads.AdActivity"
            android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"
            android:theme="@android:style/Theme.Translucent" />
        <activity
            android:name="com.google.android.gms.ads.purchase.InAppPurchaseActivity"
            android:theme="@style/Theme.IAPTheme" />

        <meta-data
            android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version" />
        <meta-data
            android:name="com.google.android.gms.wallet.api.enabled"
            android:value="true" />

        <receiver
            android:name="com.google.android.gms.wallet.EnableWalletOptimizationReceiver"
            android:exported="false" >
            <intent-filter>
                <action android:name="com.google.android.gms.wallet.ENABLE_WALLET_OPTIMIZATION" />
            </intent-filter>
        </receiver>
    </application>

</manifest>

That tells us what our app will ask for. It does not tell us exactly why it wants those permissions, other than that one XML comment hinting that Play Services is to blame.

To get a better sense of where those <uses-permission> elements are coming from, we can look at app/build/outputs/logs/manifest-merger-debug-report.txt. Since this file is 500+ lines long, here are some sample lines that relate to our permission issue:

uses-permission#android.permission.INTERNET
ADDED from com.google.android.gms:play-services-ads:7.5.0:20:5
MERGED from com.google.android.gms:play-services-analytics:7.5.0:21:5
MERGED from com.google.android.gms:play-services-analytics:7.5.0:21:5
MERGED from com.google.android.gms:play-services-appinvite:7.5.0:19:5
MERGED from com.google.android.gms:play-services-maps:7.5.0:21:5
MERGED from com.google.android.gms:play-services-maps:7.5.0:21:5
MERGED from com.google.android.gms:play-services-maps:7.5.0:21:5
MERGED from com.google.android.gms:play-services-wallet:7.5.0:20:5
MERGED from com.google.android.gms:play-services-maps:7.5.0:21:5
  android:name
    ADDED from com.google.android.gms:play-services-ads:7.5.0:20:22
uses-permission#android.permission.ACCESS_NETWORK_STATE
ADDED from com.google.android.gms:play-services-ads:7.5.0:21:5
MERGED from com.google.android.gms:play-services-analytics:7.5.0:22:5
MERGED from com.google.android.gms:play-services-analytics:7.5.0:22:5
MERGED from com.google.android.gms:play-services-maps:7.5.0:20:5
MERGED from com.google.android.gms:play-services-maps:7.5.0:20:5
MERGED from com.google.android.gms:play-services-maps:7.5.0:20:5
MERGED from com.google.android.gms:play-services-nearby:7.5.0:19:5
MERGED from com.google.android.gms:play-services-maps:7.5.0:20:5
  android:name
    ADDED from com.google.android.gms:play-services-ads:7.5.0:21:22

What adding compile 'com.google.android.gms:play-services:7.5.0' really does is pull in a bunch of other Play Services artifacts as transitive dependencies. So, we get play-services-maps and play-services-ads and play-services-analytics and so on. Many of those have manifests, asking for certain permissions. In the case of ACCESS_NETWORK_STATE, we see that play-services-ads, play-services-analytics, play-services-maps, and whatever play-services-nearby is all want that permission. So, it gets added to our app’s manifest.

If you look at the generated manifest and the manifest merger report for your app, you too can see where these extra permission requirements are coming from.


Of course, the obvious follow-on question is “how do I make it stop?” Maybe you don’t want all those <uses-permission> requests, because yours is a smaller brand and you don’t want to scare away prospective users.

The best answer is: use fewer, more targeted dependencies.

For example, maybe you are using Play Services just for Google Cloud Messaging (GCM). Rather than pulling in play-services as a dependency, you can pull in play-services-gcm instead. Now, with that one change, not only is your APK going to be smaller, but all those rogue <uses-permission> elements will go away from your merged manifest, because GCM itself does not need any permissions.

It is possible that you will find yourself in a situation where you want a dependency but you don’t want the permissions that the dependency wants. You can try to block the permission from the merger process, by having a <uses-permission> element in your own manifest (e.g., in the main sourceset) with tools:node="remove":

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

The above element would tell the build system, “yes, some dependencies want ACCESS_COARSE_LOCATION, but do not include it in my manifest”.

(note: you may need to add xmlns:tools="http://schemas.android.com/tools" to the root <manifest> element if it is not already there)

But at this point, you have one main thing to do: write the biggest damn test suite you can. The author of the library is expecting to be able to access location data, and some code in that library may fail with a SecurityException due to your manifest shenanigans. So long as you never execute that code, life is good… at least until the library gets an update that you want to take on. But the only way you can feel comfortable that you are not executing that permission-dependent code is to test, test, test.

Ideally, library authors will use PackageManager to see whether or not their app has the permission and either gracefully degrade or fail fast. For many permissions, Android M is going to shake up the world, particularly for library authors, and so this is a fine time to go in and adding in that sort of logic.


So, in summary: if permissions are showing up in your app that you are not expecting, you have nobody to blame but yourself.

Well, yourself and the library authors for the libraries that you are using.

Well, OK, really it’s yourself, the library authors, and the Android tools team for offering this slick manifest merger process rather than forcing developers to hand-merge all of this stuff in.

But given the knowledge you gain from the merged manifest and the merger report, you can better tailor your library use and try to avoid permissions that you fear your users will not appreciate.