Can't use manifest placeholders to remove a permission

Viewed 2087

I'm trying to use a manifest placeholder to remove a uses-permission node in the AndroidManifest.xml for release builds with no luck.

build.gradle

buildTypes {
    release {
        manifestPlaceholders.excludeDebugPermissions = "remove"
    }
    debug {
        manifestPlaceholders.excludeDebugPermissions = "merge"
    }
}

AndroidManifest.xml

<uses-permission
    android:name="android.permission.SYSTEM_ALERT_WINDOW"
    tools:node="${excludeDebugPermissions}" />

It produces an error like this:

Error:Execution failed for task ':app:processDebugManifest'. No enum constant com.android.manifmerger.NodeOperationType.${EXCLUDE_DEBUG_PERMISSIONS}

But using the placeholder anywhere else works properly (the merged manifest is OK and there is no error), e.g.

<uses-permission
    android:name="${excludeDebugPermissions}"
    tools:node="remove" />

So I suppose the tools:node attribute doesn't support manifest placeholders and I'm probably going to hack it by substituting the permission name instead of the node marker (merge / remove), but I would prefer to avoid it if possible.

Any advices?

3 Answers

As an alternative to the Android flavor approach, here is my solution to allow a different permission setup for a specific purpose for my app manifest.

For example, I can optionally enable/disable the Manifest.permission.READ_PHONE_STATE permission if I set my custom USE_PERMISSION_BUILD_SERIAL flag in my build.gradle as follows:

ext {
    //to enable READ_PHONE_STATE permission to access Build.SERIAL used 
    //for our bug reporting feature. 
    //Since there is a privacy concern with Build.SERIAL, we can decide 
    //to allow/disallow it right here!
    //If it is disabled, the Phone permission will not appear at all.
    //If it is enabled, the user will decide instead to allow the Phone 
    //access during its permission request as usual
    USE_PERMISSION_BUILD_SERIAL = true
    
    // whatever the rest...
}

android {

    defaultConfig {
    
        buildConfigField("boolean", "USE_PERMISSION_BUILD_SERIAL", "${USE_PERMISSION_BUILD_SERIAL}")
    }

    sourceSets {
    
        main {
            if (USE_PERMISSION_BUILD_SERIAL) {
                manifest.srcFile 'src/main/AndroidManifestBuild.xml'
            }
            else {
                manifest.srcFile 'src/main/AndroidManifest.xml'
            }
        }
    }           
}

Next, I prepare two different manifest files, AndroidManifestBuild.xml and AndroidManifest.xml, and store them under the src/main folder of my project.

Unlike the normal AndroidManifest.xml, I will request for the Manifest.permission.READ_PHONE_STATE permission in the AndroidManifestBuild.xml file as follows:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.example.app">

    <!-- required for accessing android.os.Build.getSerial() in Android 9.0+ -->
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    
</manifest>

Don't worry about the manifest name, as Gradle will build the final manifest file with the correct name for our Android application.

Using Gradle's buildConfigField() feature, I can use the BuildConfig.USE_PERMISSION_BUILD_SERIAL which stores the state of USE_PERMISSION_BUILD_SERIAL flag in my code to prepare the necessary permission request optionally.

Related