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

Commit ae3622a2 authored by Adam Lesinski's avatar Adam Lesinski Committed by Android (Google) Code Review
Browse files

Merge "AAPT2: Implement XmlActionExecutor to verify manifest" into nyc-dev

parents 522e28e9 cc5609d8
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -66,6 +66,7 @@ sources := \
	ResourceValues.cpp \
	SdkConstants.cpp \
	StringPool.cpp \
	xml/XmlActionExecutor.cpp \
	xml/XmlDom.cpp \
	xml/XmlPullParser.cpp \
	xml/XmlUtil.cpp
@@ -107,6 +108,7 @@ testSources := \
	SdkConstants_test.cpp \
	StringPool_test.cpp \
	ValueVisitor_test.cpp \
	xml/XmlActionExecutor_test.cpp \
	xml/XmlDom_test.cpp \
	xml/XmlPullParser_test.cpp \
	xml/XmlUtil_test.cpp
@@ -140,7 +142,7 @@ else
endif

cFlags := -Wall -Werror -Wno-unused-parameter -UNDEBUG
cppFlags := -std=c++11 -Wno-missing-field-initializers -fno-exceptions -fno-rtti
cppFlags := -std=c++14 -Wno-missing-field-initializers -fno-exceptions -fno-rtti
protoIncludes := $(call generated-sources-dir-for,STATIC_LIBRARIES,libaapt2,HOST)

# ==========================================================
+72 −21
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@
#include "util/StringPiece.h"
#include "util/Util.h"

