blob: c610bb0f2ff25dc74b8a53db0e7158557dac7868 [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 Lesinskica5638f2015-10-21 14:42:43 -070017#include "java/ProguardRules.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070018#include "util/Util.h"
Adam Lesinski467f1712015-11-16 17:35:44 -080019#include "xml/XmlDom.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070020
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070021#include <memory>
22#include <string>
23
24namespace aapt {
25namespace proguard {
26
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070027class BaseVisitor : public xml::Visitor {
28public:
29 BaseVisitor(const Source& source, KeepSet* keepSet) : mSource(source), mKeepSet(keepSet) {
30 }
31
32 virtual void visit(xml::Text*) override {};
33
34 virtual void visit(xml::Namespace* node) override {
35 for (const auto& child : node->children) {
36 child->accept(this);
37 }
38 }
39
40 virtual void visit(xml::Element* node) override {
41 if (!node->namespaceUri.empty()) {
Adam Lesinski467f1712015-11-16 17:35:44 -080042 Maybe<xml::ExtractedPackage> maybePackage = xml::extractPackageFromNamespace(
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070043 node->namespaceUri);
44 if (maybePackage) {
45 // This is a custom view, let's figure out the class name from this.
Adam Lesinski467f1712015-11-16 17:35:44 -080046 std::u16string package = maybePackage.value().package + u"." + node->name;
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070047 if (util::isJavaClassName(package)) {
48 addClass(node->lineNumber, package);
49 }
50 }
51 } else if (util::isJavaClassName(node->name)) {
52 addClass(node->lineNumber, node->name);
53 }
54
55 for (const auto& child: node->children) {
56 child->accept(this);
57 }
58 }
59
60protected:
61 void addClass(size_t lineNumber, const std::u16string& className) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -070062 mKeepSet->addClass(Source(mSource.path, lineNumber), className);
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070063 }
64
65 void addMethod(size_t lineNumber, const std::u16string& methodName) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -070066 mKeepSet->addMethod(Source(mSource.path, lineNumber), methodName);
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070067 }
68
69private:
70 Source mSource;
71 KeepSet* mKeepSet;
72};
73
74struct LayoutVisitor : public BaseVisitor {
75 LayoutVisitor(const Source& source, KeepSet* keepSet) : BaseVisitor(source, keepSet) {
76 }
77
78 virtual void visit(xml::Element* node) override {
79 bool checkClass = false;
80 bool checkName = false;
81 if (node->namespaceUri.empty()) {
82 checkClass = node->name == u"view" || node->name == u"fragment";
Adam Lesinski2ae4a872015-11-02 16:10:55 -080083 } else if (node->namespaceUri == xml::kSchemaAndroid) {
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070084 checkName = node->name == u"fragment";
85 }
86
87 for (const auto& attr : node->attributes) {
88 if (checkClass && attr.namespaceUri.empty() && attr.name == u"class" &&
89 util::isJavaClassName(attr.value)) {
90 addClass(node->lineNumber, attr.value);
Adam Lesinski2ae4a872015-11-02 16:10:55 -080091 } else if (checkName && attr.namespaceUri == xml::kSchemaAndroid &&
92 attr.name == u"name" && util::isJavaClassName(attr.value)) {
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070093 addClass(node->lineNumber, attr.value);
Adam Lesinski2ae4a872015-11-02 16:10:55 -080094 } else if (attr.namespaceUri == xml::kSchemaAndroid && attr.name == u"onClick") {
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070095 addMethod(node->lineNumber, attr.value);
96 }
97 }
98
99 BaseVisitor::visit(node);
100 }
101};
102
103struct XmlResourceVisitor : public BaseVisitor {
104 XmlResourceVisitor(const Source& source, KeepSet* keepSet) : BaseVisitor(source, keepSet) {
105 }
106
107 virtual void visit(xml::Element* node) override {
108 bool checkFragment = false;
109 if (node->namespaceUri.empty()) {
110 checkFragment = node->name == u"PreferenceScreen" || node->name == u"header";
111 }
112
113 if (checkFragment) {
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800114 xml::Attribute* attr = node->findAttribute(xml::kSchemaAndroid, u"fragment");
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700115 if (attr && util::isJavaClassName(attr->value)) {
116 addClass(node->lineNumber, attr->value);
117 }
118 }
119
120 BaseVisitor::visit(node);
121 }
122};
123
124struct TransitionVisitor : public BaseVisitor {
125 TransitionVisitor(const Source& source, KeepSet* keepSet) : BaseVisitor(source, keepSet) {
126 }
127
128 virtual void visit(xml::Element* node) override {
129 bool checkClass = node->namespaceUri.empty() &&
130 (node->name == u"transition" || node->name == u"pathMotion");
131 if (checkClass) {
132 xml::Attribute* attr = node->findAttribute({}, u"class");
133 if (attr && util::isJavaClassName(attr->value)) {
134 addClass(node->lineNumber, attr->value);
135 }
136 }
137
138 BaseVisitor::visit(node);
139 }
140};
141
142struct ManifestVisitor : public BaseVisitor {
143 ManifestVisitor(const Source& source, KeepSet* keepSet) : BaseVisitor(source, keepSet) {
144 }
145
146 virtual void visit(xml::Element* node) override {
147 if (node->namespaceUri.empty()) {
148 bool getName = false;
149 if (node->name == u"manifest") {
150 xml::Attribute* attr = node->findAttribute({}, u"package");
151 if (attr) {
152 mPackage = attr->value;
153 }
154 } else if (node->name == u"application") {
155 getName = true;
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800156 xml::Attribute* attr = node->findAttribute(xml::kSchemaAndroid, u"backupAgent");
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700157 if (attr) {
158 Maybe<std::u16string> result = util::getFullyQualifiedClassName(mPackage,
159 attr->value);
160 if (result) {
161 addClass(node->lineNumber, result.value());
162 }
163 }
164 } else if (node->name == u"activity" || node->name == u"service" ||
165 node->name == u"receiver" || node->name == u"provider" ||
166 node->name == u"instrumentation") {
167 getName = true;
168 }
169
170 if (getName) {
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800171 xml::Attribute* attr = node->findAttribute(xml::kSchemaAndroid, u"name");
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700172 if (attr) {
173 Maybe<std::u16string> result = util::getFullyQualifiedClassName(mPackage,
174 attr->value);
175 if (result) {
176 addClass(node->lineNumber, result.value());
177 }
178 }
179 }
180 }
181 BaseVisitor::visit(node);
182 }
183
184 std::u16string mPackage;
185};
186
Adam Lesinski467f1712015-11-16 17:35:44 -0800187bool collectProguardRulesForManifest(const Source& source, xml::XmlResource* res,
188 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 Lesinski467f1712015-11-16 17:35:44 -0800197bool collectProguardRules(const Source& source, xml::XmlResource* res, KeepSet* keepSet) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700198 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 Lesinski24b8ff02015-12-16 14:01:57 -0800230 *out << "# Referenced at " << source << "\n";
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700231 }
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 Lesinski24b8ff02015-12-16 14:01:57 -0800237 *out << "# Referenced at " << source << "\n";
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700238 }
239 *out << "-keepclassmembers class * { *** " << entry.first << "(...); }\n" << std::endl;
240 }
241 return true;
242}
243
244} // namespace proguard
245} // namespace aapt