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

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

Merge "AAPT2: Proguard rules generation added." into mnc-dev

parents 93f6fe95 a1ad4a81
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@ sources := \
	ManifestParser.cpp \
	ManifestValidator.cpp \
	Png.cpp \
	ProguardRules.cpp \
	ResChunkPullParser.cpp \
	Resource.cpp \
	ResourceParser.cpp \
+45 −4
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@
#include "ManifestValidator.h"
#include "NameMangler.h"
#include "Png.h"
#include "ProguardRules.h"
#include "ResourceParser.h"
#include "ResourceTable.h"
#include "ResourceTableResolver.h"
@@ -300,6 +301,9 @@ struct AaptOptions {
    // Directory to in which to generate R.java.
    Maybe<Source> generateJavaClass;

    // File in which to produce proguard rules.
    Maybe<Source> generateProguardRules;

    // Whether to output verbose details about
    // compilation.
    bool verbose = false;
@@ -417,7 +421,8 @@ 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) {
             const void* data, size_t dataLen, ZipFile* outApk, std::queue<LinkItem>* outQueue,
             proguard::KeepSet* keepSet) {
    SourceLogger logger(item.source);
    std::unique_ptr<xml::Node> root = xml::inflate(data, dataLen, &logger);
    if (!root) {
@@ -435,6 +440,10 @@ bool linkXml(const AaptOptions& options, const std::shared_ptr<ResourceTable>& t
        xmlOptions.maxSdkAttribute = item.config.sdkVersion ? item.config.sdkVersion : 1;
    }

    if (options.generateProguardRules) {
        proguard::collectProguardRules(item.name.type, item.source, root.get(), keepSet);
    }

    BigBuffer outBuffer(1024);
    Maybe<size_t> minStrippedSdk = xml::flattenAndLink(item.source, root.get(),
                                                       item.originalPackage, resolver,
@@ -509,7 +518,7 @@ bool copyFile(const AaptOptions& options, const CompileItem& item, ZipFile* outA

bool compileManifest(const AaptOptions& options, const std::shared_ptr<IResolver>& resolver,
                     const std::map<std::shared_ptr<ResourceTable>, StaticLibraryData>& libApks,
                     const android::ResTable& table, ZipFile* outApk) {
                     const android::ResTable& table, ZipFile* outApk, proguard::KeepSet* keepSet) {
    if (options.verbose) {
        Logger::note(options.manifest) << "compiling AndroidManifest.xml." << std::endl;
    }
@@ -557,6 +566,11 @@ bool compileManifest(const AaptOptions& options, const std::shared_ptr<IResolver
        }
    }

    if (options.generateProguardRules) {
        proguard::collectProguardRulesForManifest(options.manifest, merger.getMergedXml(),
                                                  keepSet);
    }

    BigBuffer outBuffer(1024);
    if (!xml::flattenAndLink(options.manifest, merger.getMergedXml(), options.appInfo.package,
                resolver, {}, &outBuffer)) {
@@ -805,8 +819,10 @@ bool link(const AaptOptions& options, const std::shared_ptr<ResourceTable>& outT
        return false;
    }

    proguard::KeepSet keepSet;

    android::ResTable binTable;
    if (!compileManifest(options, resolver, apkFiles, binTable, &outApk)) {
    if (!compileManifest(options, resolver, apkFiles, binTable, &outApk, &keepSet)) {
        return false;
    }

@@ -826,7 +842,7 @@ bool link(const AaptOptions& options, const std::shared_ptr<ResourceTable>& outT
            assert(uncompressedData);

            if (!linkXml(options, outTable, resolver, item, uncompressedData,
                        entry->getUncompressedLen(), &outApk, &linkQueue)) {
                        entry->getUncompressedLen(), &outApk, &linkQueue, &keepSet)) {
                Logger::error(options.output) << "failed to link '" << item.originalPath << "'."
                                              << std::endl;
                return false;
@@ -883,6 +899,26 @@ bool link(const AaptOptions& options, const std::shared_ptr<ResourceTable>& outT
        }
    }

    // Generate the Proguard rules file.
    if (options.generateProguardRules) {
        const Source& outPath = options.generateProguardRules.value();

        if (options.verbose) {
            Logger::note(outPath) << "writing proguard rules." << std::endl;
        }

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

        if (!proguard::writeKeepSet(&fout, keepSet)) {
            Logger::error(outPath) << "failed to write proguard rules." << std::endl;
            return false;
        }
    }

    outTable->getValueStringPool().prune();
    outTable->getValueStringPool().sort(
            [](const StringPool::Entry& a, const StringPool::Entry& b) -> bool {
@@ -1072,6 +1108,11 @@ static AaptOptions prepareArgs(int argc, char** argv) {
                        options.generateJavaClass = Source{ arg.toString() };
                    });

            flag::optionalFlag("--proguard", "file in which to output proguard rules",
                    [&options](const StringPiece& arg) {
                        options.generateProguardRules = Source{ arg.toString() };
                    });

            flag::optionalSwitch("--static-lib", "generate a static Android library", true,
                                 &isStaticLib);

+240 −0
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 "ProguardRules.h"
#include "Util.h"
#include "XmlDom.h"

#include <memory>
#include <string>

namespace aapt {
namespace proguard {

constexpr const char16_t* kSchemaAndroid = u"http://schemas.android.com/apk/res/android";

class BaseVisitor : public xml::Visitor {
public:
    BaseVisitor(const Source& source, KeepSet* keepSet) : mSource(source), mKeepSet(keepSet) {
    }

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

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

    virtual void visit(xml::Element* node) override {
        if (!node->namespaceUri.empty()) {
            Maybe<std::u16string> maybePackage = util::extractPackageFromNamespace(
                    node->namespaceUri);
            if (maybePackage) {
                // This is a custom view, let's figure out the class name from this.
                std::u16string package = maybePackage.value() + u"." + node->name;
                if (util::isJavaClassName(package)) {
                    addClass(node->lineNumber, package);
                }
            }
        } else if (util::isJavaClassName(node->name)) {
            addClass(node->lineNumber, node->name);
        }

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

protected:
    void addClass(size_t lineNumber, const std::u16string& className) {
        mKeepSet->addClass(mSource.line(lineNumber), className);
    }

    void addMethod(size_t lineNumber, const std::u16string& methodName) {
        mKeepSet->addMethod(mSource.line(lineNumber), methodName);
    }

private:
    Source mSource;
    KeepSet* mKeepSet;
};

struct LayoutVisitor : public BaseVisitor {
    LayoutVisitor(const Source& source, KeepSet* keepSet) : BaseVisitor(source, keepSet) {
    }

    virtual void visit(xml::Element* node) override {
        bool checkClass = false;
        bool checkName = false;
        if (node->namespaceUri.empty()) {
            checkClass = node->name == u"view" || node->name == u"fragment";
        } else if (node->namespaceUri == kSchemaAndroid) {
            checkName = node->name == u"fragment";
        }

        for (const auto& attr : node->attributes) {
            if (checkClass && attr.namespaceUri.empty() && attr.name == u"class" &&
                    util::isJavaClassName(attr.value)) {
                addClass(node->lineNumber, attr.value);
            } else if (checkName && attr.namespaceUri == kSchemaAndroid && attr.name == u"name" &&
                    util::isJavaClassName(attr.value)) {
                addClass(node->lineNumber, attr.value);
            } else if (attr.namespaceUri == kSchemaAndroid && attr.name == u"onClick") {
                addMethod(node->lineNumber, attr.value);
            }
        }

        BaseVisitor::visit(node);
    }
};

struct XmlResourceVisitor : public BaseVisitor {
    XmlResourceVisitor(const Source& source, KeepSet* keepSet) : BaseVisitor(source, keepSet) {
    }

    virtual void visit(xml::Element* node) override {
        bool checkFragment = false;
        if (node->namespaceUri.empty()) {
            checkFragment = node->name == u"PreferenceScreen" || node->name == u"header";
        }

        if (checkFragment) {
            xml::Attribute* attr = node->findAttribute(kSchemaAndroid, u"fragment");
            if (attr && util::isJavaClassName(attr->value)) {
                addClass(node->lineNumber, attr->value);
            }
        }

        BaseVisitor::visit(node);
    }
};

struct TransitionVisitor : public BaseVisitor {
    TransitionVisitor(const Source& source, KeepSet* keepSet) : BaseVisitor(source, keepSet) {
    }

    virtual void visit(xml::Element* node) override {
        bool checkClass = node->namespaceUri.empty() &&
                (node->name == u"transition" || node->name == u"pathMotion");
        if (checkClass) {
            xml::Attribute* attr = node->findAttribute({}, u"class");
            if (attr && util::isJavaClassName(attr->value)) {
                addClass(node->lineNumber, attr->value);
            }
        }

        BaseVisitor::visit(node);
    }
};

struct ManifestVisitor : public BaseVisitor {
    ManifestVisitor(const Source& source, KeepSet* keepSet) : BaseVisitor(source, keepSet) {
    }

    virtual void visit(xml::Element* node) override {
        if (node->namespaceUri.empty()) {
            bool getName = false;
            if (node->name == u"manifest") {
                xml::Attribute* attr = node->findAttribute({}, u"package");
                if (attr) {
                    mPackage = attr->value;
                }
            } else if (node->name == u"application") {
                getName = true;
                xml::Attribute* attr = node->findAttribute(kSchemaAndroid, u"backupAgent");
                if (attr) {
                    Maybe<std::u16string> result = util::getFullyQualifiedClassName(mPackage,
                                                                                    attr->value);
                    if (result) {
                        addClass(node->lineNumber, result.value());
                    }
                }
            } else if (node->name == u"activity" || node->name == u"service" ||
                    node->name == u"receiver" || node->name == u"provider" ||
                    node->name == u"instrumentation") {
                getName = true;
            }

            if (getName) {
                xml::Attribute* attr = node->findAttribute(kSchemaAndroid, u"name");
                if (attr) {
                    Maybe<std::u16string> result = util::getFullyQualifiedClassName(mPackage,
                                                                                    attr->value);
                    if (result) {
                        addClass(node->lineNumber, result.value());
                    }
                }
            }
        }
        BaseVisitor::visit(node);
    }

    std::u16string mPackage;
};

bool collectProguardRulesForManifest(const Source& source, xml::Node* node, KeepSet* keepSet) {
    ManifestVisitor visitor(source, keepSet);
    node->accept(&visitor);
    return true;
}

bool collectProguardRules(ResourceType type, const Source& source, xml::Node* node,
                          KeepSet* keepSet) {
    switch (type) {
        case ResourceType::kLayout: {
            LayoutVisitor visitor(source, keepSet);
            node->accept(&visitor);
            break;
        }

        case ResourceType::kXml: {
            XmlResourceVisitor visitor(source, keepSet);
            node->accept(&visitor);
            break;
        }

        case ResourceType::kTransition: {
            TransitionVisitor visitor(source, keepSet);
            node->accept(&visitor);
            break;
        }

        default:
            break;
    }
    return true;
}

bool writeKeepSet(std::ostream* out, const KeepSet& keepSet) {
    for (const auto& entry : keepSet.mKeepSet) {
        for (const SourceLine& source : entry.second) {
            *out << "// Referenced at " << source << "\n";
        }
        *out << "-keep class " << entry.first << " { <init>(...); }\n" << std::endl;
    }

    for (const auto& entry : keepSet.mKeepMethodSet) {
        for (const SourceLine& source : entry.second) {
            *out << "// Referenced at " << source << "\n";
        }
        *out << "-keepclassmembers class * { *** " << entry.first << "(...); }\n" << std::endl;
    }
    return true;
}

} // namespace proguard
} // namespace aapt
+58 −0
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_PROGUARD_RULES_H
#define AAPT_PROGUARD_RULES_H

#include "Resource.h"
#include "Source.h"
#include "XmlDom.h"

#include <map>
#include <ostream>
#include <set>
#include <string>

namespace aapt {
namespace proguard {

class KeepSet {
public:
    inline void addClass(const SourceLine& source, const std::u16string& className) {
        mKeepSet[className].insert(source);
    }

    inline void addMethod(const SourceLine& source, const std::u16string& methodName) {
        mKeepMethodSet[methodName].insert(source);
    }

private:
    friend bool writeKeepSet(std::ostream* out, const KeepSet& keepSet);

    std::map<std::u16string, std::set<SourceLine>> mKeepSet;
    std::map<std::u16string, std::set<SourceLine>> mKeepMethodSet;
};

bool collectProguardRulesForManifest(const Source& source, xml::Node* node, KeepSet* keepSet);
bool collectProguardRules(ResourceType type, const Source& source, xml::Node* node,
                          KeepSet* keepSet);

bool writeKeepSet(std::ostream* out, const KeepSet& keepSet);

} // namespace proguard
} // namespace aapt

#endif // AAPT_PROGUARD_RULES_H
+5 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@

#include <ostream>
#include <string>
#include <tuple>

namespace aapt {

@@ -80,6 +81,10 @@ inline ::std::ostream& operator<<(::std::ostream& out, const SourceLineColumn& s
    return out << source.path << ":" << source.line << ":" << source.column;
}

inline bool operator<(const SourceLine& lhs, const SourceLine& rhs) {
    return std::tie(lhs.path, lhs.line) < std::tie(rhs.path, rhs.line);
}

} // namespace aapt

#endif // AAPT_SOURCE_H
Loading