#include <android-base/macros.h>
#include <iostream>
#include <sstream>
#include <string>
@@ -46,7 +47,11 @@ public:
    DiagMessage(const Source& src) : mSource(src) {
    }

    template <typename T> DiagMessage& operator<<(const T& value) {
    DiagMessage(size_t line) : mSource(Source().withLine(line)) {
    }

    template <typename T>
    DiagMessage& operator<<(const T& value) {
        mMessage << value;
        return *this;
    }
@@ -59,36 +64,82 @@ public:
struct IDiagnostics {
    virtual ~IDiagnostics() = default;

    virtual void error(const DiagMessage& message) = 0;
    virtual void warn(const DiagMessage& message) = 0;
    virtual void note(const DiagMessage& message) = 0;
    enum class Level {
        Note,
        Warn,
        Error
    };

struct StdErrDiagnostics : public IDiagnostics {
    size_t mNumErrors = 0;
    virtual void log(Level level, DiagMessageActual& actualMsg) = 0;

    void emit(const DiagMessage& msg, const char* tag) {
        DiagMessageActual actual = msg.build();
        if (!actual.source.path.empty()) {
            std::cerr << actual.source << ": ";
    virtual void error(const DiagMessage& message) {
        DiagMessageActual actual = message.build();
        log(Level::Error, actual);
    }
        std::cerr << tag << actual.message << "." << std::endl;

    virtual void warn(const DiagMessage& message) {
        DiagMessageActual actual = message.build();
        log(Level::Warn, actual);
    }

    void error(const DiagMessage& msg) override {
        if (mNumErrors < 20) {
            emit(msg, "error: ");
    virtual void note(const DiagMessage& message) {
        DiagMessageActual actual = message.build();
        log(Level::Note, actual);
    }
};

class StdErrDiagnostics : public IDiagnostics {
public:
    StdErrDiagnostics() = default;

    void log(Level level, DiagMessageActual& actualMsg) override {
        const char* tag;

        switch (level) {
        case Level::Error:
            mNumErrors++;
            if (mNumErrors > 20) {
                return;
            }
            tag = "error";
            break;

        case Level::Warn:
            tag = "warn";
            break;

    void warn(const DiagMessage& msg) override {
        emit(msg, "warn: ");
        case Level::Note:
            tag = "note";
            break;
        }

    void note(const DiagMessage& msg) override {
        emit(msg, "note: ");
        if (!actualMsg.source.path.empty()) {
            std::cerr << actualMsg.source << ": ";
        }
        std::cerr << tag << ": " << actualMsg.message << "." << std::endl;
    }

private:
    size_t mNumErrors = 0;

    DISALLOW_COPY_AND_ASSIGN(StdErrDiagnostics);
};

class SourcePathDiagnostics : public IDiagnostics {
public:
    SourcePathDiagnostics(const Source& src, IDiagnostics* diag) : mSource(src), mDiag(diag) {
    }

    void log(Level level, DiagMessageActual& actualMsg) override {
        actualMsg.source.path = mSource.path;
        mDiag->log(level, actualMsg);
    }

private:
    Source mSource;
    IDiagnostics* mDiag;

    DISALLOW_COPY_AND_ASSIGN(SourcePathDiagnostics);
};

} // namespace aapt
+16 −26
Original line number Diff line number Diff line
@@ -28,33 +28,23 @@

namespace aapt {

struct ResourceTableTest : public ::testing::Test {
    struct EmptyDiagnostics : public IDiagnostics {
        void error(const DiagMessage& msg) override {}
        void warn(const DiagMessage& msg) override {}
        void note(const DiagMessage& msg) override {}
    };

    EmptyDiagnostics mDiagnostics;
};

TEST_F(ResourceTableTest, FailToAddResourceWithBadName) {
TEST(ResourceTableTest, FailToAddResourceWithBadName) {
    ResourceTable table;

    EXPECT_FALSE(table.addResource(
            ResourceNameRef(u"android", ResourceType::kId, u"hey,there"),
            ConfigDescription{}, "",
            test::ValueBuilder<Id>().setSource("test.xml", 21u).build(),
            &mDiagnostics));
            test::getDiagnostics()));

    EXPECT_FALSE(table.addResource(
            ResourceNameRef(u"android", ResourceType::kId, u"hey:there"),
            ConfigDescription{}, "",
            test::ValueBuilder<Id>().setSource("test.xml", 21u).build(),
            &mDiagnostics));
            test::getDiagnostics()));
}

TEST_F(ResourceTableTest, AddOneResource) {
TEST(ResourceTableTest, AddOneResource) {
    ResourceTable table;

    EXPECT_TRUE(table.addResource(test::parseNameOrDie(u"@android:attr/id"),
@@ -62,12 +52,12 @@ TEST_F(ResourceTableTest, AddOneResource) {
                                  "",
                                  test::ValueBuilder<Id>()
                                          .setSource("test/path/file.xml", 23u).build(),
                                  &mDiagnostics));
                                  test::getDiagnostics()));

    ASSERT_NE(nullptr, test::getValue<Id>(&table, u"@android:attr/id"));
}

TEST_F(ResourceTableTest, AddMultipleResources) {
TEST(ResourceTableTest, AddMultipleResources) {
    ResourceTable table;

    ConfigDescription config;
@@ -79,21 +69,21 @@ TEST_F(ResourceTableTest, AddMultipleResources) {
            config,
            "",
            test::ValueBuilder<Id>().setSource("test/path/file.xml", 10u).build(),
            &mDiagnostics));
            test::getDiagnostics()));

    EXPECT_TRUE(table.addResource(
            test::parseNameOrDie(u"@android:attr/id"),
            config,
            "",
            test::ValueBuilder<Id>().setSource("test/path/file.xml", 12u).build(),
            &mDiagnostics));
            test::getDiagnostics()));

    EXPECT_TRUE(table.addResource(
            test::parseNameOrDie(u"@android:string/ok"),
            config,
            "",
            test::ValueBuilder<Id>().setSource("test/path/file.xml", 14u).build(),
            &mDiagnostics));
            test::getDiagnostics()));

    EXPECT_TRUE(table.addResource(
            test::parseNameOrDie(u"@android:string/ok"),
@@ -102,7 +92,7 @@ TEST_F(ResourceTableTest, AddMultipleResources) {
            test::ValueBuilder<BinaryPrimitive>(android::Res_value{})
                    .setSource("test/path/file.xml", 20u)
                    .build(),
            &mDiagnostics));
            test::getDiagnostics()));

    ASSERT_NE(nullptr, test::getValue<Id>(&table, u"@android:attr/layout_width"));
    ASSERT_NE(nullptr, test::getValue<Id>(&table, u"@android:attr/id"));
@@ -111,37 +101,37 @@ TEST_F(ResourceTableTest, AddMultipleResources) {
                                                                languageConfig));
}

