blob: 7f4dc91eae9f7705ea1b395f7c4d64c7dd1a5779 [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
17#include "ProguardRules.h"
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070018#include "XmlDom.h"
19
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
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) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -070065 mKeepSet->addClass(Source(mSource.path, lineNumber), className);
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070066 }
67
68 void addMethod(size_t lineNumber, const std::u16string& methodName) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -070069 mKeepSet->addMethod(Source(mSource.path, lineNumber), methodName);
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070070 }
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
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700190bool collectProguardRulesForManifest(const Source& source, XmlResource* res, KeepSet* keepSet) {
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700191 ManifestVisitor visitor(source, keepSet);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700192 if (res->root) {
193 res->root->accept(&visitor);
194 return true;
195 }
196 return false;
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700197}
198
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700199bool collectProguardRules(const Source& source, XmlResource* res, KeepSet* keepSet) {
200 if (!res->root) {
201 return false;
202 }
203
204 switch (res->file.name.type) {
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700205 case ResourceType::kLayout: {
206 LayoutVisitor visitor(source, keepSet);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700207 res->root->accept(&visitor);
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700208 break;
209 }
210
211 case ResourceType::kXml: {
212 XmlResourceVisitor visitor(source, keepSet);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700213 res->root->accept(&visitor);
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700214 break;
215 }
216
217 case ResourceType::kTransition: {
218 TransitionVisitor visitor(source, keepSet);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700219 res->root->accept(&visitor);
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700220 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) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700231 for (const Source& source : entry.second) {
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700232 *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) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700238 for (const Source& source : entry.second) {
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700239 *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