Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 75f3a55c authored by Adam Lesinski's avatar Adam Lesinski
Browse files

AAPT2: Change xml file parsing to DOM based

We modify the XML of layouts and AndroidManifest enough
that it warrants we operate on the tree in memory.
These files are never very large so this should be fine.

Change-Id: I5d597abdb3fca2a203cf7c0b40fcd926aecb3137
parent 4573dddc
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -27,7 +27,6 @@ main := Main.cpp
sources := \
	BigBuffer.cpp \
	BinaryResourceParser.cpp \
	BinaryXmlPullParser.cpp \
	BindingXmlPullParser.cpp \
	ConfigDescription.cpp \
	Debug.cpp \
@@ -53,6 +52,7 @@ sources := \
	ScopedXmlPullParser.cpp \
	SourceXmlPullParser.cpp \
	XliffXmlPullParser.cpp \
	XmlDom.cpp \
	XmlFlattener.cpp \
	ZipEntry.cpp \
	ZipFile.cpp
@@ -76,6 +76,7 @@ testSources := \
	StringPool_test.cpp \
	Util_test.cpp \
	XliffXmlPullParser_test.cpp \
	XmlDom_test.cpp \
	XmlFlattener_test.cpp

cIncludes := \
+0 −259
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 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 "BinaryXmlPullParser.h"
#include "Maybe.h"
#include "Util.h"

#include <androidfw/ResourceTypes.h>
#include <memory>
#include <string>
#include <vector>

