blob: 58ddf72236f9dc80240f8c6958a72fcc6903380b [file] [log] [blame]
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.manifmerger;
import static com.android.SdkConstants.ANDROID_URI;
import com.android.SdkConstants;
import com.android.annotations.NonNull;
import com.android.xml.AndroidManifest;
import java.util.Map;
import java.util.NavigableMap;
import java.util.TreeMap;
import org.w3c.dom.Attr;
/**
* Trims the document from unwanted, repeated elements.
*/
public class ElementsTrimmer {
/**
* Trims unwanted, duplicated elements from the merged document.
* <p>
* Current trimmed elements are :
* <ul>
* <li>uses-features with glEsVersion key
* <ul>
* <li>The highest 1.x version element will be kept regardless of 'required' flag value</li>
* <li>If the above element is present and has a 'false' required flag, there can be at most
* one element of a lesser version with 'required' attribute set to true.</li>
* <li>The highest 2.x or superior element will be kept regardless of 'required' flag value
* </li>
* <li>If the above element is present and has a 'false' required flag, there can be at
* most one element of a lesser version (but higher than 2.0) with a 'required' attribute
* set to true.</li>
* </ul>
* </li>
* </ul>
*
* @param xmlDocument the xml document to trim.
* @param mergingReport the report to log errors and actions.
*/
public static void trim(
@NonNull XmlDocument xmlDocument,
@NonNull MergingReport.Builder mergingReport) {
// I sort the glEsVersion declaration by value.
NavigableMap<Integer, XmlElement> glEsVersionDeclarations = new TreeMap<Integer, XmlElement>();
for (XmlElement childElement : xmlDocument.getRootNode().getMergeableElements()) {
if (childElement.getType().equals(ManifestModel.NodeTypes.USES_FEATURE)) {
Integer value = getGlEsVersion(childElement);
if (value != null) {
glEsVersionDeclarations.put(value, childElement);
}
}
}
// now eliminate all unwanted declarations, revert the sorted map, so we get the
// higher elements first.
glEsVersionDeclarations = glEsVersionDeclarations.descendingMap();
boolean doneWithAboveTwoTrue = false;
boolean doneWithAboveTwoFalse = false;
boolean doneWithBelowTwoTrue = false;
boolean doneWithBelowTwoFalse = false;
for (Map.Entry<Integer, XmlElement> glEsVersionDeclaration :
glEsVersionDeclarations.entrySet()) {
boolean removeElement;
Attr requiredAttribute = glEsVersionDeclaration.getValue().getXml().getAttributeNodeNS(
ANDROID_URI, AndroidManifest.ATTRIBUTE_REQUIRED);
boolean isRequired = requiredAttribute == null ||
Boolean.parseBoolean(requiredAttribute.getValue());
if (glEsVersionDeclaration.getKey() < 0x20000) {
// version one.
removeElement = (doneWithBelowTwoFalse && doneWithBelowTwoTrue)
|| (isRequired && doneWithBelowTwoTrue)
|| (!isRequired && doneWithBelowTwoFalse);
if (!removeElement) {
doneWithBelowTwoFalse = true;
doneWithBelowTwoTrue = isRequired;
}
} else {
// version two or above.
removeElement = (doneWithAboveTwoFalse && doneWithAboveTwoTrue)
|| (isRequired && doneWithAboveTwoTrue)
|| (!isRequired && doneWithAboveTwoFalse);
if (!removeElement) {
doneWithAboveTwoFalse = true;
doneWithAboveTwoTrue = isRequired;
}
}
if (removeElement) {
// if the node only contains glEsVersion, then remove the entire node,
// if it also contains android:name, just remove the glEsVersion attribute
if (glEsVersionDeclaration.getValue().getXml().getAttributeNodeNS(ANDROID_URI,
SdkConstants.ATTR_NAME) != null) {
glEsVersionDeclaration.getValue().getXml().removeAttributeNS(ANDROID_URI,
AndroidManifest.ATTRIBUTE_GLESVERSION);
mergingReport.getActionRecorder().recordAttributeAction(
glEsVersionDeclaration.getValue().getAttribute(XmlNode.fromXmlName(
"android:" + AndroidManifest.ATTRIBUTE_GLESVERSION)).get(),
Actions.ActionType.REJECTED,
null /* attributeOperationType */);
} else {
xmlDocument.getRootNode().getXml().removeChild(
glEsVersionDeclaration.getValue().getXml());
mergingReport.getActionRecorder().recordNodeAction(
glEsVersionDeclaration.getValue(),
Actions.ActionType.REJECTED);
}
}
}
}
private static Integer getGlEsVersion(@NonNull XmlElement xmlElement) {
Attr glEsVersion = xmlElement.getXml()
.getAttributeNodeNS(ANDROID_URI, AndroidManifest.ATTRIBUTE_GLESVERSION);
if (glEsVersion == null) {
return null;
}
return getHexValue(glEsVersion);
}
private static Integer getHexValue(@NonNull Attr attribute) {
return Integer.decode(attribute.getValue());
}
}