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

Commit 2ae4a877 authored by Adam Lesinski's avatar Adam Lesinski
Browse files

AAPT2: Add Manifest fixing/validation

Change-Id: I7f6d8b74d1c590adc356b4da55cb6cb777cdf1da
parent 3b4cd940
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ sources := \
	flatten/TableFlattener.cpp \
	flatten/XmlFlattener.cpp \
	link/AutoVersioner.cpp \
	link/ManifestFixer.cpp \
	link/PrivateAttributeMover.cpp \
	link/ReferenceLinker.cpp \
	link/TableMerger.cpp \
@@ -67,6 +68,7 @@ testSources := \
	flatten/TableFlattener_test.cpp \
	flatten/XmlFlattener_test.cpp \
	link/AutoVersioner_test.cpp \
	link/ManifestFixer_test.cpp \
	link/PrivateAttributeMover_test.cpp \
	link/ReferenceLinker_test.cpp \
	link/TableMerger_test.cpp \
@@ -113,7 +115,7 @@ else
endif

cFlags := -Wall -Werror -Wno-unused-parameter -UNDEBUG
cppFlags := -std=c++11 -Wno-missing-field-initializers -fno-exceptions
cppFlags := -std=c++11 -Wno-missing-field-initializers -fno-exceptions -fno-rtti

# ==========================================================
# Build the host static library: libaapt2
+9 −1
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

#include "Flags.h"
#include "util/StringPiece.h"
#include "util/Util.h"

#include <iomanip>
#include <iostream>
@@ -94,7 +95,14 @@ void Flags::usage(const StringPiece& command, std::ostream* out) {
        if (flag.numArgs > 0) {
            argLine += " arg";
        }
        *out << " " << std::setw(30) << std::left << argLine << flag.description << "\n";

        // Split the description by newlines and write out the argument (which is empty after
        // the first line) followed by the description line. This will make sure that multiline
        // descriptions are still right justified and aligned.
        for (StringPiece line : util::tokenize<char>(flag.description, '\n')) {
            *out << " " << std::setw(30) << std::left << argLine << line << "\n";
            argLine = " ";
        }
    }
    *out << " " << std::setw(30) << std::left << "-h" << "Displays this help menu\n";
    out->flush();

tools/aapt2/ManifestValidator.cpp

deleted100644 → 0
+0 −217
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 "Logger.h"
#include "ManifestValidator.h"
#include "util/Maybe.h"
#include "Source.h"
#include "util/Util.h"

#include <androidfw/ResourceTypes.h>

