In general, there are three types of manifest files that need to be merged into a single resulting app manifest, here in priority order :
Product flavors and build types specific manifest files.
Main manifest file for the application.
Library manifest files.
Manifest files of types (1) are usually overriding the main manifest content since it’s specializing the application for a particular delivery. Then, manifest files of types (3) are usually merged into the resulting main manifest from previous step. The rules for merging depend on each node type and can be altered using “tools:” namespace attributes.
Because of multiple product flavors and build types there can be a matrix of possible manifest files to merge. However, for each assembly step, only one value of each flavor group and build type can be selected, leading to an ordered list of possible manifest files that overrides the main manifest file.
For instance, the following FlavorGroups: abi, density, API, Prod/Internal leads to the following matrix of possible flavors :
ABI
Density
API
Prod/Internal
x86
mdpi
9
prod
arm
high
14
internal
mips
xhigh
15
xxhigh
This leads to 3x4x3x2 possible combinations. However, for each assembly run, there can only be one flavor from each group, in the order the flavor group were declared in the original build.gradle leading to an ordered list of higher priority to lower priority potential manifest files to merge.
For instance, building the x86-high-15-prod variant will lookup the following manifest files to merge
x86/AndroidManifest.xml
high/AndroidManifest.xml
15/AndroidManifest.xml
internal/AndroidManifest.xml
Each file in this ordered list has a priority according to its rank in the list, and merging will use this priority to decide which XML element or attribute will overwrite a lower priority setting.
Therefore, the input of the merging tool will be as follows :
ordered list by priority of flavor/build type manifest files, these will be commonly referenced as the flavors manifests.
main manifest file
ordered list by declaration (or transitive dependency) of libraries manifest files.
Injectable values for placeholders and XML generation
Android Manifest file merging
Each element in a manifest file can be identified by its element type (e.g. activity, intent-filter) and an optional key value. Some elements like “activity” must have a key since there can be multiple present in a single AndroidManifest.xml. Other elements like “application” do not require a key since there can be only one.
The element type and key value pair represent the identity of a manifest element.
The merging activity is always between two identically typed elements, one coming from a higher priority manifest file, one coming from a lower priority manifest file. Each merging activity has a default behavior that will be described subsequently. Also each default merging activity on the node or on a specific attribute can potentially be overridden by including tools specific markers.
The merging process will also record all decisions taken along the way for each node, described in the "Logging" section.
Elements and Attributes merging process
Implicit declarations
A number of attributes can have default values (defined in theonlinedocs as defaults). When a higher priority element does not define an attribute which has a default value of X, if a lower priority element defines that very attribute with the same value X, it will still be added to the merged element (with value X of course) since it represents a clear choice from the library to not consider this value as the default value but the right value for the feature (in case the default ever changes).
Most attributes with default values will be ignored by the manifest merger if a lower priority attribute defines a value; it will be merged since there is no conflict between a default value and an attribute set to any value. The attribute will be set to that set value in the resulting merged element.
However, there are a few exceptions listed below:
Defaults to true. Merging with other attributes will use a OR merging policy since if any library requires the feature, the resulting application will require the feature.
Same as uses-feature:required.
Defaults to 1.
The higher priority document's version will be used but importing a library with a more recent version will generate an error.
Same as uses-sdk:minSdkVersion
Same as uses-sdk:minSdkVersion
Automatic upgrades
When importing a library with a lower target SDK than the project’s, it may be necessary to explicitly grant permissions (and perhaps make other changes) for the library to function properly in the later runtime. This will be performed automatically by the manifest merger.
Placeholder support
When an attribute value contains a placeholder (see format below), the manifest merger will swap this placeholder value with an injected value. Injected values are specified in the build.gradle.
The syntax for placeholder values is ${name} since @ is reserved for links. After the last file merging occurred, and before the resulting merged android manifest file is written out, all values with a placeholder will be swapped with injected values. A build breakage will be generated if a variable name is unknown.
The placeholder string can have a prefix and/or a suffix allowing to partially replace the value.
Examples:
android:authority="${applicationId}.foo"
android:authority=”com.acme.${localApplicationId}”
android:authority=”com.acme.${localApplicationId}.foo”
An implicit placeholder ${applicationId} resolution will be automatically provided out of the box with the build.gradle applicationId value.
Examples :
android:name=".Main">
with the following gradle declaration :
android {
compileSdkVersion 19
buildToolsVersion "19.0.2"
productFlavors {
flavor1 {
applicationId = "com.android.tests.flavorlib.app.flavor1"
}
}
Once merged, the will be
For custom placeholders replacements, use the following DSL to configure the placeholders values :
android {
defaultConfig {
manifestPlaceholders = [ activityLabel:"defaultName"]
}
productFlavors {
free {
}
pro {
manifestPlaceholders = [ activityLabel:"proName" ]
}
}
will substitute the placeholder in the following declaration :
Generic description of merging strategies
Merging XML can happen either at the node level or at the attribute level.
At the node level, the default merging policy is to merge attributes and sub-elements as long as there is no conflict. A conflict arises when two elements with the same identity have the same attribute with a different value.
For instance:
android:name=”com.foo.bar.ActivityOne”
android:theme=”@theme1”/>
merging with the following declaration will not generate a conflict:
android:name=”com.foo.bar.ActivityOne”
android:screenOrientation=”landscape/>
same for
android:name=”com.foo.bar.ActivityOne”
android:theme=”@theme1”/>
merging with the following declaration will not generate a conflict, since the “theme” attribute defined in both elements have the same value.
android:name=”com.foo.bar.ActivityOne”
android:theme=”@theme1”
android:screenOrientation=”landscape/>
however,
android:name=”com.foo.bar.ActivityOne”
android:theme=”@theme1”/>
merging with the following declaration will generate a conflict, since the “theme” attribute defined in both elements does not have the same value.
android:name=”com.foo.bar.ActivityOne”
android:theme=”@theme2”
android:screenOrientation=”landscape/>
Now, each element can have sub-elements and rules for matching those will follow the same general principle, sub-elements with the same identity will be matched together. If a sub-element exist in only one of the two parents, this is not a conflict.
Markers
A marker is a special attribute, in the tools namespace, used to express a specific decision for how to resolve conflicts.
All markers belong to the Android tools namespace, therefore you must declare the namespace in any AndroidManifest.xml containing at least one marker :
Example:
package="com.android.tests.flavorlib.app"
xmlns:tools="http://schemas.android.com/tools">
android:icon="@drawable/icon"
android:label="@string/app_name"
tools:replace=”icon, label”/>
When conflicts arise between elements to be merged, some markers must be explicitly added to guide the Manifest Merger. At the node level, the tools:node attribute should be used, at the attribute level, the tools:attr attribute should be used.
tools:node markers
A tools:node=”maker_value” attribute should be present on the node which is in conflict and need resolution.
attribute value
manifest merger action
This is the implicit default mode for node merging, nodes will be merged as long as they do not conflict
Replace the lower priority declaration with the annotated one.
This will generate a build failure when another node with the same identity is present and is not strictly equal.
Only merge attributes from the lower priority declaration.
Remove the annotated element from the resulting XML. No lower priority declaration will be merged in irrespective of potential conflicts.
Remove all elements of the same node type (no key necessary).
tools:attr markers
There can be be many attribute related markers on any particular element to resolve all attributes in conflict.
Default implicit mode for attribute, generate an error when trying the merge lower priority attribute declaration with a different value.
Remove the x, y, z attributes from any lower priority declaration when merging in.
Replace the x, y, z attributes from any lower priority declaration with the provided value (must be present on the same node).
Selector
Each tools:node or tools:attr declaration can be augmented by a tools:selector attribute which is contextual information on whether or not the merging strategy should be applied to the current lower priority XML description. For instance, this is useful when removing a permission only if coming for one particular library as opposed to any library:
android:name="permissionOne"
tools:node="remove"
tools:selector="com.example.lib1">
tools:overrideLibrary marker
A special marker that can only be used with uses-sdk declaration to override importing a library which minimum SDK version is more recent than that application's minimum SDK version.
Without such a marker, the manifest merger will fail. The marker will allow users to select which libraries can be imported ignoring the minimum SDK version.
Example, In the main android manifest :
tools:overrideLibrary="com.example.lib1, com.example.lib2"/>
will allow the library with the following manifest to be imported without error :
package="com.example.lib1">
Logging
Each operation/decision made during the manifest merger needs to be
recorded
formatted for machine capable parsing
ordered by node (since several decisions might have contributed to a node’s attributes)
The logging will not be organized as a linear set of events and decisions that ultimately yielded the output file. Instead, to make things easier for developers, the log file will be organized by top-level XML nodes encountered in the input files (whether or not it is present in the output file as we want to document node deletion as well).
Logging record:
A logging record is either a node record (describing all actions taken on that particular node) or a message record containing error messages and warnings.
logging-file = (logging-record)*
logging-record = node-record | message
Message
message=file:line_number:colum_number severity:\ndescription
description=(\tTEXT\n)*
Name
Value
file
Input file generating the log entry
line-number
Input file line number generating the log entry
column-number
Input file column number generating the log entry.
severity
Error, Warning, Info
description
Log entry payload
Examples
/Users/jedo/src/app/src/main/AndroidManifest.xml:3:9 Error:
Attribute activity@screenOrientation value=(portrait) from AndroidManifest.xml:3:9
is also present at flavorlib:lib1:unspecified:3:18 value=(landscape)
Suggestion: add 'tools:replace="icon"' to element at AndroidManifest.xml:1:5 to override
NodeRecord
Name
Value
node-type
XML node type
node-key
node key attribute value
record-type
[Action|Log] *
node-type#node-key\n
\t(node_action:Action)*
\t\t(attribute_action:Action)*
Action format
Name
Value
action-type
added | rejected | implied
target
node | attribute
target-name
node key name or attribute name
origin
origin value location.
Examples:
application
ADDED from AndroidManifest.xml:10:5
MERGED from flavorlib:lib2:unspecified:3:5
android:label
ADDED from AndroidManifest.xml:12:9
REJECTED from flavorlib:lib2:unspecified:3:55
android:icon
ADDED from AndroidManifest.xml:11:9
REJECTED from flavorlib:lib2:unspecified:3:18
receiver#com.example.WidgetReceiver
ADDED from ManifestMerger2Test0_main.xml:60:9
android:labelADDED from ManifestMerger2Test0_main.xml:61:13
android:iconADDED from ManifestMerger2Test0_main.xml:62:13
android:nameADDED from ManifestMerger2Test0_main.xml:63:13
Build error
When a build error happens, the log for the particular node failing should be displayed followed by a descriptive error message for the user. For instance :
Higher priority declaration
android:name=”com.foo.bar.ActivityOne”
android:screenOrientation=”portrait”
android:theme=”@theme1”/>
with a lower priority declaration :
android:name=”com.foo.bar.ActivityOne”
android:screenOrientation=”landscape/>
will result in both the log file and the output (exact format for human and machine and IDE readable to be determined).
/Users/jedo/src/app/src/main/AndroidManifest.xml:3:9 Error:
Attribute activity@screenOrientation value=(portrait) from AndroidManifest.xml:3:9
is also present at flavorlib:lib1:unspecified:3:18 value=(landscape)
Suggestion: add 'tools:replace="icon"' to element at AndroidManifest.xml:1:5 to override
Blame
A “blame” type of output can be obtained to qualify each element and attribute resulting in the merged XML with some indication of where the element/attribute originated from.
Merging Policies
Each element type has a specific default merging policy attached to it. For example most elements types like activity or application have a default merging policy where all attributes and children are merged (assuming no conflict) into the resulting element. However other elements like the top level manifest will have a default merging policy of merging children only which mean that no attributes of the manifest element of a lower priority AndroidManifest.xml is eligible for merging.
Each element can also have a key associated or not. For instance, application have no key, there can only be one elements per AndroidManifest.xml. Most keyed elements use the android:name attribute to express their key value, others
Merge
Non conflicting attributes are merged and children are merged according to their respective merging policy.
Merge Children Only
Attributes are not merged, only children are merged according to their respective merging policy.
Always
Elements are always kept “as is” and added to the common parent in the resulting merged file.
List of element merging policy and key
Node Type
Merging Policy
Key
action
merge
android:name attribute
activity
merge
android:name attribute
application
merge
no key
category
merge
android:name attribute
data
merge
no key
grant-uri-permission
merge
no key
instrumentation
merge
android:name attribute
intent-filter
always
sub-element actions and categories android:name attribute. Several declaration with the same key are allowed.
manifest
merge children only
no key
meta-data
merge
android:name attribute
path-permission
merge
no key
permission-group
merge
android:name attribute
permission
merge
android:name attribute
permission-tree
merge
android:name attribute
provider
merge
android:name attribute
receiver
merge
android:name attribute
screen
merge
attribute screenSize
service
merge
android:name attribute
supports-gl-texture
merge
android:name attribute
supports-screen
merge
no key
uses-configuration
merge
no key
uses-feature
merge
attribute name first and if not present attribute glEsVersion
uses-library
merge
android:name attribute
uses-permission
merge
android:name attribute
uses-sdk
merge
no key
custom elements
merge
no key
Package name smart substitution
Some attributes are package dependent attributes which means the attribute supports smart substitution of partially fully qualified class names with package settings as provided by the manifest node's package attribute.
Each of the attribute described below can have a partial class name that is either starting with a dot or do not contain any dots.
Examples :
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.app1">
will be expanded into :
This is independent of any package setting in the build.gradle for if for example, you build.gradle contains :
android {
compileSdkVersion 19
buildToolsVersion "19.0.2"
productFlavors {
flavor1 {
applicationId = "com.android.tests.flavorlib.app.flavor1"
}
}
the resulting expansion will still be
If you need the injected value as the expanded attribute value use ${applicationId} placeholder, for example :
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.app1">
List of package dependent attributes that can use this smart substitution facility :
Node Type
Attribute local names
activity
name, parentActivityName
activity-alias
name, targetActivity
application
name, backupAgent
instrumentation
name
provider
name
receiver
name
service
name
Implicit permissions addition
When importing a library with a targetSdkVersion lower than the importing application targetSdkVersion, some permissions maybe automatically added to the resulting merged manifest file. This is necessary since such libraries targeted runtimes where such permissions wereimplicitlygranted. Therefore declaring such permission was not necessary. However in more recent Android releases, these permissions are not automatically granted. Therefore, a library targeting such old runtime without the permissions would not work properly once merged in the application targeting a more recent runtime. For instance, an application with a targetSdkVersion of 16 importing a library with a targetSdkVersion of 2 will get the WRITE_EXTERNAL_STORAGE permission added. See the table below for a complete list.
permissionreason
WRITE_EXTERNAL_STORAGEAdded when importing a library with a targetSdkVersion < 4
READ_EXTERNAL_STORAGEAdded when importing a library that declared WRITE_EXTERNAL_STORAGE
READ_PHONE_STATEAdded when importing a library with a targetSdkVersion < 4
READ_CALL_LOGAdded when importing a library with a targetSdkVersion < 16 and using READ_CONTACTS permission
WRITE_CALL_LOGAdded when importing a library with a targetSdkVersion < 16 and using WRITE_CONTACTS permission
Remember that the targetSdkVersion is defaulted to minSdkVersion when notexplicitlydeclared. When no element is present, the minSdkVersion is itself defaulted to 1.
Attributes markers examples
Override an attribute coming from a library
Using tools:replace=”x, y, z” will override x,y,z attributes from the imported library’s activity XML declarations.
Higher Priority declaration
android:name=”com.foo.bar.ActivityOne”
android:screenOrientation=”portrait”
android:theme=”@theme1”
tools:replace=”theme”/>
with a lower priority declaration :
android:name=”com.foo.bar.ActivityOne”
android:theme=”@olddogtheme”
android:windowSoftInputMode=”stateUnchanged”
android:exported=”true”>
will result in :
android:name=”com.foo.bar.ActivityOne”
android:screenOrientation=”portrait”
android:theme=”@theme1”
android:windowSoftInputMode=”stateUnchanged”
android:exported=”true”/>
Remove an attribute coming from the library.
Using tools:remove=”x, y, z” will remove the x, y, z attributes declaration from the resulting XML.
Higher priority declaration
android:name=”com.foo.bar.ActivityOne”
android:hardwareAccelerated=”true”
tools:remove=”android:theme,android:screenOrientation”/>
with a lower priority declaration :
android:name=”com.foo.bar.ActivityOne”
android:screenOrientation=”landscape”
android:theme=”@olddogtheme”
android:windowSoftInputMode=”stateUnchanged”
android:exported=”true”/>
will result in :
android:name=”com.foo.bar.ActivityOne”
android:hardwareAccelerated=”true”
android:windowSoftInputMode=”stateUnchanged”
android:exported=”true”/>
Enforcing an attribute value
Implicitly, all declared attributes are virtually annotated with a “strict” merging policy so if two elements eligible for merging each have the same attribute with a different value, this is a conflict that need to be explicitly addressed.
so, ahigher priority declaration
android:name=”com.foo.bar.ActivityOne”
android:theme=”@newdogtheme”/>
with a lower priority declaration :
android:name=”com.foo.bar.ActivityOne”
android:theme=”@olddogtheme”/>
and ahigher priority declaration
android:name=”com.foo.bar.ActivityOne”
android:theme=”@newdogtheme”
tools:strict=”theme”/>
with a lower priority declaration :
android:name=”com.foo.bar.ActivityOne”
android:theme=”@olddogtheme”/>
are strictly equivalent and will fail to merge correctly until a tools:replace=”theme” is added.
Mixed operations
If the user wants to remove some attributes and override others while conserving another set of original attributes, just add all markers side by side.
for example :
android:name=”com.foo.bar.ActivityOne”
android:windowSoftInputMode=”stateUnchanged”
android:theme=”@theme1”
tools:remove=”android:exported, android:screenOrientation”
tools:replace=”android:theme”/>
with a lower priority declaration :
android:name=”com.foo.bar.ActivityOne”
android:screenOrientation=”landscape”
android:theme=”@olddogtheme”
android:exported=”true”/>
will result in :
android:name=”com.foo.bar.ActivityOne”
android:theme=”@theme1”
android:windowSoftInputMode=”stateUnchanged”/>
Note that if the lower priority declaration contained android:windowSoftInputMode or any attribute that is not explicitly tagged for removal or replacement, a build error need to be generated.
Element markers examples
Element Removal
To remove an element from any library, declare in the higher priority document
android:name=”foo.bar.alias”>
android:name=”zoo”
tools:node=”remove”/>
merged with :
android:name=”foo.bar.alias”>
android:name=”zoo”
android:value=”@string/bear”/>
will yield :
android:name=”foo.bar.alias”>
All Elements removal
To remove all elements of a certain type from any library, declare in the higher priority document
android:name=”foo.bar.alias”>
tools:node=”removeAll”/>
merged with :
android:name=”foo.bar.alias”>
android:name=”zoo”
android:value=”@string/bear”/>
android:name=”cage”
android:value=”@string/iron”/>
will yield :
android:name=”foo.bar.alias”
Element substitution
android:name=”foo.bar.alias”
tools:node=”replace”>
android:name=”zoo”/>
merged with:
android:name=”foo.bar.alias”>
android:name=”cage”
android:value=”@string/iron”/>
will yield:
android:name=”foo.bar.alias”>
android:name=”zoo”
tools:node=”remove”/>
Selector examples
Using the package name to select the library. Here we have 3 libraries merging into a main manifest file.
Main Manifest
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.example.main">
android:name="permissionOne"
tools:node="remove"
tools:selector="com.example.lib1">
tools:node="removeAll"
tools:selector="com.example.lib3">
android:name="permissionThree"
android:protectionLevel="signature"
tools:node="replace">
with library 1
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.lib1">
android:protectionLevel="signature">
android:protectionLevel="signature">
and library 2
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.lib2">
android:protectionLevel="normal">
android:protectionLevel="normal">
and library 3
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.lib2">
android:protectionLevel="normal">
will yield:
package="com.example.main" >
android:name="permissionThree"
android:protectionLevel="signature" >
android:name="permissionTwo"
android:protectionLevel="signature" >
android:name="permissionFour"
android:protectionLevel="normal" >