diff options
author | 2016-08-16 13:33:32 -0700 | |
---|---|---|
committer | 2016-08-17 11:01:05 -0700 | |
commit | a7cc3f1d79eb7a02a68928381998137f2f9b10a3 (patch) | |
tree | 23b7c77188e2a7d12271565becf65f4132c7b6e6 | |
parent | 58a00115d935ee44ac113ea68253e440dbd24087 (diff) |
AAPT2: Add support to strip namespaces from XML
The --no-xml-namespaces flag will strip namespace information from
compiled binary XML files in res/* (excluding res/raw/*) and
AndroidManifest.xml. It will also strip URI information from compiled
binary XML files in res/* (excluding res/raw/* and AndroidManifest.xml).
AndroidManifest.xml URI information is retained due to PackageParser, which
requires the Android URI for intent filters.
Bug: 29115919
Change-Id: I90cad6ed39ce02a69776f55314c1d4f38ad1aabe
-rw-r--r-- | tools/aapt2/Android.mk | 2 | ||||
-rw-r--r-- | tools/aapt2/link/Link.cpp | 21 | ||||
-rw-r--r-- | tools/aapt2/link/Linkers.h | 17 | ||||
-rw-r--r-- | tools/aapt2/link/XmlNamespaceRemover.cpp | 83 | ||||
-rw-r--r-- | tools/aapt2/link/XmlNamespaceRemover_test.cpp | 109 |
5 files changed, 232 insertions, 0 deletions
diff --git a/tools/aapt2/Android.mk b/tools/aapt2/Android.mk index 4f38e9435141..b52c530d03cb 100644 --- a/tools/aapt2/Android.mk +++ b/tools/aapt2/Android.mk @@ -40,6 +40,7 @@ sources := \ link/ReferenceLinker.cpp \ link/TableMerger.cpp \ link/VersionCollapser.cpp \ + link/XmlNamespaceRemover.cpp \ link/XmlReferenceLinker.cpp \ process/SymbolTable.cpp \ proto/ProtoHelpers.cpp \ @@ -89,6 +90,7 @@ testSources := \ link/ReferenceLinker_test.cpp \ link/TableMerger_test.cpp \ link/VersionCollapser_test.cpp \ + link/XmlNamespaceRemover_test.cpp \ link/XmlReferenceLinker_test.cpp \ process/SymbolTable_test.cpp \ proto/TableProtoSerializer_test.cpp \ diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp index 5a2bb6ad9a72..ea95dd1e6c2f 100644 --- a/tools/aapt2/link/Link.cpp +++ b/tools/aapt2/link/Link.cpp @@ -74,6 +74,7 @@ struct LinkOptions { bool generateNonFinalIds = false; std::vector<std::string> javadocAnnotations; bool outputToDirectory = false; + bool noXmlNamespaces = false; bool autoAddOverlay = false; bool doNotCompressAnything = false; std::unordered_set<std::string> extensionsToNotCompress; @@ -293,6 +294,7 @@ static std::unique_ptr<ResourceFile> loadFileExportHeader(const Source& source, struct ResourceFileFlattenerOptions { bool noAutoVersion = false; bool noVersionVectors = false; + bool noXmlNamespaces = false; bool keepRawValues = false; bool doNotCompressAnything = false; bool updateProguardSpec = false; @@ -382,6 +384,13 @@ bool ResourceFileFlattener::linkAndVersionXmlFile(const ResourceEntry* entry, return false; } + if (mOptions.noXmlNamespaces) { + XmlNamespaceRemover namespaceRemover; + if (!namespaceRemover.consume(mContext, outFileOp->xmlToFlatten.get())) { + return false; + } + } + if (!mOptions.noAutoVersion) { if (mOptions.noVersionVectors) { // Skip this if it is a vector or animated-vector. @@ -1296,6 +1305,7 @@ public: fileFlattenerOptions.extensionsToNotCompress = mOptions.extensionsToNotCompress; fileFlattenerOptions.noAutoVersion = mOptions.noAutoVersion; fileFlattenerOptions.noVersionVectors = mOptions.noVersionVectors; + fileFlattenerOptions.noXmlNamespaces = mOptions.noXmlNamespaces; fileFlattenerOptions.updateProguardSpec = static_cast<bool>(mOptions.generateProguardRulesPath); @@ -1594,6 +1604,14 @@ public: error = true; } } + + if (mOptions.noXmlNamespaces) { + // PackageParser will fail if URIs are removed from AndroidManifest.xml. + XmlNamespaceRemover namespaceRemover(true /* keepUris */); + if (!namespaceRemover.consume(mContext, manifestXml.get())) { + error = true; + } + } } else { error = true; } @@ -1732,6 +1750,9 @@ int link(const std::vector<StringPiece>& args) { .optionalSwitch("--output-to-dir", "Outputs the APK contents to a directory specified " "by -o", &options.outputToDirectory) + .optionalSwitch("--no-xml-namespaces", "Removes XML namespace prefix and URI " + "information from AndroidManifest.xml\nand XML binaries in res/*.", + &options.noXmlNamespaces) .optionalFlag("--min-sdk-version", "Default minimum SDK version to use for " "AndroidManifest.xml", &options.manifestFixerOptions.minSdkVersionDefault) diff --git a/tools/aapt2/link/Linkers.h b/tools/aapt2/link/Linkers.h index 43b8fb494f2c..82e28682f78e 100644 --- a/tools/aapt2/link/Linkers.h +++ b/tools/aapt2/link/Linkers.h @@ -86,6 +86,23 @@ struct PrivateAttributeMover : public IResourceTableConsumer { }; /** + * Removes namespace nodes and URI information from the XmlResource. + * + * Once an XmlResource is processed by this consumer, it is no longer able to have its attributes + * parsed. As such, this XmlResource must have already been processed by XmlReferenceLinker. + */ +class XmlNamespaceRemover : public IXmlResourceConsumer { +private: + bool mKeepUris; + +public: + XmlNamespaceRemover(bool keepUris = false) : mKeepUris(keepUris) { + }; + + bool consume(IAaptContext* context, xml::XmlResource* resource) override; +}; + +/** * Resolves attributes in the XmlResource and compiles string values to resource values. * Once an XmlResource is processed by this linker, it is ready to be flattened. */ diff --git a/tools/aapt2/link/XmlNamespaceRemover.cpp b/tools/aapt2/link/XmlNamespaceRemover.cpp new file mode 100644 index 000000000000..9f95177537ce --- /dev/null +++ b/tools/aapt2/link/XmlNamespaceRemover.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2016 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. + */ + +#include "ResourceTable.h" +#include "link/Linkers.h" + +#include <algorithm> + +namespace aapt { + +namespace { + +/** + * Visits each xml Node, removing URI references and nested namespaces. + */ +class XmlVisitor : public xml::Visitor { +public: + XmlVisitor(bool keepUris) : mKeepUris(keepUris) { + } + + void visit(xml::Element* el) override { + // Strip namespaces + for (auto& child : el->children) { + while (child && xml::nodeCast<xml::Namespace>(child.get())) { + if (child->children.empty()) { + child = {}; + } else { + child = std::move(child->children.front()); + child->parent = el; + } + } + } + el->children.erase(std::remove_if(el->children.begin(), el->children.end(), + [](const std::unique_ptr<xml::Node>& child) -> bool { + return child == nullptr; + }), el->children.end()); + + if (!mKeepUris) { + for (xml::Attribute& attr : el->attributes) { + attr.namespaceUri = std::string(); + } + el->namespaceUri = std::string(); + } + xml::Visitor::visit(el); + } + +private: + bool mKeepUris; +}; + +} // namespace + +bool XmlNamespaceRemover::consume(IAaptContext* context, xml::XmlResource* resource) { + if (!resource->root) { + return false; + } + // Replace any root namespaces until the root is a non-namespace node + while (xml::nodeCast<xml::Namespace>(resource->root.get())) { + if (resource->root->children.empty()) { + break; + } + resource->root = std::move(resource->root->children.front()); + resource->root->parent = nullptr; + } + XmlVisitor visitor(mKeepUris); + resource->root->accept(&visitor); + return true; +} + +} // namespace aapt diff --git a/tools/aapt2/link/XmlNamespaceRemover_test.cpp b/tools/aapt2/link/XmlNamespaceRemover_test.cpp new file mode 100644 index 000000000000..e72ea4391707 --- /dev/null +++ b/tools/aapt2/link/XmlNamespaceRemover_test.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2016 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. + */ + +#include "link/Linkers.h" +#include "test/Test.h" + +namespace aapt { + +class XmlUriTestVisitor : public xml::Visitor { +public: + void visit(xml::Element* el) override { + for (const auto& attr : el->attributes) { + EXPECT_EQ(std::string(), attr.namespaceUri); + } + EXPECT_EQ(std::string(), el->namespaceUri); + xml::Visitor::visit(el); + } + + void visit(xml::Namespace* ns) override { + EXPECT_EQ(std::string(), ns->namespaceUri); + xml::Visitor::visit(ns); + } +}; + +class XmlNamespaceTestVisitor : public xml::Visitor { +public: + void visit(xml::Namespace* ns) override { + ADD_FAILURE() << "Detected namespace: " + << ns->namespacePrefix << "=\"" << ns->namespaceUri << "\""; + xml::Visitor::visit(ns); + } +}; + +class XmlNamespaceRemoverTest : public ::testing::Test { +public: + void SetUp() override { + mContext = test::ContextBuilder() + .setCompilationPackage("com.app.test") + .build(); + } + +protected: + std::unique_ptr<IAaptContext> mContext; +}; + +TEST_F(XmlNamespaceRemoverTest, RemoveUris) { + std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF( + <View xmlns:android="http://schemas.android.com/apk/res/android" + android:text="hello" />)EOF"); + + XmlNamespaceRemover remover; + ASSERT_TRUE(remover.consume(mContext.get(), doc.get())); + + xml::Node* root = doc.get()->root.get(); + ASSERT_NE(root, nullptr); + + XmlUriTestVisitor visitor; + root->accept(&visitor); +} + +TEST_F(XmlNamespaceRemoverTest, RemoveNamespaces) { + std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF( + <View xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:foo="http://schemas.android.com/apk/res/foo" + foo:bar="foobar" + android:text="hello" />)EOF"); + + XmlNamespaceRemover remover; + ASSERT_TRUE(remover.consume(mContext.get(), doc.get())); + + xml::Node* root = doc.get()->root.get(); + ASSERT_NE(root, nullptr); + + XmlNamespaceTestVisitor visitor; + root->accept(&visitor); +} + +TEST_F(XmlNamespaceRemoverTest, RemoveNestedNamespaces) { + std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF( + <View xmlns:android="http://schemas.android.com/apk/res/android" + android:text="hello"> + <View xmlns:foo="http://schemas.example.com/foo" + android:text="foo"/> + </View>)EOF"); + + XmlNamespaceRemover remover; + ASSERT_TRUE(remover.consume(mContext.get(), doc.get())); + + xml::Node* root = doc.get()->root.get(); + ASSERT_NE(root, nullptr); + + XmlNamespaceTestVisitor visitor; + root->accept(&visitor); +} + +} // namespace aapt |