TEST_F(ResourceTableTest, OverrideWeakResourceValue) {
TEST(ResourceTableTest, OverrideWeakResourceValue) {
    ResourceTable table;

    ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:attr/foo"), ConfigDescription{},
                                  "", util::make_unique<Attribute>(true), &mDiagnostics));
                                  "", util::make_unique<Attribute>(true), test::getDiagnostics()));

    Attribute* attr = test::getValue<Attribute>(&table, u"@android:attr/foo");
    ASSERT_NE(nullptr, attr);
    EXPECT_TRUE(attr->isWeak());

    ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:attr/foo"), ConfigDescription{},
                                  "", util::make_unique<Attribute>(false), &mDiagnostics));
                                  "", util::make_unique<Attribute>(false), test::getDiagnostics()));

    attr = test::getValue<Attribute>(&table, u"@android:attr/foo");
    ASSERT_NE(nullptr, attr);
    EXPECT_FALSE(attr->isWeak());
}

TEST_F(ResourceTableTest, ProductVaryingValues) {
TEST(ResourceTableTest, ProductVaryingValues) {
    ResourceTable table;

    EXPECT_TRUE(table.addResource(test::parseNameOrDie(u"@android:string/foo"),
                                  test::parseConfigOrDie("land"),
                                  "tablet",
                                  util::make_unique<Id>(),
                                  &mDiagnostics));
                                  test::getDiagnostics()));
    EXPECT_TRUE(table.addResource(test::parseNameOrDie(u"@android:string/foo"),
                                  test::parseConfigOrDie("land"),
                                  "phone",
                                  util::make_unique<Id>(),
                                  &mDiagnostics));
                                  test::getDiagnostics()));

    EXPECT_NE(nullptr, test::getValueForConfigAndProduct<Id>(&table, u"@android:string/foo",
                                                             test::parseConfigOrDie("land"),
+188 −118
Original line number Diff line number Diff line
@@ -17,67 +17,198 @@
#include "ResourceUtils.h"
#include "link/ManifestFixer.h"
#include "util/Util.h"
#include "xml/XmlActionExecutor.h"
#include "xml/XmlDom.h"

namespace aapt {

static bool verifyManifest(IAaptContext* context, const Source& source, xml::Element* manifestEl) {
    xml::Attribute* attr = manifestEl->findAttribute({}, u"package");
/**
 * This is how PackageManager builds class names from AndroidManifest.xml entries.
 */
static bool nameIsJavaClassName(xml::Element* el, xml::Attribute* attr,
                                SourcePathDiagnostics* diag) {
    std::u16string className = attr->value;
    if (className.find(u'.') == std::u16string::npos) {
        // There is no '.', so add one to the beginning.
        className = u".";
        className += attr->value;
    }

    // We allow unqualified class names (ie: .HelloActivity)
    // Since we don't know the package name, we can just make a fake one here and
    // the test will be identical as long as the real package name is valid too.
    Maybe<std::u16string> fullyQualifiedClassName =
            util::getFullyQualifiedClassName(u"a", className);

    StringPiece16 qualifiedClassName = fullyQualifiedClassName
            ? fullyQualifiedClassName.value() : className;
    if (!util::isJavaClassName(qualifiedClassName)) {
        diag->error(DiagMessage(el->lineNumber)
                    << "attribute 'android:name' in <"
                    << el->name << "> tag must be a valid Java class name");
        return false;
    }
    return true;
}

static bool optionalNameIsJavaClassName(xml::Element* el, SourcePathDiagnostics* diag) {
    if (xml::Attribute* attr = el->findAttribute(xml::kSchemaAndroid, u"name")) {
        return nameIsJavaClassName(el, attr, diag);
    }
    return true;
}

static bool requiredNameIsJavaClassName(xml::Element* el, SourcePathDiagnostics* diag) {
    if (xml::Attribute* attr = el->findAttribute(xml::kSchemaAndroid, u"name")) {
        return nameIsJavaClassName(el, attr, diag);
    }
    diag->error(DiagMessage(el->lineNumber)
                << "<" << el->name << "> is missing attribute 'android:name'");
    return false;
}

static bool verifyManifest(xml::Element* el, SourcePathDiagnostics* diag) {
    xml::Attribute* attr = el->findAttribute({}, u"package");
    if (!attr) {
        context->getDiagnostics()->error(DiagMessage(source.withLine(manifestEl->lineNumber))
                                         << "missing 'package' attribute");
        diag->error(DiagMessage(el->lineNumber) << "<manifest> tag is missing 'package' attribute");
        return false;
    } else if (ResourceUtils::isReference(attr->value)) {
        context->getDiagnostics()->error(DiagMessage(source.withLine(manifestEl->lineNumber))
                                         << "value for attribute 'package' must not be a "
                                            "reference");
        diag->error(DiagMessage(el->lineNumber)
                    << "attribute 'package' in <manifest> tag must not be a reference");
        return false;
    } else if (!util::isJavaPackageName(attr->value)) {
        context->getDiagnostics()->error(DiagMessage(source.withLine(manifestEl->lineNumber))
                                         << "invalid package name '" << attr->value << "'");
    } else {
        diag->error(DiagMessage(el->lineNumber)
                    << "attribute 'package' in <manifest> tag is not a valid Java package name: '"
                    << attr->value << "'");
        return false;
    }
    return true;
}

bool ManifestFixer::buildRules(xml::XmlActionExecutor* executor, IDiagnostics* diag) {
    // First verify some options.
    if (mOptions.renameManifestPackage) {
        if (!util::isJavaPackageName(mOptions.renameManifestPackage.value())) {
            diag->error(DiagMessage() << "invalid manifest package override '"
                        << mOptions.renameManifestPackage.value() << "'");
            return false;
        }

static bool includeVersionName(IAaptContext* context, const Source& source,
                               const StringPiece16& versionName, xml::Element* manifestEl) {
    if (manifestEl->findAttribute(xml::kSchemaAndroid, u"versionName")) {
        return true;
    }

    manifestEl->attributes.push_back(xml::Attribute{
            xml::kSchemaAndroid, u"versionName", versionName.toString() });
    return true;
    if (mOptions.renameInstrumentationTargetPackage) {
        if (!util::isJavaPackageName(mOptions.renameInstrumentationTargetPackage.value())) {
            diag->error(DiagMessage() << "invalid instrumentation target package override '"
                        << mOptions.renameInstrumentationTargetPackage.value() << "'");
            return false;
        }
    }

static bool includeVersionCode(IAaptContext* context, const Source& source,
                               const StringPiece16& versionCode, xml::Element* manifestEl) {
    if (manifestEl->findAttribute(xml::kSchemaAndroid, u"versionCode")) {
        return true;
    // Common intent-filter actions.
    xml::XmlNodeAction intentFilterAction;
    intentFilterAction[u"action"];
    intentFilterAction[u"category"];
    intentFilterAction[u"data"];

    // Common meta-data actions.
    xml::XmlNodeAction metaDataAction;

    // Manifest actions.
    xml::XmlNodeAction& manifestAction = (*executor)[u"manifest"];
    manifestAction.action(verifyManifest);
    manifestAction.action([&](xml::Element* el) -> bool {
        if (mOptions.versionNameDefault) {
            if (el->findAttribute(xml::kSchemaAndroid, u"versionName") == nullptr) {
                el->attributes.push_back(xml::Attribute{
                        xml::kSchemaAndroid,
                        u"versionName",
                        mOptions.versionNameDefault.value() });
            }
        }

    manifestEl->attributes.push_back(xml::Attribute{
            xml::kSchemaAndroid, u"versionCode", versionCode.toString() });
    return true;
        if (mOptions.versionCodeDefault) {
            if (el->findAttribute(xml::kSchemaAndroid, u"versionCode") == nullptr) {
                el->attributes.push_back(xml::Attribute{
                        xml::kSchemaAndroid,
                        u"versionCode",
                        mOptions.versionCodeDefault.value() });
            }
        }
        return true;
    });

static bool fixUsesSdk(IAaptContext* context, const Source& source, xml::Element* el,
                       const ManifestFixerOptions& options) {
    if (options.minSdkVersionDefault &&
    // Uses-sdk actions.
    manifestAction[u"uses-sdk"].action([&](xml::Element* el) -> bool {
        if (mOptions.minSdkVersionDefault &&
                el->findAttribute(xml::kSchemaAndroid, u"minSdkVersion") == nullptr) {
            // There was no minSdkVersion defined and we have a default to assign.
            el->attributes.push_back(xml::Attribute{
                xml::kSchemaAndroid, u"minSdkVersion", options.minSdkVersionDefault.value() });
                    xml::kSchemaAndroid, u"minSdkVersion",
                    mOptions.minSdkVersionDefault.value() });
        }

    if (options.targetSdkVersionDefault &&
        if (mOptions.targetSdkVersionDefault &&
                el->findAttribute(xml::kSchemaAndroid, u"targetSdkVersion") == nullptr) {
            // There was no targetSdkVersion defined and we have a default to assign.
            el->attributes.push_back(xml::Attribute{
                    xml::kSchemaAndroid, u"targetSdkVersion",
                options.targetSdkVersionDefault.value() });
                    mOptions.targetSdkVersionDefault.value() });
        }
        return true;
    });

    // Instrumentation actions.
    manifestAction[u"instrumentation"].action([&](xml::Element* el) -> bool {
        if (!mOptions.renameInstrumentationTargetPackage) {
            return true;
        }

        if (xml::Attribute* attr = el->findAttribute(xml::kSchemaAndroid, u"targetPackage")) {
            attr->value = mOptions.renameInstrumentationTargetPackage.value();
        }
        return true;
    });

    manifestAction[u"uses-permission"];
    manifestAction[u"permission"];
    manifestAction[u"permission-tree"];
    manifestAction[u"permission-group"];

    manifestAction[u"uses-configuration"];
    manifestAction[u"uses-feature"];
    manifestAction[u"uses-library"];
    manifestAction[u"supports-screens"];
    manifestAction[u"compatible-screens"];
    manifestAction[u"supports-gl-texture"];

    // Application actions.
    xml::XmlNodeAction& applicationAction = (*executor)[u"manifest"][u"application"];
    applicationAction.action(optionalNameIsJavaClassName);

    // Activity actions.
    applicationAction[u"activity"].action(requiredNameIsJavaClassName);
    applicationAction[u"activity"][u"intent-filter"] = intentFilterAction;
    applicationAction[u"activity"][u"meta-data"] = metaDataAction;

    // Activity alias actions.
    applicationAction[u"activity-alias"][u"intent-filter"] = intentFilterAction;
    applicationAction[u"activity-alias"][u"meta-data"] = metaDataAction;

    // Service actions.
    applicationAction[u"service"].action(requiredNameIsJavaClassName);
    applicationAction[u"service"][u"intent-filter"] = intentFilterAction;
    applicationAction[u"service"][u"meta-data"] = metaDataAction;

    // Receiver actions.
    applicationAction[u"receiver"].action(requiredNameIsJavaClassName);
    applicationAction[u"receiver"][u"intent-filter"] = intentFilterAction;
    applicationAction[u"receiver"][u"meta-data"] = metaDataAction;

    // Provider actions.
    applicationAction[u"provider"].action(requiredNameIsJavaClassName);
    applicationAction[u"provider"][u"grant-uri-permissions"];
    applicationAction[u"provider"][u"meta-data"] = metaDataAction;
    applicationAction[u"provider"][u"path-permissions"];
    return true;
}

class FullyQualifiedClassNameVisitor : public xml::Visitor {
@@ -103,14 +234,7 @@ private:
    StringPiece16 mPackage;
};

static bool renameManifestPackage(IAaptContext* context, const Source& source,
                                  const StringPiece16& packageOverride, xml::Element* manifestEl) {
    if (!util::isJavaPackageName(packageOverride)) {
        context->getDiagnostics()->error(DiagMessage() << "invalid manifest package override '"
                                         << packageOverride << "'");
        return false;
    }

static bool renameManifestPackage(const StringPiece16& packageOverride, xml::Element* manifestEl) {
    xml::Attribute* attr = manifestEl->findAttribute({}, u"package");

    // We've already verified that the manifest element is present, with a package name specified.
@@ -124,32 +248,6 @@ static bool renameManifestPackage(IAaptContext* context, const Source& source,
    return true;
}

static bool renameInstrumentationTargetPackage(IAaptContext* context, const Source& source,
                                               const StringPiece16& packageOverride,
                                               xml::Element* manifestEl) {
    if (!util::isJavaPackageName(packageOverride)) {
        context->getDiagnostics()->error(DiagMessage()
                                         << "invalid instrumentation target package override '"
                                         << packageOverride << "'");
        return false;
    }

    xml::Element* instrumentationEl = manifestEl->findChild({}, u"instrumentation");
    if (!instrumentationEl) {
        // No error if there is no work to be done.
        return true;
    }

    xml::Attribute* attr = instrumentationEl->findAttribute(xml::kSchemaAndroid, u"targetPackage");
    if (!attr) {
        // No error if there is no work to be done.
        return true;
    }

    attr->value = packageOverride.toString();
    return true;
}

bool ManifestFixer::consume(IAaptContext* context, xml::XmlResource* doc) {
    xml::Element* root = xml::findRootElement(doc->root.get());
    if (!root || !root->namespaceUri.empty() || root->name != u"manifest") {
@@ -158,59 +256,31 @@ bool ManifestFixer::consume(IAaptContext* context, xml::XmlResource* doc) {
        return false;
    }

    if (!verifyManifest(context, doc->file.source, root)) {
        return false;
    if ((mOptions.minSdkVersionDefault || mOptions.targetSdkVersionDefault)
            && root->findChild({}, u"uses-sdk") == nullptr) {
        // Auto insert a <uses-sdk> element.
        std::unique_ptr<xml::Element> usesSdk = util::make_unique<xml::Element>();
        usesSdk->name = u"uses-sdk";
        root->addChild(std::move(usesSdk));
    }

    if (mOptions.versionCodeDefault) {
        if (!includeVersionCode(context, doc->file.source, mOptions.versionCodeDefault.value(),
                                root)) {
    xml::XmlActionExecutor executor;
    if (!buildRules(&executor, context->getDiagnostics())) {
        return false;
    }
    }

    if (mOptions.versionNameDefault) {
        if (!includeVersionName(context, doc->file.source, mOptions.versionNameDefault.value(),
                                root)) {
    if (!executor.execute(xml::XmlActionExecutorPolicy::Whitelist, context->getDiagnostics(),
                          doc)) {
        return false;
    }
    }

    if (mOptions.renameManifestPackage) {
        // Rename manifest package.
        if (!renameManifestPackage(context, doc->file.source,
                                   mOptions.renameManifestPackage.value(), root)) {
        // Rename manifest package outside of the XmlActionExecutor.
        // We need to extract the old package name and FullyQualify all class names.
        if (!renameManifestPackage(mOptions.renameManifestPackage.value(), root)) {
            return false;
        }
    }

    if (mOptions.renameInstrumentationTargetPackage) {
        if (!renameInstrumentationTargetPackage(context, doc->file.source,
                                                mOptions.renameInstrumentationTargetPackage.value(),
                                                root)) {
            return false;
        }
    }

    bool foundUsesSdk = false;
    for (xml::Element* el : root->getChildElements()) {
        if (!el->namespaceUri.empty()) {
            continue;
        }

        if (el->name == u"uses-sdk") {
            foundUsesSdk = true;
            fixUsesSdk(context, doc->file.source, el, mOptions);
        }
    }

    if (!foundUsesSdk && (mOptions.minSdkVersionDefault || mOptions.targetSdkVersionDefault)) {
        std::unique_ptr<xml::Element> usesSdk = util::make_unique<xml::Element>();
        usesSdk->name = u"uses-sdk";
        fixUsesSdk(context, doc->file.source, usesSdk.get(), mOptions);
        root->addChild(std::move(usesSdk));
    }

    return true;
}

+8 −3
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@

#include "process/IResourceTableConsumer.h"
#include "util/Maybe.h"
#include "xml/XmlActionExecutor.h"
#include "xml/XmlDom.h"

#include <string>
@@ -38,13 +39,17 @@ struct ManifestFixerOptions {
 * Verifies that the manifest is correctly formed and inserts defaults
 * where specified with ManifestFixerOptions.
 */
struct ManifestFixer : public IXmlResourceConsumer {
    ManifestFixerOptions mOptions;

class ManifestFixer : public IXmlResourceConsumer {
public:
    ManifestFixer(const ManifestFixerOptions& options) : mOptions(options) {
    }

    bool consume(IAaptContext* context, xml::XmlResource* doc) override;

private:
    bool buildRules(xml::XmlActionExecutor* executor, IDiagnostics* diag);

    ManifestFixerOptions mOptions;
};

} // namespace aapt
Loading