blob: 90616605b7aaf8609fb03a9f8003393ba68e7a68 [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 Lesinskid0f116b2016-07-08 15:00:32 -070046 std::string package = maybePackage.value().package + "." + 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:
Adam Lesinskid0f116b2016-07-08 15:00:32 -070061 void addClass(size_t lineNumber, const std::string& 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
Adam Lesinskid0f116b2016-07-08 15:00:32 -070065 void addMethod(size_t lineNumber, const std::string& 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()) {
Adam Lesinskid0f116b2016-07-08 15:00:32 -070082 checkClass = node->name == "view" || node->name == "fragment";
Adam Lesinski2ae4a872015-11-02 16:10:55 -080083 } else if (node->namespaceUri == xml::kSchemaAndroid) {
Adam Lesinskid0f116b2016-07-08 15:00:32 -070084 checkName = node->name == "fragment";
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070085 }
86
87 for (const auto& attr : node->attributes) {
Adam Lesinskid0f116b2016-07-08 15:00:32 -070088 if (checkClass && attr.namespaceUri.empty() && attr.name == "class" &&
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070089 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 &&
Adam Lesinskid0f116b2016-07-08 15:00:32 -070092 attr.name == "name" && util::isJavaClassName(attr.value)) {
Adam Lesinskia1ad4a82015-06-08 11:41:09 -070093 addClass(node->lineNumber, attr.value);
Adam Lesinskid0f116b2016-07-08 15:00:32 -070094 } else if (attr.namespaceUri == xml::kSchemaAndroid && attr.name == "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()) {
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700110 checkFragment = node->name == "PreferenceScreen" || node->name == "header";
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700111 }
112
113 if (checkFragment) {
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700114 xml::Attribute* attr = node->findAttribute(xml::kSchemaAndroid, "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() &&
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700130 (node->name == "transition" || node->name == "pathMotion");
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700131 if (checkClass) {
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700132 xml::Attribute* attr = node->findAttribute({}, "class");
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700133 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 {
Rohit Agrawale49bb302016-04-22 12:27:55 -0700143 ManifestVisitor(const Source& source, KeepSet* keepSet, bool mainDexOnly)
144 : BaseVisitor(source, keepSet), mMainDexOnly(mainDexOnly) {
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700145 }
146
147 virtual void visit(xml::Element* node) override {
148 if (node->namespaceUri.empty()) {
149 bool getName = false;
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700150 if (node->name == "manifest") {
151 xml::Attribute* attr = node->findAttribute({}, "package");
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700152 if (attr) {
153 mPackage = attr->value;
154 }
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700155 } else if (node->name == "application") {
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700156 getName = true;
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700157 xml::Attribute* attr = node->findAttribute(xml::kSchemaAndroid, "backupAgent");
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700158 if (attr) {
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700159 Maybe<std::string> result = util::getFullyQualifiedClassName(mPackage,
160 attr->value);
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700161 if (result) {
162 addClass(node->lineNumber, result.value());
163 }
164 }
Rohit Agrawale49bb302016-04-22 12:27:55 -0700165 if (mMainDexOnly) {
166 xml::Attribute* defaultProcess = node->findAttribute(xml::kSchemaAndroid,
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700167 "process");
Rohit Agrawale49bb302016-04-22 12:27:55 -0700168 if (defaultProcess) {
169 mDefaultProcess = defaultProcess->value;
170 }
171 }
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700172 } else if (node->name == "activity" || node->name == "service" ||
173 node->name == "receiver" || node->name == "provider" ||
174 node->name == "instrumentation") {
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700175 getName = true;
176 }
177
178 if (getName) {
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700179 xml::Attribute* attr = node->findAttribute(xml::kSchemaAndroid, "name");
Rohit Agrawale49bb302016-04-22 12:27:55 -0700180 getName = attr != nullptr;
181
182 if (getName && mMainDexOnly) {
183 xml::Attribute* componentProcess = node->findAttribute(xml::kSchemaAndroid,
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700184 "process");
Rohit Agrawale49bb302016-04-22 12:27:55 -0700185
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700186 const std::string& process = componentProcess ? componentProcess->value
187 : mDefaultProcess;
188 getName = !process.empty() && process[0] != ':';
Rohit Agrawale49bb302016-04-22 12:27:55 -0700189 }
190
191 if (getName) {
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700192 Maybe<std::string> result = util::getFullyQualifiedClassName(mPackage,
193 attr->value);
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700194 if (result) {
195 addClass(node->lineNumber, result.value());
196 }
197 }
198 }
199 }
200 BaseVisitor::visit(node);
201 }
202
Rohit Agrawale49bb302016-04-22 12:27:55 -0700203private:
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700204 std::string mPackage;
Rohit Agrawale49bb302016-04-22 12:27:55 -0700205 const bool mMainDexOnly;
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700206 std::string mDefaultProcess;
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700207};
208
Adam Lesinski467f1712015-11-16 17:35:44 -0800209bool collectProguardRulesForManifest(const Source& source, xml::XmlResource* res,
Rohit Agrawale49bb302016-04-22 12:27:55 -0700210 KeepSet* keepSet, bool mainDexOnly) {
211 ManifestVisitor visitor(source, keepSet, mainDexOnly);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700212 if (res->root) {
213 res->root->accept(&visitor);
214 return true;
215 }
216 return false;
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700217}
218
Adam Lesinski467f1712015-11-16 17:35:44 -0800219bool collectProguardRules(const Source& source, xml::XmlResource* res, KeepSet* keepSet) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700220 if (!res->root) {
221 return false;
222 }
223
224 switch (res->file.name.type) {
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700225 case ResourceType::kLayout: {
226 LayoutVisitor visitor(source, keepSet);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700227 res->root->accept(&visitor);
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700228 break;
229 }
230
231 case ResourceType::kXml: {
232 XmlResourceVisitor visitor(source, keepSet);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700233 res->root->accept(&visitor);
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700234 break;
235 }
236
237 case ResourceType::kTransition: {
238 TransitionVisitor visitor(source, keepSet);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700239 res->root->accept(&visitor);
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700240 break;
241 }
242
243 default:
244 break;
245 }
246 return true;
247}
248
249bool writeKeepSet(std::ostream* out, const KeepSet& keepSet) {
250 for (const auto& entry : keepSet.mKeepSet) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700251 for (const Source& source : entry.second) {
Adam Lesinski24b8ff02015-12-16 14:01:57 -0800252 *out << "# Referenced at " << source << "\n";
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700253 }
254 *out << "-keep class " << entry.first << " { <init>(...); }\n" << std::endl;
255 }
256
257 for (const auto& entry : keepSet.mKeepMethodSet) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700258 for (const Source& source : entry.second) {
Adam Lesinski24b8ff02015-12-16 14:01:57 -0800259 *out << "# Referenced at " << source << "\n";
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700260 }
261 *out << "-keepclassmembers class * { *** " << entry.first << "(...); }\n" << std::endl;
262 }
263 return true;
264}
265
266} // namespace proguard
267} // namespace aapt