namespace aapt {

static XmlPullParser::Event codeToEvent(android::ResXMLParser::event_code_t code) {
    switch (code) {
        case android::ResXMLParser::START_DOCUMENT:
            return XmlPullParser::Event::kStartDocument;
        case android::ResXMLParser::END_DOCUMENT:
            return XmlPullParser::Event::kEndDocument;
        case android::ResXMLParser::START_NAMESPACE:
            return XmlPullParser::Event::kStartNamespace;
        case android::ResXMLParser::END_NAMESPACE:
            return XmlPullParser::Event::kEndNamespace;
        case android::ResXMLParser::START_TAG:
            return XmlPullParser::Event::kStartElement;
        case android::ResXMLParser::END_TAG:
            return XmlPullParser::Event::kEndElement;
        case android::ResXMLParser::TEXT:
            return XmlPullParser::Event::kText;
        default:
            break;
    }
    return XmlPullParser::Event::kBadDocument;
}

BinaryXmlPullParser::BinaryXmlPullParser(const std::shared_ptr<android::ResXMLTree>& parser)
    : mParser(parser), mEvent(Event::kStartDocument), mHasComment(false), sEmpty(), sEmpty8(),
      mDepth(0) {
}

XmlPullParser::Event BinaryXmlPullParser::next() {
    mStr1.clear();
    mStr2.clear();
    mAttributes.clear();

    android::ResXMLParser::event_code_t code;
    if (mHasComment) {
        mHasComment = false;
        code = mParser->getEventType();
    } else {
        code = mParser->next();
        if (code != android::ResXMLParser::BAD_DOCUMENT) {
            size_t len;
            const char16_t* comment = mParser->getComment(&len);
            if (comment) {
                mHasComment = true;
                mStr1.assign(comment, len);
                return XmlPullParser::Event::kComment;
            }
        }
    }

    size_t len;
    const char16_t* data;
    mEvent = codeToEvent(code);
    switch (mEvent) {
        case Event::kStartNamespace:
        case Event::kEndNamespace: {
            data = mParser->getNamespacePrefix(&len);
            if (data) {
                mStr1.assign(data, len);
            } else {
                mStr1.clear();
            }
            data = mParser->getNamespaceUri(&len);
            if (data) {
                mStr2.assign(data, len);
            } else {
                mStr2.clear();
            }

            Maybe<std::u16string> result = util::extractPackageFromNamespace(mStr2);
            if (result) {
                if (mEvent == Event::kStartNamespace) {
                    mPackageAliases.emplace_back(mStr1, result.value());
                } else {
                    assert(mPackageAliases.back().second == result.value());
                    mPackageAliases.pop_back();
                }
            }
            break;
        }

        case Event::kStartElement:
            copyAttributes();
            // fallthrough

        case Event::kEndElement:
            data = mParser->getElementNamespace(&len);
            if (data) {
                mStr1.assign(data, len);
            } else {
                mStr1.clear();
            }
            data = mParser->getElementName(&len);
            if (data) {
                mStr2.assign(data, len);
            } else {
                mStr2.clear();
            }
            break;

        case Event::kText:
            data = mParser->getText(&len);
            if (data) {
                mStr1.assign(data, len);
            } else {
                mStr1.clear();
            }
            break;

        default:
            break;
    }
    return mEvent;
}

XmlPullParser::Event BinaryXmlPullParser::getEvent() const {
    if (mHasComment) {
        return XmlPullParser::Event::kComment;
    }
    return mEvent;
}

const std::string& BinaryXmlPullParser::getLastError() const {
    return sEmpty8;
}

const std::u16string& BinaryXmlPullParser::getComment() const {
    if (mHasComment) {
        return mStr1;
    }
    return sEmpty;
}

size_t BinaryXmlPullParser::getLineNumber() const {
    return mParser->getLineNumber();
}

size_t BinaryXmlPullParser::getDepth() const {
    return mDepth;
}

const std::u16string& BinaryXmlPullParser::getText() const {
    if (!mHasComment && mEvent == XmlPullParser::Event::kText) {
        return mStr1;
    }
    return sEmpty;
}

const std::u16string& BinaryXmlPullParser::getNamespacePrefix() const {
    if (!mHasComment && (mEvent == XmlPullParser::Event::kStartNamespace ||
            mEvent == XmlPullParser::Event::kEndNamespace)) {
        return mStr1;
    }
    return sEmpty;
}

const std::u16string& BinaryXmlPullParser::getNamespaceUri() const {
    if (!mHasComment && (mEvent == XmlPullParser::Event::kStartNamespace ||
            mEvent == XmlPullParser::Event::kEndNamespace)) {
        return mStr2;
    }
    return sEmpty;
}

bool BinaryXmlPullParser::applyPackageAlias(std::u16string* package,
                                            const std::u16string& defaultPackage) const {
    const auto endIter = mPackageAliases.rend();
    for (auto iter = mPackageAliases.rbegin(); iter != endIter; ++iter) {
        if (iter->first == *package) {
            if (iter->second.empty()) {
                *package = defaultPackage;
            } else {
                *package = iter->second;
            }
            return true;
        }
    }
    return false;
}

const std::u16string& BinaryXmlPullParser::getElementNamespace() const {
    if (!mHasComment && (mEvent == XmlPullParser::Event::kStartElement ||
            mEvent == XmlPullParser::Event::kEndElement)) {
        return mStr1;
    }
    return sEmpty;
}

const std::u16string& BinaryXmlPullParser::getElementName() const {
    if (!mHasComment && (mEvent == XmlPullParser::Event::kStartElement ||
            mEvent == XmlPullParser::Event::kEndElement)) {
        return mStr2;
    }
    return sEmpty;
}

size_t BinaryXmlPullParser::getAttributeCount() const {
    return mAttributes.size();
}

XmlPullParser::const_iterator BinaryXmlPullParser::beginAttributes() const {
    return mAttributes.begin();
}

XmlPullParser::const_iterator BinaryXmlPullParser::endAttributes() const {
    return mAttributes.end();
}

void BinaryXmlPullParser::copyAttributes() {
    const size_t attrCount = mParser->getAttributeCount();
    if (attrCount > 0) {
        mAttributes.reserve(attrCount);
        for (size_t i = 0; i < attrCount; i++) {
            XmlPullParser::Attribute attr;
            size_t len;
            const char16_t* str = mParser->getAttributeNamespace(i, &len);
            if (str) {
                attr.namespaceUri.assign(str, len);
            }
            str = mParser->getAttributeName(i, &len);
            if (str) {
                attr.name.assign(str, len);
            }
            str = mParser->getAttributeStringValue(i, &len);
            if (str) {
                attr.value.assign(str, len);
            }
            mAttributes.push_back(std::move(attr));
        }
    }
}

} // namespace aapt

