blob: 44314772fbd474fa2f61bee23e37b2d9ab3319bf [file] [log] [blame]
Adam Lesinskia1ad4a82015-06-08 11:41:09 -07001/*
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
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070017#include "XmlDom.h"
18
Adam Lesinskica5638f2015-10-21 14:42:43 -070019#include "java/ProguardRules.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070020#include "util/Util.h"
21
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070022#include <memory>
23#include <string>
24
25namespace aapt {
26namespace proguard {
27
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070028class BaseVisitor : public xml::Visitor {
29public:
30 BaseVisitor(const Source& source, KeepSet* keepSet) : mSource(source), mKeepSet(keepSet) {
31 }
32
33 virtual void visit(xml::Text*) override {};
34
35 virtual void visit(xml::Namespace* node) override {
36 for (const auto& child : node->children) {
37 child->accept(this);
38 }
39 }
40
41 virtual void visit(xml::Element* node) override {
42 if (!node->namespaceUri.empty()) {
43 Maybe<std::u16string> maybePackage = util::extractPackageFromNamespace(
44 node->namespaceUri);
45 if (maybePackage) {
46 // This is a custom view, let's figure out the class name from this.
47 std::u16string package = maybePackage.value() + u"." + node->name;
48 if (util::isJavaClassName(package)) {
49 addClass(node->lineNumber, package);
50 }
51 }
52 } else if (util::isJavaClassName(node->name)) {
53 addClass(node->lineNumber, node->name);
54 }
55
56 for (const auto& child: node->children) {
57 child->accept(this);
58 }
59 }
60
61protected:
62 void addClass(size_t lineNumber, const std::u16string& className) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -070063 mKeepSet->addClass(Source(mSource.path, lineNumber), className);
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070064 }
65
66 void addMethod(size_t lineNumber, const std::u16string& methodName) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -070067 mKeepSet->addMethod(Source(mSource.path, lineNumber), methodName);
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070068 }
69
70private:
71 Source mSource;
72 KeepSet* mKeepSet;
73};
74
75struct LayoutVisitor : public BaseVisitor {
76 LayoutVisitor(const Source& source, KeepSet* keepSet) : BaseVisitor(source, keepSet) {
77 }
78
79 virtual void visit(xml::Element* node) override {
80 bool checkClass = false;
81 bool checkName = false;
82 if (node->namespaceUri.empty()) {
83 checkClass = node->name == u"view" || node->name == u"fragment";
Adam Lesinski2ae4a872015-11-02 16:10:55 -080084 } else if (node->namespaceUri == xml::kSchemaAndroid) {
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070085 checkName = node->name == u"fragment";
86 }
87
88 for (const auto& attr : node->attributes) {
89 if (checkClass && attr.namespaceUri.empty() && attr.name == u"class" &&
90 util::isJavaClassName(attr.value)) {
91 addClass(node->lineNumber, attr.value);
Adam Lesinski2ae4a872015-11-02 16:10:55 -080092 } else if (checkName && attr.namespaceUri == xml::kSchemaAndroid &&
93 attr.name == u"name" && util::isJavaClassName(attr.value)) {
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070094 addClass(node->lineNumber, attr.value);
Adam Lesinski2ae4a872015-11-02 16:10:55 -080095 } else if (attr.namespaceUri == xml::kSchemaAndroid && attr.name == u"onClick") {
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070096 addMethod(node->lineNumber, attr.value);
97 }
98 }
99
100 BaseVisitor::visit(node);
101 }
102};
103
104struct XmlResourceVisitor : public BaseVisitor {
105 XmlResourceVisitor(const Source& source, KeepSet* keepSet) : BaseVisitor(source, keepSet) {
106 }
107
108 virtual void visit(xml::Element* node) override {
109 bool checkFragment = false;
110 if (node->namespaceUri.empty()) {
111 checkFragment = node->name == u"PreferenceScreen" || node->name == u"header";
112 }
113
114 if (checkFragment) {
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800115 xml::Attribute* attr = node->findAttribute(xml::kSchemaAndroid, u"fragment");
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700116 if (attr && util::isJavaClassName(attr->value)) {
117 addClass(node->lineNumber, attr->value);
118 }
119 }
120
121 BaseVisitor::visit(node);
122 }
123};
124
125struct TransitionVisitor : public BaseVisitor {
126 TransitionVisitor(const Source& source, KeepSet* keepSet) : BaseVisitor(source, keepSet) {
127 }
128
129 virtual void visit(xml::Element* node) override {
130 bool checkClass = node->namespaceUri.empty() &&
131 (node->name == u"transition" || node->name == u"pathMotion");
132 if (checkClass) {
133 xml::Attribute* attr = node->findAttribute({}, u"class");
134 if (attr && util::isJavaClassName(attr->value)) {
135 addClass(node->lineNumber, attr->value);
136 }
137 }
138
139 BaseVisitor::visit(node);
140 }
141};
142
143struct ManifestVisitor : public BaseVisitor {
144 ManifestVisitor(const Source& source, KeepSet* keepSet) : BaseVisitor(source, keepSet) {
145 }
146
147 virtual void visit(xml::Element* node) override {
148 if (node->namespaceUri.empty()) {
149 bool getName = false;
150 if (node->name == u"manifest") {
151 xml::Attribute* attr = node->findAttribute({}, u"package");
152 if (attr) {
153 mPackage = attr->value;
154 }
155 } else if (node->name == u"application") {
156 getName = true;
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800157 xml::Attribute* attr = node->findAttribute(xml::kSchemaAndroid, u"backupAgent");
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700158 if (attr) {
159 Maybe<std::u16string> result = util::getFullyQualifiedClassName(mPackage,
160 attr->value);
161 if (result) {
162 addClass(node->lineNumber, result.value());
163 }
164 }
165 } else if (node->name == u"activity" || node->name == u"service" ||
166 node->name == u"receiver" || node->name == u"provider" ||
167 node->name == u"instrumentation") {
168 getName = true;
169 }
170
171 if (getName) {
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800172 xml::Attribute* attr = node->findAttribute(xml::kSchemaAndroid, u"name");
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700173 if (attr) {
174 Maybe<std::u16string> result = util::getFullyQualifiedClassName(mPackage,
175 attr->value);
176 if (result) {
177 addClass(node->lineNumber, result.value());
178 }
179 }
180 }
181 }
182 BaseVisitor::visit(node);
183 }
184
185 std::u16string mPackage;
186};
187
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700188bool collectProguardRulesForManifest(const Source& source, XmlResource* res, KeepSet* keepSet) {
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700189 ManifestVisitor visitor(source, keepSet);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700190 if (res->root) {
191 res->root->accept(&visitor);
192 return true;
193 }
194 return false;
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700195}
196
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700197bool collectProguardRules(const Source& source, XmlResource* res, KeepSet* keepSet) {
198 if (!res->root) {
199 return false;
200 }
201
202 switch (res->file.name.type) {
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700203 case ResourceType::kLayout: {
204 LayoutVisitor visitor(source, keepSet);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700205 res->root->accept(&visitor);
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700206 break;
207 }
208
209 case ResourceType::kXml: {
210 XmlResourceVisitor visitor(source, keepSet);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700211 res->root->accept(&visitor);
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700212 break;
213 }
214
215 case ResourceType::kTransition: {
216 TransitionVisitor visitor(source, keepSet);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700217 res->root->accept(&visitor);
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700218 break;
219 }
220
221 default:
222 break;
223 }
224 return true;
225}
226
227bool writeKeepSet(std::ostream* out, const KeepSet& keepSet) {
228 for (const auto& entry : keepSet.mKeepSet) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700229 for (const Source& source : entry.second) {
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700230 *out << "// Referenced at " << source << "\n";
231 }
232 *out << "-keep class " << entry.first << " { <init>(...); }\n" << std::endl;
233 }
234
235 for (const auto& entry : keepSet.mKeepMethodSet) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700236 for (const Source& source : entry.second) {
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700237 *out << "// Referenced at " << source << "\n";
238 }
239 *out << "-keepclassmembers class * { *** " << entry.first << "(...); }\n" << std::endl;
240 }
241 return true;
242}
243
244} // namespace proguard
245} // namespace aapt