Manifest Merger
This new merger was introduced in version 0.10 of the plugin. As of 0.11, this tool is used by default by the gradle plugin.
In order to revert to the old manifest merger, please add to your build.gradle the following configuration :
android {
useOldManifestMerger true } Manifest files orderingIn general, there are three types of manifest files that need to be merged into a single resulting app manifest, here in priority order :
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 :
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
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 :
Android Manifest file mergingEach 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 processImplicit declarationsA number of attributes can have default values (defined in the online docs 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:
Automatic upgradesWhen 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 supportWhen 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 : <activity android:name=".Main"> <intent-filter> <action android:name="${applicationId}.foo"> </action> </intent-filter> </activity> with the following gradle declaration : android { compileSdkVersion 19 buildToolsVersion "19.0.2" productFlavors { flavor1 { applicationId = "com.android.tests.flavorlib.app.flavor1" } } Once merged, the <action android:name> will be <action android:name=“com.android.tests.flavorlib.app.flavor1.foo”> 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 :
<activity android:name=".MainActivity"
android:label="${activityLabel}" >
Generic description of merging strategiesMerging 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: <activity android:name=”com.foo.bar.ActivityOne” android:theme=”@theme1”/> merging with the following declaration will not generate a conflict: <activity android:name=”com.foo.bar.ActivityOne” android:screenOrientation=”landscape/> same for <activity 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. <activity android:name=”com.foo.bar.ActivityOne” android:theme=”@theme1” android:screenOrientation=”landscape/> however, <activity 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. <activity 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. MarkersA 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 : xmlns:tools="http://schemas.android.com/tools" Example: <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.tests.flavorlib.app" xmlns:tools="http://schemas.android.com/tools"> <application android:icon="@drawable/icon" android:label="@string/app_name" tools:replace=”icon, label”/> </manifest> 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 markersA tools:node=”maker_value” attribute should be present on the node which is in conflict and need resolution.
tools:attr markersThere can be be many attribute related markers on any particular element to resolve all attributes in conflict.
SelectorEach 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: <permission 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 :
<uses-sdk android:targetSdkVersion="14" android:minSdkVersion="2"
tools:overrideLibrary="com.example.lib1, com.example.lib2"/>
will allow the library with the following manifest to be imported without error :
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.lib1"> <uses-sdk android:minSdkVersion="4" /> </manifest> LoggingEach operation/decision made during the manifest merger needs to be
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 Messagemessage=file:line_number:colum_number severity:\ndescription description=(\tTEXT\n)*
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 <activity> element at AndroidManifest.xml:1:5 to override NodeRecord
node-type#node-key\n \t(node_action:Action)* \t\t(attribute_action:Action)* Action format
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:label ADDED from ManifestMerger2Test0_main.xml:61:13 android:icon ADDED from ManifestMerger2Test0_main.xml:62:13 android:name ADDED from ManifestMerger2Test0_main.xml:63:13 Build errorWhen 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 <activity android:name=”com.foo.bar.ActivityOne” android:screenOrientation=”portrait” android:theme=”@theme1”/> with a lower priority declaration : <activity 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 <activity> element at AndroidManifest.xml:1:5 to override BlameA “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 PoliciesEach 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 <application> elements per AndroidManifest.xml. Most keyed elements use the android:name attribute to express their key value, others MergeNon conflicting attributes are merged and children are merged according to their respective merging policy. Merge Children OnlyAttributes are not merged, only children are merged according to their respective merging policy. AlwaysElements are always kept “as is” and added to the common parent in the resulting merged file. List of element merging policy and key
Package name smart substitutionSome 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 : <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.app1"> <application> <activity android:name=".Main" /> </application> </manifest> will be expanded into : <application> <activity android:name="com.example.app1.Main" /> </application> 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 <activity android:name=”com.example.app1.Main”> If you need the injected value as the expanded attribute value use ${applicationId} placeholder, for example : <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.app1"> <application> <activity android:name="${applicationId}.Main" /> </application> </manifest> List of package dependent attributes that can use this smart substitution facility :
Attributes markers examplesOverride an attribute coming from a libraryUsing tools:replace=”x, y, z” will override x,y,z attributes from the imported library’s activity XML declarations. Higher Priority declaration <activity android:name=”com.foo.bar.ActivityOne” android:screenOrientation=”portrait” android:theme=”@theme1” tools:replace=”theme”/> with a lower priority declaration : <activity android:name=”com.foo.bar.ActivityOne” android:theme=”@olddogtheme” android:windowSoftInputMode=”stateUnchanged” android:exported=”true”> will result in : <activity 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 <activity android:name=”com.foo.bar.ActivityOne” android:hardwareAccelerated=”true” tools:remove=”android:theme,android:screenOrientation”/> with a lower priority declaration : <activity android:name=”com.foo.bar.ActivityOne” android:screenOrientation=”landscape” android:theme=”@olddogtheme” android:windowSoftInputMode=”stateUnchanged” android:exported=”true”/> will result in : <activity android:name=”com.foo.bar.ActivityOne” android:hardwareAccelerated=”true” android:windowSoftInputMode=”stateUnchanged” android:exported=”true”/> Enforcing an attribute valueImplicitly, 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, a higher priority declaration <activity android:name=”com.foo.bar.ActivityOne” android:theme=”@newdogtheme”/> with a lower priority declaration : <activity android:name=”com.foo.bar.ActivityOne” android:theme=”@olddogtheme”/> and a higher priority declaration <activity android:name=”com.foo.bar.ActivityOne” android:theme=”@newdogtheme” tools:strict=”theme”/> with a lower priority declaration : <activity 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 operationsIf 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 : <activity 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 : <activity android:name=”com.foo.bar.ActivityOne” android:screenOrientation=”landscape” android:theme=”@olddogtheme” android:exported=”true”/> will result in : <activity 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 examplesElement RemovalTo remove an element from any library, declare in the higher priority document <activity-alias android:name=”foo.bar.alias”> <meta-data android:name=”zoo” tools:node=”remove”/> </activity-alias> merged with : <activity-alias android:name=”foo.bar.alias”> <meta-data android:name=”zoo” android:value=”@string/bear”/> </activity-alias> will yield : <activity-alias android:name=”foo.bar.alias”> </activity-alias> All Elements removalTo remove all elements of a certain type from any library, declare in the higher priority document <activity-alias android:name=”foo.bar.alias”> <meta-data tools:node=”removeAll”/> </activity-alias> merged with : <activity-alias android:name=”foo.bar.alias”> <meta-data android:name=”zoo” android:value=”@string/bear”/> <meta-data android:name=”cage” android:value=”@string/iron”/> </activity-alias> will yield : <activity-alias android:name=”foo.bar.alias” </activity-alias> Element substitution<activity-alias android:name=”foo.bar.alias” tools:node=”replace”> <meta-data android:name=”zoo”/> </activity-alias> merged with: <activity-alias android:name=”foo.bar.alias”> <meta-data android:name=”cage” android:value=”@string/iron”/> </activity-alias> will yield: <activity-alias android:name=”foo.bar.alias”> <meta-data android:name=”zoo” tools:node=”remove”/> </activity-alias> Selector examplesUsing the package name to select the library. Here we have 3 libraries merging into a main manifest file. Main Manifest<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" package="com.example.main"> <permission android:name="permissionOne" tools:node="remove" tools:selector="com.example.lib1"> </permission> <permission tools:node="removeAll" tools:selector="com.example.lib3"> </permission> <permission android:name="permissionThree" android:protectionLevel="signature" tools:node="replace"> </permission> </manifest> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.lib1"> <permission android:name="permissionOne" android:protectionLevel="signature"> </permission> <permission android:name="permissionTwo" android:protectionLevel="signature"> </permission> </manifest> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.lib2"> <permission android:name="permissionThree" android:protectionLevel="normal"> </permission> <permission android:name="permissionFour" android:protectionLevel="normal"> </permission> </manifest> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.lib2"> <permission android:name="permissionFive" android:protectionLevel="normal"> </permission> </manifest> will yield: <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.main" > <permission android:name="permissionThree" android:protectionLevel="signature" > </permission> <permission android:name="permissionTwo" android:protectionLevel="signature" > </permission> <permission android:name="permissionFour" android:protectionLevel="normal" > </permission> </manifest> |