tools/aapt2/BinaryXmlPullParser.h

deleted100644 → 0
+0 −76
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 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.
 */

#ifndef AAPT_BINARY_XML_PULL_PARSER_H
#define AAPT_BINARY_XML_PULL_PARSER_H

#include "XmlPullParser.h"

#include <androidfw/ResourceTypes.h>
#include <memory>
#include <string>
#include <vector>

namespace aapt {

/**
 * Wraps a ResTable into the canonical XmlPullParser interface.
 */
class BinaryXmlPullParser : public XmlPullParser {
public:
    BinaryXmlPullParser(const std::shared_ptr<android::ResXMLTree>& parser);
    BinaryXmlPullParser(const BinaryXmlPullParser& rhs) = delete;

    Event getEvent() const override;
    const std::string& getLastError() const override;
    Event next() override;

    const std::u16string& getComment() const override;
    size_t getLineNumber() const override;
    size_t getDepth() const override;

    const std::u16string& getText() const override;

    const std::u16string& getNamespacePrefix() const override;
    const std::u16string& getNamespaceUri() const override;
    bool applyPackageAlias(std::u16string* package, const std::u16string& defaultpackage)
            const override;

    const std::u16string& getElementNamespace() const override;
    const std::u16string& getElementName() const override;

    const_iterator beginAttributes() const override;
    const_iterator endAttributes() const override;
    size_t getAttributeCount() const override;

private:
    void copyAttributes();

    std::shared_ptr<android::ResXMLTree> mParser;
    std::u16string mStr1;
    std::u16string mStr2;
    std::vector<Attribute> mAttributes;
    Event mEvent;
    bool mHasComment;
    const std::u16string sEmpty;
    const std::string sEmpty8;
    size_t mDepth;
    std::vector<std::pair<std::u16string, std::u16string>> mPackageAliases;
};

} // namespace aapt

