blob: 902ec4cf3a0de08038d6d6b1a84d4e7f1ddd2416 [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" ||
Ivan Gavrilovicf580d912016-07-19 12:03:33 +0100173 node->name == "receiver" || node->name == "provider") {
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700174 getName = true;
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700175
Ivan Gavrilovicf580d912016-07-19 12:03:33 +0100176 if (mMainDexOnly) {
Rohit Agrawale49bb302016-04-22 12:27:55 -0700177 xml::Attribute* componentProcess = node->findAttribute(xml::kSchemaAndroid,
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700178 "process");
Rohit Agrawale49bb302016-04-22 12:27:55 -0700179
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700180 const std::string& process = componentProcess ? componentProcess->value
181 : mDefaultProcess;
182 getName = !process.empty() && process[0] != ':';
Rohit Agrawale49bb302016-04-22 12:27:55 -0700183 }
Ivan Gavrilovicf580d912016-07-19 12:03:33 +0100184 } else if (node-> name == "instrumentation") {
185 getName = true;
186 }
187
188 if (getName) {
189 xml::Attribute* attr = node->findAttribute(xml::kSchemaAndroid, "name");
190 getName = attr != nullptr;
Rohit Agrawale49bb302016-04-22 12:27:55 -0700191
192 if (getName) {
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700193 Maybe<std::string> result = util::getFullyQualifiedClassName(mPackage,
194 attr->value);
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700195 if (result) {
196 addClass(node->lineNumber, result.value());
197 }
198 }
199 }
200 }
201 BaseVisitor::visit(node);
202 }
203
Rohit Agrawale49bb302016-04-22 12:27:55 -0700204private:
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700205 std::string mPackage;
Rohit Agrawale49bb302016-04-22 12:27:55 -0700206 const bool mMainDexOnly;
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700207 std::string mDefaultProcess;
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700208};
209
Adam Lesinski467f1712015-11-16 17:35:44 -0800210bool collectProguardRulesForManifest(const Source& source, xml::XmlResource* res,
Rohit Agrawale49bb302016-04-22 12:27:55 -0700211 KeepSet* keepSet, bool mainDexOnly) {
212 ManifestVisitor visitor(source, keepSet, mainDexOnly);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700213 if (res->root) {
214 res->root->accept(&visitor);
215 return true;
216 }
217 return false;
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700218}
219
Adam Lesinski467f1712015-11-16 17:35:44 -0800220bool collectProguardRules(const Source& source, xml::XmlResource* res, KeepSet* keepSet) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700221 if (!res->root) {
222 return false;
223 }
224
225 switch (res->file.name.type) {
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700226 case ResourceType::kLayout: {
227 LayoutVisitor visitor(source, keepSet);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700228 res->root->accept(&visitor);
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700229 break;
230 }
231
232 case ResourceType::kXml: {
233 XmlResourceVisitor visitor(source, keepSet);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700234 res->root->accept(&visitor);
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700235 break;
236 }
237
238 case ResourceType::kTransition: {
239 TransitionVisitor visitor(source, keepSet);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700240 res->root->accept(&visitor);
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700241 break;
242 }
243
244 default:
245 break;
246 }
247 return true;
248}
249
250bool writeKeepSet(std::ostream* out, const KeepSet& keepSet) {
251 for (const auto& entry : keepSet.mKeepSet) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700252 for (const Source& source : entry.second) {
Adam Lesinski24b8ff02015-12-16 14:01:57 -0800253 *out << "# Referenced at " << source << "\n";
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700254 }
255 *out << "-keep class " << entry.first << " { <init>(...); }\n" << std::endl;
256 }
257
258 for (const auto& entry : keepSet.mKeepMethodSet) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700259 for (const Source& source : entry.second) {
Adam Lesinski24b8ff02015-12-16 14:01:57 -0800260 *out << "# Referenced at " << source << "\n";
Adam Lesinskia1ad4a82015-06-08 11:41:09 -0700261 }
262 *out << "-keepclassmembers class * { *** " << entry.first << "(...); }\n" << std::endl;
263 }
264 return true;
265}
266
267} // namespace proguard
268} // namespace aapt