ProguardRules.cpp revision ca5638fd85098c3d0a699492751043545f75553a
1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "XmlDom.h"
18
19#include "java/ProguardRules.h"
20#include "util/Util.h"
21
22#include <memory>
23#include <string>
24
25namespace aapt {
26namespace proguard {
27
28constexpr const char16_t* kSchemaAndroid = u"http://schemas.android.com/apk/res/android";
29
30class BaseVisitor : public xml::Visitor {
31public:
32    BaseVisitor(const Source& source, KeepSet* keepSet) : mSource(source), mKeepSet(keepSet) {
33    }
34
35    virtual void visit(xml::Text*) override {};
36
37    virtual void visit(xml::Namespace* node) override {
38        for (const auto& child : node->children) {
39            child->accept(this);
40        }
41    }
42
43    virtual void visit(xml::Element* node) override {
44        if (!node->namespaceUri.empty()) {
45            Maybe<std::u16string> maybePackage = util::extractPackageFromNamespace(
46                    node->namespaceUri);
47            if (maybePackage) {
48                // This is a custom view, let's figure out the class name from this.
49                std::u16string package = maybePackage.value() + u"." + node->name;
50                if (util::isJavaClassName(package)) {
51                    addClass(node->lineNumber, package);
52                }
53            }
54        } else if (util::isJavaClassName(node->name)) {
55            addClass(node->lineNumber, node->name);
56        }
57
58        for (const auto& child: node->children) {
59            child->accept(this);
60        }
61    }
62
63protected:
64    void addClass(size_t lineNumber, const std::u16string& className) {
65        mKeepSet->addClass(Source(mSource.path, lineNumber), className);
66    }
67
68    void addMethod(size_t lineNumber, const std::u16string& methodName) {
69        mKeepSet->addMethod(Source(mSource.path, lineNumber), methodName);
70    }
71
72private:
73    Source mSource;
74    KeepSet* mKeepSet;
75};
76
77struct LayoutVisitor : public BaseVisitor {
78    LayoutVisitor(const Source& source, KeepSet* keepSet) : BaseVisitor(source, keepSet) {
79    }
80
81    virtual void visit(xml::Element* node) override {
82        bool checkClass = false;
83        bool checkName = false;
84        if (node->namespaceUri.empty()) {
85            checkClass = node->name == u"view" || node->name == u"fragment";
86        } else if (node->namespaceUri == kSchemaAndroid) {
87            checkName = node->name == u"fragment";
88        }
89
90        for (const auto& attr : node->attributes) {
91            if (checkClass && attr.namespaceUri.empty() && attr.name == u"class" &&
92                    util::isJavaClassName(attr.value)) {
93                addClass(node->lineNumber, attr.value);
94            } else if (checkName && attr.namespaceUri == kSchemaAndroid && attr.name == u"name" &&
95                    util::isJavaClassName(attr.value)) {
96                addClass(node->lineNumber, attr.value);
97            } else if (attr.namespaceUri == kSchemaAndroid && attr.name == u"onClick") {
98                addMethod(node->lineNumber, attr.value);
99            }
100        }
101
102        BaseVisitor::visit(node);
103    }
104};
105
106struct XmlResourceVisitor : public BaseVisitor {
107    XmlResourceVisitor(const Source& source, KeepSet* keepSet) : BaseVisitor(source, keepSet) {
108    }
109
110    virtual void visit(xml::Element* node) override {
111        bool checkFragment = false;
112        if (node->namespaceUri.empty()) {
113            checkFragment = node->name == u"PreferenceScreen" || node->name == u"header";
114        }
115
116        if (checkFragment) {
117            xml::Attribute* attr = node->findAttribute(kSchemaAndroid, u"fragment");
118            if (attr && util::isJavaClassName(attr->value)) {
119                addClass(node->lineNumber, attr->value);
120            }
121        }
122
123        BaseVisitor::visit(node);
124    }
125};
126
127struct TransitionVisitor : public BaseVisitor {
128    TransitionVisitor(const Source& source, KeepSet* keepSet) : BaseVisitor(source, keepSet) {
129    }
130
131    virtual void visit(xml::Element* node) override {
132        bool checkClass = node->namespaceUri.empty() &&
133                (node->name == u"transition" || node->name == u"pathMotion");
134        if (checkClass) {
135            xml::Attribute* attr = node->findAttribute({}, u"class");
136            if (attr && util::isJavaClassName(attr->value)) {
137                addClass(node->lineNumber, attr->value);
138            }
139        }
140
141        BaseVisitor::visit(node);
142    }
143};
144
145struct ManifestVisitor : public BaseVisitor {
146    ManifestVisitor(const Source& source, KeepSet* keepSet) : BaseVisitor(source, keepSet) {
147    }
148
149    virtual void visit(xml::Element* node) override {
150        if (node->namespaceUri.empty()) {
151            bool getName = false;
152            if (node->name == u"manifest") {
153                xml::Attribute* attr = node->findAttribute({}, u"package");
154                if (attr) {
155                    mPackage = attr->value;
156                }
157            } else if (node->name == u"application") {
158                getName = true;
159                xml::Attribute* attr = node->findAttribute(kSchemaAndroid, u"backupAgent");
160                if (attr) {
161                    Maybe<std::u16string> result = util::getFullyQualifiedClassName(mPackage,
162                                                                                    attr->value);
163                    if (result) {
164                        addClass(node->lineNumber, result.value());
165                    }
166                }
167            } else if (node->name == u"activity" || node->name == u"service" ||
168                    node->name == u"receiver" || node->name == u"provider" ||
169                    node->name == u"instrumentation") {
170                getName = true;
171            }
172
173            if (getName) {
174                xml::Attribute* attr = node->findAttribute(kSchemaAndroid, u"name");
175                if (attr) {
176                    Maybe<std::u16string> result = util::getFullyQualifiedClassName(mPackage,
177                                                                                    attr->value);
178                    if (result) {
179                        addClass(node->lineNumber, result.value());
180                    }
181                }
182            }
183        }
184        BaseVisitor::visit(node);
185    }
186
187    std::u16string mPackage;
188};
189
190bool collectProguardRulesForManifest(const Source& source, XmlResource* res, KeepSet* keepSet) {
191    ManifestVisitor visitor(source, keepSet);
192    if (res->root) {
193        res->root->accept(&visitor);
194        return true;
195    }
196    return false;
197}
198
199bool collectProguardRules(const Source& source, XmlResource* res, KeepSet* keepSet) {
200    if (!res->root) {
201        return false;
202    }
203
204    switch (res->file.name.type) {
205        case ResourceType::kLayout: {
206            LayoutVisitor visitor(source, keepSet);
207            res->root->accept(&visitor);
208            break;
209        }
210
211        case ResourceType::kXml: {
212            XmlResourceVisitor visitor(source, keepSet);
213            res->root->accept(&visitor);
214            break;
215        }
216
217        case ResourceType::kTransition: {
218            TransitionVisitor visitor(source, keepSet);
219            res->root->accept(&visitor);
220            break;
221        }
222
223        default:
224            break;
225    }
226    return true;
227}
228
229bool writeKeepSet(std::ostream* out, const KeepSet& keepSet) {
230    for (const auto& entry : keepSet.mKeepSet) {
231        for (const Source& source : entry.second) {
232            *out << "// Referenced at " << source << "\n";
233        }
234        *out << "-keep class " << entry.first << " { <init>(...); }\n" << std::endl;
235    }
236
237    for (const auto& entry : keepSet.mKeepMethodSet) {
238        for (const Source& source : entry.second) {
239            *out << "// Referenced at " << source << "\n";
240        }
241        *out << "-keepclassmembers class * { *** " << entry.first << "(...); }\n" << std::endl;
242    }
243    return true;
244}
245
246} // namespace proguard
247} // namespace aapt
248