#endif // AAPT_BINARY_XML_PULL_PARSER_H
+65 −63
Original line number Diff line number Diff line
@@ -17,7 +17,6 @@
#include "AppInfo.h"
#include "BigBuffer.h"
#include "BinaryResourceParser.h"
#include "BinaryXmlPullParser.h"
#include "BindingXmlPullParser.h"
#include "Debug.h"
#include "Files.h"
@@ -128,7 +127,7 @@ void versionStylesForCompat(const std::shared_ptr<ResourceTable>& table) {
                    auto iter = style.entries.begin();
                    while (iter != style.entries.end()) {
                        if (iter->key.name.package == u"android") {
                            size_t sdkLevel = findAttributeSdkLevel(iter->key.name.entry);
                            size_t sdkLevel = findAttributeSdkLevel(iter->key.name);
                            if (sdkLevel > 1 && sdkLevel > configValue.config.sdkVersion) {
                                // Record that we are about to strip this.
                                stripped.emplace_back(std::move(*iter));
@@ -300,6 +299,42 @@ struct AaptOptions {
    ResourceName dumpStyleTarget;
};

struct IdCollector : public xml::Visitor {
    IdCollector(const Source& source, const std::shared_ptr<ResourceTable>& table) :
            mSource(source), mTable(table) {
    }

    virtual void visit(xml::Text* node) override {}

    virtual void visit(xml::Namespace* node) override {
        for (const auto& child : node->children) {
            child->accept(this);
        }
    }

    virtual void visit(xml::Element* node) override {
        for (const xml::Attribute& attr : node->attributes) {
            bool create = false;
            bool priv = false;
            ResourceNameRef nameRef;
            if (ResourceParser::tryParseReference(attr.value, &nameRef, &create, &priv)) {
                if (create) {
                    mTable->addResource(nameRef, {}, mSource.line(node->lineNumber),
                                        util::make_unique<Id>());
                }
            }
        }

        for (const auto& child : node->children) {
            child->accept(this);
        }
    }

private:
    Source mSource;
    std::shared_ptr<ResourceTable> mTable;
};

bool compileXml(const AaptOptions& options, const std::shared_ptr<ResourceTable>& table,
                const CompileItem& item, ZipFile* outApk) {
    std::ifstream in(item.source.path, std::ifstream::binary);
@@ -308,20 +343,19 @@ bool compileXml(const AaptOptions& options, const std::shared_ptr<ResourceTable>
        return false;
    }

    BigBuffer outBuffer(1024);

    // No resolver, since we are not compiling attributes here.
    XmlFlattener flattener(table, {});

    XmlFlattener::Options xmlOptions;
    xmlOptions.defaultPackage = table->getPackage();
    xmlOptions.keepRawValues = true;
    SourceLogger logger(item.source);
    std::unique_ptr<xml::Node> root = xml::inflate(&in, &logger);
    if (!root) {
        return false;
    }

    std::shared_ptr<XmlPullParser> parser = std::make_shared<SourceXmlPullParser>(in);
    // Collect any resource ID's declared here.
    IdCollector idCollector(item.source, table);
    root->accept(&idCollector);

    Maybe<size_t> minStrippedSdk = flattener.flatten(item.source, parser, &outBuffer,
                                                     xmlOptions);
    if (!minStrippedSdk) {
    BigBuffer outBuffer(1024);
    if (!xml::flatten(root.get(), options.appInfo.package, &outBuffer)) {
        logger.error() << "failed to encode XML." << std::endl;
        return false;
    }

@@ -369,19 +403,13 @@ bool shouldGenerateVersionedResource(const std::shared_ptr<const ResourceTable>&
bool linkXml(const AaptOptions& options, const std::shared_ptr<ResourceTable>& table,
             const std::shared_ptr<IResolver>& resolver, const LinkItem& item,
             const void* data, size_t dataLen, ZipFile* outApk, std::queue<LinkItem>* outQueue) {
    std::shared_ptr<android::ResXMLTree> tree = std::make_shared<android::ResXMLTree>();
    if (tree->setTo(data, dataLen, false) != android::NO_ERROR) {
    SourceLogger logger(item.source);
    std::unique_ptr<xml::Node> root = xml::inflate(data, dataLen, &logger);
    if (!root) {
        return false;
    }

    std::shared_ptr<XmlPullParser> parser = std::make_shared<BinaryXmlPullParser>(tree);

    BigBuffer outBuffer(1024);
    XmlFlattener flattener({}, resolver);

    XmlFlattener::Options xmlOptions;
    xmlOptions.defaultPackage = item.originalPackage;

    xml::FlattenOptions xmlOptions;
    if (options.packageType == AaptOptions::PackageType::StaticLibrary) {
        xmlOptions.keepRawValues = true;
    }
@@ -392,16 +420,12 @@ bool linkXml(const AaptOptions& options, const std::shared_ptr<ResourceTable>& t
        xmlOptions.maxSdkAttribute = item.config.sdkVersion ? item.config.sdkVersion : 1;
    }

    std::shared_ptr<BindingXmlPullParser> binding;
    if (item.name.type == ResourceType::kLayout) {
        // Layouts may have defined bindings, so we need to make sure they get processed.
        binding = std::make_shared<BindingXmlPullParser>(parser);
        parser = binding;
    }

    Maybe<size_t> minStrippedSdk = flattener.flatten(item.source, parser, &outBuffer,
                                                     xmlOptions);
    BigBuffer outBuffer(1024);
    Maybe<size_t> minStrippedSdk = xml::flattenAndLink(item.source, root.get(),
                                                       item.originalPackage, resolver,
                                                       xmlOptions, &outBuffer);
    if (!minStrippedSdk) {
        logger.error() << "failed to encode XML." << std::endl;
        return false;
    }

@@ -431,30 +455,6 @@ bool linkXml(const AaptOptions& options, const std::shared_ptr<ResourceTable>& t
                                      << buildFileReference(item) << "' to apk." << std::endl;
        return false;
    }

    if (binding && !options.bindingOutput.path.empty()) {
        // We generated a binding xml file, write it out.
        Source bindingOutput = options.bindingOutput;
        appendPath(&bindingOutput.path, buildFileReference(item));

        if (!mkdirs(bindingOutput.path)) {
            Logger::error(bindingOutput) << strerror(errno) << std::endl;
            return false;
        }

        appendPath(&bindingOutput.path, "bind.xml");

        std::ofstream bout(bindingOutput.path);
        if (!bout) {
            Logger::error(bindingOutput) << strerror(errno) << std::endl;
            return false;
        }

        if (!binding->writeToFile(bout)) {
            Logger::error(bindingOutput) << strerror(errno) << std::endl;
            return false;
        }
    }
    return true;
}

@@ -504,13 +504,15 @@ bool compileManifest(const AaptOptions& options, const std::shared_ptr<IResolver
        return false;
    }

    BigBuffer outBuffer(1024);
    std::shared_ptr<XmlPullParser> xmlParser = std::make_shared<SourceXmlPullParser>(in);
    XmlFlattener flattener({}, resolver);
    SourceLogger logger(options.manifest);
    std::unique_ptr<xml::Node> root = xml::inflate(&in, &logger);
    if (!root) {
        return false;
    }

    XmlFlattener::Options xmlOptions;
    xmlOptions.defaultPackage = options.appInfo.package;
    if (!flattener.flatten(options.manifest, xmlParser, &outBuffer, xmlOptions)) {
    BigBuffer outBuffer(1024);
    if (!xml::flattenAndLink(options.manifest, root.get(), options.appInfo.package, resolver, {},
                &outBuffer)) {
        return false;
    }

+47 −3
Original line number Diff line number Diff line
@@ -14,11 +14,51 @@
 * limitations under the License.
 */

#include "SdkConstants.h"

#include <algorithm>
#include <string>
#include <unordered_map>
#include <vector>

namespace aapt {

static const std::vector<std::pair<uint16_t, size_t>> sAttrIdMap = {
    { 0x021c, 1 },
    { 0x021d, 2 },
    { 0x0269, SDK_CUPCAKE },
    { 0x028d, SDK_DONUT },
    { 0x02ad, SDK_ECLAIR },
    { 0x02b3, SDK_ECLAIR_0_1 },
    { 0x02b5, SDK_ECLAIR_MR1 },
    { 0x02bd, SDK_FROYO },
    { 0x02cb, SDK_GINGERBREAD },
    { 0x0361, SDK_HONEYCOMB },
    { 0x0366, SDK_HONEYCOMB_MR1 },
    { 0x03a6, SDK_HONEYCOMB_MR2 },
    { 0x03ae, SDK_JELLY_BEAN },
    { 0x03cc, SDK_JELLY_BEAN_MR1 },
    { 0x03da, SDK_JELLY_BEAN_MR2 },
    { 0x03f1, SDK_KITKAT },
    { 0x03f6, SDK_KITKAT_WATCH },
    { 0x04ce, SDK_LOLLIPOP },
};

static bool lessEntryId(const std::pair<uint16_t, size_t>& p, uint16_t entryId) {
    return p.first < entryId;
}

size_t findAttributeSdkLevel(ResourceId id) {
    if (id.packageId() != 0x01 && id.typeId() != 0x01) {
        return 0;
    }
    auto iter = std::lower_bound(sAttrIdMap.begin(), sAttrIdMap.end(), id.entryId(), lessEntryId);
    if (iter == sAttrIdMap.end()) {
        return SDK_LOLLIPOP_MR1;
    }
    return iter->second;
}

static const std::unordered_map<std::u16string, size_t> sAttrMap = {
    { u"marqueeRepeatLimit", 2 },
    { u"windowNoDisplay", 3 },
@@ -682,12 +722,16 @@ static const std::unordered_map<std::u16string, size_t> sAttrMap = {
    { u"colorEdgeEffect", 21 }
};

size_t findAttributeSdkLevel(const std::u16string& name) {
    auto iter = sAttrMap.find(name);
size_t findAttributeSdkLevel(const ResourceName& name) {
    if (name.package != u"android" && name.type != ResourceType::kAttr) {
        return 0;
    }

    auto iter = sAttrMap.find(name.entry);
    if (iter != sAttrMap.end()) {
        return iter->second;
    }
    return 0;
    return SDK_LOLLIPOP_MR1;
}

} // namespace aapt
Loading