namespace aapt {

ManifestValidator::ManifestValidator(const android::ResTable& table)
: mTable(table) {
}

bool ManifestValidator::validate(const Source& source, android::ResXMLParser* parser) {
    SourceLogger logger(source);

    android::ResXMLParser::event_code_t code;
    while ((code = parser->next()) != android::ResXMLParser::END_DOCUMENT &&
            code != android::ResXMLParser::BAD_DOCUMENT) {
        if (code != android::ResXMLParser::START_TAG) {
            continue;
        }

        size_t len = 0;
        const StringPiece16 namespaceUri(parser->getElementNamespace(&len), len);
        if (!namespaceUri.empty()) {
            continue;
        }

        const StringPiece16 name(parser->getElementName(&len), len);
        if (name.empty()) {
            logger.error(parser->getLineNumber())
                    << "failed to get the element name."
                    << std::endl;
            return false;
        }

        if (name == u"manifest") {
            if (!validateManifest(source, parser)) {
                return false;
            }
        }
    }
    return true;
}

Maybe<StringPiece16> ManifestValidator::getAttributeValue(android::ResXMLParser* parser,
                                                          size_t idx) {
    android::Res_value value;
    if (parser->getAttributeValue(idx, &value) < 0) {
        return StringPiece16();
    }

    const android::ResStringPool* pool = &parser->getStrings();
    if (value.dataType == android::Res_value::TYPE_REFERENCE) {
        ssize_t strIdx = mTable.resolveReference(&value, 0x10000000u);
        if (strIdx < 0) {
            return {};
        }
        pool = mTable.getTableStringBlock(strIdx);
    }

    if (value.dataType != android::Res_value::TYPE_STRING || !pool) {
        return {};
    }
    return util::getString(*pool, value.data);
}

Maybe<StringPiece16> ManifestValidator::getAttributeInlineValue(android::ResXMLParser* parser,
                                                                size_t idx) {
    android::Res_value value;
    if (parser->getAttributeValue(idx, &value) < 0) {
        return StringPiece16();
    }

    if (value.dataType != android::Res_value::TYPE_STRING) {
        return {};
    }
    return util::getString(parser->getStrings(), value.data);
}

bool ManifestValidator::validateInlineAttribute(android::ResXMLParser* parser, size_t idx,
                                                SourceLogger& logger,
                                                const StringPiece16& charSet) {
    size_t len = 0;
    StringPiece16 element(parser->getElementName(&len), len);
    StringPiece16 attributeName(parser->getAttributeName(idx, &len), len);
    Maybe<StringPiece16> result = getAttributeInlineValue(parser, idx);
    if (!result) {
        logger.error(parser->getLineNumber())
                << "<"
                << element
                << "> must have a '"
                << attributeName
                << "' attribute with a string literal value."
                << std::endl;
        return false;
    }
    return validateAttributeImpl(element, attributeName, result.value(), charSet,
                                 parser->getLineNumber(), logger);
}

bool ManifestValidator::validateAttribute(android::ResXMLParser* parser, size_t idx,
                                          SourceLogger& logger, const StringPiece16& charSet) {
    size_t len = 0;
    StringPiece16 element(parser->getElementName(&len), len);
    StringPiece16 attributeName(parser->getAttributeName(idx, &len), len);
    Maybe<StringPiece16> result = getAttributeValue(parser, idx);
    if (!result) {
        logger.error(parser->getLineNumber())
                << "<"
                << element
                << "> must have a '"
                << attributeName
                << "' attribute that points to a string."
                << std::endl;
        return false;
    }
    return validateAttributeImpl(element, attributeName, result.value(), charSet,
                                 parser->getLineNumber(), logger);
}

bool ManifestValidator::validateAttributeImpl(const StringPiece16& element,
                                              const StringPiece16& attributeName,
                                              const StringPiece16& attributeValue,
                                              const StringPiece16& charSet, size_t lineNumber,
                                              SourceLogger& logger) {
    StringPiece16::const_iterator badIter =
            util::findNonAlphaNumericAndNotInSet(attributeValue, charSet);
    if (badIter != attributeValue.end()) {
        logger.error(lineNumber)
                << "tag <"
                << element
                << "> attribute '"
                << attributeName
                << "' has invalid character '"
                << StringPiece16(badIter, 1)
                << "'."
                << std::endl;
        return false;
    }

    if (!attributeValue.empty()) {
        StringPiece16 trimmed = util::trimWhitespace(attributeValue);
        if (attributeValue.begin() != trimmed.begin()) {
            logger.error(lineNumber)
                    << "tag <"
                    << element
                    << "> attribute '"
                    << attributeName
                    << "' can not start with whitespace."
                    << std::endl;
            return false;
        }

        if (attributeValue.end() != trimmed.end()) {
            logger.error(lineNumber)
                    << "tag <"
                    << element
                    << "> attribute '"
                    << attributeName
                    << "' can not end with whitespace."
                    << std::endl;
            return false;
        }
    }
    return true;
}

constexpr const char16_t* kPackageIdentSet = u"._";

bool ManifestValidator::validateManifest(const Source& source, android::ResXMLParser* parser) {
    bool error = false;
    SourceLogger logger(source);

    const StringPiece16 kAndroid = u"android";
    const StringPiece16 kPackage = u"package";
    const StringPiece16 kSharedUserId = u"sharedUserId";

    ssize_t idx;

    idx = parser->indexOfAttribute(nullptr, 0, kPackage.data(), kPackage.size());
    if (idx < 0) {
        logger.error(parser->getLineNumber())
                << "missing package attribute."
                << std::endl;
        error = true;
    } else {
        error |= !validateInlineAttribute(parser, idx, logger, kPackageIdentSet);
    }

    idx = parser->indexOfAttribute(kAndroid.data(), kAndroid.size(),
                                   kSharedUserId.data(), kSharedUserId.size());
    if (idx >= 0) {
        error |= !validateInlineAttribute(parser, idx, logger, kPackageIdentSet);
    }
    return !error;
}

} // namespace aapt
+9 −3
Original line number Diff line number Diff line
@@ -74,9 +74,11 @@ bool tryParseReference(const StringPiece16& str, ResourceNameRef* outRef, bool*
            return false;
        }

        if (outRef != nullptr) {
            outRef->package = package;
            outRef->type = *parsedType;
            outRef->entry = entry;
        }
        if (outCreate) {
            *outCreate = create;
        }
@@ -88,6 +90,10 @@ bool tryParseReference(const StringPiece16& str, ResourceNameRef* outRef, bool*
    return false;
}

bool isReference(const StringPiece16& str) {
    return tryParseReference(str, nullptr, nullptr, nullptr);
}

bool tryParseAttributeReference(const StringPiece16& str, ResourceNameRef* outRef) {
    StringPiece16 trimmedStr(util::trimWhitespace(str));
    if (trimmedStr.empty()) {
+5 −0
Original line number Diff line number Diff line
@@ -48,6 +48,11 @@ void extractResourceName(const StringPiece16& str, StringPiece16* outPackage,
bool tryParseReference(const StringPiece16& str, ResourceNameRef* outReference,
                       bool* outCreate = nullptr, bool* outPrivate = nullptr);

/*
 * Returns true if the string is in the form of a resource reference (@[+][package:]type/name).
 */
bool isReference(const StringPiece16& str);

/*
 * Returns true if the string was parsed as an attribute reference (?[package:]type/name),
 * with `outReference` set to the parsed reference.
Loading